AI 审查 AI:代码审查智能体的非对称架构
如果代码审查流水线中的作者和审阅者都是在重叠语料库上训练的语言模型,那么它就不是一个质量关卡,而是一个信心放大器。作者编写的代码在 Transformer 看来是合理的,审阅者以同样的合理性视角阅读代码,双方最终达成“看起来没问题”的共识,于是代码变更带着一个绿色的勾合并了,而这对于变更是否真正正确毫无意义。最近的行业数据清楚地展示了这种不对称性:在同等规模下,与 AI 共同编写的 PR 产生的严重问题(critical issues)比人类编写的高出约 40%,重大问题(major issues)高出约 70%,其中逻辑和正确性错误占了差距的大部分。而为了捕捉这些错误而发布的审阅代理(reviewer agents),从构造上来说,恰恰是最不具备发现这些错误能力的。
那些从 AI 代码审阅中获得真实信号的团队已经不再将“审阅”视为“生成”的某种变体,而是开始将审阅设计为一种本质上不同的认知任务。生成式提示词(Generation prompting)要求模型产生连贯的内容。而审阅式提示词(Review prompting)则必须要求模型发现缺失的东西——去关注 Diff 中的负空间而不是正空间——这种反向思维比一行系统提示词所暗示的要难诱发得多。
为什么审阅提示词不是生成提示词的反向操作
代码生成存在于一个生成循环中:模型提议 Token,下一个 Token 的分布奖励合理性,输出结果看起来像训练分布,因为这就是损失函数所要求的。代码审阅则需要相反的立场。审阅者必须阅读已完成的文本并询问“这里漏掉了什么”——缺失的空检查、不存在的错误路径、未处理的状态转换、未测试的边界情况,或者是函数假设但从未验证的前置条件。这些失败都不会作为可读的内容出现,它们表现为“本应存在却不存在”的东西,而一个训练用于预测下一个合理 Token 的模型,在发现缺失方面机制较弱。
随之而来有两个实际后果。首先,朴素的审阅提示词(“审阅此 Diff 并寻找 Bug”)表现不佳,因为它们让模型自己决定审阅意味着什么,而默认行为通常是总结和赞扬,而非审问。其次,显而易见的修复方法——列举每一项检查的冗长且详细的审阅提示词——则会向另一个方向过度矫正:审阅代理开始制造问题以证明它阅读了 Diff,误报率攀升至工程师们开始无视机器人的地步。关于 LLM 代码编辑的研究也记录了同样的模式:详细的提示词可能会引入过度找错的偏见,模型会在正确的代码中标记不存在的错误,因为提示词奖励了“看起来很彻底”的审阅。
避开这种权衡的团队将审阅视为具有明确锚点的结构化分析任务。Meta 最近在半形式化推理模板方面的工作大幅提升了代码审阅的准确性,它强制模型陈述前提、针对特定测试用例追踪执行路径,并从有名有实的证据而非“感觉”中得出结论。其机制很简单:当提示词要求“如果 X 那么 Y 因为 Z”的链条时,幻觉产生的问题显然比有据可查的问题更经不起推敲,模型会自动抑制那些让 AI 审阅感觉像噪音的通用风格挑刺。
多阶段架构:规范、不变性与对抗
单阶段审阅——一个模型、一个提示词、一个响应——在上述不对称性面前表现糟糕。其通过率上限受限于生成式训练模型在一次阅读变更行时能注意到的内容,而这恰恰是与生产环境中最致命问题相关性最低的一组问题。在大规模场景下有效的架构会将审阅分解为多个专门的阶段,每个阶段都有不同的问题和不同的上下文形式。
规范检查(Specification check)首先运行,只询问一件事:此 Diff 是否实现了规范或工单要求的内容?审阅者会获得问题描述、面向用户的需求以及 Diff,并被禁止对风格、性能或修饰进行评论。该阶段的成功与否取决于对意图的忠实度。类似于 SpecRover 的方法将测试和规范视为可执行契约,能捕捉到一类 Bug——补丁通过了 CI 但实际上并没有解决客户的问题——这种 Bug 是任何逐行审阅都无法发现的。
不变性检查(Invariant check)随后运行,询问变更是否保留了系统现有的属性。上下文不仅是 Diff,还包括周围的模块、调用 的函数以及隐含的契约(例如:此函数返回非空、此列表已排序、此状态机永远不会从 archived 转为 active)。大多数微妙的生产环境 Bug 都是原作者认为显而易见的不变性冲突;必须告知审阅代理这些不变性是什么。
对抗性探测(Adversarial probe)最后运行,并提出最有用的一类问题:攻击者、恶意输入或混沌测试会如何破坏这段代码?对抗性代码审阅模式将构建代理(Builder Agent,优化速度和合成)与评论代理(Critic Agent,优化敌对性推理)分开,并要求评论者尝试推翻构建者关于变更正确的说法。这种模式之所以奏效,是因为评论者的成功衡量标准是拒绝而非批准——其提示词奖励的是找到反例,而不是签字通过。
当多个评论者并行运行时,协调员(Moderator)的角色就从可选变成了结构性的必然。三个独立的阶段会产生重叠的问题,如果将所有问题直接抛进 PR 线程中,正是那种让工程师学会忽略机器人的失败模式。博弈论式的多代理设计通过将分析(只读的评论者)与合成(负责去重、按严重程度排序并撰写最终审阅意见的单一协调员)分离来处理这一问题。协调员也是强制执行精准度底线的天然场所:如果一个发现无法链接到特定的行、特定的后果以及能够捕捉到该回归问题的特定测试,它就不会被发布。
“审查者非作者”约束
在 AI 代码审查中,最被低估的架构决策是“由哪个模型审查哪些代码”。当审查者代理(Reviewer Agent)和作者代理(Author Agent)共享同一个基础 模型时,它们会继承相同的盲点——相同的训练数据、相同的对齐(Alignment)偏好,以及在预训练期间从未因遗漏而被惩罚的相同模式。由 Claude 编写并由 Claude 审查的函数,本质上是一个伪装成两个环节的单点故障。
跨家族审查(Cross-family review)打破了这一困局。来自不同家族的三个模型,在被给予相同的 diff(差异比对)和刻意设置的不同上下文框架时,可以降低一个模型的解释“毒害”另一个模型的概率。其核心机制并非某个模型更聪明,而是它们的失效模式(Failure modes)是不相关的。在漏洞检测基准测试中表现出色的“批判者-验证者”(Critic-Verifier)架构直接利用了这一点:基于云端的专家代理从互补的角度(结构、安全、控制流)分析代码,而另一个独立的本地验证者——专门针对专家代理的误报(False-positive)分布进行训练——负责最终裁定。
对于大多数团队来说,这体现为一个实用的规则:不要让编写代码的模型成为审查代码的模型。如果你的 IDE 助手和你的 CI 审查者来自同一个供应商的同一个模型版本,你就构建了一个闭环,而唯一的故障信号只能来自于最终阅读合并提交(Merged commit)的人类。
Diff 感知上下文:改变的是语义,而非行号
另一个在无形中破坏审查质量的上下文决策是:如何向模型呈现 diff。大多数早期的审查机器人只是将 unified diff(统一差异格式)直接丢进提示词中,并称之为上下文。结果就是,审查评论过度关注发生变化的行,而忽略了变化所在的“语义邻域”——例如,现在传递了错误类型的调用方、不再覆盖重命名分支的测试,或者仍假设使用旧列名的数据库迁移。
实践者们已经总结出了一些能显著提升效果的模式。使用 unified diff(而非文件重写式响应)能大幅减少模型的“偷懒”行为——Aider 发布的数据显示,当提示词格式为 unified diff 时,“偷懒”响应减少了约 3 倍。感知数据块(Hunk-aware)的上下文窗口包含了变化行所在的函数,以及任何被修改签名的调用点,这给了审查者足够的空间来推断连锁反应。符号级检索(Symbol-level retrieval)——获取 diff 中引用的每个类型和函数的定义——现在已成为严肃审查流水线的标准配置,因为如果没有它,审查者就在推理一个它从未见过的 AccountState。
扼杀天真方案的约束是上下文预算(Context budget)。一次简单的依赖升级就可能让 diff 膨胀到五万行,而超过一定限度后,将所有内容塞进模型窗口不仅会增加成本,还会降低输出质量。有效的 diff 感知审查流水线会投入精力研究“哪些行值得进行上下文扩展”——标记高爆炸半径(High-blast-radius)的数据块(如安全敏感文件、公共 API 签名、模式迁移、并发原语的变更)以包含完整上下文,而对模板化(Boilerplate)的数据块仅进行粗略扫描。信噪比的博弈就在这种分流处理中决定胜负。
评估:植入缺陷与回归测试集
运行代码审查代理最难的部分在于确定它 是否真的有效。采纳率(Acceptance rate)——即导致代码变更的机器人评论占比——是引用最多的指标,但孤立来看也是最具误导性的。一个只标记风格问题的机器人会有很高的采纳率(工程师会修复这些琐碎问题),但与预防 Bug 毫无关联。而一个标记真实并发问题的机器人采纳率可能较低(工程师会争论,然后修复,有时又会不予理会),但与减少下游事故高度相关。你真正想要的指标是热修复(Hotfix)是否按季度下降;你今天能衡量的指标是采纳率;而两者之间的差距,正是审查代理投资回报率(ROI)主张的博弈空间。
两种评估模式区分了“确切知道审查器是否有效”的团队和“仅寄希望于其有效”的团队。植入缺陷基准测试(Planted-bug benchmarks) 将已知的错误变更注入到正常的 PR 流中,并测量审查者是否能发现它们——例如明显位置的空指针引用(Null dereference)、新端点缺失权限检查、分页中的差一错误(Off-by-one)、错误路径中遗忘的清理操作。基准测试必须定期更新;一旦审查者针对静态的植入集进行了微调,它就会产生过拟合,召回率(Recall)也将无法预测生产环境中的表现。
历史回归测试集(Historical regression suites) 甚至更有用:提取过去 12 个月中人类审查发现(或未能发现并发布)的生产环境 Bug,重构引入这些 Bug 的 diff,然后通过审查代理重新运行。机器人在该集合上的召回率会告诉你,你的审查者对哪类真实 Bug 存在“结构性盲点”——“结构性盲点”是这里的关键,因为一个漏掉过某类 Bug 的审查者还会再次漏掉它,通过植入基准测试发现这个差距,要比通过 P0 级事故发现它的代价小得多。
较新的行为评估模式——观察开发者是在收到审查评论后修复了代码 还是忽略了它——在某种程度上比精确率/召回率标签更真实。它们避开了“这条评论是否正确”的问题,转而代之以“工程师是否将这条评论视为有价值的信号”。后者才是审查工具真正优化的目标,而且这些数据是运行工具时免费产生的副产品。
组织失效模式
上述技术模式是必要的,但并不充分。大多数失败的 AI 代码评审方案,其原因在于任何架构都无法绕过的组织性问题。评审机器人上线后,第一周在每个 PR 下发表上百条评论,工程师们学会了直接划走,一个月内该机器人就成了形同虚设的背景板。解决方法不是更好的提示词,而是在上游强制执行的准确率底线。如果团队不愿意让评审机器人仅在不到一半的 PR 中发表评论——因为发表评论的门槛应该是“我有一个具体且可证伪的担忧”,而不是“我注意到这里有个 diff”——那么机器人就会被无视,而它是否真的能抓到 Bug 也就成了一个学术上的伪命题。
更深层次的失败在于“不对称论点”所预测的情况:那些将 AI 生成的代码推入 AI 评审流水线的组织,正在进行一场实验,其原假设(“这能捕捉到关键问题”)从未在最严苛的场景下(“作者和评审者具有共同的盲点”)得到验证。做得出色的团队会按顺序完成三件事:他们对评审者进行跨模型系列(cross-family)部署,通过埋入 Bug 和历史回放对其进行检测,并执行严格的准确率纪律——将每一个误报都视为评审机器人的 Bug,而不是必经的业务成本。没能做到这些的团队最终会发现,他们 PR 上的绿色勾选标记是由一个旨在生成 绿色勾选标记的系统提供的,而原本应该下降的 Bug 数量,只是掉进了仪表盘的另一个统计项中。
代码评审的形式正在发生变化。以前由资深工程师在下午 4 点阅读 diff 时投入的认知劳动,正被分解为规范检查、不变性探测和对抗性验证——每项运行的成本更低、速度更快,且比人工评审更具一致性。但这些分解都无法解决根本性的约束:如果评审流水线的作者和评审者在统计学上是等同的,那么它只是一个信心机器,而不是质量门禁。架构必须刻意保持不对称,否则它根本算不上真正的评审。
- https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report
- https://www.helpnetsecurity.com/2025/12/23/coderabbit-ai-assisted-pull-requests-report/
- https://devblogs.microsoft.com/engineering-at-microsoft/enhancing-code-quality-at-scale-with-ai-powered-code-reviews/
- https://arxiv.org/html/2509.01494v1
- https://arxiv.org/html/2506.10322v1
- https://arxiv.org/html/2508.16419v2
- https://asdlc.io/patterns/adversarial-code-review/
- https://arxiv.org/html/2604.21282v1
- https://news.ycombinator.com/item?id=47360961
- https://aider.chat/docs/unified-diffs.html
- https://abhikrc.com/pdf/ICSE25.pdf
- https://www.seas.upenn.edu/~asnaik/assets/papers/tse24_ticoder.pdf
- https://venturebeat.com/orchestration/metas-new-structured-prompting-technique-makes-llms-significantly-better-at
- https://arxiv.org/html/2508.12358v1
- https://arxiv.org/html/2504.20196v1
- https://www.coderabbit.ai/blog/framework-for-evaluating-ai-code-review-tools
- https://bryanfinster.substack.com/p/ai-broke-your-code-review-heres-how
