跳到主要内容

AI 审查 AI:代码审查智能体的非对称架构

· 阅读需 14 分钟
Tian Pan
Software Engineer

如果代码审查流水线中的作者和审阅者都是在重叠语料库上训练的语言模型,那么它就不是一个质量关卡,而是一个信心放大器。作者编写的代码在 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 线程中,正是那种让工程师学会忽略机器人的失败模式。博弈论式的多代理设计通过将分析(只读的评论者)与合成(负责去重、按严重程度排序并撰写最终审阅意见的单一协调员)分离来处理这一问题。协调员也是强制执行精准度底线的天然场所:如果一个发现无法链接到特定的行、特定的后果以及能够捕捉到该回归问题的特定测试,它就不会被发布。

“审查者非作者”约束

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates