这两年AI编程工具的发展速度,说实话超出了我入行时的想象。从最早用Copilot自动补全几行代码,到后来用ChatGPT帮我查报错、写正则、生成单元测试,再到现在直接用Codex和Claude这类AI Agent承担不少端到端的开发任务,我的开发方式已经被彻底改写了。
但接触的开发者越多,我越发现一个普遍误区:很多人以为“用AI写代码”就是把需求丢给AI,然后复制粘贴。这么干的人,通常会在项目跑到一半时被各种隐蔽bug、架构混乱、安全漏洞折磨到怀疑人生。真正用AI打造高品质Web应用,不是让AI替你写代码,而是把它当成一个能力极强但偶尔会“胡说八道”的结对编程搭档,你需要负责架构设计、需求拆解、代码审查、质量把控和安全合规,这些才是高品质的保障。
这篇文章我会从工具链选型、提示词工程、前后端实操、测试调优、部署上线、问题排查几个维度,完整复盘我用AI从零搭建一个内部工具型Web应用的全过程。适合正在用或准备用AI辅助开发的前后端工程师、全栈开发者,也适合想引入AI编程流程的技术负责人参考。
1. AI辅助Web应用开发的整体思路与工具链选型
1.1 为什么Web应用最适合AI辅助开发
先回答一个很多人问过我的问题:AI编程适合干什么类型的项目?我的答案是,Web应用是当前AI辅助开发性价比最高的领域,没有之一。
原因在于Web应用的开发模式非常结构化。前端页面本质上是组件树的组合,后端接口本质上是“接收请求→处理数据→返回响应”的固定范式,数据库操作本质上是增删改查加索引优化。这些内容在开源社区和训练语料里存量极大,AI模型见得足够多,生成的代码质量自然就高。
我对比过用AI辅助写一个Web后台和写一个嵌入式驱动,后者的痛苦程度高一个数量级。因为嵌入式开发涉及大量芯片寄存器操作、硬件时序、平台差异,模型没见过那么多真实案例。而Web应用你随便让AI写一个分页查询接口,它大概率能写得像模像样。这不是玄学,是训练数据分布决定的。
另外,Web应用有非常短的反馈闭环。写完前端马上能在浏览器里看效果,写完接口马上能用curl调试,写完业务逻辑马上能跑单元测试。AI生成代码有错误,你很快就能发现,这种快速纠错能力让AI“边写边改”的协作模式可以真正跑起来。
1.2 主流AI编程工具怎么选
现在市面上的AI编程工具层出不穷,我按自己的实际使用体验把它们分成三类。
第一类是IDE内嵌的代码补全工具,代表是GitHub Copilot。它的强项是“你写一半它帮你续写”,适合处理样板代码、重复性代码、单元测试这类任务。它的响应速度极快,几乎不打断编码心流,但对于“从零生成一个完整功能模块”这种大任务,能力有限。
第二类是对话式AI编程助手,代表是ChatGPT、Claude这类通用大模型产品。它们的强项是理解模糊需求、生成完整方案、解释陌生代码。我经常在两种场景下用它们,一是写Prompt让它直接输出一个完整的页面或接口代码,二是把一段晦涩代码贴给它问“这段逻辑在干什么”。缺点是生成的代码和你的既有工程结构经常不匹配,需要大量修改。
第三类是Agent型AI编码工具,代表是OpenAI Codex、Claude Code、Cursor的Agent模式、通义灵码这类国内产品。它们的特点是能自己读你仓库里的代码、自己改多个文件、自己执行命令、自己根据报错信息迭代修复。这类工具最接近“AI同事”的体验,但也最容易翻车——它改文件的速度太快,你稍不留神整个项目就被改成你不认识的样子了。
我自己实际使用的组合是:日常编码用Copilot续写,涉及复杂功能设计用对话式AI做方案预演,重构或跨文件修改用Agent型工具。没必要迷信某一个工具,关键是根据任务类型灵活切换。
| 工具类型 | 代表产品 | 强项 | 弱项 | 适合场景 |
|---|---|---|---|---|
| 代码补全 | GitHub Copilot、Codeium | 行级/函数级补全,速度快 | 对全局架构理解有限 | 日常增删改查、写测试 |
| 对话式助手 | ChatGPT、Claude | 理解模糊需求,生成完整代码 | 需要手动粘贴集成 | 方案设计、代码解释、批量生成 |
| Agent型 | Codex、Claude Code、Cursor | 自主读库、改多文件、执行命令 | 改动范围大,需要严格审核 | 功能开发、重构、跨文件协作 |
1.3 我的AI驱动开发工作流
使用AI一年半之后,我总结出一套比较稳定的工作流,现在基本每个项目都按这个流程走。
第一步是需求拆解。把产品需求拆成可以独立交付的模块清单,每个模块明确输入、输出、技术约束。比如“用户登录”这个需求,拆成前端登录页、后端登录接口、Token生成与校验、登录状态持久化四个子任务。
第二步是方案预演。先不急着写代码,把每个子任务的需求描述、技术栈、边界条件整理成提示词,让AI给出实现方案。我会重点看它选择的方案是否贴合现有架构,比如项目已经用了Django5,AI却建议换Flask,这种答案直接打回重写。
第三步是编码实现。根据方案让AI生成代码,或者让Agent型工具直接在仓库里落地。这个阶段我对代码质量的要求是能跑通基本流程,不追求一次成型,因为AI生成的初版代码一定有不合理的地方。
第四步是审查重构。把自己的角色切换成严格的代码审查者,逐行检查AI生成的代码,重点看安全漏洞、性能隐患、异常处理、命名规范。发现问题后要么手工修改,要么带着具体问题让AI重新生成。
第五步是集成联调。将AI生成的模块集成到主工程中,跑通全链路测试。这一步是问题高发区,跨模块字段不匹配、时间格式混乱、依赖冲突都是常客。
2. 核心细节解析:把需求“翻译”成AI能听懂的语言
2.1 提示词四要素:目标、技术栈、约束、验收标准
很多开发者抱怨AI生成的代码“又臭又长”“偏要用冷门库”,我看了他们的Prompt之后基本都能发现问题:需求描述太空泛了。比如“帮我写一个用户管理页面”,这种提示词AI真的不知道该怎么做,它只能基于自己的“平均理解”生成一个通用模块,当然不满足你的需求。
我写提示词有一套固定的四要素框架。
第一是目标,用一两句话描述这个功能要解决什么问题。不要写“写一个用户列表”,要写“做一个管理员后台的用户列表页,支持按姓名模糊搜索、按创建时间倒序排列、分页显示、单条禁用和启用”。目标描述越具体,AI越知道往哪个方向使劲。
第二是技术栈,明确告诉AI用什么框架、什么语言、什么UI库、什么ORM。我一般写成“使用Vue3 + TypeScript + Element Plus,接口请求使用axios,状态管理使用Pinia”。别让AI自己选,否则它可能会用你没装过的库,或者用和你现有代码风格完全不搭的写法。
第三是约束条件,包括项目里已有的规范、必须兼容的浏览器版本、性能要求、安全要求。比如“所有接口请求必须带token,统一在axios拦截器中处理”,这句话能避免AI生成一堆直接调接口的裸代码。
第四是验收标准,告诉AI怎么才算写完。比如“生成的代码必须能通过TypeScript类型检查”“接口需要处理用户名重复的情况并返回400错误”。验收标准其实是变相要求AI做边界处理的提示词,非常有效。
我举个例子。现在我要让AI生成一个“按部门筛选员工列表的后端接口”,最开始我可能只会写“写一个员工筛选接口”,后来改进为:
使用FastAPI编写一个GET /api/v1/employees接口,接收department_id(必填)、page(默认1)、page_size(默认10)参数。按department_id精确过滤员工,结果按入职时间倒序排列,使用SQLAlchemy 2.0的select语法查询,返回格式为{ "items": [...], "total": 100, "page": 1, "page_size": 10 }。当department_id不存在时返回空列表而不是报错。需要处理page小于1的情况,自动修正为1。
同样的功能,两个提示词生成出来的代码质量天差地别。前者生成的是一个让你改了又改的半成品,后者基本可以直接用。
2.2 任务拆解:大任务降维成小任务
这里必须先说一个我踩过的坑。早期我用AI开发,尝试过一次性把整个Web应用的描述丢给AI,让它“全部生成出来”。结果它确实生成了,但前端代码引用了一个根本不存在的后端接口,后端代码里硬编码了一堆假数据,数据库模型之间缺少外键关联,整个项目能看不能跑。
后来我意识到,AI编程的核心是任务颗粒度。AI适合处理的是一个“模块”级别的任务,不是一个“系统”级别的任务。系统设计这部分必须由你自己完成,AI没有你的业务认知,也没有你对技术栈的把控能力。
我现在拆任务的原则是“一个任务对应一个页面或一个资源”。“一个用户管理页面”是一个任务,“一个订单列表”是一个任务,“一个登录流程”是一个任务。每一个任务我都要求AI在已有工程结构内完成,而不是让它新建一个独立项目。
拿我上个月做的会议纪要归档系统举例,整个系统的需求拆成了八个任务。
| 序号 | 任务 | 技术要点 | 依赖 |
|---|---|---|---|
| 1 | 初始化前后端工程 | 前端Vite + Vue3,后端FastAPI | 无 |
| 2 | 用户登录与Token鉴权 | JWT生成与校验、登录态持久化 | 1 |
| 3 | 会议纪要列表页 | 分页、关键字搜索、状态筛选 | 2 |
| 4 | 会议纪要详情页 | Markdown渲染、附件下载、操作记录 | 2 |
| 5 | 会议纪要上传与解析 | 文件上传、前端docx解析、后端存储 | 2 |
| 6 | 纪要全文搜索接口 | MySQL全文索引或ES | 2 |
| 7 | 标签管理体系 | 批量打标签、按标签筛选 | 2 |
| 8 | 操作日志与审计 | 记录操作人、操作时间、操作内容 | 2 |
每个任务我单独开一轮AI对话,单独写提示词,单独验证。这样做的好处是,一旦某个任务的生成结果有问题,影响范围可控,不会牵连其他模块。另外一个好处是方便复用——下次做类似系统时,已经验证过的提示词可以直接拿来改一改再用。
2.3 上下文管理:有效喂给AI的信息比提示词更关键
还有一个常见的坑是“AI失忆”。你早上让它生成了用户模型,下午让它生成订单功能时提到“关联用户表”,它已经完全不记得上午的对话了。解决办法是把上下文管理当成一个正式工作来做。
具体操作上,我会在与AI对话时附上一个“项目上下文块”,内容包括当前工程的目录结构、数据库表结构、已有的API风格约定、常用的工具函数。这个上下文块看起来不起眼,但效果立竿见影。
举个实际例子,我在让AI生成会议纪要详情页时,附带的上下文是这样的:
当前项目为前端Vite + Vue3 + TypeScript + Element Plus,后端FastAPI + SQLAlchemy 2.0 + MySQL。 已有API约定:所有接口统一返回{ "code": 0, "data": ..., "message": "success" }。code为0表示成功,非0表示失败。 已有数据库表:meetings(id, title, content_md, creator_id, created_at, updated_at, status)。
如果我把这些信息写在提示词里,AI生成的代码会和现有工程无缝衔接,很多字段名、返回结构、错误处理都不用我改了。如果没有这些上下文,AI就会自己发明一套返回格式,前端和后端联调时要改一堆地方。
我也推荐把上下文块保存成一个模板文件,每次需要AI干活的时候先粘贴这个模板,再写具体任务描述。这个过程虽然麻烦,但从长远看省下的修改时间远远大于这点麻烦。
3. 实操过程:用AI完成一个内部工具的完整开发流程
3.1 准备阶段:初始化工程和约定规范
我这次带领大家走一遍完整的实操流程,就拿上面的会议纪要归档系统当例子。先说明一下,为了便于大家复现,这里选用FastAPI后端加Vue3前端,这种技术栈在Python和前端工程师群体里都非常通用。
前端部分,我直接用Vite创建项目。传统上有两条路,用npm create vite@latest生成项目,或者把项目目录结构丢给AI让它自己写。我更推荐前者,因为Vite的官方模板已经处理好了TypeScript配置、热更新等一堆基础问题,没必要让AI重新发明轮子。
后端部分同理,我手动创建虚拟环境、安装依赖、初始化FastAPI应用。初始化完成后的目录结构是固定的,前端src下按views、components、api、router、store分层,后端app下按routers、models、schemas、services分层。
初始化的过程中建议积累一份项目约定说明,在后面会反复用到。我通常会在项目根目录创建一个说明文档,记录接口返回格式、错误码约定、命名规范等。这些内容既是给团队看的,也是给AI看的——每次让它生成代码时,把这份约定的关键部分复制进提示词,出来的代码基本不会跑偏。
3.2 用AI生成前端页面和交互
稳定起见,我从最简单的登录功能开始。按照前面的四要素框架,我准备了这样一段提示词:
项目技术栈是Vue3 + TypeScript + Element Plus + Pinia + Vue Router。请为登录页面编写完整代码,需求如下:在views/login/index.vue中实现登录表单,包含账号和密码两个输入项。登录成功后调用POST /api/auth/login接口,将返回的token保存到localStorage,并写入Pinia中的user模块。路由守卫中统一判断页面是否需要登录,未登录跳转到/login。登录页要有基础的表单验证,账号和密码不能为空,密码长度不少于6位。
这段话的信息密度很高。它把技术栈、页面位置、接口定义、状态管理方式、路由守卫逻辑、校验规则全部说清楚了。AI生成的代码,我review之后主要做了一处调整:用async await替代了它习惯性生成的promise链式调用。
然后是会议纪要列表页。核心功能是分页列表、关键字搜索、按状态筛选、行内操作按钮。生成之前我自己先把接口约定写清楚,再让AI写前端。
请实现会议纪要列表页面,位于views/meetings/index.vue。列表通过GET /api/meetings接口获取数据,请求参数为keyword(搜索关键字)、status(状态筛选)、page(页码)、page_size(每页数量),返回数据格式为{ "code": 0, "data": { "items": [...], "total": 100, "page": 1, "page_size": 10 } }。items中的字段为id、title、creator_name、created_at、status。页面使用el-table渲染列表,顶部提供el-input搜索框和el-select状态下拉框,操作列包含“查看详情”和“归档”两个按钮。
这种提示词生成出来的页面,90%的代码可以直接用。剩下的10%改动主要是样式细节,比如调整表格列的宽度、添加空数据提示、修改分页组件的布局。
实际开发中,AI生成前端页面最大的问题是可用性细节。它不会自动考虑加载状态、错误状态、空列表展示、按钮的loading防重复提交,这些都需要你在提示词里明确提出来,或者review时自己补上。
3.3 用AI生成后端接口和业务逻辑
后端部分的生成过程更考验你的架构把控力。还是以会议列表接口为例,我先定义好Pydantic模型,再让AI生成路由和业务逻辑。
当前后端使用FastAPI,已有SQLAlchemy模型Meeting,见下方定义。请实现GET /api/meetings接口,支持keyword、status、page、page_size参数。keyword对title字段做模糊搜索,status为可选筛选条件,结果按created_at倒序排列。返回格式参考项目统一约定。分页需要同时返回total、page、page_size字段。
SQLAlchemy模型定义: class Meeting(Base):tablename= "meetings" id = Column(Integer, primary_key=True, index=True) title = Column(String(200), nullable=False) content_md = Column(Text, nullable=False) creator_id = Column(Integer, ForeignKey("users.id")) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) status = Column(String(20), default="draft")
AI生成的核心代码逻辑上是通的。但我注意到它直接访问了Meeting模型上的creator_id,而没有关联查询出creator_name,这会导致前端拿不到创建人的名字。我让它补充修改,改用outerjoin关联查询,返回creator_name字段。
下面这段代码是我调整后的核心逻辑,也是前后端联调时最需要注意的部分。
@router.get("/meetings", response_model=PageResult[MeetingOut]) async def list_meetings( keyword: str | None = None, status: str | None = None, page: int = Query(1, ge=1), page_size: int = Query(10, ge=1, le=100), db: Session = Depends(get_db), ): query = db.query(Meeting).outerjoin(User, Meeting.creator_id == User.id) if keyword: query = query.filter(Meeting.title.like(f"%{keyword}%")) if status: query = query.filter(Meeting.status == status) total = query.count() items = ( query.order_by(Meeting.created_at.desc()) .offset((page - 1) * page_size) .limit(page_size) .all() ) return PageResult(items=[to_meeting_out(m) for m in items], total=total, page=page, page_size=page_size)生成接口的过程中最有价值的部分是边界条件。让AI处理page小于1、page_size超过100、keyword为空字符串这些情况,它会认真对待并逐条给出处理逻辑。这一点比我见过的一些初级工程师写的代码还要严谨。
3.4 联调阶段:AI生成代码的集成和排错
前后端都生成完之后进入联调阶段,这个阶段我积累了非常多血泪经验。AI生成的代码,单看前端好像是通的,单看后端也好像是通的,但放在一起跑就各种问题。
最常见的问题是字段命名不一致。前端用的是create_time,后端返回的是created_at;前端传的是pageSize,后端接收的是page_size。提示词里明明写了统一命名,但AI在不同对话中生成的代码风格还是会有出入。解决办法有两个:聪明的办法是在提示词里反复强调命名规范;笨办法是联调遇到一个改一个。
我在实际项目中的做法是准备一份接口文档化的提示词,在生成前端页面和后端接口时都粘贴同样的接口定义。这样至少能保证两边对接口的认知是一致的。
联调中另一个高频问题是时间格式。默认情况下,FastAPI的DateTime序列化出来是ISO 8601格式,比如2025-06-01T12:00:00,而前端的日期选择器返回的是2025-06-01 00:00:00。如果前端直接拿这个字符串去筛选,后端解析就会报错。这类问题AI很难自动规避,必须靠review时留意。
跨域问题也是联调阶段的老大难。前端开发服务器运行在5173端口,后端运行在8000端口,浏览器会直接拦截跨域请求。需要在FastAPI中配置CORSMiddleware,允许本地的开发服务器地址访问。
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )这里有一个安全细节:生产环境千万不要用allow_origins=["*"],否则等于给任何网站留了一个可以跨域调你接口的后门。AI有时候为了省事会这么写,review的时候必须改掉。
3.5 重构和优化:把AI生成的“能跑”代码变成“好维护”代码
AI生成的代码能跑,但不代表好维护。我通常会在功能跑通之后安排一次专门的重构。这个阶段也可以借助AI,但你必须给出非常明确的优化方向。
以AI生成的会议详情页为例,它最初把所有逻辑都放在一个巨大的Vue组件里,包括数据加载、Markdown渲染、附件下载、评论列表。这个组件有400多行,虽然能跑,但任何人接手都会头痛。
我的重构方案是把Markdown渲染写成独立组件,把附件下载封装成工具函数,把详情页的数据加载拆到组合式函数里。优化完成后,主组件只剩100多行,阅读和维护的体验明显提升。
后端同样需要重构关注点。AI生成的业务逻辑经常把所有代码堆在路由函数里,没有分层意识。models、schemas、services、routers各层混在一起还不明显,业务复杂之后就是灾难。我让AI先把查询逻辑迁移到services层,把返回结构定义整理到schemas层,路由只保留HTTP处理逻辑。分层重构后,加新接口的速度明显快了很多。
4. AI辅助的质量保障:测试、性能和安全性
4.1 用AI生成单元测试,但要人工补边界条件
AI辅助开发最容易被人忽略的环节是测试。很多人让AI写完业务代码就上线了,这种行为在我眼里和裸奔没有区别。AI生成代码,天然带有“看似正确实则细节错误”的风险,没有测试兜底,出问题只是时间问题。
让AI生成测试代码的质量其实相当不错。因为测试逻辑相对固定,输入输出明确,非常适合AI生成。我通常会用这样的提示词:
为GET /api/meetings接口编写pytest单元测试。需要覆盖以下场景:正常分页返回、keyword模糊搜索、status筛选、page小于1时自动修正、数据为空时返回空列表、未登录时返回401。测试使用mock方式,不要访问真实数据库。
AI会很快生成一批测试用例。但我不完全依赖它,因为AI写测试时容易陷入“为了过而过”的误区,只覆盖它自己生成的代码路径,忽略真正的业务边界。
我自己还会额外补几个关键场景:数据库字段超长的输入、并发请求下的数据一致性、依赖服务超时的降级表现。这些场景AI很少能主动覆盖到。
4.2 N+1查询和慢SQL:AI代码的性能杀手
AI生成的后端代码,性能瓶颈最常见的就是N+1查询。所谓N+1查询,就是先查一次列表拿到N条记录,再循环每条记录去查一次关联数据,总共执行了N+1次SQL查询。数据量小的时候没感觉,数据量一大性能急剧恶化。
我遇到过很典型的案例。某个列表接口,AI生成的代码先查询出当前页的会议记录,然后循环每个会议去查询创建人姓名。每页10条数据就需要执行1+10次查询。等数据量涨到几万条时,这个接口的响应时间从50毫秒飙升到3秒多。
解决方法是使用join查询一次性查出关联字段,或者对于确实需要单独查询的场景,使用in查询批量取出关联数据。前者适合一对一关联,后者适合一对多关联。AI其实知道这个知识点,但它不会主动应用,需要在代码审查时人工提出来。
前端性能问题也很突出。AI生成的大列表页面,经常会为每条数据创建多个响应式对象,或者在一个循环里使用大量复杂的计算属性,导致页面滚动卡顿。review时我会特别注意v-for循环里的组件粒度、是否缺少key、是否有不必要的响应式深度代理。
4.3 安全防线:AI代码必须人工审查的五个点
安全是AI生成代码最需要人工把关的环节。AI模型是基于海量开源代码训练的,开源代码里本身就存在大量不安全的写法,模型有样学样,自然也会生成有漏洞的代码。
第一是SQL注入。虽然ORM框架能挡住大部分注入风险,但如果AI生成了拼接SQL字符串的代码,或者使用了raw query,注入风险就回来了。审查时看到execute(text(...))这类代码要格外警觉。
第二是XSS攻击。AI生成前端代码时经常直接用v-html渲染用户提交的富文本内容,这就是典型的XSS注入点。如果确实需要渲染富文本,必须对内容做白名单过滤,并避免在v-html中拼接不可信数据。
第三是硬编码密钥。AI特别习惯把数据库密码、JWT密钥、API Key以字符串形式硬编码在代码里。这个问题极其普遍,因为训练数据里到处都是这种写法。我要求项目里所有的密钥必须从环境变量或密钥管理服务中读取,并让AI在代码中注释掉真实密钥,只保留占位符。
第四是越权访问。AI生成接口时不会自动考虑权限控制。它会假设当前用户是管理员,然后生成一个能操作所有数据的管理接口。实际业务中必须显式校验当前用户是否有权限访问操作对应资源。
第五是文件上传漏洞。AI生成文件上传功能时往往不做文件后缀名和文件内容校验,攻击者可以上传恶意脚本文件。必须限制允许的后缀名,对上传内容做二次校验,并将上传目录设置为不可执行脚本。
4.4 数据合规和隐私保护:被很多人忽略的红线
很多开发者用AI编程时,习惯把项目的真实代码、数据库结构、甚至生产环境的真实数据直接粘贴给AI大模型。这件事早期没人管,但现在绝对踩红线。ChatGPT、Claude这类在线服务的API提供商,在服务条款里就对数据使用有比较严格的限制。你把真实业务代码和用户数据喂给外部AI服务,等于变相将公司数据转移给了第三方。
我的原则是:喂给外部AI的内容必须脱敏。所有涉及真实用户信息的字段,比如姓名、手机号、邮箱、身份证号,一律用test_xxx这样的占位符替代。数据库结构如果过于特殊,也要做模糊化处理,避免暴露内部表结构。
如果项目对数据安全等级要求高,建议考虑私有化部署的开源AI编码工具。比如某些国产厂商提供私有化部署的代码生成模型。虽然效果和顶尖在线模型有差距,但数据完全留在内网,合规上更稳妥。
5. 从开发到上线:部署、CI/CD和稳定运行
5.1 构建产物:AI时代不能忽视的一环
现在的AI Agent型工具越来越强,有些甚至能自己执行构建命令、自己修构建错误。但构建这一步依然不要全交给AI,尤其是生产环境的构建。
前端构建这块,我碰到过几次AI改完代码后,本地开发模式跑得正常,打包就报错。常见原因是TypeScript类型不通过、有未使用的import、或某个依赖的版本在生产构建时和开发环境表现不一致。所以每次AI改动完代码,我必须先跑tsc --noEmit检查类型,再跑vite build确认生产产物正常。
后端构建环节相对简单,但也别裸奔。FastAPI用uvicorn启动的话,至少要把依赖锁文件管理起来。Python的pip freeze看着省事,但会把一堆间接依赖也锁进去,换台机器经常装不上。更推荐做一套标准的requirements.in加pip-compile流程,或者直接用Poetry管理依赖。
构建时还需要关注产物大小和启动速度。AI生成的代码经常有“把整个UI库引入”“把没用的工具函数也打包进去”这类问题。前端构建后产物从预期的一百多KB变成几MB,就是这种问题直接带来的结果。
5.2 CI/CD流水线:让AI生成的代码经过统一检查再上线
我强烈建议每个AI辅助开发的项目都配上CI/CD流水线,而且至少包含静态检查、单元测试、构建三步。
以GitHub Actions为例,一个基础的流水线配置大概是这样的。
name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: frontend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - name: Install dependencies run: cd frontend && npm install - name: Lint run: cd frontend && npm run lint - name: Type check run: cd frontend && npx tsc --noEmit - name: Build run: cd frontend && npm run build backend: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: cd backend && pip install -r requirements.txt - name: Run tests run: cd backend && pytest有了这层防线,AI生成的代码会被自动检查一遍,类型错误、lint问题、单测失败都能在合并前暴露出来。这比靠人工review更可靠,也更高效。
部署环节,只要条件允许就上容器化,把前端打包成Nginx镜像,把后端打包成Python镜像,用docker-compose统一编排。环境差异问题在容器化后基本消失,部署环境从开发机到测试服务器,行为基本一致。
5.3 项目自带模型能力时的部署选型
回到标题本身,《用AI打造高品质Web应用》——如果应用的“高品质”里包含接入AI大模型能力,那部署选型就是一个绕不开的话题。
目前主流有两种方案。第一种是直接调用大模型API,比如国内的通义千问、文心一言、智谱AI,国外的OpenAI、Anthropic,都有成熟的API接口。这个方案的好处是省事,不需要自己维护GPU基础设施,按量付费。缺点是有网络延迟和费用问题,而且对外部服务的稳定性有依赖。
第二种方案是私有化部署开源模型。像Qwen系列、DeepSeek系列、Llama系列都可以本地部署。以一个大参数模型为例,量化之后通常需要几十GB显存,单张A100 80G或者两张4090勉强能跑起来。如果业务量不大,也可以用中等规模的模型搭配一个小模型做路由,低成本下兼顾效果和速度。
我个人经验是:大规模对外提供AI能力,优先考虑API方案。API方案能让你快速上线、快速验证产品价值。当业务量起来之后,再评估自建模型服务的成本收益比。AI应用架构里最忌讳的就是在业务还没验证清楚的时候,先砸几十万买GPU集群。
接口层面,无论选哪种部署方式,都要考虑超时控制和重试策略。大模型推理时间通常以秒为单位,HTTP默认超时时间肯定不够。AI生成的代码很少考虑这些细节,你必须手动把超时时间调宽,并加上有限次数的重试机制。
5.4 监控、日志和AI调用的成本控制
上线不是结束,而是运维的开始。AI生成代码的项目尤其需要重视可观测性,因为AI生成的逻辑里,埋日志和监控点的意识非常弱。
前端加错误边界和错误上报,后端加请求日志、错误日志、慢接口统计,这是基本盘。工具上可以用Sentry做前端错误监控,用Prometheus加Grafana做后端指标监控。日志平台如果公司有现成的,直接接上就好。
如果应用里集成大模型能力,成本监控是无论如何都不能省的。大模型按Token计费,一次对话可能消耗几千Token,一个月下来费用非常惊人。我见过不少团队因为忘了加成本开关,月底账单出来才发现AI功能占了大半成本。预算帽、单用户日调用次数限制、缓存相同问题的答案,都是有效的降本手段。
6. 常见问题与AI协作排查技巧实录
6.1 AI生成代码的典型问题速查
| 问题现象 | 根本原因 | 排查与解决办法 |
|---|---|---|
| 前端页面白屏,控制台报错 | AI生成的组件引用了不存在的模块或变量 | 查看控制台报错定位到具体文件,优先检查import路径和变量名 |
| 接口返回500错误 | AI生成代码时有未处理的空指针或类型错误 | 查看后端日志,定位到具体堆栈,把错误信息贴回AI让它修复 |
| 跨域请求被拦截 | 后端CORS配置缺失或配置错误 | 检查CORSMiddleware配置,注意生产环境不能使用通配符 |
| 数据列表加载慢 | N+1查询或缺少分页索引 | 查看SQL日志,确认查询次数;对条件字段加索引 |
| 登录后刷新页面失效 | Token只存在内存中没有持久化 | 将Token保存到localStorage或使用cookie,初始化时恢复登录态 |
| 日期时间相差8小时 | 前后端时区处理不一致 | 统一使用UTC存储,展示层转换为本地时区 |
| 部署后接口路径404 | 前端请求的是开发地址,生产路径不一致 | 用环境变量管理API基础路径,构建时注入 |
| 文件上传后无法访问 | 上传目录不在静态资源映射范围内 | 配置静态资源挂载,或改用对象存储服务 |
这些问题是过去大半年里我和AI协作开发中真实遇到过的频率最高的类型,每一类我都修复过不止一次。对号入座可以快速定位。
6.2 AI “幻觉”代码的识别和处理
AI编程中最难防的是“幻觉”——它生成一段看起来合情合理、语法正确、但逻辑上完全错误的代码。这种问题靠lint和编译检查发现不了,只有跑到特定业务分支才会暴露。
我遇到过一次典型幻觉。让AI写一个“导出会议纪要Excel”的功能,它凭空生成了一个名为export_utils的工具模块,里面有一个create_excel_file函数。问题是项目里根本没有这个模块,它也没有帮我创建,运行的时候直接ModuleNotFoundError。这种幻觉还算好抓,更可怕的是它生成了一段“假设某个函数存在”的代码,然后那个函数在另一个模块里确实存在但参数完全不同,运行时才会报错。
我的经验是:AI生成代码必须把它们当实习生代码审查,而不是当权威答案直接采用。尤其注意它提到的函数签名、类名、字段名是否真实存在于当前代码库中。如果AI引用了某个不存在的内部方法,多半是它在编造接口。带着这个怀疑去检查,能避掉大半的坑。
另一个经验是让AI生成代码时,明确要求“只使用项目中已存在的内容,不得引用未定义的变量、函数或模块”。这句话能显著降低幻觉代码出现的概率。AI会因此先查现有代码再作答,而不是凭空编造。
6.3 上下文丢失时的补救方法
AI对话超过一定长度后,早期定义的技术栈、变量名、接口约定会逐渐失效。如果你发现它开始用Vue2写法写Vue3代码,或者把Python包名张冠李戴,多半就是上下文丢失了。
补救方法很简单但往往被忽视:开一条新对话,把上下文块重新贴一遍,再继续任务。旧对话里已经确认过的代码,我建议先把它们保存到本地Git提交,然后在新对话里明确告诉AI“请参考当前仓库代码”。
我今年养成了一个习惯,每次完成一个大功能,先把代码提交到Git,然后用一个简短的总结描述提交内容。下次AI需要了解以往功能的时候,直接把这个总结贴给AI看。这比让它自己读几千行代码高效得多,也减少了上下文丢失后的信息错乱。
6.4 依赖地狱与版本冲突
AI生成代码时喜欢挑最新版本,这听起来是好事,实际不然。新建项目问题不大,但嵌入到已有项目时,最新版本的库经常和老代码发生冲突。
我碰到过最典型的一次:AI为了处理Excel文件,在requirements里加了openpyxl最新版。但项目原来用的是pandas加xlrd的组合。openpyxl一装,和pandas自带的Excel引擎冲突了,连累了一批和文件导入导出相关的老接口全部崩掉。
现在我对AI生成代码有一个硬性要求:如果项目里已有某个依赖,不得重复加入一个新依赖来替代它,除非我明确说明了原因。实在需要新依赖时,先人工确认它对现有依赖树的影响再决定。
6.5 经验之谈:让AI从“工具”变成“结对搭档”
用AI辅助开发这么久,我最大的体会是心态转变。早期我把AI当成魔法棒,期待一条咒语解决所有问题;后来我把它当成搜索引擎,让它给我提供候选方案;再到现在,我把AI定位成结对编程的搭档——我做架构,它做执行;我提验收标准,它出实现方案;我负责疑惑和判断,它负责速度和体力。
这种模式下,我每天交付的代码量比以前翻了一倍不止,而且质量更高。因为AI帮我省掉了大量样板代码和查找文档的时间,我把省下来的时间投入到需求分析、方案设计、代码审查和安全加固上。这些才是决定一个Web应用是否“高品质”的核心。
如果你打算引入AI辅助开发,我给的建议是从一个小项目开始练手。先做内部工具,不要直接上新业务的核心系统。跑通一遍完整流程,感受AI在代码生成、测试、部署各个环节的真实表现,再慢慢扩大应用范围。把它当成一个得力的新同事,认真沟通需求、及时纠正错误、严格验收结果,它会回报你远超预期的产出。