最近一段时间,AI coding 类工具的演进速度非常快,已经从“单点代码补全”走向了“任务规划—自动编码—测试验证”的完整智能体闭环。很多团队开始把编码智能体接入日常开发流程,但随之而来的一个现实问题是:模型自主产出的代码,谁来保证质量?
我在使用编码智能体时经常遇到一种现象:模型写出来的代码看起来结构完整,可一跑测试就报错;让同一个模型解释缺陷原因,它又沿着原来的错误假设重新推导一遍,最后给出一个“看似修复了、实际没修复”的结果。问题根源并不在代码能力,而在于单模型自审存在显著的盲区。
本文围绕“面向编码 agent 的跨模型同行评审(Cross-model peer review for coding agents)”展开,介绍如何引入另一个独立模型对编码智能体的产出进行交叉审查,并给出一个可以运行的 Python 最小实现,从协议设计、提示词模板、工程权衡到落地建议逐层拆解。无论你是在做 AI coding 工具的产品研发,还是想为团队内部智能体加一道质量闸门,本文都有可参考的价值。
1. 从单模型自审到跨模型评审
1.1 AI 编码智能体为什么需要质量控制
过去我们对 AI 编程的认知大多停留在“IDE 里的代码补全”,模型根据上文预测下一行代码。而现在的 AI coding agent 已经完全不同:它是一个能理解仓库结构、维护多轮上下文、自主生成 coding plan、读写文件、执行命令并运行测试的系统。
举个典型例子。开发者在智能体对话窗口里输入“为当前订单模块增加 Excel 导出功能”,智能体会先拆解任务,生成一个可执行计划,然后按计划创建工具类、修改 Controller、补充依赖配置,最后还会尝试运行测试来验证结果。整个过程可能涉及十几个文件的变更,而且是一次性自动完成的。
这种模式下,质量风险被显著放大。因为每个步骤都是模型在有限上下文里做出的决策,早期一个不准确的判断,可能被后续代码补丁逐渐放大,甚至形成结构性的错误。如果等到代码合并进主干后,再靠人力 code review 去发现问题,返工成本会非常高。因此,在智能体工作流中加入自动化的质量闸门,已经变成工程落地的刚需。
放在这个背景下看,模型能力本身不是唯一瓶颈,如何控制自主产出的可靠性,才是编码智能体能否进入生产环境的关键。
1.2 单模型自审的盲区
最容易想到的质量闸门,是让生成代码的模型自己再检查一遍,也就是“自反思”。这个思路实现成本低,对简单任务也有一定效果,但一旦任务复杂度上升,问题就会明显暴露。
第一个问题来自同源偏置。生成和评审使用的是同一个模型、同一套权重,模型对自己刚产出的代码天然存在偏好。评审时它更倾向于维护已有实现,而不是从外部视角质疑方案本身。
第二个问题是同源上下文错误。编码过程中如果出现了一个错误的技术假设,比如误用了某个 API 的返回值结构,自审时模型面对的还是同一段上下文,很难跳出这个假设去怀疑前提是否成立。于是经常出现“模型解释了错误原因,但给出的修复方案仍然建立在错误前提上”的尴尬情况。
第三个问题更隐蔽。即便我们给自审环节写了很强的评审提示词,模型内部共享的推理路径仍然和生成阶段一致,容易出现“形式上满足了评审要求、实质上没有改变决策”的假修复。这也是为什么在某些 benchmark 上,模型自反思提升有限甚至出现回退的原因。
1.3 跨模型评审:核心思想
要打破单模型自审的闭环,一个直接的思路是借鉴软件工程中的同行评审:代码作者不审查自己的代码,而是交给另一个没有参与编码的开发者去 review。
跨模型同行评审把这个逻辑移植到了智能体场景,只不过评审者变成了另一个异构模型。完整的流程大致是:
+------------+ 生成代码/修改diff +------------+ | 生成 Agent | ------------------------> | 评审 Agent | | Model A | | Model B | +------------+ +------------+ | 结构化评审意见(JSON) | +-----------+-----------+ | | 通过/质量达标 未通过/存在缺陷 | | 结束并合并变更 反馈给生成 Agent A 进行修订后再次评审由于模型 B 和模型 A 在架构、训练数据、对齐方式上存在差异,它对 A 的推理盲区更敏感,更容易发现边界条件、安全隐患、逻辑漏洞这类单模型自审会忽略的问题。
这里需要澄清一点:跨模型评审的核心不是“用更大的模型压小模型”,而是利用模型之间的差异性形成交叉验证。有时候一个能力稍弱但与生成模型差异明显的评审模型,反而比更强的同源模型更能发现有效问题。
2. 跨模型评审的系统设计
2.1 角色与责任
跨模型评审系统至少包含两类角色,在复杂流程中还可以进一步扩展。
生成者负责根据需求、约束和上下文产出代码变更。它可以是初始编写代码的模型,也可以是收到评审意见后执行修订的模型。评审者负责审查生成者交付的代码,并输出结构化结论。最简单的设计里,生成者和修订者是同一个模型 A,评审者是另一个模型 B。
如果你想进一步降低同源风险,也可以把修订者拆成模型 C,也就是“A 生成、B 评审、C 修改”。这种多角色拆分的代价是调用次数增加、成本上升,所以在实际项目中,我建议从“A 生成、B 评审、A 修订”这种最简模式入手,等发现特定模式问题后再引入第三个模型。
2.2 工作流协议
跨模型评审的完整协议可以表示为一个循环:
- 输入任务需求和约束条件。
- 生成者根据需求创建代码变更。
- 如果有测试或静态检查,先执行,并把执行结果同步给评审者。
- 评审者检查代码,给出结构化评审意见。
- 系统判断评审是否通过,以及质量评分是否达到阈值。
- 通过则结束流程,返回产物;未通过则把评审意见转成修订指令。
- 修订者根据意见修改代码,回到第 3 步,直到通过或达到最大迭代次数。
循环层数不能设计得太深。每轮调用都会增加延迟和成本,而且评审意见本身可能存在噪声,过多迭代可能让代码在反复修改中偏离原始需求。我一般把最大迭代次数限制在 2 到 3 轮,宁可允许少量缺陷遗漏到人工评审阶段,也不让自动流程陷入无意义空转。
2.3 评审结果的结构化表达
要让生成者和评审者顺畅协作,评审者不能只输出一段自由文本。两个模型之间需要一种可靠的中间协议,我建议使用 JSON 作为评审结果的主要格式。
评审结果至少应该包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| summary | string | 评审总体结论摘要 |
| verdict | enum | PASS / CHANGE / REJECT |
| score | number | 质量打分,0 到 100 |
| issues | array | 缺陷列表,每项包含位置、类型、描述、优先级 |
| suggestions | array | 可执行的修改建议 |
| model_name | string | 执行评审的模型标识 |
结构化输出的价值在于:系统可以稳定地解析判分、定位问题、归类缺陷,并通过阈值控制是否进入修订流程。更重要的是,当评审意见需要被追溯审计时,结构化格式比自然语言更容易检索和统计。
3. 实现一个最小跨模型评审工具
3.1 环境与依赖
下面我们编写一个最小可运行的 Python 工具,用来演示完整的跨模型评审流程。示例代码调用 OpenAI 兼容的接口,你可以根据实际使用的模型提供方修改 base_url 和 model 名称。
需要准备:
- Python 3.10 或以上版本
- requests 库
- python-dotenv 库(用来读取环境变量)
安装依赖:
pip install requests python-dotenv在项目目录创建.env文件,内容如下:
OPENAI_COMPATIBLE_BASE_URL=https://api.example.com/v1 API_KEY=sk-xxxx GENERATOR_MODEL=model-a REVIEWER_MODEL=model-b这里把生成模型和评审模型分别配置成 model-a 和 model-b,实际使用时替换成对应服务商提供的模型名称即可。
3.2 项目结构
整个示例比较简单,只有一个 Python 文件:
cross_model_review/ ├── .env ├── main.py └── requirements.txtrequirements.txt内容为:
requests==2.31.0 python-dotenv==1.0.0如果你不想把依赖版本写死,可以只写requests和python-dotenv,安装时自动拉取最新版本。
3.3 核心代码:调用模型
先封装一个 call_model 函数,统一处理请求发送、超时和响应解析。
# 文件路径:cross_model_review/main.py import json import os from typing import Dict, List, Optional import requests from dotenv import load_dotenv load_dotenv() BASE_URL = os.getenv("OPENAI_COMPATIBLE_BASE_URL", "https://api.example.com/v1") API_KEY = os.getenv("API_KEY", "") GENERATOR_MODEL = os.getenv("GENERATOR_MODEL", "model-a") REVIEWER_MODEL = os.getenv("REVIEWER_MODEL", "model-b") def call_model( messages: List[Dict[str, str]], model: str, temperature: float = 0.2, max_tokens: int = 4096, response_format: Optional[Dict] = None, ) -> str: """调用 OpenAI 兼容的 chat/completions 接口。""" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json", } payload: Dict = { "model": model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, } if response_format: payload["response_format"] = response_format resp = requests.post( f"{BASE_URL}/chat/completions", headers=headers, json=payload, timeout=120, ) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]这个函数的优点是兼容大量使用 OpenAI 协议的模型服务,切换模型时只需改配置,不需要改业务代码。
3.4 核心代码:生成、评审、修订、主循环
接下来定义四个关键函数。
首先是生成代码函数:
def generate_code(requirement: str, model: str) -> str: """根据需求生成初始代码。""" prompt = f""" 你是一名资深工程师,请根据下面的需求编写完整代码。 需求: {requirement} 要求: 1. 只输出代码,不要输出解释。 2. 代码需要包含必要的异常处理和边界判断。 3. 如果函数有输入参数,请在 docstring 中说明参数含义。 """ messages = [{"role": "user", "content": prompt}] return call_model(messages, model=model, temperature=0.1)然后是评审函数:
def review_code(requirement: str, code: str, model: str) -> Dict: """使用评审模型对代码进行结构化审查。""" prompt = f""" 你是一名严格的代码评审专家。请从正确性、安全性、可维护性、边界条件、性能五个维度 审查下面的代码,并输出 JSON 格式的评审结果。 评审结果 JSON 结构如下: {{ "verdict": "PASS 或 CHANGE 或 REJECT", "score": 0, "issues": [ {{ "dimension": "正确性", "severity": "high / medium / low", "location": "代码片段或行列信息", "description": "问题描述", "suggestion": "修改建议" }} ], "summary": "总体结论" }} 需求: {requirement} 代码: {code} """ messages = [{"role": "user", "content": prompt}] content = call_model( messages, model=model, temperature=0.0, response_format={"type": "json_object"}, ) return json.loads(content)评审提示词里明确要求模型输出 JSON,并定义了 verdict、score、issues 等字段,这样主流程可以直接解析。
接下来是修订函数:
def revise_code( requirement: str, code: str, review: Dict, model: str, ) -> str: """根据评审意见修订代码。""" issues_text = json.dumps(review.get("issues", []), ensure_ascii=False, indent=2) prompt = f""" 你是一名工程师,请根据评审意见修改代码,只输出修改后的完整代码。 需求: {requirement} 原代码: {code} 评审意见: {issues_text} 要求: 1. 逐条处理评审意见,不要遗漏。 2. 只输出修改后的代码,不要解释。 """ messages = [{"role": "user", "content": prompt}] return call_model(messages, model=model, temperature=0.1)最后是主循环,负责把上面的函数串起来:
def run_pipeline( requirement: str, max_rounds: int = 3, pass_score: int = 80, ) -> None: """执行跨模型评审主流程。""" print("========== 第 1 轮:生成初始代码 ==========") code = generate_code(requirement, GENERATOR_MODEL) for round_idx in range(1, max_rounds + 1): print(f"========== 第 {round_idx} 轮:评审 ==========") review = review_code(requirement, code, REVIEWER_MODEL) score = review.get("score", 0) verdict = review.get("verdict", "REJECT") print(f"评审结论: {verdict}, 评分: {score}") print(f"缺陷数量: {len(review.get('issues', []))}") for issue in review.get("issues", []): print(f" - [{issue.get('severity')}] {issue.get('description')}") if verdict == "PASS" and score >= pass_score: print("========== 评审通过 ==========") save_result(code, review) return if round_idx == max_rounds: print("========== 达到最大轮次,仍未能通过评审 ==========") save_result(code, review) return print(f"========== 第 {round_idx + 1} 轮:修订 ==========") code = revise_code(requirement, code, review, GENERATOR_MODEL) def save_result(code: str, review: Dict) -> None: """保存最终代码和评审结果。""" with open("result.py", "w", encoding="utf-8") as f: f.write(code) with open("review.json", "w", encoding="utf-8") as f: json.dump(review, f, ensure_ascii=False, indent=2) print("结果已保存到 result.py 和 review.json")3.5 运行与验证
在main.py底部添加入口:
if __name__ == "__main__": demo_requirement = "编写一个 Python 函数,读取 CSV 文件并统计每列非空值数量,要求处理文件不存在和空文件的情况。" run_pipeline(demo_requirement, max_rounds=3, pass_score=80)运行命令:
python main.py预期你会看到类似下面的输出:
========== 第 1 轮:生成初始代码 ========== ========== 第 1 轮:评审 ========== 评审结论: CHANGE, 评分: 65 缺陷数量: 2 - [high] 文件不存在时直接抛出异常,未按需求处理 - [medium] 空文件场景下 pandas 读取可能报错,缺少边界判断 ========== 第 2 轮:修订 ========== ========== 第 2 轮:评审 ========== 评审结论: PASS, 评分: 86 缺陷数量: 0 ========== 评审通过 ========== 结果已保存到 result.py 和 review.json实际输出取决于模型能力,但流程本身是确定的。这个最小实现已经具备“生成—评审—修订—循环”的完整闭环,你可以在此基础上扩展出更多能力。
4. 评审提示词与评价维度设计
4.1 通用评审维度
在编码智能体场景中,评审提示词的质量几乎决定了整个跨模型评审系统的效果。我把评审维度划分为五个核心方向,你可以根据团队规范增删。
正确性是最基础的维度,包括代码是否满足需求描述、核心逻辑是否正确、分支条件是否完整、返回值是否符合预期。安全性关注注入攻击、命令执行、敏感信息泄露、资源释放等问题。可维护性关注命名是否清晰、函数切分是否合理、是否有多余的重复代码。边界条件关注空值、超长输入、并发、网络超时、文件不存在等场景。性能则关注时间复杂度和不必要的资源消耗。
在评审提示词中,不必要求每个问题都涉及所有维度,而是让模型按维度逐项排查。逐维度排查比泛泛批评更能发现问题。
4.2 一套可直接复用的评审提示词模板
我给出一套比较通用的评审提示词模板,你可以直接复制到自己的提示词工程中:
你是一名代码评审专家。你的任务是根据需求文档,对代码进行结构化审查。 请从以下五个维度逐项检查,不要遗漏任何维度: 1. 正确性:代码是否满足需求?是否存在逻辑错误? 2. 安全性:是否存在注入、越权、敏感信息泄露、资源泄漏风险? 3. 可维护性:命名是否清晰?函数是否过长?是否存在重复代码? 4. 边界条件:空值、空文件、超长输入、并发、依赖服务不可用等场景是否考虑? 5. 性能:是否有明显的时间或空间复杂度优化空间? 输出要求: - 使用 JSON 格式输出,不要输出额外文字。 - verdict 字段使用 PASS、CHANGE 或 REJECT。 - score 字段为 0 到 100 的整数。 - issues 数组中每条问题必须包含 dimension、severity、location、description、suggestion。 - 如果问题存在,description 必须引用具体的代码片段或行列信息。 - summary 字段用一句话总结总体结论。 需求文档: {requirement} 待审查代码: {code}这套模板的关键在于“给模型明确的审查框架 + 严格的输出格式”,比单纯说“请检查这段代码”要可靠得多。
4.3 评审与执行验证结合
除了让评审模型直接阅读代码,更好的做法是把执行结果作为评审上下文。
比如,让编码智能体先运行 pytest,然后把测试输出和代码一起交给评审模型。这样评审模型可以同时看到“代码本身”和“运行结果”两类信息,对问题的判断会更准确。
在智能体工作流中,这相当于构造了一个更完整的评审信息空间:
评审输入 = 需求文档 + 代码 diff + 测试运行日志 + 静态检查结果如果测试失败,评审模型可以直接定位到失败的测试用例,并指出是生产代码问题还是测试代码问题。这能明显减少误报。
5. 跨模型评审在编码智能体工作流中的位置
5.1 在 coding plan 阶段介入
很多编码智能体在开始写代码前,会先输出一个 coding plan,把大任务拆解为多个子任务。跨模型评审完全可以作用在 plan 阶段。
生成模型 A 输出 coding plan 后,评审模型 B 可以先审查计划本身,判断子任务拆分是否合理、是否有遗漏约束、执行顺序是否依赖正确。计划阶段评审的成本很低,因为还没有代码生成,但收益很高,一个错误的计划会导致后续所有步骤白做。
这种“先评审计划,再评审代码”的做法,可以理解为把质量闸门前移,属于一种预防性控制。
5.2 在代码 diff 阶段介入
最常见的介入点是代码变更完成之后。此时评审模型查看的是 diff,而非完整文件。diff 评审的好处是聚焦,模型只需要关注本次改动的部分,不会因为仓库过大而分心。
在 CI/CD 流程里,可以把这个环节做成自动化的 pre-merge 检查。当开发者或智能体提交 PR 时,系统自动拉取两个模型,一个生成变更,一个评审变更。评审通过后才能人工合并。
5.3 人机协同边界
需要明确的是,跨模型评审不是要替代人工 review,而是把人工从低效的格式检查、变量命名、重复代码这类机械问题中解放出来,让人集中精力解决架构设计、业务语义、产品需求这些更深层问题。
最终的合并权应该始终保留在人类工程师手中。模型给出的评审意见再合理,也只是“建议”,不是“决策”。在工程实践中,我会把模型评审结论标记为“自动建议”,并保留完整的修改历史,方便人工追溯。
6. 工程落地中的坑与权衡
6.1 成本与延迟
跨模型评审相比单模型自审,调用次数至少翻倍。每轮迭代要调一次生成模型、一次评审模型,如果需要修订,还要再调一次生成模型。假设一个任务平均需要两轮评审,那么总调用次数大约是 4 到 5 次。
对于个人开发者,这个成本可能还好;但对于每天执行成千上万个任务的自动化平台,成本会线性放大。
几个控制成本的策略:一是在低风险模块使用小参数模型做评审,高风险模块才启用强模型;二是先做规则过滤,比如改动只涉及注释或配置时跳过评审;三是限制最大迭代轮数,避免模型反复修改产生额外调用。
6.2 评审模型的误报与幻觉
评审模型本身也会产生幻觉,可能给出不存在的缺陷,甚至把正确代码当成错误代码。这类误报会让修订模型做无用功,严重时还会把原本正确的代码改坏。
降低误报的思路是要求评审意见“有证据”。在提示词里明确要求:每个 issue 必须引用代码中的具体片段、函数名或行号,必须描述出触发场景。没有证据的评审意见,系统可以选择忽略。
另外可以加一层相似度去重。多轮评审中出现的相似意见,如果第一次已经被处理过,后续轮次就不应该再重复触发。
6.3 上下文长度与多文件评审
当代码变更涉及多个文件时,简单地把所有文件拼成一个大 prompt 往往不可行。一方面 token 消耗巨大,另一方面模型在超长上下文中的关注力会分散,评审精度反而下降。
推荐策略是将评审拆成两层:先对每个关键文件做独立评审,再把多个文件的评审摘要汇总给一个“总评审模型”,让它针对跨文件一致性和接口兼容性做最终判断。这种分层评审能兼顾细节和全局。
6.4 模型选择策略
跨模型评审的模型组合不应该是一成不变的。常见的策略包括:
- 使用异构模型组合,通常是不同厂商或不同架构的模型,保证差异度。
- 定期统计评审意见采纳率,淘汰总是产生无效意见的模型。
- 针对不同语言和框架选择不同的评审模型,比如 JavaScript 项目用一种组合,Python 项目用另一种组合。
选择模型时,不要只看模型排行榜的分数,更关键的是在你自己项目数据上的“缺陷发现率”和“误报率”。建立一个小规模的评测集,提前验证模型组合的效果,是值得做的一件事。
7. 最佳实践建议
7.1 提示词工程建议
基于前面多轮实验,我总结了几条提示词设计上的最佳实践。
给评审模型一个固定角色,比如“高级代码评审专家”,有助于稳定输出。要求模型逐维度排查,不要一次性笼统评价。强制输出结构化 JSON,方便自动化处理。必须引用代码位置和触发场景,提升评审意见可执行性。最后,在提示词里明确定义“通过”的标准,比如评分达到多少、不存在 high 级别缺陷,避免模型对阈值产生主观理解。
7.2 流程与审计
跨模型评审过程应该完整留痕。每次评审都记录评审模型版本、生成模型版本、评审输入摘要、输出 JSON、最终合并结果。这些数据有双重价值:一是出现线上事故时可以回溯是哪一轮评审漏掉了缺陷;二是可以积累成训练集,用来优化未来的评审提示词和模型选择。
7.3 渐进式灰度上线
如果要在团队内推广跨模型评审,不建议一步到位直接拦截所有合并请求。
我建议的演进路径是:
- 离线阶段阶段:对历史 PR 跑一遍跨模型评审,对比人工评审结果,统计命中率和误报率。
- 建议模式:评审结果以 comment 形式出现在 PR 中,不阻塞合并,观察团队反馈。
- 强制模式:对部分高风险模块强制执行,未通过则不能合并。
- 全量模式:条件成熟后放开到所有模块,同时保留人工复核通道。
在这个路径里,团队可以在每个阶段积累信心,也能让工程师逐步接受“自动评审意见”。
8. 总结
跨模型同行评审本质上是用“模型之间的差异”对抗“单模型推理的盲区”,它把代码审查从个人行为变成了结构化的工程流程。本文从为什么需要这种模式讲起,给出了一套最小可运行的 Python 实现,并围绕评审提示词、系统设计、成本控制、模型选择和实践落地展开了讨论。
如果你正在开发 AI coding agent,或者想给现有智能体工作流加一道质量闸门,建议先从一个小模块开始,把生成模型与评审模型跑通,再逐步扩展评审维度与迭代策略。技术方案永远没有一步到位的,关键是先跑起来,拿到真实数据,再持续迭代。
跨模型评审不会是编码智能体质量控制的终点,未来一定会出现更细粒度的评测工具、更智能的缺陷定位方法和更高效的模型协作协议。但无论工具怎么演进,“独立评审者 + 结构化反馈 + 环环留痕”这套内核,都会是值得长期坚持的工程思路。