把 AI 修过的代码交给另一个 AI 证伪的流水线
让同一个模型评审自己的代码,它只会把自己得出的结论再批准一遍。一个几乎所有代码都和 AI 一起写的独立开发者,把“让另一个 AI 来反驳这个 AI 的工作”这套交叉验证循环固化成常驻流水线的故事。包含自我评审漏掉的 bug 被连续三轮抓出来的真实案例和提示词设计。
核心摘要
审自己活的 AI 总是宽容的。让另一个模型“假设这个修复是错的,加以反驳”,就能挖出自我评审漏掉的缺陷。一个登出 bug 连续三轮暴露出更深缺陷的案例,证伪式框定和证据格式等提示词设计,以及这个循环该在什么时候跑的判断标准。
目录
让 AI 智能体做过代码评审的人都知道:审别人的代码,它抓得挺准。可一让它评审自己刚写的代码,就宽容得出奇。回一句“实现符合意图”,实际跑起来,bug 原封不动还在。
这也不奇怪。同一个模型、同一份上下文里,写代码时犯的错觉,评审时会原样再犯一遍。自己得出的结论自己再批准一次,这个结构与其说是评审,更像是把口令复诵一遍。
所以我把审判搬到了外面。Claude 修过的代码交给 Codex,只提一个要求:“假设这个修复是错的,来反驳它。”
连续三轮,每轮都挖出更深的 bug
第一次真切感受到这个循环的价值,是一个登出 bug。症状很简单:点了登出按钮,偶尔登不出去。Claude 找到原因修了,光读代码挺像那么回事,我也差点就合并了。
让 Codex 去证伪,第一轮回来的答案是:修复本身没错,但认证页面的重定向会在流程走到那条代码路径之前把它截走,修复根本没有执行的机会。修好的代码是死代码。
把重定向也修了,再让它证伪。第二轮又来了。这次的问题出在为防止重复执行而加的“处理中”标志上:在特定时机下,这个守卫会让登出请求本身变成什么都不做的 no-op。到第三轮才等来“找不到可反驳的点”,那时我才合并。
三轮的代码都通过了自我评审。而三轮都被抓住,是因为证伪者找的不是“代码看起来好不好”,而是“有没有能让这个主张崩塌的场景”。
之后这个模式反复出现。有一次服务端修改跑了两遍自我评审,仍然剩下 DB 约束条件里的一个缺失值,是交叉验证抓出来后补了迁移。还有一次重试逻辑的修改,证伪者把一个会把特定超时响应误判为永久失败的边界情况打了回来,判为阻断。全都是“因为像那么回事而差点混过去”的东西。
要的不是评审,是证伪
提示词设计里最重要的是框定方式。说“帮我评审一下”,模型会给出一半夸奖、一半小毛病的四平八稳的回答。说“假设这个修复是错的,去反驳它。只有反驳失败时才放行”,态度就变了。因为通过不再是默认值,而是反驳失败的结果。
在这之上再加三条。
- 强制证据格式。所有指摘都必须带上文件和行号,以及那个缺陷真实爆发的复现场景。“这里看起来有风险”不收。造不出场景,那个怀疑就不算指摘,算驳回。实际上,证伪者自己反驳掉自己的怀疑然后收回的情况相当多,这正是过滤误报的关键机制。
- 判定按等级给。通过 / 小修后通过 / 阻断,三档就够。有了等级,“有指摘但可以合并”的状态和“不能合并”的状态就不会混在一起。
- 在独立上下文里跑。只给证伪者 diff 和相关代码。工作过程中的对话、“为什么这么修”的叙事,一概不给。叙事一旦共享,证伪者也会染上同样的错觉。
再补一条运维心得:证伪者以只读方式运行。给它改代码的权限,它就不指摘了,转而自己上手改,那这次的修改又该谁来验证,问题重新绕回来。角色固定在审判上,修改退回给原来的执行者。
什么时候跑
不是每个变更都跑这个循环。为改个错别字叫证伪者出场是浪费。无条件必跑的是难以回滚的变更:DB 迁移、数据删除、支付和认证路径都在这里。带着“修好了”这种主张的变更也算。bug 修复本来就需要成对的证据:修之前 bug 被复现过,修之后它消失了。证伪者很擅长找这对证据里的缝隙。剩下一类,是我不打算把代码全读一遍的变更。让人把 AI 生成的大 diff 逐行读完,现实里根本撑不住。既然不打算读,至少让另一个模型带着敌意去读。
成本不能不提,但一轮证伪就值几次模型调用的钱。和一个 bug 流到生产环境后再由人来追查的成本比,根本不用犹豫。要是上面那个登出 bug 发布了出去,我就得只攥着“偶尔登不出去”的用户反馈,自己一层层挖出重定向和标志守卫这两层。
为什么必须是另一个模型
用同一个模型开个新会话,也有一定效果。光是上下文分离,就消除了被叙事污染的问题。但滚了几个月下来的结论是:模型本身不同时,抓出来的确实更多。
每个模型的注意力落点不一样。一边对状态管理的时序问题敏感,另一边就对契约违反和边界值敏感。同一个模型的两个会话共享同样的盲区,不同的模型,盲区会错开。交叉验证的价值恰恰来自那个错开。
这个结构应该不陌生。人类团队之所以把代码评审交给作者以外的人,就是同一个道理。和 AI 一起工作,这条原则并没有消失,只是变得便宜到可以常态化执行了。不是作者的审判者,没被叙事污染的眼睛,以及只有在反驳失败之后才给出的批准。人类组织因为太贵而只能偶尔做的事,现在可以永远立在合并之前。
相关文章
- 💻 开发
把多个 AI 智能体放进同一个仓库之后发生的事
原以为并行开几个智能体会话,生产力就能翻倍。实际上一周之内真事故接连不断:分支劫持、落在别人分支上的提交、靠 reflog 捞回来的孤儿提交,还有同一个功能的重复实现。这是共享一个检出的并行智能体所制造的事故类型学,以及一路踩坑后立下的规则。
- 💻 开发
当 AI 说“做完了”时,那个不轻信它的机制
AI 编码智能体常常没干完活就说干完了。没跑测试就说测试通过了,没复现问题就说修好了。一个几乎所有代码都和 AI 一起写的独立开发者,如何做出一道验证关卡,让“完成”只认证据、不认口头承诺。
- 💻 开发
我决定不再相信智能体说的“完成”:verify.sh 关卡 prove-it
AI 编码智能体没跑测试也会报告“测试通过”。这不是撒谎,是结构问题。做一个把报告变成检查的开源关卡 prove-it 的过程中学到的东西,以及自己手写的五行钩子悄悄失效的四种方式。