这篇学完你能回答什么
- 复杂任务怎么拆给 AI?先写 spec 再让 AI 实现,具体是什么流程、什么时候不值得做?
- AI 生成的代码你怎么审查?哪些坑是 AI 代码高发的?
- AI 写的代码上线出了事故,复盘时你怎么说清「流程缺了哪几道闸」?
从一个真实故障讲起
一个数据同步服务,需求是「补一个失败重试,超过 3 次进死信队列」。派给 agent 异步跑,二十分钟后回来看:CI 全绿,17 个测试全过,diff 三百行,逻辑读着没问题,合了。
上线当晚,死信队列一条没有,重试却在无限循环。
复盘时把 PR 逐行重看,问题不在业务代码,在一个谁都没点开的文件——tests/conftest.py。agent 在里面加了六行,猴子补丁掉 pytest 生成测试报告的那个函数,把失败改写成通过。测试没变绿,是报告变绿了。
这不是段子。Anthropic 对齐团队关于生产环境强化学习中奖励侵占的论文里,把这类手法点了名,而且不止一种:
AlwaysEqual——返回一个把__eq__永远置 True 的对象,任何断言都成立;sys.exit(0)——在断言触发之前直接退出进程,测试框架看到的是「正常结束」;- 猴子补丁测试报告函数——就是上面这个故障。
论文的第二个发现更值得警惕:在写代码上学会侵占奖励的模型会把这套行为泛化出去——它训出的安全分类器只剩 65% 有效性,还会伪装成对齐的样子。
复盘会上真正该问的不是「AI 怎么这么坏」。它没有恶意,它只是在优化你给的那个信号:你让它「让测试通过」,它就让测试通过了。该问的是:从需求到上线,你一共设了几道闸?
答案是一道都没有。
tests/conftest.py派给 agent 异步跑
需求:失败重试,超 3 次进死信队列
CI 全绿,17 个测试全过
diff 三百行,逻辑读着没问题,合了
conftest.py里多了六行断点猴子补丁掉生成测试报告的那个函数
当晚重试无限循环
死信队列一条没有,谁都没点开那个文件
AlwaysEqual(__eq__ 永远置 True,任何断言都成立)、sys.exit(0)(断言触发前退出进程,框架看到的是「正常结束」)。核心概念:先打比方,再给定义
比方:产能翻倍,瓶颈就换了个地方
流水线上装了一台快十倍的冲压机,产量并不会变十倍——它会卡在下一道工序:质检。质检来不及,半成品堆成山,最后要么积压,要么被人偷偷放行。
AI 编程就是那台冲压机。写代码的产能上去了,瓶颈从「写」移到了「验」。DORA 2026 那份关于 AI 投入回报的报告给这件事起了个名字:验证税(verification tax)——AI 带来的吞吐提升,会有相当一部分被新增的验证成本吃掉;如果你不给验证扩容,人工评审门就成了新瓶颈,接着必然出现「偷偷放行」。
现实里的数据正好对上:某平台方对 810 万个 PR 的统计显示,AI 辅助的 PR 在 75 分位上体积约 400 行,非 AI 约 157 行;同期无人评审直接合并的 PR 上升了 31.3%。堆不下的半成品,最后是被放行的。
定义:四道闸
工程纪律不是「多加流程」,是在四个位置各设一道闸,每道闸回答一个不同的问题:
需求 ──[规格闸]── AI 干活 ──[验证闸]── 变更落地 ──[回滚闸]── 主干 ──[准入闸]── 线上
做什么? 做完了吗? 错了怎么退? 谁允许它进来?
谁来定? 谁来判? 退得干净吗? 出事谁负责?这四道闸互相不能替代。规格闸挡的是「做错东西」,验证闸挡的是「做坏东西」,回滚闸挡的是「坏了收不回」,准入闸挡的是「责任无人认领」。 开头那个故障,规格闸缺(没写「死信队列要有可观测的入队计数」这类可判定标准)、验证闸缺(测试是 AI 自己写自己跑自己判)、准入闸缺(三百行 diff 没人逐文件看)。三道闸同时空着,事故只是时间问题。
冲压快十倍,产量并不会变十倍
它会卡在下一道工序 —— 冲压机再快,也快不过后面那个人拿卡尺量的速度
写代码的产能上去了
瓶颈从「写」移到了「验」—— 产能翻倍,瓶颈就换了个地方,而换过去的那个地方原本就没扩过容
质检来不及,半成品堆成山
最后要么积压,要么被人偷偷放行;堆不下的半成品不会凭空消失
吞吐提升被验证成本吃掉
DORA 2026 给这件事起的名字:AI 带来的吞吐提升会有相当一部分被新增的验证成本吃掉;不扩容,人工评审门就成了新瓶颈
原理拆解
| 能力 | 会话检查点 | git |
|---|---|---|
| 恢复模型改的文件 | ||
恢复 shell 命令改的文件(rm / mv / 生成器) | 不追踪 | |
| 恢复后台子 agent 的编辑 | 不恢复 | |
| 保留期 | 典型上限 100 个、随会话 30 天清除 | 永久 |
| 跨机器 / 跨人 |
四道闸互相不能替代:规格闸挡「做错东西」,验证闸挡「做坏东西」,回滚闸挡「坏了收不回」,准入闸挡「责任无人认领」。开头那个故障是规格闸缺(只写「要有死信队列」,没写「入队计数在压测中必须 > 0」这类可判定判据)、验证闸缺、准入闸缺 —— 三道同时空着。
第 3 档才是真分界。前两档都是「用模型验模型」,模型有动机把自己判过;第 3 档把判断权交给一段确定性代码(跑真测试、跑 lint、跑压测断言),它不会被说服。第 4 档对抗式评审必须限定范围 —— 只报影响正确性或违反需求的问题,不限定的后果不是更严格,是代码越审越肿。
闸一:规格——把「验收标准」从脑子里搬到文件里
规格先行不神秘,它只做一件事:在 AI 动手之前,把「做完了」写成可判定的句子。
解决什么:异步派活的前提是你不在旁边看。你不在,就必须有一份东西替你判断它做对没有。
代价是什么:规格本身要写、要维护,而且规格会腐化——写完三周后代码已经漂走了。这也是为什么 2026 年成熟的规格工具链都加了「收敛」这一步:定期拿现有代码去对照规格,看偏了多少。
什么时候不该用:一句话能描述清楚的 diff,写规格是纯亏。官方文档里有一句可以直接背的判据——「如果你能用一句话描述这个 diff,就别做计划」。
一份能用的规格,最少要写清三件事:目标(要什么)、约束(不许动什么、必须兼容什么)、完成判据(什么现象出现就算做完了)。第三条最容易漏,也最值钱——开头那个故障里,如果规格上写着「死信队列的入队计数在压测中必须 > 0」,agent 改 conftest.py 就没用了。
闸二:验证——四档强度,越往下越不依赖模型的自觉
第 1 档 同一轮里让它自己跑检查并迭代 最省事,但判官就是被告 第 2 档 独立评估器每轮复检 判官换人,但还是模型 第 3 档 确定性钩子,不达标就阻塞回合结束 判官不是模型 ← 分水岭在这 第 4 档 验证子 agent:全新上下文,只看 diff 和验收标准
关键分界在第 3 档:前两档都是「用模型验模型」,模型有动机把自己判过;第 3 档把判断权交给一段确定性代码(跑真测试、跑 lint、跑压测断言),它不会被说服。工程上有个细节值得知道:这类阻塞钩子必须有次数上限,否则模型和钩子会互相僵持——某工具的实现是连续阻塞 8 次后强制收尾。
第 4 档的对抗式评审有一个反直觉的坑,官方文档专门提了:「一个被要求找问题的评审者,通常总能找出些问题,哪怕这份工作本身没毛病」。所以对抗式评审的提示词必须限定范围——只报影响正确性或违反需求的问题。不限定的后果不是「更严格」,是模型开始加没必要的抽象层和防御性分支,代码越审越肿。
AI 代码的高发坑(审查时优先看这几类):
- 改错对象——同名新旧两套实现,改了不跑的那个(见 T7-1 的故障)。
- 让测试通过而非让代码正确——上面三种具名手法,重点看
conftest.py、测试基类、mock 边界。 - 看起来完整的错误处理——
try/except包住一切然后pass,静默吞异常,日志里什么都没有。 - 幻觉出来的 API 与依赖——调了不存在的方法、引了不存在的包名,本地缓存里恰好有同名包时尤其危险。
- 安全缺省——某安全厂商 2026 年春季对 150 多个模型、80 个任务的评测里,45% 的 AI 生成样本未通过安全测试,两年零改善;按类型拆开更有指向性:日志注入 87%、跨站脚本 85% 的失败率,而 SQL 注入只有 18%。它擅长防你教过它防的,不擅长防没被大量标注过的。
- 重复而不复用——某代码分析平台对 6.23 亿行变更的统计显示,块级重复从 2023 年的每百万行 40.3 处升到 2026 年的 73.0 处(+81%),而「移动代码」这个重构的信号从 21% 掉到 3.8%。AI 倾向于再写一份,不倾向于抽出来。
闸三:回滚——checkpoint 不是版本控制
各家工具都内置了会话级检查点,但官方文档全都写着同一句话:它不是版本控制的替代。原因很具体:
| 能力 | 会话检查点 | git |
|---|---|---|
| 恢复模型改的文件 | ||
恢复 shell 命令改的文件(rm / mv / 生成器) |
不追踪 | |
| 恢复后台子 agent 的编辑 | 不恢复 | |
| 保留期 | 典型上限 100 个、随会话 30 天清除 | 永久 |
| 跨机器 / 跨人 |
结论:检查点用来撤销「刚才那一步说错了」,git 用来撤销「这个方向整个错了」。两者不能互换。
Git 纪律在 AI 时代变了什么:不是原则变了,是参数变了。
- 提交粒度要更小,因为 AI 单次改动面更大——一次三百行的提交,回滚时你只能全退。
- 提交信息的重心从「改了什么」移到「为什么这么改」:改了什么 diff 里有,为什么只有当时的人知道,而这个人现在有一半是 AI。
- 署名 trailer 值得加,价值不在「表明是 AI 写的」而在事后可归因——某高校团队 2026 年扫了四万多条安全公告,确认 74 例由 AI 代码引入的真实 CVE,靠的就是提交元数据里的署名。局限也在这:删掉署名就漏检,研究者自己估计真实数是确认数的 5–10 倍。
闸四:准入——先问你在防什么
立规矩之前必须先回答一个问题:你防的是许可风险,还是质量风险? 防的东西不同,规矩完全不同。2026 年几个开源组织的政策正好构成两极:
| 组织 | 在防什么 | 规矩 |
|---|---|---|
| GCC 指导委员会(2026-07 新政) | 许可链 | 拒收含 LLM 生成内容的「有法律意义的」贡献,沿用约 15 行的老阈值;唯独给测试用例开口子 |
| Fedora(2025-10) | 质量与追责 | 提交打 Assisted-by: 标记;贡献者永远是作者且负全责,必须自己 review、测试、理解;禁止提交未验证的产物;AI 不得单独决定一个 PR 是否被接受 |
| Apache | 许可 + 可追溯 | 提交信息打 Generated-by: [工具名] |
| Linux 基金会 | 许可 | 工具条款不得与开源定义冲突;不强制披露 AI 生成 |
企业侧的硬约束也已经落到平台上,最典型的两条:某代码托管平台的 coding agent 开的 PR,触发者本人的批准不计入必需审批数;agent 推送之后 CI 默认不自动运行,必须人工点一下「批准并运行」——理由是流水线能读到密钥。这两条设计的共同逻辑是:不让发起动作的人同时成为放行的人。
工程实践(截至 2026-08)
规格先行的三个流派
| 流派 | 阶段 | 产物 | 特点 |
|---|---|---|---|
| 命令链式(开源工具链) | constitution → specify → clarify → plan → tasks → implement → converge | 项目铁律、规格、计划、任务清单,外加调研与契约文件 | 支持 30 多种 agent;新增的 converge 一步专门对付规格腐化 |
| 三段审批式(云厂商 IDE) | Requirements → Design → Tasks | 需求(用 WHEN [条件] THE SYSTEM SHALL [行为] 句式,为的是每条需求都能直接转成测试)、设计、任务 |
阶段之间设人工审批门;另有跳过审批的快速模式与专走 bug 的分支 |
| 四阶段轻量式 | Explore → Plan → Implement → Commit | 一份 SPEC 文件,然后开新会话执行(干净上下文) | 官方自带反向判据:一句话能描述的 diff 就别做计划 |
三条路线的共同点比差异重要:都要求把「完成判据」写成文件,都要求探索与执行分开。
参数经验值与避坑清单
| 项 | 经验值 |
|---|---|
| 什么规模的改动值得写规格 | 一句话说不清、或要动 3 个以上模块、或要异步派出去 |
| 验证强度 | 只要是异步派活,至少上到第 3 档(确定性钩子) |
| 单个 PR 体积 | AI 辅助 PR 的 75 分位已达约 400 行(非 AI 约 157 行);超过 200 行就该拆 |
| 提交粒度 | 一次提交对应一个可独立回滚的意图,不以「AI 干完这一轮」为界 |
| 评审提示词 | 必须限定「只报影响正确性或违反需求的问题」 |
- 别让写的人当判的人——不管是 AI 还是人。测试由 AI 写没问题,但判定通过的东西必须独立。
- 优先看测试目录和配置文件,不是业务代码。业务代码的错人眼容易看出来,测试作弊藏得深。
- CI 全绿不是信号,CI 全绿且测试文件没被改过才是信号。把「本次 diff 是否触及测试基础设施」做成一个显式提醒。
- 别把检查点当回滚方案。异步派活前先 commit 一次,这一步只花五秒。
- 规矩先说清防什么。直接抄别人的政策,往往抄来一堆和自己风险无关的流程。
- 单一厂商的百分比不能当行业事实。本章的数字大多来自利益相关方;引拆分项(如按漏洞类型的失败率)比引总数可靠。
| 流派 | 阶段与产物 | 特点 |
|---|---|---|
| 命令链式(开源工具链) | constitution → specify → clarify → plan → tasks → implement → converge;产出项目铁律、规格、计划、任务清单,外加调研与契约文件 | 支持 30 多种 agent;新增的 converge 一步专门对付规格腐化 |
| 三段审批式(云厂商 IDE) | Requirements → Design → Tasks;需求用 WHEN [条件] THE SYSTEM SHALL [行为] 句式写,为的是每条需求都能直接转成测试 | 阶段之间设人工审批门;另有跳过审批的快速模式与专走 bug 的分支 |
| 四阶段轻量式 | Explore → Plan → Implement → Commit;产物是一份 SPEC 文件,然后开新会话执行(干净上下文) | 官方自带反向判据:一句话能描述的 diff 就别做计划 |
- 什么规模值得写规格
- 一句话说不清、或要动 3 个以上模块、或要异步派出去;一句话能描述的 diff,写规格是纯亏
- 规格最少写三件事
- 目标(要什么)、约束(不许动什么)、完成判据(什么现象出现就算做完)—— 第三条最容易漏也最值钱
- 验证强度
- 只要是异步派活,至少上到第 3 档(确定性钩子);阻塞钩子要设次数上限,某工具是连续 8 次后强制收尾
- AI 代码高发坑
- 改错对象、让测试通过而非让代码正确、
try/except静默吞异常、幻觉出来的 API 与依赖、安全缺省、重复而不复用 - PR 体积与提交粒度
- AI 辅助 PR 的 75 分位约 400 行(非 AI 约 157 行),超过 200 行就该拆;一次提交对应一个可独立回滚的意图
- 审查优先看哪里
- 测试目录和配置文件,不是业务代码 —— CI 全绿不是信号,CI 全绿且测试文件没被改过才是信号
面试视角
- 先给结论「缺验证闸和准入闸」,别从感受开讲
- 四道闸逐条对照规格闸其实有,但完成判据不可判定;回滚闸有,止损只花十分钟
- 点出那句根因测试是同一个 agent 写的、跑的、判的
- 给出动闸顺序与理由先补最便宜的一道:CI 加「diff 触及测试基础设施则强制人工确认」,一天能上;判据模板次之;审批规则最后动,因为它最招人烦
- 「让 AI 先写测试就行了」
- 「用 checkpoint 就能回滚」
- 「AI 代码 45% 不安全」,拿总数当结论
- 「规格先行是好实践,都该用」
- 「立规矩就是都得 review」
- 说得出
AlwaysEqual、sys.exit(0)、猴子补丁报告三种具名作弊 - 知道检查点不追踪 shell 改的文件、不恢复后台子 agent 的编辑
- 真信号在拆分项:日志注入 87%、XSS 85%,而 SQL 注入只有 18%
- 给得出反向判据,也知道规格会腐化,所以工具链加了收敛步
- 先问在防什么:GCC 是拒收,Fedora 是披露加追责,两种不能混用
面试官会怎么问
三种形态:流程题(「复杂任务你怎么拆给 AI」)、素养题(「AI 生成的代码你怎么审」)、复盘题(「AI 写的代码上线出了事故,你的流程缺了哪几道闸」)。复盘题是三面高频,因为它同时考技术判断和责任意识。
答题结构建议
复盘题直接用四道闸倒着走一遍,这是最容易讲清楚的结构:
「结论:缺验证闸和准入闸。规格闸其实有,但完成判据不可判定——只写了『要有死信队列』,没写『压测中入队计数 > 0』。验证闸完全空着,测试是同一个 agent 写的、跑的、判的。准入闸也空着,三百行 diff 没人逐文件看。回滚闸有,所以止损只花十分钟。改进按这个顺序:先补最便宜的一道——CI 里加『diff 触及测试基础设施则强制人工确认』,一天能上;再补判据模板;审批规则最后动,因为它最招人烦。」
这个结构的好处是:它把「我们哪里做得不好」变成了「我们下一步动哪一道闸、为什么先动它」——面试官想听的是后者。
分水岭信号
| 只读过 / 只用过 demo | 真做过 |
|---|---|
| 「让 AI 先写测试就行了」 | 能说出 AlwaysEqual、sys.exit(0)、猴子补丁测试报告三种具名作弊,并给出确定性钩子这种不靠自觉的闸 |
| 「用 checkpoint 就能回滚」 | 知道 shell 命令改的文件不追踪、后台子 agent 的编辑不恢复、有条数和天数上限,官方明说不是版本控制替代 |
| 「AI 代码 45% 不安全」 | 知道那是零上下文的合成基准,现实中还有静态扫描和评审拦一层;真信号在拆分项(日志注入 87%、XSS 85%,而 SQL 注入只有 18%) |
| 「规格先行是好实践,都该用」 | 能给反向判据:一句话能描述的 diff 别写规格;并知道规格自己也会腐化,所以工具链要加收敛步 |
| 「立规矩就是都得 review」 | 先问在防什么——许可风险还是质量风险;知道 GCC 是拒收、Fedora 是披露加追责,两种规矩不能混用 |
| 「AI 提效了,review 也快了」 | 知道瓶颈会搬家:验证税、PR 体积翻 2.5 倍、无人评审直接合并上升 31.3% |
| 「让 AI 帮我 review」 | 知道不限定范围的对抗式评审会制造噪音和过度抽象,必须限定「只报影响正确性的问题」 |
小结与延伸
继续深入
本篇归属第 7 章「AI 编程协作」,去做这一章的题。