AI法庭:多Agent对抗评审如何破解大模型自查失效难题

AI法庭:多Agent对抗评审如何破解大模型自查失效难题 《Show HN: The courtroom inside our AI——AI 内部法庭机制拆解与多 Agent 对抗评审实战》先复现一个几乎每个用大模型写代码的人都经历过的场景。你让模型写一段 SQL它交出来一段看起来结构完整的查询。你把它丢进数据库报语法错误。你把报错贴回去它道歉并重写第二次还是错。你让它自己检查一遍它信誓旦旦地回答这段 SQL 已经正确。问题到底出在哪其实不是模型“态度”不好而是单次生成和单次自评本质上是用同一套概率分布在完成两种任务。没有新信息、没有对立视角模型很难发现自己推理链上的漏洞。最近 Show HN 上出现了一个非常值得琢磨的标题The courtroom inside our AI直译过来是“我们 AI 内部的法庭”。从标题看作者把多 Agent 协作或者说自我校验收敛成了一套审判流程有人负责挑错有人负责辩护有人负责裁决。项目本身的具体实现细节我们无法从标题中读到但这类工程模式的核心思路完全可以拆解成一套可落地的方法论。这也是本文最想做的事不评价具体产品而是把“AI 法庭”这个设计模式讲透并给出一个最小可运行的 Python demo。读完这篇文章你会理解多 Agent 对抗评审为什么比“让模型自查”可靠知道法庭模式里的角色提示词应该怎么写能完整体验一遍从立案、质证、辩护到宣判的流程也会清楚在实际项目里接入这类机制时最常踩的坑。无论你是 AI 应用开发者、对 Agent 工程感兴趣的后端工程师还是正在做 AI 产品方案的技术负责人这篇文章都值得读完再收藏。1. 为什么单次推理解决不了纠错问题1.1 模型的“自信”与“幻觉”来自哪里先明确一个前提大模型没有独立的“验证模块”。它的核心能力是逐 token 预测下一个词也就是在给定上文的情况下从概率分布中采样。多步推理之所以容易出错是因为任意一步的概率选择都可能偏离正确方向而后面的内容会顺着这个偏离方向继续生成最终形成一段读起来很流畅、但事实或逻辑站不住脚的输出。这就是“幻觉”的机制来源。它更像概率生成机制的副产物而不是模型故意编造。理解了这一点就能理解为什么很多工程同学的第一反应“让模型自己再检查一遍”往往无效你没有给它新的信息源也没有给它新的推理视角它只是用同一套参数重新生成一次。1.2 自查为什么低效让同一个模型评价自己的回答至少会遇到三个问题。第一视角同源。生成答案和评估答案用的是同一个概率分布模型对错误点的注意力很难发生实质转移。第二顺从倾向。指令微调和 RLHF 阶段模型被训练成一个乐于助人的助手在收到“你刚才是不是说错了”这类指令时它倾向于道歉甚至可能把本来正确的答案也改成错误版本。第三缺乏锚点。如果模型不知道什么是正确答案它无法通过“再想想”凭空获得一个外部验证信号。所以最简单的“自查”只能改善非常明显的格式错误对深层逻辑漏洞基本无能为力。这也是为什么业内开始研究自我一致性、Reflexion、多 Agent 辩论等更复杂的方案。1.3 对抗式评审的工程价值对抗式评审的核心思路是引入一个以上拥有不同立场的角色让它们对同一份答案进行交叉检验。质疑方负责寻找漏洞辩护方负责判断质疑是否成立最终由评审方在双方交锋的基础上给出结论。这个设计之所以有效不是因为它增加了参数量而是因为它改变了模型的注意力和推理路径。当模型被要求“找出这段答案里的错误”时它会倾向于搜索反例和异常点当模型被要求“为这段答案辩护”时它又会重新组织支持理由。两种任务叠加等于在同一模型内部开辟了多条不同的推理轨迹再通过评审将轨迹收敛回一个更可用的结果。成本是要付出的多角色、多轮调用会带来更高的 Token 开销和延迟但换来的是更低的幻觉传播概率和更可审计的决策过程。对于高价值场景这笔成本通常值得。2. 法庭模式把纠错变成一场结构化审判2.1 为什么是“法庭”而不是“辩论”法庭隐喻在工程上最大的价值是它给了多 Agent 协作一个清晰的终止条件和输出契约。辩论可以是发散性的双方越辩越多但不一定有结论。而法庭审判天然包含质证、辩护、合议、宣判这些阶段最终必须产出唯一判决。把这个结构搬到 AI 系统里就得到了一个非常适合工程化的多 Agent 流程。法庭角色工程组件核心职责原告 / 质疑方批评者 Agent找出答案中的事实错误、逻辑漏洞、边界条件遗漏被告 / 辩护方辩护者 Agent对有依据的质疑做出让步对无依据的质疑进行反驳法官 / 合议庭评审 Agent综合双方陈述给出裁决并输出最终答案陪审团可选多重采样 Agent对争议问题提供平行意见适合高风险场景从工程实现看每一个角色本质上就是一个带有不同 System Prompt 的 LLM 调用。所谓“法庭”就是一套对这些调用的编排节奏和决策规则。2.2 角色提示词的三条设计原则很多人在实现时会犯一个错误把角色写得太“像人”。比如“你是一位经验丰富的资深律师性格严谨擅长从细节中发现破绽”。这类拟人化描述对模型的能力调用没有实质帮助反而让输出变得不可控。更好的做法是遵循三条原则。第一任务边界清晰。直接告诉模型“你的输入是什么你的输出是什么你只能做什么”。不要让它自由发挥。第二要求具体证据。质疑方不能只说“我觉得这里有风险”必须指出是事实错误、逻辑错误还是边界条件遗漏并给出理由。这一点直接决定了法官之后能否做出可靠裁决。第三输出结构化。法官必须输出 JSON包含裁决类型和最终答案质疑方必须输出限定数量的质疑点。结构化输出是后期解析和自动化判断的基础。3. 法庭模式与常见方案对比在实际项目中处理大模型输出质量不只有“开法庭”一条路。把各类方案放在一起看会更清楚它的定位。方案原理优势局限直接回答单次推理速度快、成本低幻觉高无法自校正自我评价同一模型“再想想”实现简单视角同源经常无效Self-Consistency多次采样投票在数学类任务表现好开放性问题不稳定成本线性上升Reflexion记录错误并反思能积累长期经验需要额外评估器迭代周期长RAG / 工具校验引入外部知识或执行环境能补知识盲区对推理错误没有直接帮助Multi-Agent Debate多角色自由辩论覆盖面广难以收敛没有明确终止条件法庭模式结构化对抗评审 法官裁决收敛性好输出契约清晰Prompt 工程复杂Token 成本高从中可以得出一个判断法庭模式本质上是多 Agent 辩论的结构化升级版。它没有放弃“对抗”只是给对抗加了规则、加了法官、加了判决书。这样的设计天然适合接进一个完整的 AI 应用流水线因为它有明确的输入、输出和终止条件。4. 环境与模型选型4.1 最低环境要求本文的 demo 代码非常轻只依赖两个 Python 库运行环境要求很低。Python 3.9 或更高版本代码用到了 dataclass 和类型注解。openai 库用于调用模型接口。pyyaml用于读取配置文件。安装命令如下pip install openai pyyaml版本没有写死你可以在自己的虚拟环境里使用当前稳定版本。如果网络受限也可以用国内镜像源安装。4.2 模型选型建议Demo 阶段最简单的方式是让一个模型扮演所有角色。OpenAI 兼容协议下只需要改一下model参数就能切换不同的模型。生产环境建议采用“分层模型”策略法官使用能力更强的模型因为它要对最终结果负责质疑方和辩护方可以使用推理能力稍弱但更便宜的模型。这既降低了成本又让不同角色的能力差异成为系统设计的一部分。4.3 OpenAI 兼容协议意味着什么OpenAI 兼容协议指的是服务端暴露/v1/chat/completions接口客户端使用 OpenAI SDK 就能调用。现在很多主流推理服务都支持这个协议包括本地部署的 Ollama、vLLM以及各类云服务提供方。这意味着你的应用层代码不需要针对不同后端做适配只要在创建OpenAI客户端时传入对应的base_url即可。切换模型对代码几乎没有侵入性这对工程化是一个很大的便利。5. 核心流程拆解一场完整审判怎么走5.1 立案输入任务标准化一个请求进入系统第一步不是直接调用大模型而是把它转成一个结构化的“案件”。这里至少需要包含问题本身、预设的答案策略、期望的输出格式以及该问题是否值得启动法庭模式。在 demo 中我们简单使用一个问题字符串作为输入。5.2 初审生成原始答案先让一个普通的生成角色回答原始问题得到original_answer。这一步可以理解为“被告的原始陈述”。设置较低的温度可以让初始答案更稳定、更保守。5.3 质证质疑方挑出关键问题质疑方收到的问题材料包括原始问题、当前答案、前序庭审记录。它需要在约定的数量限制内指出最可疑的问题并给出理由。这里真正容易踩坑的地方是质疑方可能会“为了挑错而挑错”。所以 prompt 中必须强调质疑必须有依据无依据的怀疑不要列出。5.4 辩护应诉方回应辩护方阅读质疑意见后逐条回应。辩护并不是无条件维护原答案而是要求模型在证据充分时承认问题并提出修改建议在证据不足时给出反驳依据。辩护环节最怕出现“无原则道歉”。很多模型在指令微调后对批评非常顺从会直接把正确内容改掉。所以 prompt 里要明确写如果质疑方证据不足必须反驳而不是为了讨好用户而认错。5.5 复庭多轮交叉审查一轮质证和辩护结束后可以设置最多轮次让双方在既有结论基础上继续交锋。demo 默认两轮实际项目建议不超过三轮。轮次越多Token 消耗增长越快边际收益也会递减。5.6 评议法官阅读庭审记录法官拿到的是完整庭审记录而不是单轮结论。它会判断哪一方的论证更有依据原始答案是否需要修改。这里的关键是法官必须拥有最终裁决权并且它的输出必须符合预先定义的 JSON 格式。5.7 宣判输出最终答案与裁决理由法官的输出就是整个法庭流程的终点通常包含三个字段verdict表示裁决类型reason表示裁决理由final_answer表示裁决后的最终答案。下游系统可以根据这个 JSON 决定使用哪个版本。6. 完整代码实现最小可运行 demo为了让这套思路能够被直接复现我把代码拆成三个文件llm.py负责统一模型调用courtroom.py负责法庭流程编排main.py负责加载配置和运行入口。同时使用一个config.yaml管理运行参数。6.1 统一模型调用层# 文件路径courtroom/llm.py from typing import List from openai import OpenAI def chat( messages: List[dict], model: str gpt-4o-mini, temperature: float 0.2, max_tokens: int 1024, ) - str: 统一的模型调用入口。 默认走 OpenAI SDK通过环境变量 OPENAI_API_KEY 读取密钥。 如果你连接本地推理服务或兼容接口可以在创建客户端时指定 base_url。 client OpenAI() resp client.chat.completions.create( modelmodel, messagesmessages, temperaturetemperature, max_tokensmax_tokens, ) return resp.choices[0].message.content这个模块之所以独立出来是为了后续切换模型时只需改这一个文件。无论你用的是云端大模型、本地 Ollama还是 vLLM 部署的模型只需调整OpenAI()的初始化方式。6.2 法庭主流程# 文件路径courtroom/courtroom.py import json from dataclasses import dataclass, field from typing import List, Dict from llm import chat ROLE_PROMPTS { prosecutor: ( 你是 AI 法庭中的质疑方。你的任务是从事实、逻辑、边界条件三个角度 审查给定答案找出它最可疑的问题。每轮最多列出 3 个质疑点 每条必须说明理由禁止使用可能不够好这类空泛表述。 ), defender: ( 你是 AI 法庭中的辩护方。你的任务是为给定答案辩护但辩护必须基于证据。 如果质疑方说得对就承认问题并提出修改建议如果质疑方证据不足 就给出你的反驳依据不要无原则地反复道歉。 ), judge: ( 你是 AI 法庭中的主审法官。你要根据质疑方与辩护方的陈述对原始答案做出裁决。 请严格输出 JSON 格式字段包括verdict取值 maintain/moderate/overrule、 reason裁决理由200字以内、final_answer裁决后的最终答案。 ), } dataclass class CourtSession: question: str model: str gpt-4o-mini max_rounds: int 2 temperatures: Dict[str, float] field( default_factorylambda: { prosecutor: 0.7, defender: 0.5, judge: 0.0, } ) def run(self) - dict: # 1. 生成原始答案 original_answer chat( [{role: user, content: self.question}], modelself.model, temperature0.3, ) trial_log [] # 2. 多轮质证与辩护 for round_no in range(1, self.max_rounds 1): prosecutor_msgs [ {role: system, content: ROLE_PROMPTS[prosecutor]}, { role: user, content: ( f问题{self.question}\n f当前答案{original_answer}\n f前序庭审记录{json.dumps(trial_log, ensure_asciiFalse)}\n f第 {round_no} 轮请提出质疑。 ), }, ] criticism chat( prosecutor_msgs, modelself.model, temperatureself.temperatures.get(prosecutor, 0.7), ) defender_msgs [ {role: system, content: ROLE_PROMPTS[defender]}, { role: user, content: ( f问题{self.question}\n f当前答案{original_answer}\n f质疑意见{criticism}\n f第 {round_no} 轮请进行辩护。 ), }, ] defense chat( defender_msgs, modelself.model, temperatureself.temperatures.get(defender, 0.5), ) trial_log.append( {round: round_no, criticism: criticism, defense: defense} ) # 3. 法官合议与宣判 judge_msgs [ {role: system, content: ROLE_PROMPTS[judge]}, { role: user, content: ( f问题{self.question}\n f原始答案{original_answer}\n f完整庭审记录{json.dumps(trial_log, ensure_asciiFalse)}\n 请给出最终裁决并直接输出 JSON。 ), }, ] verdict_output chat( judge_msgs, modelself.model, temperatureself.temperatures.get(judge, 0.0), ) return { question: self.question, original_answer: original_answer, trial_log: trial_log, verdict_output: verdict_output, } def extract_verdict_json(text: str) - dict: 从法官输出中提取 JSON 片段方便下游程序使用。 start text.find({) end text.rfind(}) if start -1 or end -1: return {raw: text} try: return json.loads(text[start : end 1]) except json.JSONDecodeError: return {raw: text}这段代码的核心并不复杂但有几个设计点值得解释。第一temperatures分角色配置。质疑方温度高一点输出更有探索性辩护方温度适中法官温度设为 0保证裁决尽可能稳定可复现。第二完整庭审记录会传入法官。法官需要看到双方交锋的原始内容而不是只看单轮摘要这样它才能判断哪一方真正提出了有效证据。第三extract_verdict_json负责从法官的文本输出中提取 JSON。实际工程中模型偶尔会在 JSON 前后加解释文字所以不能直接使用完整输出做解析必须有这个兜底逻辑。6.3 配置文件# 文件路径courtroom/config.yaml model_name: gpt-4o-mini max_rounds: 2 temperatures: judge: 0.0 prosecutor: 0.7 defender: 0.5生产环境建议把角色 Prompt 也外置到 YAML 或 JSON 文件中这样调整措辞不需要改代码、重新发版只要刷新配置文件即可。demo 中为了阅读方便Prompt 直接写在 Python 文件里。6.4 入口与运行命令# 文件路径courtroom/main.py import sys import yaml from courtroom import CourtSession, extract_verdict_json def load_config(path: str config.yaml) - dict: with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def main(): cfg load_config() question ( sys.argv[1] if len(sys.argv) 1 else 请用 Python 写一个判断字符串是否为回文的函数并说明时间和空间复杂度。 ) session CourtSession( questionquestion, modelcfg.get(model_name, gpt-4o-mini), max_roundscfg.get(max_rounds, 2), temperaturescfg.get(temperatures, {}), ) result session.run() print( 问题 ) print(result[question]) print(\n 原始答案 ) print(result[original_answer]) print(\n 庭审记录 ) for item in result[trial_log]: print(f--- 第 {item[round]} 轮 ---) print(质疑方:, item[criticism]) print(辩护方:, item[defense]) print(\n 法官宣判原始输出) print(result[verdict_output]) print(\n 法官宣判解析 JSON) print(extract_verdict_json(result[verdict_output])) if __name__ __main__: main()运行命令如下cd courtroom export OPENAI_API_KEYsk-xxxx python main.py如果连接本地推理服务可以在代码中把OpenAI()改为指定base_url的形式client OpenAI(base_urlhttp://localhost:11434/v1, api_keynot-needed)这里需要提醒一下api_key也不要硬编码到代码仓库使用环境变量或密钥管理服务管理更稳妥。7. 运行结果与效果验证7.1 如何判断一场审判是否成功运行结束后你需要关注四个部分原始答案、质疑方输出、辩护方输出、法官判决。判断成功的标准不是“原始答案被修改”而是“修改是否有依据”。具体来说要看三点法官是否输出了可解析的 JSON且verdict字段取值合法。reason是否指向了具体的逻辑问题或事实错误而不是泛泛而谈。final_answer与原始答案的差异是否与reason一致。如果法官说“原答案边界条件不完整”但改完的代码并没有补上边界条件那这个裁决就是失败的。7.2 最容易出现的失败模式法庭模式最常见的失败模式是法官被质疑方带偏。质疑方为了完成任务可能会提出一个听起来合理、但实际上站不住脚的怀疑而法官又没有足够强的能力去识别结果把正确的原始答案“修改”成了错误版本。应对方法有三个一是限制质疑方数量要求它只列出最有把握的问题二是在法官 prompt 中增加原则性约束例如“没有充分证据时应维持原答案”三是提升法官模型的能力层级让强模型来承担最终裁决。7.3 小规模评测思路不要凭感觉判断法庭模式有没有效果。建议每轮迭代准备一份 10 到 20 条测试题的小型评测集包含事实型、代码型、逻辑推理型、开放主观型四类问题。对每条测试题分别记录单次回答输出和法庭模式输出然后使用两类指标主观质量评分和事实错误率。人工评测是最可靠的但在预算有限时可以让一个高性能模型作为评测员给两个输出分别打分并说明理由。需要注意评测集的质量比数量更重要。如果你只在问题答案已知的情况下评测会忽略掉真实业务中大量开放的、没有标准答案的问题这可能让评测结果过于乐观。8. 常见问题与排查思路问题现象可能原因排查方式解决方案接口返回 401 / 404密钥错误或 base_url 配置不对查看完整报错信息用官方示例确认接口连通性修正OPENAI_API_KEY或OpenAI(base_url...)参数多轮输出几乎一样温度太低或提示词没有引起新视角对比每一轮的 criticism 差异提高质疑方温度增加“排除上一轮已提出的问题”约束法官把正确答案改成错误答案质疑方提出误导性意见法官能力不足查看质疑方输出确认是否在“为挑错而挑错”要求质疑方只列最有把握的问题提升法官模型能力添加“证据不足不得推翻”原则法官输出不是合法 JSON模型指令遵循能力不足打印法官原始输出在 prompt 中提供 JSON 示例解析失败时降级返回原答案Token 成本增长过快多角色多轮调用且上下文未裁剪查看每次调用的 token usage 日志限制最大轮次每轮只传上一轮结论抽取关键论点替代全文上下文超长庭审记录完整累积检查 messages 中 token 数量对 trial_log 做压缩只保留每轮双方结论系统被错误文字带跑偏没有系统级安全过滤检查主流程是否只依赖多 Agent 输出在法庭外层保留独立的安全审核和权限校验9. 最佳实践与工程建议9.1 法官质量决定系统上限法庭模式的效果上限由法官决定。质疑方和辩护方的争论再精彩最终落到系统里的仍是一份判决书。如果法官本身不够强它会放大质疑方的错误甚至引入新的问题。因此宁可让质疑方和辩护方用轻量模型也要确保法官使用的是当前能力最强的模型。9.2 不要对所有请求开启法庭多角色、多轮次调用带来的成本是实打实的。一个更理性的做法是在请求入口增加一个分级判断普通问题直接单次回答只有高风险问题、代码生成、策略建议、数学推理等场景才进入法庭流程。判断逻辑可以是关键词规则、敏感性分类模型或者是用户主动勾选的“高可靠模式”。9.3 庭审记录要完整留痕每一轮质疑、辩护和最终判决都应该写入日志或数据库。这不仅是审计需要更是后续优化的重要数据资产。当你发现法官持续做出错误裁决时可以回溯庭审记录定位是质疑方误导还是法官 prompt 设计问题然后针对性优化。9.4 Prompt 外置改动不发版角色 Prompt 是这套系统里最需要反复调优的部分。把它从代码中抽离到配置中心或远程配置服务可以极大提升迭代效率。生产环境建议使用配置中心管理和普通业务配置保持一致。9.5 设置超时与降级法庭模式涉及多次大模型调用整体耗时可能从几秒到几十秒不等。在面向真实用户的接口中必须设置超时和降级策略。当法庭流程超时、接口报错或法官输出解析失败时系统应该回退到原始答案而不是直接返回 50x 错误。9.6 安全边界要留在系统层不要以为法庭模式可以替代系统级安全审核。多 Agent 对抗评审大大增加了推理维度但它仍然在同一个模型家族的“认知框架”内运行。涉及支付、权限变更、删除操作、生产环境变更等高风险场景仍然必须遵守最小权限、测试环境验证、备份与回滚、双人复核等安全规范。法庭可以帮忙检查答案质量但不能作为唯一的安全防线。10. 总结与后续方向AI 内部的“法庭”并不是一个神秘的新技术它是对多 Agent 辩论、自我纠错、对抗式评审这些方法的一次结构化收敛。通过引入质疑方、辩护方和法官三个角色把“让模型检查自己的答案”变成“让多个模型角色以对抗方式交叉审查答案”并借助明确的裁决输出形成可落地的工程闭环。本文的 demo 代码已经可以完整跑通一场小型审判。建议你直接把courtroom目录保存下来换掉默认问题把法官 Prompt 改成自己业务的语言风格跑通一轮之后再回头设计触发规则、日志和降级策略。相比空想架构这种最小实现更有可能帮助你判断这套机制是否真正适合当前业务。如果你还想继续深入下一步可以关注三个方向多 Agent 辩论的学术论文学习不同对抗策略对模型推理质量的影响把庭审记录积累成数据集用于微调一个更懂你业务的评审模型把法庭模式接到评测流水线里用批量测试持续评估每一次 prompt 改动带来的效果变化。但要记住多 Agent 对抗评审解决的是“如何让模型在推理层面更可靠”它不解决“如何让模型知道它不知道的事情”。当问题涉及事实查证或实时数据时仍然需要把 RAG、外部工具和结构化数据源接进系统。技术选择没有银弹组合使用才是在生产环境里更稳妥的做法。