三个月前,我带着一个三人小团队,接下了一家制造企业核心交易系统重构的活。为了赶工期,我们全面切换到AI Coding工作流,大量使用AI辅助生成代码。三个月后项目交付,代码总量一统计——16万行。这里面大约八成以上是AI直接生成的,剩下的是我们人肉修补和Review改出来的。
整个过程中最重要的一次认知转变,就是从单纯“让AI帮我写代码”的AI Coding状态,进化成把AI当团队成员来管理、围绕AI建立整套研发流程的AI Engineering状态。这篇东西我就把这16万行代码背后的思路、方法、踩过的坑和沉淀下来的规范,完整复盘一遍。对正在用或准备用AI编码工具的团队,应该都能从里面找到点能直接用的东西。
1. 16万行代码是怎么炼成的:从“让AI写代码”到“让AI按规范写代码”
1.1 项目背景:传统业务系统重构的窘境
客户的老系统是一个.NET Web Forms时代遗留的ERP中台,单体应用,代码耦合严重,光一个订单模块就牵扯了十几个业务表的直接读写。老系统维护了六七年,团队走了两拨人,文档基本是空的。客户要求三个月内把核心交易链路重写一遍,并且要支持后续逐步迁移。
按传统人力,这个量级至少需要一个8到10人的后端团队,加上2个前端,再配一个测试,三个月都够呛。但我们手上只有3个后端加1个前端。所以我们做了一个大胆的决定:所有业务代码,包括API、数据库访问层、前端页面、单元测试,全部用AI Coding工具来生成初稿,人来负责设计、审查、修改和兜底。
这个决定在今天看来很合理,但实际操作中经历了一段痛苦的磨合期。刚开始确实像是在放飞自我——让AI写一个模块,生成速度极快,几分钟就是几百行,但我们很快发现,无约束地让AI写代码,三个月后的代码库会变成一个无法维护的黑洞。16万行代码如果只是堆出来,那么迎接我们的不是交付,是灾难。
1.2 为什么最终会累积到16万行:AI Coding的加速效应
很多没实际做过大规模AI生成的人会问,一个交易系统重构,人工写可能也就6到8万行,为什么AI出来就成了16万行?这里我直接说结论:AI生成代码的体量天然会膨胀,这是它的“生成模式”决定的。
传统人写代码,会有很多“复用”思维。你心里清楚已经有个工具函数了,就会去调用它。AI并不天然具备这种全局记忆,尤其是当上下文窗口有限时,它为了确保某个功能“能跑通”,倾向于把逻辑完整地、自包含地写在当前文件里。一个分页工具函数,它可能在不同的Service里被复制了六七遍。一个相同的数据校验逻辑,也可能在每个Controller里以略有差异的形态重复出现。
另外,AI Coding的试错成本极低,这本身就鼓励了重写而不是重构。遇到需求变更,我们经常直接让AI重新生成整个service,而不是人工去改。功能确实变对了,但代码量就成倍地涨。等代码量到了10万行左右,我们做了一个代码体检,坏味道集中爆发,尤其是重复代码和过深的条件嵌套。那会儿我才认真思考一个问题:AI Coding带来了数量,但谁来保证质量?
这个问题的答案只有一个:引入工程化手段。也就是我后面要重点讲的AI Engineering。
1.3 AI Coding 与 AI Engineering:一字之差,天壤之别
很多人把AI Coding理解成用Copilot补全代码,或者让ChatGPT写个函数。这没错,但这是最表层的东西。
我自己对这两个概念做了个清晰划分:
- AI Coding:关注“单次代码生成”。输入需求,产出代码,人来看一眼,能跑就过。
- AI Engineering:关注“代码从生成到上线全生命周期的管控”。包括规范定义、上下文管理、质量门禁、审查流程、测试标准、甚至代码退役。
我打个比方,AI Coding是让一个实习生用最快速度帮你把方案初稿写出来,AI Engineering是在此基础上,你还得定写作规范、配校对、定审核流程、明确哪些内容能发出去。没有后者,前者只是在制造文字垃圾。
一个团队如果只停留在AI Coding阶段,最典型的现象就是“AI写代码,人给AI擦屁股”。代码跑不通,把报错甩给AI;逻辑有漏洞,让人一点点查。看起来有AI加持,实际效率比人肉写还低。真正进入AI Engineering阶段后,我们做的事情变成了:让AI在清晰边界内生成代码,再用另一套AI工具和工程手段去审计、约束、修正第一篇AI生成的内容。
从表格能看出,两者的核心差异不是工具,而是思维模型。
| 对比维度 | AI Coding | AI Engineering |
|---|---|---|
| 核心关注点 | 单次生成代码的正确性 | 整个代码库的健康度与可持续性 |
| 主要动作 | 写Prompt、生成代码 | 定规范、管上下文、设门禁、多Agent协作 |
| 代码质量保障 | 靠人肉Review | 靠自动化规则+分层审查 |
| 团队结构 | 个人开发者独立使用 | AI Agent分角色协作,人做决策 |
| 风险控制 | 事后修复 | 事前约束与事中拦截 |
2. 整体设计思路:把AI Agent当成有脾气的团队成员来管理
2.1 像管团队一样管Agent
如果你带过一个15人的开发团队,你会发现一个规律:完全放任每个人按自己的风格写代码,项目必崩。你会有代码规范、有评审、有架构约束。AI Agent本质上也是在你的代码库上并行工作的“虚拟组员”,不管理它,它就会在你的仓库里“野蛮生长”。
我给AI Agent设了三种岗位,和真实团队一一对应:
- 架构师Agent:负责全局技术选型、模块划分、接口契约。它不直接写业务代码,但所有重要Prompt的上下文都来自它生成的架构设计。
- 开发Agent:负责按模块写具体实现。划分为订单、库存、用户、支付、报表等多个独立Agent,每个Agent只专注于自己负责的领域。
- 审查Agent:负责审查开发Agent写出的代码,对标代码规范、检查边界条件、寻找潜在bug。它和开发Agent之间形成了流水线式的协作关系。
这样设置的好处是:每个Agent的职责边界非常清晰。开发Agent不需要去思考什么全局架构,审查Agent也不需要理解业务全貌。人的工作就是判断这三个环节的产出质量。
2.2 代码生成规范示例:Prompt模板是第一批交付物
进入AI Engineering状态后的第一件事,不是写代码,而是写Prompt规范。我们内部管它叫“代码生成规范V1.0”。这不是让我们去背Prompt模板,而是把团队真实的代码规范翻译成AI能理解的语言。
一份合格的代码生成规范里应该包含这些要素:
- 角色定义:告诉AI它现在扮演什么角色(例如“你是一名Python后端工程师,熟悉FastAPI和SQLAlchemy”)。
- 技术栈约束:明确核心依赖版本、不允许使用的库。
- 代码风格:命名规范、注释规范、是否允许类型注解。
- 错误处理要求:禁止吞异常、统一异常响应格式。
- 性能要求:N+1查询禁止、必须在数据库层分页。
- 测试要求:新功能必须生成配套单元测试。
- 交付格式:生成代码时同时返回简要说明和变更点。
我贴一段我们实际用过的精简版Prompt模板:
# Role: Python后端工程师(FastAPI + SQLAlchemy 2.0) # Task 实现用户订单列表查询接口,需求见下文。 # Rules - 所有查询必须使用SQLAlchemy 2.0的select()语句,禁止使用原生SQL。 - 字段命名使用snake_case,禁止缩写。 - 涉及列表查询必须分页,分页参数page和page_size,默认page=1, page_size=20。 - 不允许捕获Exception后返回None,异常统一抛出,由全局异常处理器处理。 - 接口响应统一使用response_model。 - 生成的代码必须包含对应的pytest单元测试,测试数据使用factory_boy。 # Existing Code Context 相关模型定义如下: [贴模型代码] 相关依赖注入方式如下: [贴依赖代码] # User Request 实现GET /api/v1/orders接口,支持按订单状态过滤,返回当前用户所有订单。这个模板在执行中效果很好,很核心的一点是把“Existing Code Context”放在最后。我看到很多失败案例就是把上下文放在开头,AI会因为上下文过长而遗忘规则。而我给出的是一个清晰顺序:角色先立住,规则其次,最后给具体代码语境。
2.3 多智能体协作架构:不同Agent各司其职
我们把AI Agent的协作架构精炼成了三层逻辑:编排层、执行层、质量层。
编排层是人的工作台。我们用的是自建的任务管理看板,每个任务卡片里写清楚需求描述、涉及模块、技术约束和验收标准。编好任务后,会触发执行层的开发Agent进行编码。
执行层是多个开发Agent的集合。这里一个常见误区是会有人问,为什么不是一个全能的Agent从头写到尾?因为上下文窗口是有限的。如果让一个Agent同时负责用户、订单、支付、库存四个模块,它很快就会遗忘模块边界和接口定义,然后开始生造函数。我们发现,领域隔离是防止AI“精神分裂”的有效手段。每个Agent只维护自己领域内的相关上下文。
质量层的审查Agent定期检查执行层的产出。它会使用静态分析工具扫描代码,也会自己读代码做逻辑审查。发现问题时,它不直接改代码,而是生成一个“Review反馈单”,里面包含问题描述、问题定位、修改建议和参考示例。开发Agent收到反馈单后,再进行一轮修订。这很像真实研发流程里的“打回修改”。
整套协作的核心一句话:让AI之间先互相约束,把人都要从“看代码的体力活”里解放出来。
3. 核心实操:把AI生成的16万行代码“跑”起来的完整流程
3.1 先搭骨架:人工划定模块边界和依赖规则
在让任何Agent写代码之前,我们手工搭好了整个项目的骨架。这不是出于控制欲,而是出于对“AI天然缺乏全局观”的清醒认知。
骨架的内容包括:
- 目录结构(backend/app/modules下的订单、用户、支付等模块)
- 每个模块对外暴露的接口清单
- 模块间的依赖方向(例如:支付模块不得反向依赖订单模块)
- 公共包(utils、common、config)的统一出口
我们用一个最笨也最有效的方式强制执行依赖规则:每个模块目录下放一个__init__.py,只允许导出公开接口。模块内部定义的所有类和方法,如果没被导出,别的模块就import不到。这不是Java里的访问控制那么严格,但至少让AI知道,跨模块访问是有门槛的。
这个骨架非常重要。没有它,后续所有Agent生成的代码都会变成一锅粥。因为Prompt写再多“注意模块边界”,AI也很容易在context里发现自己需要一个工具函数,就直接在当前业务文件里重新写了一个。骨架加上物理目录的隔离,能把这种“偷懒”限制在单一模块内。
3.2 上下文投喂的质量,决定AI生成代码的质量
用AI Coding写过东西的人都知道,上下文决定了AI的上限。在AI Engineering里,这个上下文管理被提到了更重要的位置。
实践中我总结了一套“三层上下文投喂法”。
第一层是项目级上下文。我们维护了一份AGENTS.md文件放在代码库根目录,里面包含了整个项目的技术栈、目录结构、命名规范、依赖白名单、以及最常用的公共函数说明。每个Agent在开工前,会被要求先读这份文件。
第二层是领域级上下文。比如订单Agent,它除了读AGENTS.md,还需要读订单模块的README.md,里面描述了订单模块的领域模型、关键表结构、核心流程。这样它生成订单相关的代码时,就不会跑偏到去操作库存表。
第三层是任务级上下文。在每次具体任务里,把相关的模型定义、接口契约、参考实现贴到Prompt里。前两层是长期记忆,第三层是短期记忆,三者缺一不可。
我在实际推这个方案时,团队里有人觉得维护三层上下文太累。后来发生了一件事,改变了所有人的态度:一个Agent在没看领域上下文的情况下生成了订单取消接口,它自己定义了一个“已支付且出库”的组合状态,这跟我们系统的状态机定义完全不符。那次事故之后,“上下文不是给AI看的,是给我们的保险”成了团队共识。
3.3 代码诊断与代码审查:把AI生成代码的“隐形炸弹”挖出来
如果说AI Coding是在生产代码,那AI Engineering就是要给这个生产线上装质检仪。
我们的质检体系分两道:
第一道是自动化诊断。在CI管道里跑了四种工具:ESLint(前端静态检查)、mypy(Python类型检查)、SonarQube(重复率和圈复杂度检测)、Bandit(安全扫描)。AI生成的代码会在跑完这些工具后才能进入代码评审环节。这里我发现一个很有趣的事:AI生成的代码里,安全漏洞类型和人写的很不一样。人写的漏洞更多是逻辑漏洞,AI生成的漏洞则非常典型地集中在“信任外部输入”——比如直接拼SQL、没有对文件上传做类型校验。静态扫描工具抓这类问题效率极高。
第二道是AI审查Agent。我一直在思考一个问题:如果让开发Agent生成代码,再让人来逐行Review,那效率瓶颈还是在人身上。于是我们训练了一个专门的审查Agent,规则是:它必须站在“怀疑一切”的立场上审查代码,不能因为代码风格好看就放过逻辑问题。它会对每个函数找边界值、追异常路径、检查数据一致性。
审查Agent的输出会生成一个Markdown格式的Review报告,按严重程度分级:Blocker、Major、Minor、Nit。只有Blocker和Major被清零后,代码才允许合入主干。人工在这个环节只做抽检,抽检率控制在30%左右,关键模块我们会重点抽。这样既压了成本,也守住了质量线。
3.4 测试才是“跑起来”的最后一道防线
16万行代码,靠人肉点界面做回归测试是不可能的事。所以从AI Coding转型AI Engineering时,我们做了一个硬性规定:AI生成代码时,必须同步生成单元测试。
很多开发者抱怨AI生成代码质量不高,但没想过拿AI生成的代码去跑测试。我们尝试过几次后发现,AI写单测的功力其实经常比写业务代码更靠谱,尤其是在边界条件覆盖上。它能自然地想到空列表、超长字符串、非法状态这些用例,比很多刚入行的新人覆盖面都要宽,这部分帮助超出了我预期。
配合单元测试,我们还让测试Agent专门生成关键链路的集成测试脚本。例如支付回调、订单超时关闭、库存预占释放这几条主链路,每个都有对应的集成测试用例,全部数据跑在本地容器化的MySQL和Redis里。我们的CI流水线是这样的:commit触发单测,合入主分支触发集成测试,集成测试通过后自动部署到Staging环境并跑一轮冒烟。
有了这套测试体系,我们才敢在后期频繁地让AI大规模重构代码。因为测试会替我们守住“改了A没坏B”的底线。
4. 踩坑实录与排查技巧:16万行代码里我最想删掉的5个瞬间
4.1 问题一:AI生成了看似正常,但根本没跑过的“鬼代码”
遇到过最诡异的一个bug:系统上线两周后,有一个定时任务突然报错,报错的代码路径是一个我们从未见过的函数。拉出那个函数的代码一看,外表完全正常,有类型注解、有docstring、逻辑也通,但函数内部调用了另一个模块里的一个私有方法。
用git blame一查,这段代码是开发Agent在两个月前生成的。因为当时单独测过接口Swagger返回正常,而底层定时任务的调用路径完全没被入口测试覆盖到。等生产环境第一次触发这个路径,才暴露出它依赖的私有接口早就被另外一个Agent改名了。
这个问题的根因是:开发Agent在生成代码时,没能感知到跨模块私有方法的不稳定性。这也验证了让不同Agent各写各的模块所带来的信息偏差。
排查与规避经验:
- 关键业务链路必须建立从入口到出口的集成测试,不能只测单个API。
- 在审查规则里明确禁止import其他模块的下划线私有方法,一经发现直接打回。
- 全局搜索私有方法引用的脚本要进入CI,发现有引用就报警。
4.2 问题二:AI的“幻觉依赖”——明明不存在的包
审查Agent在一个订单导出模块里发现了一行import:from openpyxl import Workbook。当时系统里确实装了openpyxl,但版本是2.6.4,而AI生成的代码用到了一个2.6.4版本里不存在的参数。要命的是,开发环境里其他人装的是3.1.2,本地跑起来完全正常。只有部署到生产环境时,因为锁定的依赖版本不同,直接崩了。
这也说明了一个AI Coding时代的常见现象:AI会根据训练语料里的流行用法来写代码,它可不知道你这个项目里锁的是什么版本。
排查与规避经验:
- 团队必须维护一份“依赖白名单”,白名单之外的库即便AI用了,也要在代码评审里被拦截掉。
- 所有Python依赖必须使用
requirements.txt里的精确版本号,禁用>=和*这类模糊范围。 - 我让审查Agent在Review时专门核对import语句,把它对应的库及其版本号列出来,再和依赖白名单比对。
4.3 问题三:命名混乱造成的“逻辑幽灵”
这个问题的典型表现是:在一个模块里,用户ID字段叫user_id;到另一个模块,同一个字段变成了accountId。如果每个命名都各自一致,其实还能接受,但问题是AI本身偏好模仿上下文里的写法,如果不同任务的Prompt里给出来的示例代码命名不一致,那AI就会忠实“继承”这种不一致,最后把代码库搞成一个罗生门。
更可怕的是,这种命名混乱会直接导致“看起来像bug,其实不是;看起来没问题,其实藏着bug”。我们在排查一个订单超时bug时,发现有的代码用order_id做匹配,有的用orderNo做匹配,两边值一样,但来源一个是数据库主键、一个是业务单号。在某些边界情况下它们并不相等,于是本该超时关闭的订单漏掉了。
排查与规避经验:
- 在代码生成规范里把实体字段名做成“业务字典”,统一使用
id作为主键,order_no作为业务单号,并声明两者严禁互换使用。 - 审查Agent的规则里加入“术语一致性检查”,让它找出所有可能对应同一个概念的字段别名。
- 每周跑一次全局字段搜索报告,人眼快速扫一遍,重点看有没有高频字段名变体。
4.4 问题四:多Agent并行下的“合并地狱”
我们前期让订单Agent和支付Agent同时开工,两个Agent都涉及订单的状态流转逻辑。因为它们讲的是同一份领域模型的两种方言,导致Git里的冲突多得像叙利亚战场一样。
传统开发里的代码冲突,靠人花时间就能解决。但AI生成的代码量大,冲突引发的连带修改太多。有一次解决完冲突后,支付回调里读取的订单状态值又对不上了,折腾了一整天才定位到。
排查与规避经验:
- 模块边界不是写给别人看的,是高耦合模块之间“排他性产权”。订单和支付这种强关联模块,同一时间只允许一个Agent在改动。
- 把公共领域模型和数据表结构定义放在独立的核心包里,业务Agent只能读取该包,不允许直接修改。如果要改,必须走人工流程。
- 合并时不要用工具自动合并后盲目相信结果,强制在合并后的代码上跑一遍完整集成测试。
4.5 问题五:僵尸模块和僵尸依赖
到项目后期,我们用了knip和depcheck做了一次全库扫描,结果发现至少有5000行以上的代码,全项目没有任何地方引用。这些僵尸模块大多是AI早期探索性生成的替代方案。它们还会拖累构建速度、让IDE索引变慢,更严重的是,某个僵尸模块依赖的第三方库存在已知漏洞,安全扫描就被这个拖累。
排查与规避经验:
- 每个功能合入前,必须在任务描述里写明“入口引用点”,没有引用点的代码不允许合入主干。
- CI里定期跑dead code检测,超过一周未被引用的代码自动标记为“待清理”,下个迭代删除。
- 一开始觉得AI生成代码规模到16万行很吓人,做完整理后,真正在生产路径里活跃的代码大约是13万行。删掉的那些僵尸代码,省下来的是长期的维护成本。
5. 常见问题速查表:AI Engineering实战者的Top问答
一个人或者一个团队想完全拥抱AI Engineering时,总会在各种细节上卡住。这里我整理了一些在实践和对外分享时被问得最多的问题,结合自己的实操经验给出看法。
| 问题 | 我的处理方式与核心观点 |
|---|---|
| AI生成的代码真的能直接读吗? | 初稿能看,但别指望直接合入。我要求开发Agent在代码中额外提供“变更点说明”,这样Review时人能快速抓住重点。 |
| 16万行代码,人就这几个,怎么Review得过来? | 分层Review:静态工具抓低级错误,审查Agent抓逻辑和规范,人只盯架构和关键链路。人不需要看所有代码,所有代码交给机器去看。 |
| 用了AI之后代码质量是不是必然下降? | 直接回答:不一定。以此项目的最终交付为准,缺陷密度相比老系统是显著下降的。关键在于有没有建立“质量门禁”和“代码规范”。 |
| 做AI Engineering需要什么基础? | 首先得懂代码,懂软件工程,懂项目管理。AI Engineering不是让你不用懂开发,而是让你把开发管理能力从人扩展到AI Agent。 |
| 用什么AI编码工具比较好? | 工具迭代很快,核心是看它是否支持项目级上下文管理、自定义规则注入、多Agent协作。具体工具不重要,工作流才重要。 |
| 上下文把我搞晕了,有没有简单的原则? | 项目级上下文一页、模块级上下文一页、任务级上下文每次按需贴。超出这个量的上下文管理都是在过度设计。 |
| 需求频繁变更,AI能顶得住吗? | 能,但需要配合敏捷的模块化设计。AI改代码的速度快,但需求变更如果动了领域模型,通常比人改还得慢一些,因为所有依赖都要跟着改。 |
| 线上出故障了,怎么快速定位是不是AI代码的锅? | 我们给所有AI生成的代码自动加上一个标记,日志里会带上source=ai的标签。出问题后可以先按这个字段过滤,快速圈定影响范围。 |
| 团队里有人不愿意用AI怎么办? | 不强推。让愿意用的人先用并分享成果,用实际效率差距说话。实践下来,抵触情绪大多来自“怕被替代”,明确AI是工具、人是决策者会缓解。 |
| 现在有些公司搞AI Coding笔试,到底在考什么? | 考的是你驾驭AI的工程能力。同一个需求,高手会先拆解、定义接口和边界,再让AI分段生成;新手可能从头到尾一句Prompt之后就卡住了。差别不在用没用AI,而在有没有工程化思维。 |
6. 从AI Coding到AI Engineering,我个人的真实体会
如果只看数字,16万行代码听起来很多。但真正让这个项目能交付、能上线、能维护的,并不是AI生成代码的速度,而是围绕AI建立的整套工程化约束。
我最大的体会简单概括:AI Coding解决的是“从无到有”,AI Engineering解决的是“从有到稳”。无约束的AI Coding会在三个月里造出一个你不敢重构、不敢上线的代码怪物;而加上规范、上下文管理、代码审查、测试门禁这些工程化手段,AI才真正变成一个“靠谱的高产组员”。
这个项目做完以后,我们复盘时算了一笔账:如果完全靠人力,按原来的老团队规模,这个改造需要大约7个月。而实际用了AI加工程化管控,3个月出头就交付了,算上人工Review的成本,整体开发效率大概提升了一倍多。没有翻倍以上的提升,因为质检、规范和上下文管理本身也要消耗人的精力。把这部分成本重视起来,才不会对AI Coding抱有不切实际的期望。
此外还有一个细节想分享给所有带团队的人:别把AI生成的代码直接扔给新人去改,也别把AI排除在团队之外。最好的方式,是让有经验的工程师先定义好规范,让AI在规范里干活,让新人在旁观摩AI如何遵守规范。这套模式下,新人上手比看老代码快很多,团队整体战斗力反而上来了。
最后再提一个小技巧,也是在这次迭代中我一直坚持的:每隔两周强制对代码库做一次“AI自检日”。这半天不做新功能,只让审查Agent全面巡检一遍现有代码,查找规范偏离、死代码、以及潜在的错误处理盲区。这个习惯帮我们拦下了不少潜在的生产事故。如果你正带队走上AI Coding这条路,建议从第一天就把它制度化。