单次 AI 审查容易产生大量误报导致人工忽略真正问题。建议拆分为「Find」和「Verify」两阶段:首阶段宽泛收集所有候选问题,次阶段独立验证每个发现,只保留可构造具体输入或场景证明的问题。
大多数团队构建的第一个 AI 代码审查工具在一个月内就会被抛弃。原因并非它漏掉了 bug,而是它发现了太多根本不是 bug 的东西,审查者不再阅读它的评论。一旦人类发现一半的发现都是噪音,他们就会跳过所有评论——包括那些真正有价值的。这比完全不运行审查更糟:它训练团队忽略一个偶尔才传递真信息的渠道。
直觉上的解决办法是收紧 prompt——"只报告高置信度问题"、"保守一些"、"不要挑剔"。这有一点帮助,但很快就会遇到瓶颈,因为根本问题不在于单次调用中的置信度校准,而在于单次调用没有办法检查自己的工作。
将审查拆分为两次调用,而非一次,由两个独立的 Agent 执行,且不共享上下文:
Find(发现)。 一个 Agent 阅读 diff,报告它能找到的每一个候选问题——正确性 bug、安全问题、缺失的测试。这里刻意撒大网;召回率比精确率更重要。
Verify(验证)。 对于每个候选问题,第二个 Agent 只获得该发现和相关代码——不获得第一个 Agent 的推理过程——并尝试构造一个具体的输入或场景,让所声称的 bug 真正被触发。如果它无法做到,该发现就被丢弃。
只有通过验证的发现才会作为评论发布。其他一切静默消失——人类审查者永远不会看到那个单次调用 Agent 本来会以同等置信度暴露出来的、被丢弃的 60%–70%。
这之所以有效,是由于结构性原因,而非提示技巧:发现者和验证者在优化不同的东西。被告知去抓 bug 的发现者会匹配那些看起来像 bug 的模式——缺失的空值检查、不寻常的控制流——因为这就是"抓 bug"所奖励的行为。验证者的唯一工作是"证明这个特定说法成立,否则就毙掉它",它没有动机去含糊其辞。强迫它命名一个具体的失败输入才是真正的过滤器——"这理论上可能是个问题"过不了这个门槛,但"用空数组调用它会抛出异常"可以。
那些上线单次调用审查的团队通常会按顺序注意到相同的三个症状:
第一周: 审查者印象深刻,AI 在第二天就抓住了一个真正的 bug。
第二周: 审查者开始看到一些技术上是真但无关紧要的问题(一段死代码中风格相关的"问题"、一个函数的"缺失"测试,而该函数在其他地方已被平凡覆盖)。
第三周及以后: Bot 的 PR 评论遭到不加阅读的直接 resolve。二十个里面那一个真正的发现和其余的一起被埋没了。
验证步骤不只是减少噪音量——它改变了存活下来的东西的种类。只做发现的 Agent 的输出按每个问题听起来有多合理排序。带验证的 Agent 的输出按每个问题是否被论证排序。审查者可以在几个 PR 内分辨出区别,而这决定了他们是否继续阅读。
给发现者 Agent 一个狭窄的契约:它狩猎什么(正确性、安全性、缺失的测试覆盖),以及它明确忽略什么(风格、格式、命名偏好)。没有忽略规则的发现者会愉快地在两个真正的 bug 旁边报告一百个 nit。
只把发现和它引用的代码传给验证者——不传发现者的推理链。如果验证者看到发现者的理由,它倾向于同意它而不是独立检查它。
要求验证者生成一个具体的失败场景,而非置信度分数。"我 80% 确定这是个 bug"不是验证;"这些输入导致这个崩溃"才是。
随时间记录确认率(通过验证的发现 ÷ 总发现)。如果它攀升到接近 100%,你的发现者可能已经开始少报以取悦验证者——把它的网扩大回来。
所有这些都不需要特定工具。这是一个控制流模式:两次调用,一道关卡,记录丢弃率。值得构建的版本是让审查者信任每一条评论、足以不自己重新推导 bug 就采取行动——那是唯一真正重要的数字。
完整 playbook,包括如何将其接入 PR 流水线以及运行后追踪什么:https://agentkitworks.com/use-cases/automate-code-review