AI生成80%代码后:AI编程流水线、质量红线与暂停思考 上周在群里刷到这个标题的时候我第一反应不是惊讶而是打开了本地的提交记录翻了翻自己最近三个月的 commit。说实话如果只按行数算我这边新增的代码里大概也有七八成是先由模型吐出来、再由我改出来的。所以当看到「代码80%是AI写的」这个说法我一点都不觉得夸张真正让我停下来想了半天的是后半句——一家做 AI 的公司自己站出来呼吁暂停 AI 开发。这两句话放在一起本身就是一道很好的工程思考题。它不只是一个行业新闻而是把三个问题直接摊到了桌面上代码交给 AI 写到底写到了什么程度这套流程在真实项目里靠不靠谱以及当最激进的推动者自己开始踩刹车作为一线写代码的人我该做什么调整。这篇东西不是新闻解读我想从一个每天和代码、AI、CI 流水线打交道的人的角度把 AI 编程这件事拆开讲清楚——包括我怎么搭流水线、怎么设卡点、怎么踩坑、怎么给 AI 生成的代码定红线也包括我对「暂停」这个提法的真实看法。不管你是刚用上 AI 编程的新手还是已经在团队里推 AI 开发流程的负责人下面这些内容应该都能直接抄作业。1. AI编程到底走到哪一步了先把那个80%拆开看1.1 这个百分比统计的到底是什么东西很多人看到「80%」的第一反应是AI 已经能独立写完一个项目了。这是把「生成量」和「交付量」搞混了。在绝大多数公开讨论里这类百分比统计的是代码进入仓库前的生成比例——也就是一段 diff 里有多少字符是模型先写出来、人再改的。这两种口径差得非常远。我自己的习惯是给每个文件的提交打一个内部标记分成三档纯人工从零手写或者改动超过 60% 的逻辑结构。AI 起草 人工重写模型给骨架我把关键逻辑、错误处理、边界条件全部过一遍通常改动在 30% 到 60% 之间。AI 起草 人工微调只改命名、日志、类型标注逻辑基本不动改动低于 30%。如果你按「AI 起草 人工微调」这一档去算头部团队的比例确实能到五六成但如果你把三档里所有「模型参与过」的都算进来八成这个数字并不夸张。问题在于后两档的工程质量完全不是一个量级。举个很具体的例子。让模型写一个把嵌套 JSON 拍平成字典的函数它三秒钟就能给你一版读起来还挺优雅。但这版代码通常缺三样东西深度超过递归上限怎么办、key 冲突时是覆盖还是加后缀、值本身是列表或 None 的时候怎么处理。这三样东西加起来可能只有二十行却是整个函数真正值钱的部分。所以「80% 由 AI 生成」这句话更准确的翻译是打字量被 AI 接管了但判断量没有。这个区分很重要因为它直接决定了后面所有流程设计。你要是把 AI 当成一个能独立交付的工程师那你的评审会形同虚设你要是把它当成一个打字飞快、但完全没有上下文的实习生整套流程就顺了。1.2 AI写起来最顺手的代码其实有很明显的特征干了两年多 AI 辅助开发我总结出一个规律模型的能力分布不是均匀的它在某些类型的代码上强得离谱在另一些上弱得让你怀疑人生。这个规律不搞清楚你的提示词怎么写都是碰运气。代码类型AI 表现我的实际处置方式样板代码、DTO、序列化极强几乎不用改直接生成配个格式检查就提交常见算法快速排序、二分、BFS强但边界常错生成后必须补边界测试用例框架胶水层路由、依赖注入中等依赖版本易错生成后逐个核对官方文档版本复杂业务状态机弱逻辑常自相矛盾只让它写骨架状态迁移手写并发、锁、事务边界很弱几乎必错不交给模型最多让它审一遍性能敏感的热点路径弱倾向写好看的写法人写第一版让模型做对照实现测试用例强但倾向讨好实现生成后手动补反例和异常路径这张表是我踩了无数次坑之后才慢慢成型的。最典型的一次是并发相关一个批量写入的模块模型给出了一版用锁保护列表的方案逻辑上读起来天衣无缝压测的时候偶发丢数据。排查了两天才发现问题不在锁本身而在于它在锁外面做了一次长度判断——典型的「先检查后执行」竞态。这种错误你光看代码是看不出来的因为它读起来太合理了。反过来说那些「看起来就该由机器写」的代码AI 确实做得非常好。我现在几乎不再手写配置文件、数据模型、API 的类型定义这些东西这部分时间省下来相当可观。所以我的态度一直很明确不是让 AI 写更多代码而是把合适的那部分代码让出去。1.3 那家公司为什么要喊暂停从工程视角看三个真实担忧抛开所有宏大叙事从工程角度去看「呼吁暂停」这件事我觉得至少有三个担忧是站得住脚的而且每一个一线开发者都能验证。第一个是评审能力的稀释。假设一个十人团队人均每天提交 300 行。以前这 300 行是人一字一句敲出来的评审的人心里有底知道作者至少想过一遍。现在这 300 行可能有 200 行是模型生成的评审的人面对的是「一份自己没有思考痕迹的产出」。更麻烦的是量的问题——生成成本几乎为零提交量会自然膨胀评审时间却不会跟着变多。我见过一个项目AI 上线后两周内 PR 数量翻了一倍半review 的平均停留时间从 4 小时掉到 40 分钟。这种情况下评审基本等于点个 approve。第二个是技能断层的隐蔽性。这是我最担心的一点。一个刚入行的同学如果从第一天起就是「描述需求 → 拿代码 → 改改就提交」他会缺少一个非常关键的训练环节在脑子里把逻辑跑一遍。这个能力不是靠读别人的代码就能获得的必须靠自己写错、调试、再写对。AI 把这个过程压缩了短期看效率很高两三年后遇到一个需要从零设计的复杂模块就会发现自己手里没有工具。第三个是供应链风险。模型生成的代码里经常夹带它「记忆」里的库和 API有些是真实存在的有些是幻觉出来的还有一些是许可证不干净的。一个团队如果没做依赖审计和许可证扫描这类风险会在几个月后集中爆发。这不是危言耸听我确实在一个内部工具里发现过一段引用了某个冷门包老版本 API 的代码那个包已经三年没维护了。所以「暂停」这个词我的理解更像是一种夸张的表达方式真正想说的是速度上去了配套的约束没跟上得先把刹车装好。具体怎么装就是下面几节的内容。2. 我这条AI编程流水线从需求到可合并代码2.1 第一步不是写提示词是把需求切到模型能吃的颗粒度新手最容易犯的错是把一整块需求扔给模型然后抱怨它写出来的东西不能用。我的经验是如果你自己都没法用三句话说清楚这块需求模型一定写不对。我给自己定了一个颗粒度标准叫「单函数可验证」——切出来的每一块最终应该对应一到三个函数能独立写测试输入输出明确不依赖外部状态。切到这一步再交给模型命中率会有质的提升。具体的切法我一般这么走先把需求写成一串动词短语比如「解析上传文件」「校验字段」「写入临时表」「生成摘要」。给每个动词短语标注数据流向输入是什么结构体输出是什么结构体。找出其中「模型大概率写不对」的并发、事务、加密、支付标红自己写。剩下的按依赖顺序排个队一次只喂一个。这套流程听起来笨但它解决了一个很实际的问题当模型出错的时候你能立刻判断是需求没描述清楚还是模型能力不够而不是在一堆混杂的代码里大海捞针。切分的颗粒度还直接决定了测试的写法——一个函数对应一个测试文件这是我能想到的最省心的组织方式。提示切分的粒度太细也会出问题。我试过把需求切到「单语句级别」结果是模型失去了上下文写出来的函数之间风格割裂参数命名都不统一。一到三个函数是比较舒服的区间。2.2 上下文准备别让模型猜任何东西模型的输出质量七成取决于你喂进去的上下文。这一点我在早期严重低估过。我现在的做法是给每一个模块准备一份「上下文包」固定在项目里的docs/ai-context/目录下内容包括四样东西接口契约这个模块对外的函数签名、参数类型、返回类型用代码写不用自然语言。数据样例真实的输入输出样本脱敏后放几条越多越好。既有风格样本从项目里挑两三个写得最好、最能代表团队风格的函数原样贴进去。禁止事项不许引入新依赖、不许用某个被团队禁掉的库、不许写同步阻塞调用。第三样是我觉得最被低估的。模型会模仿你给它的风格你贴一段用dataclass的代码它后面就会用dataclass你贴一段全用字典的代码它也会跟着用字典。这比在提示词里写十句「请遵循项目代码风格」有用得多。上下文包是要维护的不是写完就完事。我的做法是每次重构完一个模块顺手把上下文包更新一遍绑定在同一个 PR 里提交。这样过半年回头看上下文包和代码不会脱节。团队里如果有新人让他先读上下文包比读代码效率高多了。2.3 生成、筛选、重写我很少一次就要成品我基本不用「一次生成、直接采用」的模式。更常见的节奏是第一轮只让它给方案不给代码。我通常这么问「这个函数有几种实现路径各自的取舍是什么先不要写代码。」这一步的价值在于它会暴露出一些我自己没想到的边界情况。有一次处理时间区间合并的需求模型列出了「区间相切算不算重叠」「跨天怎么处理」「时区是不是要带」三个问题其中时区那条我确实漏了。第二轮让它写两个版本我做对比。同一个需求用两种不同的思路各写一版摆在一起看。这两版里通常各自的毛病都会暴露出来——A 版简洁但没做错误处理B 版啰嗦但覆盖了边界。我一般在中间取一个。第三轮才是我自己动手。命名改一遍、错误处理补一遍、日志加上、类型标注写全、把模型加的没用上的辅助函数删掉。这一步的时间往往占总时间的四成左右但它才是这段代码真正属于我的部分。这套节奏听着慢实际比「生成完发现不行、推翻重来」快得多。关键是每一轮的产出都是可验证的第一轮验证方案第二轮验证思路第三轮验证细节。2.4 三道质量关卡静态检查、测试、人工评审这是我整条流水线里最不愿意妥协的部分。任何 AI 生成的代码要进主干必须按顺序过三道关顺序不能换。第一道是静态检查。格式、lint、类型、复杂度、循环依赖这一层能拦掉大概三成的低级问题。它的价值在于快——秒级反馈不消耗任何人的注意力。这一层必须在提交前跑而不是等 CI。第二道是自动化测试。单测覆盖率、关键路径的集成测试、以及我自己维护的一组「反例测试」。反例测试是我特别强调的专门针对边界值和异常输入写起来很快但拦下的 bug 特别多。第三道是人工评审。这一层我只关注四件事错误处理是否完整、有没有隐藏的状态耦合、命名是否表达了意图、以及这段代码删掉会怎样。最后这条很好用——如果一段代码删掉之后行为不变那它大概率是模型「顺手加上」的冗余。三道关的顺序之所以不能换是因为成本递增静态检查几乎零成本测试成本中等人工评审最贵。把便宜的工具放在前面人的注意力才能留给真正难的问题。注意不要用「AI 审 AI」。我试过用另一个模型做代码评审它能找出明显的安全问题和风格问题但对业务逻辑的对错几乎无能为力因为它没有业务上下文。它可以作为第一道关的补充绝不能替代人工评审。3. 关键环节实操提示词、检查工具和量化红线3.1 提示词模板的四个必备要素我在网上看过大量「AI 编程提示词大全」大部分都没法用在真实项目里因为它们缺少约束。一个能用的提示词我认为必须有四块内容缺一个都会明显掉质量。[角色与目标] 你是这个项目的后端工程师。现在需要实现 函数名它负责 一句话职责。 [接口契约] 输入OrderBatch字段id: str, items: list[Item], created_at: datetime 输出Summary字段total: Decimal, count: int, failed: list[str] 约束不得抛出异常所有失败项记录在 failed 中返回。 [上下文样本] 粘贴两段项目里同类函数的真实代码 [禁止事项] 1. 不得引入新的第三方依赖 2. 不得使用同步阻塞 IO 3. 不得使用全局变量或模块级可变状态 4. 不要写测试代码测试我自己来四块内容里「禁止事项」是复用性最强的。我把它单独抽成了一个文件叫ai-rules.md每次提问的时候直接附在前面。这样提示词的主体部分只写业务不用重复啰嗦。还有一个小技巧把「不要写测试代码」明确写进去。模型的默认行为是顺手给你写一堆测试这些测试往往是「按实现写的」——它测的是自己刚写的代码而不是需求。这种测试留着是负资产删掉又花时间不如一开始就不要。3.2 静态检查与诊断工具的配置实操静态检查这一层我的原则是「能自动化的一律自动化能在本地跑的不放到 CI」。我的提交前钩子大概长这样用的是比较通用的 pre-commit 框架# .pre-commit-config.yaml repos: - repo: local hooks: - id: format name: 格式化 entry: ruff format language: system types: [python] - id: lint name: 静态检查 entry: ruff check --fix language: system types: [python] - id: type name: 类型检查 entry: mypy --strict language: system types: [python] - id: complexity name: 复杂度检查 entry: radon cc -n C -s language: system types: [python]几个参数值得说一下。radon cc -n C的意思是只报出复杂度等级 C 以上的函数也就是圈复杂度超过 10 的。为什么定 10因为我的经验是圈复杂度超过 10 的函数AI 生成出错的概率会明显上升而人在评审时也很容易看漏分支。定在 C 这一档既能拦住问题又不会天天报噪音。前端项目里我会加上eslint和tsc --noEmit重点关注的规则是no-floating-promises和no-explicit-any。前者能拦住大量「忘了 await」的问题后者能拦住模型为了省事到处写 any 的习惯。这两条规则我加进流水线之后运行时报错的量下降了非常明显。还有一个容易被忽略的点在 IDE 里装代码诊断插件让模型生成的那一刻就报警。我现在用的组合是「编辑器内置诊断 一个专注于未使用变量和死代码的插件」。AI 生成的代码里未使用的导入、未调用的辅助函数、复制粘贴剩下的变量比例高得惊人。这些东西不影响运行但会持续污染代码库越早清理成本越低。3.3 让AI写测试怎么避免写出一堆摆设用 AI 生成测试效率能提升得非常明显但前提是你要给它「不给糖吃」的指令。默认情况下模型写的测试是这个样子def test_parse_ok(): result parse_batch(sample_batch) assert result.count 3这种测试的问题在于它的期望值是从实现里反推出来的而不是从需求里来的。实现改了它就跟着改永远不失败也就永远没有价值。我的做法是把测试生成拆成两步。第一步让模型列测试点不给代码实现。提示词大概是「这是一个函数的接口契约和三条真实输入样例。请列出需要覆盖的测试点包括正常路径、边界值、异常输入、以及你认为容易被实现者忽略的情况。」这一步列出来的清单我会自己筛一遍把重复的和不重要的划掉。第二步才是写代码而且我会明确要求必须包含反例。import pytest from decimal import Decimal from app.parse import parse_batch def test_基本路径(): result parse_batch(make_batch(items[item(a, 10), item(b, 20)])) assert result.total Decimal(30) assert result.count 2 assert result.failed [] def test_空批次不报错(): result parse_batch(make_batch(items[])) assert result.total Decimal(0) assert result.count 0 def test_单项失败不影响其他项(): batch make_batch(items[item(a, 10), item(bad, -1), item(c, 30)]) result parse_batch(batch) assert result.total Decimal(40) assert result.failed [bad] def test_超过上限时截断而不是抛异常(): batch make_batch(items[item(fk{i}, 1) for i in range(1000)]) result parse_batch(batch) assert result.count 500 assert len(result.failed) 500这四个用例里第一个和第二个是模型自己会写的第三个和第四个是我要求补的。区别在哪第三个验证的是「局部失败不污染全局」第四个验证的是「超过容量时的降级行为」——这两条都来自需求而不是来自实现。判断一个测试是不是摆设我的标准很简单把实现改错一行这个测试会不会红。不会红的测试删掉。3.4 我给AI代码定的几张量化红线纯靠感觉管理质量是不行的尤其是当代码量因为 AI 暴涨之后。我给自己和团队定了一套数字化的红线写进 CI 里超了直接拦。指标红线值为什么定这个值怎么测单函数圈复杂度≤ 10超 10 后漏看分支的概率明显上升radon / 内置复杂度工具单文件行数≤ 500便于整体阅读和评审行数统计脚本新增代码单测覆盖率≥ 80%与行业常见实践一致覆盖率工具分支覆盖率≥ 70%防止只测正常路径覆盖率工具重复代码率≤ 3%AI 极易复制粘贴重复度扫描新增第三方依赖需单独评审供应链风险集中在这里锁文件 diff单个 PR 变更行数≤ 400超过后评审质量断崖下降提交钩子统计这里面我想重点说「单个 PR 变更行数 ≤ 400」这条。这是所有规则里最不受欢迎、但效果最明显的一条。原因很简单AI 让生成变便宜了人会不自觉地一次提交更多东西而评审注意力是有上限的。我做过一段时间的统计400 行以下的 PR我平均能找出 2 到 3 个实质问题超过 800 行的 PR我基本上只能看个大概。把这条写进 CIPR 大小被强行压住评审质量立刻回来了。4. 踩坑实录AI代码最常见的故障与排查4.1 六类高频故障与现场表现这两年里我整理了一份「AI 代码故障模式」清单现在基本上看到现象就能定位到原因。下面这几类是最常出现的。第一类幻觉 API。表现是代码引用了一个不存在的方法或参数本地一跑就报 AttributeError 或 TypeError。典型例子是给某个库的方法加了一个看起来很像真事的参数名。排查方法很直接去官方文档或源码里搜一下这个方法签名别信 IDE 的补全。第二类版本漂移。模型训练数据里有多个版本的库它经常把不同版本的 API 混着写。表现是本地能跑、CI 上跑不了或者反过来。排查方法是把所有依赖锁定到具体版本并且在上下文包里明确写清楚版本号。第三类静默的错误吞掉。模型特别喜欢写try: ... except Exception: pass或者except Exception as e: log(e)然后继续往下走。这类代码最危险因为它不报错数据错了你几天后才发现。我的处置方式是全局禁用裸 exceptCI 里加规则直接拦。第四类逻辑看似合理实则颠倒。比如条件判断写反、循环边界差一、默认值选了错误的那一个。这类错误静态检查抓不到只能靠测试。这也是我坚持要写反例测试的原因。第五类并发与状态。前面提过的那次锁外检查就属于这类。表现是偶发、难复现、本地压测不一定能触发。排查手段只能是代码审查加并发测试没有捷径。我的做法是这类代码一律不让模型生成。第六类无意义的抽象。模型很喜欢为了一个只用一次的场景引入基类、接口、工厂。表现是文件数量增加了一倍但业务逻辑没变复杂。判断标准很简单这个抽象有几个实现只有一个就删掉。4.2 依赖和许可证最容易被忽略的雷这一条单独拎出来说因为它的后果最严重、也最容易被忽略。模型生成的代码经常会import一个你没听过的包。我遇到过几种情况一种是真的存在但很小众、已经停止维护一种是存在的库但用了一个已经被移除的旧 API还有一种是许可证类型和项目不兼容。我的应对流程是这样的CI 里跑一次依赖审计和许可证扫描任何新增依赖都会触发人工确认。同时我在ai-rules.md里写死了一条生成的代码不得引入新的第三方依赖需要新依赖必须单独提一个 PR 说明理由。这条规则加上之后新增依赖的数量从每月十几个降到了一两个而且每一个都是真的需要。注意许可证扫描不要只看名字。有些包的名称里带「free」「open」这类词实际许可证可能带有限制条款。工具扫出来之后人工再看一眼许可证原文这一步不能省。4.3 排查速查表下面这张表是我贴在显示器旁边的出问题的时候按顺序过一遍能省下大量瞎找的时间。现象大致率的成因先查什么处置动作本地能跑CI 报错版本漂移、环境变量缺失锁文件 diff、CI 环境变量锁定版本补环境配置报方法不存在幻觉 API官方文档方法签名按真实签名重写偶发数据不一致并发竞态有没有锁外的读写改为原子操作或加锁数据静默丢失异常被吞搜所有 except 块禁止裸 except加日志压测时性能骤降热点路径写法问题循环里有没有查库或建对象批量处理、移出循环覆盖率上不去只测了正常路径反例测试是否缺失按需求补边界与异常用例PR 无人认真评审单次变更过大PR 行数拆分提交卡 400 行代码越写越多越乱无意义抽象与死代码未使用的函数和文件定期清理删掉零调用代码这张表里我想强调最后一行。清理这件事AI 时代比之前更重要。因为生成便宜代码库会自然膨胀而人清理的意愿是有限的。我的做法是每个月固定抽半小时跑一次死代码扫描把零调用的函数和文件删掉。这个动作不做半年后你就会面对一个自己都不敢改的仓库。4.4 团队协作里最容易吵起来的三件事第一件是「AI 生成的代码要不要标注」。有人觉得标注是浪费时间有人觉得必须标。我的折中方案是不在代码里标而是在 PR 描述里用一行说明「本 PR 中 X 文件由 AI 起草后重写Y 文件为纯手工」。这样既保留了追溯信息又不会污染代码本身。第二件是评审标准要不要放松。有一种很有诱惑力的说法是AI 生成的代码正确率高评审可以简化。我坚决反对理由在前面说过评审松一点幻觉和静默错误就会全套进来。我们的做法是标准不变但把评审的重点从「格式和风格」转移到「逻辑和边界」格式那部分交给工具。第三件是效率指标怎么算。如果继续用「代码行数」衡量产出AI 时代这个指标会彻底失灵因为它会激励大家疯狂生成。我们内部改成看三个数需求交付周期、线上缺陷密度、评审发现的问题数。前两个衡量结果第三个衡量过程质量。换成这套指标之后盲目堆代码的现象明显减少。5. 我对「暂停」的理解以及个人层面的应对5.1 工程侧的自律清单「暂停开发」这种说法落到一个具体团队身上最有意义的解读是在流程里加上几道明确的自我约束。我把自己坚持的几条整理成了一份清单可以直接拿去用。不生成的东西并发与锁、事务边界、加密与签名、支付金额计算、权限判定。这几类代码一律手写模型最多做一次对照审阅。必须人工确认的东西新增依赖、数据库迁移脚本、配置文件的默认值、任何涉及删除数据的逻辑。必须有测试的东西所有错误处理分支、所有边界值、所有从外部读入的数据解析。每次上线后要复盘的东西线上缺陷中有多少来自 AI 起草部分占比趋势是升还是降。这份清单的价值不在于它有多完整而在于它是一条明确的线。有了线讨论就不用每次从零开始没有线所有的争论都会变成立场之争。5.2 个人能力保值我这两年刻意保留的三个动作说句实话用了 AI 之后我最怕的不是失业而是自己变懒。所以我刻意保留了三件「低效」的事。第一件是每周手写一个算法或数据结构。不用大可能就是自己实现一个 LRU 缓存、一个简单的限流器。目的不是产出而是保持「在脑子里跑代码」的手感。这个手感一旦丢掉评审 AI 代码的时候你就只能看表面。第二件是读源码。遇到不熟悉的库我会花时间去看它的实现而不是只查文档。这个过程对理解力系统的帮助最大也最能识别出模型写的代码哪里不对。模型很少去看别人的源码它对 API 的理解是统计性的所以它经常忽略一些隐含约定。第三件是自己写测试的期望值。也就是前面说的反例测试我会自己写这部分不交给 AI。写反例的过程其实就是把需求再过一遍脑子这个动作不能外包。这三件事加起来每周大概花三到四个小时。听起来是纯支出但我的体会是它们决定了我在整个流程里是「审阅者」还是「转发者」。区别很大。5.3 这套流程还能往哪扩展如果你已经把上面的流水线跑起来了还能往前走几步我自己正在试的方向有三个。第一个是把上下文包做成结构化的知识库按模块和业务域组织起来让模型在生成之前先检索相关模块和约束而不是靠人手工粘贴。这一步的收益在大型项目里非常明显。第二个是把线上缺陷和对应的代码模式关联起来形成一份「这个团队最容易犯的错」清单直接写进提示词的禁止事项里。比如如果我们发现过去半年有一半的线上问题都来自时区处理那就在规则里写死所有时间处理必须显式指定时区。这种从真实故障里反推出来的规则比通用的最佳实践有用得多。第三个是重构环节的加速。新功能让 AI 起草效率很高但存量代码的重构上模型的优势更明显因为它可以不知疲倦地给出多个方案做对比。我最近一个季度把大部分重构时间花在「让模型给三版方案、我挑一版再改」上比之前自己改快了不少。最后分享一个我用了很久的小习惯每次让模型生成代码之前先自己写下这个函数的输入输出和三条测试条件哪怕只有三行。写完之后再打开对话窗口。这个动作大概花两分钟但它让我的「验收标准」在生成之前就定死了模型写得再好也得按我的标准来而不是我被它的输出牵着走。踩过的坑很多这条小习惯帮我省下的时间是最多的。