通过冻结测试套件阻断AI读取、并由不同模型家族执行审查,可有效解决AI自行评分时的测试软化与虚假通过问题。
让 AI coder 评审自己写的代码和测试,会导致代码质量妥协、测试软化、误报通过。本文引入一种实用的多 Agent 治理层,灵感来自 Ralph loop,但加入了两条核心约束进行加固:
锁定预言机(Locked Oracle):编码 Agent 完全被禁止读取或编辑测试套件——测试套件在编码开始前就已冻结。
跨模型检查器(Cross-Model Checker):代码评审和测试严格委托给不同模型族,以显著减少盲点和自我验证偏差。

修正循环架构:Director 预先冻结测试(锁定预言机)。Builder 在无法访问测试的情况下盲目编码,跨模型的 Reviewer 根据人类的 Gate 对 diff 进行评估,以防止偏差。
开发者尝试自动化 AI 编码时,往往会撞上自我验证偏差这堵墙——Agent 会悄悄软化你的测试,然后开心地宣布成功。我刚分享了一个在内部使用了一段时间的基于循环的 AI builder。这个 builder 在解决这个问题上,给我带来了比之前测试过的任何方案都要好的效果。
最显著的影响是大幅减少了"伪测试"问题——即为了让应用标记为完成而将测试软化。另一个显著影响是对 PRD 合规性,现在 AI-dev 流程"遗忘"需求部分的情况少了很多。
它重度依赖循环概念进行自动化 AI 编码,但加入了我认为能产生更稳固开发流程的改进。你基本上可以把它看作 Ralph loop 的锁定预言机跨模型变体(一种迭代式 AI 编码模式)。
它也大量借鉴了 AI dev 领域其他一些流行的想法,并将它们组合成在我开发多个小型项目时效果良好的方案。
下面指定的这些想法都不是我的!我很高兴地从别人那里借鉴的,你也可以随意使用我分享的这个模型。
由于 AI 领域尚未达到命名共识阶段,我会使用我认为最常见的名字。我可能错了。
我借鉴的想法
从 Ralph 那里我借鉴得最多。尤其是每次循环中用刷新后的会话上下文进行迭代,以及使用 git 提交作为事实来源和检查点。
我没有从 Ralph 借鉴的是其永恒循环,因为我相信过多的代码/评审迭代是流程出问题的明确信号,需要停下来审视。
我也没有采用其单 Agent 自我评审——我认为这对于有效的代码评审来说是根本错误的。取而代之的是:
这里我为滚动开发循环设置了严格的访问限制。
规则很简单:builder 可以读取所有代码,甚至看起来与当前任务无关的代码。但它不能读取或修改用于系统验证和后续验收的冻结验收套件。那些套件作为系统设计的一部分编写一次,然后在第一行应用代码存在之前就被锁定。
通过这样做,我解决了两 个常见的测试设计问题:
测试偏见的代码,即 builder 知道测试检查什么,于是创建一个并不完整但仍能通过测试的解决方案。
测试软化,即流程逐渐放宽测试条件,直到测试不再代表 PRD 设定的正确性硬性要求。
加入这个机制后,我更有把握认为绿色测试套件真的意味着产品行为正确,而不是 Agent 调整了自己的成功标准。
这个锁不是规则文件中的一个指令。它是一条拒绝规则加上 pre-tool hook,在模型外部评估,针对模型无法影响的路径。
# EXIT CODE SEMANTICS — the easiest way to disable this guard accidentally:
# exit 0 allow the tool call
# exit 2 BLOCK the tool call; stderr is returned to the model
# exit 1 does NOT block in Claude Code; the tool call proceeds.
# Using the conventional Unix failure code here silently disables the guard.
经典的 Ralph 只有一个 Agent,没有独立验证器。同一个 Agent 写代码、写测试,然后决定自己完成了。在使用这种方法一段时间后我放弃了它。我根本无法信任它的正确性。
相反,我发现用检查模型更有效——每个评审/测试会话由属于与 builder 不同模型的新 Agent 实例执行。
为什么这很重要?
因为自我验证是万恶之源。生成代码的同一个模型在测试阶段总是携带某些"盲点",写入代码时的考量因素和上下文会渗透到评分中。出于这个原因,你永远不会指定人类编码者同时担任代码评审员。
使用不同的模型族(例如 Claude 构建、GPT 评审、或 Gemini 测试)会带来一组新的推理和训练偏差,使拦截错误的可能性大大提高。
每个评审 Agent 都是重新启动的这一事实(因此提供干净的上下文)也加强了我们 的评审阶段,防止"上下文腐化"——即上下文被先前任务塞满,以至于无法再依赖它对当前任务给出合理的答案。重新启动 Agent 确实解决了这个问题,不可否认代价是每次向评审实例引入当前代码时有较高的学习曲线。
内环/外环模型
外环迭代执行步骤直到穷尽,内环通过修复和尝试来验证每个步骤执行的正确性。
我多次尝试不使用这个模型,因为它对我来说看起来过于复杂,但从未能够用单一循环正确完成。我倾向于认为原因在于:这些两级循环对于真正实现 dev 任务的自动化是内在必需的。
目录在实际应用代码存在之前就编写好了。这个时序使得锁定可执行而非只是愿望。代码存在之后再写的目录已经被代码塑造了,到那时就没有什么值得冻结的了。
差异化 Agent 技能
这个模型依赖 3 个不同实体:
每个实体都配有其角色所需的不同技能。
不是每个角色都需要强模型
三个角色不需要同等质量的模型,把它们当作需要同等质量是这里最昂贵的错误。
Builder 质量的重要性最低。它产生的每个缺陷都会被它无法影响的冻结预言机捕获。一个较弱的 builder 写出更差的一稿,需要更多修复轮次,但它无法让不合格的东西通过裁判。这使得在这个设置下一个便宜的 builder 是安全的——而在普通设置下它是不安全的。
Director 质量的重要性最高。Director 编写裁判,而下游没有任何东西检查裁判,只有人类读一次。一个弱的 Director 产生浅薄的测试目录,循环一路绿灯通过,差距只在人工测试时才暴露。没有自动化恢复手段。
Reviewer 质量的重要性居中,而且它的模型族比其原始能力更重要。来自 builder 同族的 Reviewer 会有相同的盲点并遗漏相同的 case。
一个实践建议:不要按基准分数选择 builder 模型。在一个一次性项目上测量每步的修复轮次。一到两轮很好。三轮或更多持续出现意味着模型没有通过规范而非没有通过门槛,一个需要三遍的便宜模型并不便宜。
这种分配的成本节省是真实的,但这是一个副作用。即使 token 是免费的,builder 和 Reviewer 之间的族分离仍然是必需的。
为什么它适合我
这个模型有几个好处,非常适合我的需求——中小型项目的快速开发。
它在其他事情之前审核 PRD
它首先对产品手册(PRD)运行大量测试,在 PRD 被纠正为自包含且易于分解为开发步骤之前有效地阻止开发。通常,"AI-dev 友好"。
它包含双循环模式
整个项目自动分解为多个执行步骤,使每一步都可以在单个 AI builder 会话中管理,且没有显著的上下文窗口衰减。
注意,这里有一个内在优势:让 AI 自己做分解(我本可以自己做的):AI 应该最清楚保持上下文窗口有效所需的任务长度。
每步完成后,流程委托给新创建的、不同模型的 Agent 进行"修正评审"。说是修正,是因为流程期望评审返回变更列表,并处理评审、变更请求、新代码评审的流程。
虽然强制 Reviewer 与 builder 使用不同模型的理由不言自明(你不希望工作评审者像工作者那样思考),但在每步都重新启动新的 Reviewer Agent 这个问题我并不确定。有很多好处是保持 Reviewer 上下文向上下文并了解整个项目的所有方面。最后我选择重新启动 Reviewer 以保持其上下文窗口干净,这样做之后得到了足够好的结果。
它永远不会为了通过而弱化测试
它强制执行对每个阶段进行激进测试的政策,并且永远不会为了通过而弱化测试。测试失败意味着要在代码中修复一个行动项。
作为这些测试的一部分,自动流程将部署本地真实测试环境(Docker 容器中的数据库、node 服务器、可添加更多组件)并运行真实场景的本地测试。
它最小化人工干预
它非常擅长在循环中最小化人工干预(它为此而构建)。这里定义的人工关卡只有:
即使这种程度的人工干预也可以减少。只需添加一个简短提示,说明 Agent 应该:
"以完全自动化模式工作,绕过任何产品手册评审和许可请求,但不得降低产品质量,尤其必须保持分步工作和由不同模型执行每步大量评审。"
这样 Agent 基本上是独立运作的,所以这应该只限于内部/一次性项目。
它默认选择无聊的方案
该模型默认采用非常基本的开发方式,几乎总是选择"稳固简单"。
后端运行在 Node 上,数据库层是无关的 RDBMS,移动应用基于 Capacitor 编写,但如果应用定义不允许,则回退到 React Native,只有在不可能的情况下(例如复杂动画、需要大量移动 OS API)才会使用原生代码。
除脚本、必要的移动原生代码等外,所有代码都用 TypeScript 编写,这为模型增加了额外的简单性。它也增加了弹性,因为根据我的经验,AI 生成代码的头号"代码级 bug"是类型搞错。
它可以扩展而无需重写
在保持简单的同时,该模型被设计为以极低成本适应应用使用的增长。
后端被指示为无状态,以使水平扩展微不足道。数据库层构建时已声明最有利的索引并且与具体数据库无关,因此应该允许切换到更强的数据库只需很少的改动。Node 本身也非常高效和扩展友好。
所有这些都是为了说明:一旦成功降临,即使比你开始时多很多用户,你可能也不需要重写。
同一模式,不同领域:Solidity 审计
对于智能合约安全,这个相同的模式甚至更强。在任何 pragma solidity 行之前,Director(例如 Opus)起草安全规范:重入保护、访问控制、溢出检查。现在这些成为锁定的测试目录。
现在一个便宜的 builder(例如 Sonnet)编写合约。来自不同模型族的 Reviewer(GPT-4o)使用 Slither + 冻结的规范进行审计。Builder 看不到审计清单,所以它无法软化自己的保护。
在一个辅助合约(< 900 行代码)上使用,Reviewer 在 Gate A 捕获了一个规范不匹配——这本来会在之后需要完整重写。
尝试之前的两点说明
这是一个起点,不是成熟产品。我分享的是核心想法——实现方式由你调整。不过,仓库中有一个 demo/ 文件夹,里面有可运行的示例。克隆并运行它们,你应该就能上手了。
知道天花板。在大约 2 万行代码以内运行良好。超过这个规模,上下文窗口限制就开始出现了。可以扩展——我只是还没需要到那个程度。
获取代码:CorrectingLoopManagedFlow
觉得有用?给仓库加个星,留言评论,或分享你如何管理你的自主 AI 编码循环。
最初发表于 Medium