一个编码智能体配上一个AI审查员,会陷入同一种循环:审查员读一遍PR留下意见,智能体逐条修改,每次修改生成一个新提交,审查员再跑一遍。循环在第一次“没东西可改”的审查处停下。

打开网易新闻 查看精彩图片

这个停止规则和CI里“跑到绿为止”的重试逻辑是同一个毛病。测试套件里有几个不稳定的用例,有人加了重试:跑到通过为止。结果构建变慢,而一个只在半数情况下出现的真实失败,靠一次侥幸的绿就跑过去了。停在哪一次,由噪声决定。

Anthropic的Claude Code团队量过这件事。在Claude写下约90%代码的环境里,改规则之前,8.9%的内部PR需要五轮或更多审查。有一个PR一夜之间跑了五十多轮。后期轮次里审查员反复标记的内容,约59%是改动之前就已经存在的代码。

长尾消耗的是CI时间和注意力。更安静的那笔账是:单次干净的审查是弱证据,因为审查员在任何一遍里都可能漏掉一半的bug。

三类意见让循环停不下来

琐碎意见(Nit)每一遍都是新的,因为审查员每次重新采样。旧代码里的问题会重现,因为旧代码还在那里。同一个问题还会换一种说法回来。智能体把它们全修了,每次修都开启新一轮。

直接限制轮数,或者只复审新增的行,都能减少轮次,也都漏bug。每一遍完整审查,都是对上一遍漏掉的真bug的一次独立复查。在模拟中,审查员的召回率是50%,即每个真bug每遍有一半概率被抓到。砍掉噪声但只要求一次干净审查,逃逸率是24.4%;嘈杂的循环逃逸率是13.1%,它多出来的轮次成了意外的二次检查。

什么能阻塞,什么时候能停

只有阻塞性发现才启动修复轮。定义是:新的、被标记为重要、且落在本次PR改动的行上。已存在的问题记进清单一次即可。发现以文件、行号、规则三者识别,重复的直接丢弃。连续两次干净审查才算过闸,第二次干净审查只花一次审查,不产生智能体提交,也不跑CI。

模拟里,双干净审查闸把每个PR的修复轮从2.07降到1.01,没有任何PR需要五轮,逃逸率维持在13.1%,与嘈杂循环持平。代价是13.4%的PR在三轮之后转给人工。

三个角色各改一处

  • 配置审查员的人决定什么被标记为重要:只认改动行上的bug并附文件行号证据,风格、命名、清理一律标为Nit,未触及的代码标为Pre-existing。要求审查员以JSON输出发现,规则名在各遍之间保持稳定,闸门靠它匹配重复。
  • CI侧把闸门设为每次推送的必需状态检查,为每个PR维护一个小状态文件,跨推送累计干净遍数和修复轮数。改动行用git diff --unified=0对合并基取,避免变基把旧行算成新行。
  • 智能体的指令里写明:只修闸门列为阻塞的发现,不修Nit和Pre-existing,不为满足意见去改diff之外的行。缺了这条,智能体照样修琐碎意见,每条都花一个提交。

怎么知道它生效了

切换之前,先记录每个PR的修复轮数、需要五轮以上的比例,以及合并后的回滚情况。两周后轮数应下降,五轮长尾应消失。升级到人工的比例应接近十分之一,远高于此说明重要标签太松。回滚情况不能变差,若变差说明只是把工作挪了位置,此时应先提高所需的干净审查遍数。

什么时候不该用双干净审查闸

两遍不独立时,第二遍帮不上忙。温度为零、提示和上下文都相同的审查员,可能两次返回同样的答案,第二次干净什么也证明不了。需要在两遍之间变动点什么:换一个模型、换一个关注点,或者让并行审查员投票。

代码由人写时,作者会顶回琐碎意见,很少全修,这个闸门要控制的循环基本不形成。改动小且风险低时,一次干净审查就够,审查遍数应与风险匹配。还有一种情况:没人处理升级。把13%的PR送给人,就得有人接,否则这些PR只是躺在那里,工程师会学会绕开这项检查。

采用之前先问三个问题:审查员的两遍之间真的有差异吗?三轮修复之后谁来接手这个PR?今天的回滚情况如何?