AI代码审查的真实漏洞:“第二意见”为何沦为表演?
原文:https://dev.to/debashish_ghosal/most-ai-second-opinions-are-theater-i-built-a-system-that-actually-fights-back-1994(作者 @debashish_ghosal)
第一次让两个大语言模型评审同一个Pull请求时,我发现它们89%的“辩论”都是造假。
不是错误,也不是低质量,而是造假。第二个模型在复述预生成的文本。两个模型在交换消息,但立场没有改变,没有引用任何证据——系统却输出了一份充满复杂交锋转录稿的、看似确凿的结论。如果我没有深入查看原始日志,我很可能就这样发布了它,并宣称这是一个可用的系统。
这正是我试图解决的核心问题——不是“如何让两个模型评审同一内容”,而是“如何阻止两个模型在没有真正相互挑战的情况下,表演共识?”因为这是两个完全不同的问题。而我见过的几乎所有的多模型评审流程,都在解决前者,同时默默忽略了后者。
没人谈论的结构性缺陷
大多数“AI第二意见”工作流实际上是这样的:
模型A评审一个制品。模型B评审同一制品——外加模型A的输出。模型B生成回应。你称之为独立评审。
其实不是。那是一种验证。这个区别至关重要。
一旦评审者B的上下文中包含了评审者A的结论,B就不再是在分析了。B所做的事情更接近于抵抗社交压力——这需要主动努力才能克服。人类评审者经常做不到这一点。这就是学术出版中存在双盲评审的原因。几十年前我们就为此发明了这种结构化保障机制。如今我们正在AI中重建相同的认知基础设施,却不知为何总是漏掉了使其奏效的那部分。
其结果是推理管道中的锚定偏见。模型没有损坏,而是系统设计必然导致输出腐败。即使升级到更聪明的模型,你也会得到同样的问题,只是其表述听起来更加自信。
唯一的修复方法是机械性的:从结构上确保评审者B在完全表明自身立场之前,不可能看到评审者A的输出。不是通过提示词,不是通过请求,而是通过机械强制。
这就是 AdversarialDebate 项目的核心思想。
这个问题无处不在——不止于代码
在讲解系统工作原理之前,我认为有必要指出:这不是一个代码评审问题,而是一个推理架构问题,只不过在代码领域最容易被度量。
事件响应。 当所有人同时撰写事后复盘报告时,第二位工程师的根本原因分析会锚定在第一位的分析上。第一位遗漏的诱因会继续被忽略——不是因为没人足够聪明去发现它们,而是因为没人是从零开始的独立分析。
变更管理。 架构师A生成迁移风险评估。架构师B被要求去“验证”。验证不等于分析。B是在审查A的框架,而不是迁移本身。
安全审查。 第一个团队构建威胁模型。第二个团队评审它。第一个团队未建模的攻击向量永远不会进入讨论。
医疗诊断。 第二意见的医生在得出结论前阅读首位医生的笔记,这是一个有据可查的临床问题——它会对诊断准确性产生可衡量的后果。本项目将受监管版本推迟给合作伙伴,但其结构性缺陷是相同的。
法律。 第一律师的风险分析会决定第二律师会费心去阅读什么。
本项目的实地测试策略映射了27个存在此失败模式的领域。我已测试了四个。还有23个。我敢打赌,在大多数领域中,这种崩溃的景象看起来都是一样的。
强制独立,否则一切免谈
对抗辩论(AdversarialDebate) 有一条架构上的硬性规定,说出来并不复杂:在评审员 B 完全提交自己的分析之前,绝对不能看到评审员 A 的回答。
这听起来是理所当然的。但现实中几乎从未被实施。大多数多模型流程都会传递之前的上下文,因为这样更方便——你使用同一个对话线程、同一个 API 调用结构、同一个提示词模板。隔离需要刻意的工程努力。没有人会在无意中构建出这种刻意的摩擦。
该流程如下:
- 输入工件到达——可能是 PR、事件报告、变更请求或安全发现。
- 两个独立的 LLM 同时进行分析,零共享上下文。
- 每个模型提交一份裁决:包含结构化声明、证据引用和置信度。
- 辩论开启——每个模型针对对方的具体声明逐点进行质疑,进行有界限的回合。
- 输出要么是一份联合裁决,要么是一份结构化的异议报告——两种观点都会被保留,并记录未解决的要点。
最后一种输出类型至关重要。大多数多智能体系统都被设计为达成共识。分歧被视为一种失败状态,必须在返回结果前解决。对抗辩论则将保留的分歧视为一种一等输出——如果两个独立模型分析同一事件,却依据不同的证据链得出不同的结论,这种张力本身就是信号。将其强行坍缩为一个裁决,恰恰会丢掉你设置两个评审员原本想要发现的东西。
辩论本身基于一个证据模式进行:每个声明都必须引用工件中的特定文本,每个反对都必须引用一个具体的反声明,每个让步都会被追踪。这正是消除“表演”的关键。当每一步都需要证据时,你就无法伪造同意。当系统要求对具体质疑做出实时回应时,你就无法重放预存的文本。修复前的表演率为 89%,修复后降至 0.2%,在 v0.2.0 版本的全部 217 场辩论中为 0%。
首次现场测试真正发现了什么(以及暴露了什么)
v0.1.0 现场测试 在 70 个来自公共仓库的真实 PR 上运行了 411 场辩论。核心问题是:引擎产生的辩论是否对应了这些 PR 中真正出错的地方?
核心结论成立:81% 的辩论声明与记录的 PR 结果相符。 当系统发现问题时,通常就是真正的问题。一旦提示词设置正确,表演率就接近于零。
但在能信任这些数字之前,我修复了 13 个 bug。最严重的问题是:
- 辩论引擎实际上并未在辩论。 一个存储重放提供者只是在回放预生成的文本。两个模型在技术上交换了消息,但没有任何交互性,表演率高达 89%。替换为实时提供者、加入带证据的提示词后,表演消失了。
- 在真实 PR 正文中解析 CSV 失败。 PR 描述中的逗号导致行记录在中途被错误分割。
DictWriter的引用设置缺失。事后看来显而易见,但在单元测试中无法发现。 - 模型 slug 不匹配。 模型名称中的点在错误的位置变成了破折号,导致静默路由到错误的提供者。
- 基础事实生成逻辑错误。 依赖 GitHub 标签检测,但不同仓库的标签使用不一致。改为手动的结果轮换以获取平衡数据。
这些都不是“架构错误”类的 bug,而是“你还没有在真实仓库上大规模运行过”这类 bug。真实数据总会发现与合成数据不同类别的问题。
v0.1.0 → v0.2.0:实际变更内容
详见完整变更日志。摘要版本如下:
关键修复:
- 非 PR 输入解析损坏。 该流程假设工件布局是扁平的 PR 结构。事件报告、变更请求、安全发现使用的是嵌套结构。重写了具有领域感知能力的工件遍历逻辑。
- HTML 实体正在破坏非代码类工件。 事件报告正文包含的 HTML 破坏了解析器。为非 PR 内容增加了清洗层。
- 基础事实合并错误。 这个问题很严重。一个连接逻辑错误已静默地将数据集从 2,333 行缩减到了 359 行。v0.2.0 的早期数字是错误的——我在不知情的情况下,仅基于真实数据集 15% 的片段进行报告。通过手动追踪行数发现了此问题。修复后使用了完整的 2,333 行数据集,所有数字重新运行。
改进内容:
语料库从仅包含 70 个 PR 工件,扩展到涵盖四个领域的 150 个工件。确认的默认配对组合为 gpt_mistral——表演率为零,收敛分数为 0.536,在四个领域中生成了最有价值的争议。在修正后的 2,333 行数据集上,二元匹配率达到了 88.7%。部分匹配率为 11.3%。虚构内容接近于零。总成本:运行 360 次评审,仅花费 0.42 美元。
最后一个数字总让人难以置信。四个模型,150 个工件,四个领域,每个都进行了完整的有界限辩论回合。成本不到一杯咖啡。算力不是瓶颈。瓶颈在于提示词工程和基础事实的度量——而这两项并不会通过为推理投入更多资金就变得更便宜。
为什么 GPT-4o-mini + Mistral 胜出,而这并非显而易见
这里我有一个超越数据本身的理论。
经过实地测试确认的默认搭配是 GPT-4o-mini 和 Mistral Small 3.2。不是 GPT-4o 完整版 + Gemini Ultra,也不是“当前最强模型”的组合。而是一个小型 OpenAI 模型加一个小型 Mistral 模型。
数据显示,GPT+Gemini 产生了最糟糕的辩论:大量反驳轮次,几乎为零的让步率,几乎无法得出结论。DeepSeek+Mistral 以 97% 的达成率收敛,并伴有有意义的让步。GPT+Mistral 则处于富有成效的中间地带:存在真实的争论、实质性的让步,最终要么达成共识,要么形成有据可查的分歧。
关于原因,我的理论如下:
GPT-4o-mini 通过 RLHF 强力训练,对人类偏好信号极其敏感。 OpenAI 实际上在 2025 年曾回滚过一次 GPT-4o 的更新,原因正是该模型变得过于谄媚——被公开描述为“奉承且随和,到了支持明显妄想观点的程度”。该模型家族有着倾向于妥协的已知特性。如果让两个这样的模型对决,它们会很快达成一致,因为两者都被训练为偏好和谐。
Mistral 由一家欧洲实验室构建,其训练目标和约束条件不同。 它不像美国那些重度依赖 RLHF 的模型那样带有相同的“合规优化”。Mistral 更可能在压力下坚持立场,更愿意强力反驳它判定为错误的说法,更不倾向于将分歧柔化为外交辞令。它并非被训练得同样“随和”。
当将它们配对时,你会得到不对称的辩论动态。 GPT-mini 倾向于做出自信的初始主张,但当面临具体证据的挑战时会做出让步——其训练机制奖励“有帮助的让步”。Mistral 则倾向于更长时间地坚持立场,并更激烈地推动反驳轮次。模型们向不同方向拉扯,这正是你想要的。两者都不会立即妥协,也不会在所有证据面前固执己见。
GPT+Gemini 的失败是同一问题的另一面。 两者都是经过 RLHF 优化的、来自大实验室的、“有帮助且无害”的模型。它们的推理先验相似,不是因为架构,而是因为它们被训练去满足相似的人类偏好分布。它们在表面细节上意见一致,在结论上意见一致,生成大量词汇但实际分歧极小。你得到的是用更华丽词汇包装的表演。
我的假说是:预测辩论质量的不是模型能力,而是训练目标的多样性。 来自具有相似 RLHF 哲学的实验室的模型会迅速收敛,因为它们共享优化目标。而在不同框架、不同文化背景、不同安全权衡下训练的模型,其分歧是真实存在的。
我尚未对此进行严格证明。但这与我在实地测试数据中观察到的一切相符,也是我的模型选择建议首先提出“从不同实验室选择”,之后才考虑基准分数的原因。
已测试与候选配对
以下是我实际运行过的,以及我认为值得下一步测试的配对:
| 配对 | 状态 | 辩论质量 | 表演性率 | 最佳领域 |
|---|---|---|---|---|
| GPT-4o-mini + Mistral Small 3.2 | ✅ 已测试 (v0.1, v0.2) | 高 - 有成效的争论,真实的让步 | 0% | 代码审查、安全 |
| DeepSeek-V3 + Mistral Small 3.2 | ✅ 已测试 (v0.2) | 高 - 强收敛性 (0.572) | 0% | 事件响应 |
| GPT-4o-mini + Gemini 2.5 Flash | ✅ 已测试 (负控制组) | 低 - 无尽轮次,无结论 | ~0% 但内容空洞 | 不推荐 |
| GPT-4o-mini + GPT-4o-mini | ✅ 已测试 (同质控制组) | 极低 - 立即收敛 | 0% | 仅用于对照 |
| Claude Sonnet + Mistral Medium | 🔲 尚未测试 | 假设:高 - 不同推理风格 | 未知 | 变更管理 |
| Llama 3 (微调) + Mistral | 🔲 尚未测试 | 假设:中等 - 取决于微调效果 | 未知 | 特定领域 |
| 法律/临床微调模型 + GPT | 🔲 合作伙伴限制 | 未知 | 未知 | 受监管领域 |
领域特定 LLM 的问题
这里有一些我尚未完全探索但一直在思考的问题:如果你根本不使用通用模型会怎样?
实地数据表明,GPT-mini + Mistral 在代码审查方面效果很好。但事件响应开始退化——事后分析的叙事结构与适用于 diff 文件的主张提取模式不匹配。v0.2.0 中那 11.3% 的差距就集中在这里。
值得验证的假说是:使用针对特定产出类型进行专门训练的模型,而非通用模型。
- 对于代码审查:使用一个在代码上训练重的模型(如 DeepSeek-Coder, CodeLlama)对阵一个通用推理模型(如 Mistral)
- 对于事件响应:使用一个具有强因果推理能力的模型(可能是 Claude,它擅长追踪因果链)对阵一个具有强事实依据的模型
- 对于安全:使用一个安全领域微调模型对阵通用模型——安全领域有专门的术语和威胁分类法,通用模型处理得不一致
- 对于法律:使用专门为法律构建的 LLM(如 Harvey, Spellbook)对阵通用模型——训练数据上的分歧将是巨大的
理论在于:你不该选择两个推理方式相似的模型。审查者配对应当能反映出对审查对象截然不同的认知模型。代码专家与通用型模型产生的分歧点,会与两个通用型模型之间的分歧点大不相同。代码专家可能会捕捉到通用型模型忽略的架构气味;而通用型模型可能会发现专家一笔带过的文档缺口。
这便是我目前尚无法回答的 v0.3.0 问题:审查模型的领域专业化是否能提高差异性问题发现率,还是会在某些方面限制讨论从而损害覆盖范围? 我不知道。我希望能找出答案。
我们尚未尝试的:RAG
还有一件事,我在所有这些实地测试中完全未曾使用,我认为这是此处最值得探索的杠杆:检索增强生成。
AdversarialDebate 中的每个审查者目前仅基于审查对象本身进行推理——即 PR diff、事故报告或变更提案。模型调用其预训练知识,阅读该对象,然后形成主张。仅此而已。除了训练数据中所包含的,没有任何领域背景信息。
试想,如果将每个审查者锚定在特定的领域知识库中,情况会如何改变:
- 安全审查者 能够访问你组织的威胁模型历史、过往的 CVE 分析、内部安全准则。它不必从头发明一个安全框架——而是基于你实际的安全基线进行推理。
- 事故响应审查者 能够查阅你的运维手册、过往的故障分析报告、SLO 定义。像“此次事故违反了延迟 SLO”这样的主张,就能基于你具体的阈值来断言,而非泛泛猜测。
- 变更管理审查者 能够获取你的架构决策记录、依赖关系图谱、过往的迁移故障分析报告。它能以“我们在 2024 年尝试过这种方法,并导致了 X 问题”来质疑某项提议——这是任何通用模型都无从知晓的。
这对模型选择的意义重大:一个辅以强大 RAG 基础的小模型,可能比一个在完全陌生环境下运行的大模型产生更优质的领域辩论。 基于丰富检索语料库进行推理的 GPT-4o-mini,可能比仅依靠权重进行推理的 GPT-4o 提出更相关的主张——因为这些辩论的瓶颈不在于原始智力,而在于主张生成时能否获得正确的上下文。
它还创造了一种你可以刻意利用的有趣不对称性:给审查者 A 提供 RAG 访问权限,关联一个知识库(比如你的安全威胁模型);给审查者 B 提供 RAG 访问权限,关联另一个知识库(比如你的系统可靠性历史)。现在,他们的独立分析不仅源于不同的推理先验,更源于不同的知识库。辩论将浮现出安全关切与可靠性关切之间的张力——而这在真实的架构决策中,往往正是最关键的张力所在。
我们尚未测试这一点。我不知道基于 RAG 的审查者是否能引发更优的辩论,或者检索噪音是否会淹没辩论信号。但这将是下一个有意义的实验——并且它将彻底改变模型选择问题的性质。问题不再是“哪个预训练模型最适合这个领域”,而是“哪个模型最善于基于检索到的上下文进行推理,以及每个审查者应该看到什么样的上下文。”
无法解释的 11.3%
- 7% 的二元匹配率听起来不错。与单次审查相比,这确实是个进步。
但问题在于:假阴性是隐形的。
如果两个独立模型都漏掉了同一个真实问题,并一致认为“看起来没问题”,系统就会给出一个干净的结论。在下游环节问题暴露之前,你根本无从得知判断是错的。假阳性很烦人——你只是去调查了一个并不存在的问题。但假阴性很危险——它让你产生了不应有的信心。
那 11.3% 部分错误或完全错误的判断,主要集中在事件响应和变更管理领域——这些叙事性领域的判定依据本身更为模糊。这不是随机噪声。它表明通用提示词无法在不同领域均匀奏效。代码是结构化的:差异有清晰的边界,变更有明确的责任人,行为有可靠的测试。而事件报告是叙事性的,变更提案是论证性的。那些在代码审查中有效的主张提取和证据模板,无法直接移植过来。
针对这一切,架构本身的防御机制就是“独立性”要求。要求两个模型独立地漏掉同一个问题,才会给出“没问题”的结论,这比单次审查的标准要高得多。但“标准高得多”并不等于“万无一失”,我也不会告诉你它绝对可靠。
接下来的路——以及真正的难点
还有 23 个领域未经验证。这是目前诚实的路线图。
现有的四个领域证明了引擎可行,并确立了测量框架。接下来的领域会更难,因为当你离开代码领域,真实情况的判定会变得越来越模糊。对于代码拉取请求,你可以通过代码回退来验证。但对于法律风险或临床诊断,你无法运行自动化的真值核查——你需要领域专家来评估辩论质量。对每个层级而言,测量问题都完全不同。
现场测试暴露出了一些尚未完成的具体问题:
- 基于LLM生成的解决建议——当辩论以“保留异议”结束时,由第三个综合模型提出解决路径。这在 v0.1.0 版本中被有意推迟,至今尚未实现。
- 领域专用的提示包——通用提示词对代码有效,但在处理事件叙事时效果会下降。每个领域都需要针对主张提取和证据模板进行调优。
- 大规模下的结论稳定性——在 v0.2.0 中,对两个采样工件的测试显示一致性为 100%。但这不是样本量,只是一个占位符。
这些都不是什么登月计划。它们决定的是一个研究项目与一个你敢在生产环境中信赖的工具之间的区别。
试试看,搞破坏,告诉我哪里有问题
代码、现场测试报告、架构文档以及辩论模式定义,全部托管在 github.com/deghosal-2026/adversarial-debate。采用 MIT 许可证。更新日志记录了所有曾出现的问题及其原因。
如果你所在的领域很重视“第二种意见”,并且认为这个工具有用,我希望能听到你的反馈。如果你认为我的模型配对理论有问题,我更希望听到这些。这个项目最有趣的部分,正是来自那些比我懂得多的人的挑战。
我唯一确信的一点是:如果第二种意见已经看到了第一种意见的结论,那它就不是独立的。你可以称之为复核,也可以称之为验证,但不能称之为独立分析。
大多数 AI 审查流程都悄无声息地建立在这种混淆之上。而这个项目不是。
你在哪些地方遇到过这种“并不独立的第二种意见”——无论是在代码审查、法律、医学还是工程设计领域——我都想知道我发现的这种模式是领域特定的,还是仅仅当你不再仔细审视信息流时,“审查”一词的本质。请在评论区分享。
原文:https://dev.to/debashish_ghosal/most-ai-second-opinions-are-theater-i-built-a-system-that-actually-fights-back-1994(作者 @debashish_ghosal)