Sub-Agent 的 Maker-Checker 跑偏,往往不是 prompt 写得不好,而是评估器和生产者共用了一套上下文。TaoToken(https://taotoken.net/?utm_source=taotoken_aicg_blog_end)在这件事上的用法很直接:同一把 Key,只改 model 字段,Checker 就换了一副眼睛。《从 Harness engineering 到 Loop engineering》第 9 章 9.4 节把这个判断写得很克制——独立评估的价值,取决于评估器是不是真的独立。
书里给的三条建议是:换新 session、给 Checker 一个不同的目标、条件允许时用跨家族模型做复核。前两条靠工程纪律,第三条得靠工具链。多数团队卡在第三条:想给 Checker 单独换个模型,按老办法就得再注册一个账号、再申请一把 Key、再维护一套环境变量,成本一上来,评估器就顺手复用了 Maker 的配置,锚定幻觉照旧复现。下面这条路径就是冲着这个卡点去的——Maker 和 Checker 共用一把 Key,模型各指各的。
1. Sub-Agent 的 Checker 沿用同一 session 时发生了什么
1.1 锚定幻觉在 Maker-Checker 里的具体形态
Maker 干活的时候,上下文里会沉淀一堆中间判断:为什么选这个方案、为什么跳过那个边界、为什么这段 SQL 用这个写法。这些东西在 Maker 自己的视野里是自洽的,因为它是从第一行推理走到结论的。问题在于,如果 Checker 是被塞进同一个 session 里被叫起来的,它看到的不是产物,而是「产物加一整套已经论证过自己的过程」。
这时候 Checker 做的事情,本质上不是评估,而是补签。它会沿着 Maker 铺好的论证路径往下走,只在路径末端找几个措辞问题、变量命名问题、注释缺失问题,然后给出「整体没问题,建议加两处边界判断」这种不痛不痒的结论。真正的错误——比如业务口径理解错了、并发时序假设错了、异常分支吞掉了关键信号——恰恰是在那条路径的起点就被定下的,Checker 站在终点回望,几乎看不见。
1.2 同模型互评为什么会收敛到「虚假共识」
换成同家族、同版本的模型来当 Checker,情况会好一点,但也只是好一点。同一个模型在训练阶段见过相似的数据分布、被相似的偏好拉过一遍,它对某一类错误是有共同盲区的。Maker 觉得这段代码理所当然,Checker 也会觉得这段代码理所当然,两边给出的理由甚至可能措辞相近。
这种「虚假共识」最难发现,因为它伪装成了「两个独立来源互相印证」。日志里看是两条独立的评估记录,实际上是一条认知路径被走了两遍。9.4 节之所以强调换模型,针对的正是这一点:先验不同,盲区才可能不重叠。
2. 跨家族模型做评估,为什么只改 model 字段就够
2.1 独立性来自不同的先验,而不是不同的提示词
很多人的第一反应是给 Checker 写一段严厉的提示词:「你要独立判断,不要被上面的推理影响,请重新审视每一个假设。」这段提示词不能说没用,但它改变的是 Checker 的表达风格,改变不了它读进去的内容。只要上下文里还躺着 Maker 的推理链,只要背后还是同一个模型的同一套权重,独立判断就只是语气上的独立。
真正的独立性来自两处:一是 Checker 看到的输入不一样(只看到产物和验收标准,看不到推理过程),二是 Checker 背后的模型不一样(不同的训练先验,不同的失败模式)。第一条靠流程设计,第二条靠接入层能低成本切模型。
2.2 同一把 Key 下切换模型的实际含义
在传统的多账号方案里,「切模型」这件事的重量被放大了:它意味着账号、计费、Key、环境变量一整条链路都要重来一遍。所以很多人宁可让 Checker 复用 Maker 的配置,也不愿意为了评估去多维护一套东西。
统一接入之后,这件事的颗粒度就降下来了。同一把 Key 覆盖多个模型的调用,切模型退化成改一个字符串——把ANTHROPIC_MODEL或者配置文件里的model字段从 Maker 用的那个换成 Checker 用的那个,请求照样打到https://taotoken.net/api,账照样记在同一把 Key 上。成本从「再建一套」变成「改一行」,流程上才有机会真的落地。
2.3 跨家族不是必须,同家族换版本也能拉开距离
跨家族模型效果通常更明显,但不是硬性要求。如果预算或者延迟有约束,同一个家族里换一个不同代际、不同尺寸的版本,也能把先验差距拉开一些。关键判断标准是:Checker 和 Maker 在面对同一段可疑代码时,第一反应是否可能完全一样。如果答案大概率是「会」,那这两个模型当评估对,价值就有限。
至于具体能选哪些模型、它们的 ID 长什么样,以 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 模型广场当时的列表为准。ID 是会变的,别把文章里的示例当常量抄进配置。
3. 在 ~/.claude/settings.json 里给 Checker 配第二个模型
3.1 先创建那把统一的 Key
如果手上还没有 Key,打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 注册,进控制台创建一把 API Key,后面 Maker 和 Checker 都用这一把。创建完先别急着关页面,顺手去模型广场把要用的两个模型 ID 复制出来,Maker 一个、Checker 一个,记在便利贴上也行。
这里提醒一句:落地页和接口地址是两个东西,别混。注册、创建 Key、看模型广场、看用量走官网;填进工具里的 Base URL 是https://taotoken.net/api,末尾不带/v1,也不带任何查询参数。
3.2 Maker 的默认环境:settings.json 的 env 三件套
Claude Code 读~/.claude/settings.json里的env段,把三个变量写进去就够了。这里配的是 Maker 用的默认模型,Checker 后面单独处理:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY", "ANTHROPIC_MODEL": "Maker 用的模型 ID,从模型广场复制" } }YOUR_API_KEY换成 3.1 里创建的那把 Key。三个变量名一个都别改:ANTHROPIC_BASE_URL决定请求打到哪儿,ANTHROPIC_AUTH_TOKEN决定用哪把 Key,ANTHROPIC_MODEL决定默认拿哪个模型干活。
3.3 用 CC Switch 把两套模型做成可切换的供应商
Maker 的默认配好之后,Checker 需要另一套模型。手改环境变量很容易漏,用 CC Switch 更稳:在自定义供应商里加两条记录,两条的 Base URL 都填https://taotoken.net/api,Key 都填同一把YOUR_API_KEY,只有模型 ID 不同——一条写 Maker 的,一条写 Checker 的。
切过去之后,新的 Claude Code 进程就会带着 Checker 的模型 ID 启动。这里的关键点是:两套供应商共用同一把 Key,切换不会带来额外的账号成本,也不会让用量统计断成两截。要在同一把 Key 下换模型,改的就是这个模型 ID 字段。
3.4 Codex 侧对应改 ~/.codex/config.toml
如果评估循环里用的是 Codex,别把上面那套ANTHROPIC_*变量搬过去,Codex 认的是自己的配置文件。在~/.codex/config.toml里单独声明一个 provider:
model = "Checker 用的模型 ID,从模型广场复制" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY"env_key指向的环境变量里放YOUR_API_KEY。Checker 那一路想换模型,同样是改model这一行,provider 段不用动。
4. 把评估循环拆成两个进程:新 session 与只读产物
4.1 新 session 意味着不复用 Maker 的对话历史
配置改完只是把「用哪个模型」这件事准备好了,真正决定 Checker 视不视野锁死的,是它能不能被塞进一个干净的会话。Maker 跑完一轮之后,把产物落成文件或者结构化输出,然后关掉这一轮会话;Checker 由一个新的进程、新的 session 拉起来,上下文里只有产物、验收标准和必要的背景信息。
判断标准很简单:如果 Checker 的上下文里能找到 Maker 说过的「因为……所以……」,那就不是新 session,只是同一段对话里换了个角色名。这种复用会让 9.4 节的锚定问题原样保留,模型换得再远也救不回来。
4.2 Checker 的输出目标要跟 Maker 不同
书里提到的第二条是「不同目标」。Maker 的目标是让产物跑通,Checker 的目标应该是找反例。这两个目标听起来像一回事,落到提示词上差别很大:Maker 需要充分理解需求然后给出实现,Checker 需要假设实现有问题,然后去构造能让它露馅的输入。
把 Checker 的目标写成「哪些输入会让这段逻辑给出错误答案」而不是「这段逻辑写得对不对」,产出的质量差距很明显。前者逼着它去构造用例,后者容易滑回「代码风格不错,建议加注释」。
4.3 让 Checker 只读产物、不读推理过程
最后一条纪律:Checker 只拿产物,不拿过程。产物包括代码文件、SQL、配置文件、验收用例;过程包括 Maker 的中间思考、被否决的方案、失败的尝试记录。很多人舍不得把过程丢掉,觉得信息越多 Checker 判断越准,实际结果是 Checker 顺着 Maker 的思路又走了一遍。
这里也要划一条线:Checker 的职责是判断产物本身,不是替读者去执行任何线上动作。它可以在本地或 CI 里对照代码、解释逻辑、生成待验证的用例,但真正跑诊断、跑编译、跑数据库查询的,得由读者自己在本地环境里做,再把结果贴回来。工具不代替人执行生产环境里的操作。
5. 跑通之后:401、404 和模型名对不上的排查
5.1 Base URL 末尾多写 /v1 会怎样
最常见的错是把https://taotoken.net/api写成https://taotoken.net/api/v1或者https://taotoken.net/v1。前者路径会多一层,请求可能落到不存在的端点;后者直接把/api丢了,报 404 的概率很高。写完之后回头看一眼配置文件的这一行,把末尾多余的斜杠和版本号去掉。
另一个高频错误是把官网地址填进 Base URL。落地页是给人点的,https://taotoken.net/?utm_source=taotoken_aicg_blog_end这个形式里带查询参数,工具解析不了,会直接报错或者连不上。两条地址各归各的用途,别互相串。
5.2 模型 ID 写错时的报错形态
模型 ID 写错一般不会连不上,而是请求发出去之后被拒,报错里会明确提到模型不存在或者无权限。这种情况先检查三件事:ID 有没有多空格、大小写有没有抄错、这个 ID 在当前 Key 的可用范围里有没有。最省事的做法是回模型广场重新复制一遍,别对着记忆手动敲。
如果 Maker 那条路通了、Checker 那条报错,那就是模型 ID 的问题,不可能是 Key 的问题——同一把 Key 已经在正常工作。这种排查顺序能省不少时间。
5.3 回控制台看这次 Checker 调用有没有记账
配置改完、会话也拆干净之后,最直接的验证办法是让 Checker 跑一轮真实评估,然后打开 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 看用量面板。同一把 Key 下,Maker 和 Checker 的请求会分别记在不同的模型条目上。如果只看到 Maker 那条在涨、Checker 那条纹丝不动,说明 Checker 的模型 ID 没生效,大概率还在用默认配置。
用量面板在这里的作用不只是看花费,它同时是一次「切换是否真的发生」的验证。看到两个模型各自有请求记录,就说明评估器确实换了眼睛。
6. 下一步:把单点切换升级成 Loop 级评估编排
单次切换解决的是「Checker 不要复用 Maker 视野」这一个点,放到长程任务里,要解决的问题是:每一轮 Loop 的评估器怎么轮换、什么时候该升级到跨家族模型、评估失败之后怎么回灌给下一轮的 Maker。这些是 9.4 节往后延伸的话题,前面把同一把 Key 下的模型切换打通了,后面的编排才有落地的基础。
想先把这条路走一遍,可以按这个顺序来:在 TaoToken 模型对话 里用同一把 Key 发两条消息,分别指定 Maker 和 Checker 的模型 ID,确认两边都能正常返回;确认无误之后去 控制台 API Keys 核对这把 Key 的状态;长期跑评估循环的话,Coding Plan 里能看到套餐是否撑得住双模型的调用量。Claude Code 侧的环境变量写法,接入文档 里有逐项对照,照着改settings.json就行。