同一个Claude Opus,换个壳差4.5分——2026最被低估的agent设计约束
当Claude Opus 4.6在ForgeCode和Capy上出现4.5分的性能差距时,Nicolas Bustamante揭示了AI领域一个被忽视的真相:模型与壳的匹配度(Model-Harness-Fit)才是决定性能的关键因素。本文通过拆解Codex、Claude Code和GitHub Copilot CLI三种架构,证明模型性能不仅取决于权重,更与工具调用、记忆系统等壳设计深度绑定。

我最近看到一条很反直觉的事实:同一个 Claude Opus 4.6,配 ForgeCode 能跑到 79.8 分,配 Capy 只有 75.3 分。权重一模一样,一个 byte 都没改,分数差了 4.5 个点。
在 Terminal-Bench 2.0 这种每 0.1 分都在死磕的榜上,4.5 分是一个代差。
更离谱的是 Cursor 团队——同一个模型,没换权重,只改壳,榜单排名从 Top 30 冲到 Top 5。一跳 25 位。
Nicolas Bustamante 这篇长文把这件事讲透了。他把 Codex、Claude Code、GitHub Copilot CLI 三个 agent 的内部实现扒了个干净,得出一个结论:模型不是单独存在的,壳是模型的一部分。
他给这件事起了个名字,叫 Model-Harness-Fit——模型和壳的匹配度。

故事的起点:一个 OpenAI 工程师发的梗图。给 Codex 工具和上下文,两边都是聪明的做法,中间那拨人搞所谓的通用 harness、捣鼓 token 效率、给 benchmark 多刷 10 分。
01 先看一个让人不舒服的榜单
Nicolas 贴了 2026 年 4 月 30 日的 Terminal-Bench 2.0 前十名:Codex + GPT-5.5 → 82.0%ForgeCode + GPT-5.4 → 81.8%TongAgents + Gemini 3.1 Pro → 80.2%ForgeCode + Claude Opus 4.6 → 79.8%…Capy + Claude Opus 4.6 → 75.3%
两件事特别扎眼。
第一,同一个 Claude Opus 4.6,配 ForgeCode 是 79.8,配 Capy 是 75.3。4.5 分的差距。
第二,榜单上没有一个模型作者独占前列。ForgeCode 是第三方壳,占了前六名的三个席位。斯坦福 IRIS Lab 搞了个叫 Meta-Harness 的
自动壳进化系统,把 Opus 4.6 在同一个榜上从基线推到了 76.4 分。
换句话说,这一年壳带来的分数,比模型升级带来的分数还多。
LangChain 的 Vivek Trivedy 说得最直白:Opus 4.6 跑在 Claude Code 里,分数明显低于 Opus 4.6 跑在其他 harness 里。
Anthropic 的旗舰模型,装在 Anthropic 自己的旗舰壳里,打不过装在第三方壳里的同一批权重。你光看模型的 spec sheet,根本预测不出这个结果。
02 为什么会这样
Nicolas 的核心判断是:模型不是只针对 API 做 post-training 的,它是针对壳做 post-training 的。
壳里的每一个细节——工具叫什么名字、输入 schema 长什么样、引用标签怎么写、skill 文件放哪里、计划协议什么格式——这些都不是模型的通用能力,
它们是 byte 级别的约定,被烤进了 post-training。你把模型拖出它的壳,性能的损耗是你再也拿不回来的。除非你把另一边也重写一遍。
这就是为什么每个号称 “model agnostic” 的 agent 都碰过同一堵墙——你没法就这么换个模型。
要干净地换模型,你得把壳也一起换掉:工具面、schema 形状、skill 里点名调哪些工具的主体、引用契约、记忆仪式、system prompt 结构,有时连 planning 协议都得换。
壳之上的所有东西,都得跟着模型一起动。

同一个模型,配不同的壳,活出三种人格。Nicolas 给这件事起的名字:Model-Harness-Fit。
03 三个壳,三种合约
Nicolas 扒了三个开源/半开源的 agent 实现。结果是:它们不是同一个东西的三种写法,它们是三种完全不同的协议。
Codex 走的是 typed asynchronous protocol。模型发出一个 Submission,拿回一串 typed Event。协议定义在 codex-rs/protocol/src/protocol.rs,用 Rust 的 serde 枚举。模型被训练成 “发 submission、读 event” 的思维模式。
Claude Code 走的是 direct typed conversation loop。ConversationRuntime::run_turn 每轮吃一个 Vec<AssistantEvent>。模型被训练成在 assistant message 里直接打出工具调用,下一轮接上 tool result。
GitHub Copilot CLI 走的是 supervisor protocol。host 不跑 agent loop,它 spawn 一个 @github/copilot 子进程,用 vscode-jsonrpc 走 stdio,然后 session.create。agent loop 跑在子进程里。

三个壳、三种编排协议。每个模型都是对着协议的精确 wire format 训出来的。
Codex 的 AGENTS.md 里写着:不要给 codex-core 加代码。这是最大的 crate,已经明确不再接纳新 feature。每个 Rust 模块软上限 500 行,硬上限 800 行。新 feature 要以新 crate 的形式上。
这是编译器工具链的组织纪律,被拿来治理一个 agent harness。模型就是在这种架构里 post-train 出来的。
现在设想你换模型。把一个被训练成发 Submission 的模型,喂给 Claude Code 的 AssistantEvent 流。模型学会的是一种 wire shape,壳期待的是另一种。
错配不会直接报错,它会变成一种安静的退化——漏掉工具调用、推理强度档位不对、压缩触发不一致、引用标签壳根本不解析。
04 工具表:模型的方言
这是 post-training 痕迹最明显的地方。
表面上看,前六个工具都差不多:read、write、bash、grep、glob。一旦走到第七个,就开始发散。
Codex 有 apply_patch——自家的 diff 格式,两种变体:一个 Lark 语法版本,一个 JSON 版本。模型被训练成发这种 patch。它和 Claude Code 的 Edit(old_string / new_string)不通用。
Codex 的多 agent 编排有 八个动词:spawn_agent_v1、spawn_agent_v2、wait_agent_v1、wait_agent_v2、send_message、close_agent_v1、close_agent_v2、resume_agent。模型八个全认识。
Claude Code 的 subagent 调度就一个动词:Agent。
Copilot CLI 又是另外一套:read_agent / write_agent 搞多轮 subagent 控制,加上 task 搞 dispatch。
一个在 Codex 八动词体系下训出来的模型,知道怎么给在跑的 subagent 发消息。一个在 Claude Code Agent 工具下训出来的模型,在它的本能里根本没有这个动词。
Cursor 的壳团队把这件事说得最白:OpenAI 的模型被训练成用 patch 格式编辑文件,Anthropic 的模型被训练成用字符串替换。两个模型都能用对方的工具,但是用陌生的那个,要多花推理 token,而且出错率更高。所以在我们的壳里,每个模型都只拿到它训练时用过的那种工具格式。
这不是抽象偏好,这是可以测量的成本:
多花的推理 token + 明显上升的出错率,在每天上百万次的 agent 调用里被记录下来。
05 Skill 是 .md 文件,运行起来却是三件事
Skill 看上去最像 “跨壳通用” 的东西。三个 harness 都用 SKILL.md + YAML frontmatter。Codex 还特地做了兼容,core-skills 能解析 Claude 风格的 markdown skill。Copilot CLI 明确会读 .claude/ 配置。格式近到—— 同一个 SKILL.md body,三个壳都能解析。
但 skill 不只是 markdown。一个 skill 藏着一份隐式契约:它期待调用哪些工具。
这份契约不在 frontmatter 里,而在 body 里。
Nicolas 举了个很直观的例子。一个 skill body 说:
步骤 1:调 TodoWrite,写三个子任务。
步骤 2:每个子任务用 Agent 派发,subagent_type=’Explore’。
步骤 3:等结果回来,用 Bash 校验后再 claim done。
这份 skill 在 Claude Code 里:TodoWrite 是内建的,Agent 是内建的,Explore 有壳强制的工具白名单,Bash 返回截断的输出模型知道怎么读。完美工作。
同一个 skill 在 Codex 里:TodoWrite 不存在,最接近的是 update_plan_tool,但 schema 不一样。Agent 没有这个 shape,Codex 有 spawn_agent_v1/v2。Bash 的对等物是 local_shell。skill 默默失败,或者跑出一个残血版本。
同一个 skill 在 Copilot CLI 里更惨。TodoWrite 这个动词根本不存在,subagent 调度走 task 或 read_agent/write_agent,Bash 是三动词的 read_bash/write_bash/stop_bash。
更要命的是,v1.0.5 加了一个实验功能:embedding 排序器决定这一轮塞哪三个 skill 到 prompt 里。一个假设 “所有 skill 都会出现在 system reminder 里” 的 skill body,在这种壳里行为完全不一样。
结论:
“我们都用 SKILL.md” 是个误导性的说法。格式一样,底下的契约根本不一样。
06 记忆层:最密集的碰撞面
记忆是 Model-Harness-Fit 里最密集的错配面——工具、schema、引用标签、衰减信号、检索仪式,这些东西在 post-training 里是一起学的,你拆一边另一边就全碎。
三种记忆架构,三种完全不同的押注。
Codex 是延迟批量写,两阶段流水线,触发条件是 6 小时以上空闲。Phase 1 用 gpt-5.4-mini 抽取原始记忆,写成一份严格 JSON schema 的 artifact(description、task、task_group、task_outcome、cwd、keywords)。Phase 2 用 gpt-5.4 对着 git baseline 做 consolidation,prompt 长达 841 行。模型的记忆引用是包在 <oai-mem-citation> XML 块里的。
Claude Code 是同步 live write,每条记忆一个 .md 文件,MEMORY.md 作为 index 永远加载,body 按需 read。每次 body read 都被壳包在 <system-reminder> 里,带上 “这条记录已经 X 天没更新,先验证再用” 的提醒。
GitHub Copilot CLI 是服务端记忆,有个专门的 store_memory 工具,记忆存在远端后端。v1.0.23 的 changelog 特地写了:”Agent no longer hangs on the first turn when the memory backend is unavailable.”——在此之前,后端挂了 agent 会卡在第一轮。
现在来混搭看看。
把 Codex 训的模型扔进 Claude Code:模型会找写记忆的工具,找到 Write,然后写出一份 Codex 格式的文件——带 task_group: 和 cwd: 标签——但写到了 Claude Code 下一次不会自动加载的目录里。下一次会话它看不到这份记忆。更要命的是,模型会发出 <oai-mem-citation> 块,但 Claude Code 根本不 parse 这个 tag。对下一个 turn 来说,记忆等于没存在。
把 Claude 训的模型扔进 Codex:模型不会发 citation tag。Codex 的 usage_count 永远不增长,衰减信号认为它们是没被用过的。几周之内,好记忆被当作没用的记忆悄悄淘汰掉。
07 一个六字符的 XML tag
<oai-mem-citation> 这个标签是整个故事的缩影。
Codex 的模型在每次用到记忆后,都会在 message 末尾挂一个 XML 块,带上 thread_id 和 raw_memory_id。壳有个 parser(citations.rs:6-43)把这个块剥掉,同时把对应记忆的 usage_count 和 last_usage 在 SQLite 里 +1。
模型和壳之间就是这样签的契约:你用了哪条记忆就告诉我。我会用 citation 去奖励它——记忆活下来。没人引用、30 天没有新的 source_updated_at 的那些,Phase 2 会把它们清掉。
Claude Code 的模型没有对应的 citation tag。因为壳不需要——记忆是通过 Read 读的,每次 read 前的 verification 本身就是 “我用了这条” 的信号。
Copilot CLI 的模型更没有 citation,因为 read / rank / 衰减全在服务端。
现在看看跨壳运行的结果。
Codex 模型扔进 Claude Code:模型依然在 assistant text 里挂 <oai-mem-citation>。Claude Code 的壳不解析这个 tag,它就原封不动显示给用户。用户看到一串 raw XML,Claude Code 的记忆系统里也没有衰减信号。
Claude 模型扔进 Codex:模型内联用记忆,不挂 tag。Codex 的 usage_count 永远是 0,被好好用着的记忆反而因为显得没被引用而排到最低,被淘汰。
六个字符的 XML tag,决定了一个记忆系统是越用越好还是默默腐烂。
08 GitHub Copilot CLI 的诚实做法
Copilot CLI 是三个壳里最有意思的一个,因为它是唯一明确尝试跨模型路由的。它的 picker 里有 Sonnet、Opus、Haiku 和 GPT-5.x 整条线,v1.0.32 甚至加了 auto 模式,按会话选择。
它是怎么处理 Model-Harness-Fit 的?三条路同时走:
第一:per-model 工具包含。v0.0.366 的 changelog:”Codex specific patch toolchain.” apply_patch 只在当前模型是 Codex 家族时才加载。Anthropic 的模型拿到的是它被训练过的 Edit / Write。
第二:per-model 工具搜索。v1.0.13:”Tool search for Claude models.” Claude 系模型被训练过 deferred tool loading(ToolSearch),壳只给它们开这个 loop。OpenAI 系模型被训练过一次性看到全部工具,就给它们完整工具列表。
第三:Critic agent 用互补模型。v1.0.18:”Critic agent 用互补模型自动审查 plan 和复杂实现。” Claude 系实验模式下启用。
这是一个真正意义上的路由。不是 “把所有东西翻译成公共方言”,而是 “把对的方言喂给对的模型”。
代价是诚实——壳必须承认 “Claude on Copilot CLI” 和 “GPT on Copilot CLI” 是两个不同的产品。用户选择了一个就是一个,没有中间那个公约数。
09 换模型要付的隐藏成本
Nicolas 引了 Cursor 研究团队的一段描述,讲会话中途切换模型时会有三件事同时崩。
第一,对话历史变成 out of distribution。前一个模型打出来的工具调用是它的方言:apply_patch block、<oai-mem-citation> tag、八动词 subagent dispatch。新模型被训练成另一种方言,现在要对着一份它自己绝不会打出来的 transcript 做推理。Cursor 的做法是在切换时塞一段 “你是从另一个模型手里接手” 的 meta instruction,能缓解但消不掉。
第二,prompt cache 断了。cache 是 provider × model 特定的,切换一次必然 cache miss。在长会话里,这意味着切换后的第一轮要按原价把整个 system prompt 和历史重新过一遍。
第三,工具表也换了形状。如果你刚走到 subagent dispatch 的一半,下一轮模型拿到的是一套不同的动词。
Cursor 做完所有缓解之后,给出的建议是:除非有理由,一般建议一个会话里只用一个模型。
最干净的变通是派一个 subagent 用另一个模型,而不是切换主对话。subagent 新开上下文窗口,没有 transcript 偏置,没有 cache 要断,而且从第一轮就在它自己的工具面上。
一次模型切换,其实是工具切换 + cache 失效 + 壳切换,三件事一起发生。

壳把分数往上推的量,这一年比模型升级推的还多。同一个 Claude Opus 4.6,换个 harness,Cursor 从 Top 30 冲到 Top 5。
10 对我们意味着什么
Nicolas 在文章最后给出了三条结论,我觉得都挺扎的。
对 agent 平台来说:选一个壳、选一个模型,把它们当成一个产品发。别假装模型可以跨壳移植,也别假装壳是中立的。Copilot CLI 的 “不同模型给不同工具” 是诚实版。那些装作 “一套公约数接所有人” 的,在每个模型上都会打折。
对模型实验室来说:壳是产品战略,不是基础设施。Anthropic 的 system-reminder 注入机制、typed memory 分类、每次 body read 的 verification;Codex 的两阶段记忆流水线、citation tag、严格 JSON schema;Copilot CLI 的十节 system prompt 骨架——这些都不是施工细节。它们是模型被雕塑的那个表面,是壳把模型变得不可替代的护城河。
对用户来说:换模型的成本比看上去高,比 vendor 希望你以为的低。高,是因为模型和壳融合了几个月的训练;低,是因为只要你肯复刻工具面、复刻 citation 契约、复刻 system prompt 结构、复刻记忆仪式,大部分差距能补回来——代价大致等于当初那次 post-training。
还有一个很有意思的观察:matched pair 不是静态的,它会随模型成熟而漂移。Rajasekaran 给 Sonnet 4.5 造的壳——context reset、sprint 拆分、激进压缩——在 Opus 4.6 身上变成了死重。他直接砍掉所有这些脚手架,跑了一个两小时连续会话。
Cursor 的话是一样的意思:harness 里的每个组件都编码了一个 “模型自己搞不定” 的假设。这些假设会过期。
所以回到 Nicolas 最开头的那个问题:为什么同一个 prompt 在三个壳上跑出明显不同的输出?
因为同一批权重跑在三个壳上,其实是三个不同的模型——权重 byte-for-byte 一样,本能却被各自的 post-training 条件化了。
落在 assistant 输出里的那部分,主要是本能,不是权重。
2026 年有意思的设计动作,不是更好的模型,也不是更好的壳,而是成对设计的 matched pair——post-training 和 runtime 互相加强,一轮一轮跑,模型在这个壳奖励的事情上越来越强。
Nicolas 最后一句话也挺狠:想待在前沿,新模型发布时你得删掉你大部分代码。LLM 把脚手架当早餐吃。
相关链接:
原文:Model-Harness-Fit by Nicolas Bustamante• Terminal-Bench 2.0 leaderboard(2026-04-30)
Cursor 研究团队 harness engineering 博客(2026-04-30)
LangChain Vivek Trivedy:Agent = Model + Harness
Anthropic rgb_prithvi:long running app development
数据来源:Nicolas Bustamante on X, 2026-05-04
本文由人人都是产品经理作者【汪仔8951】,微信公众号:【深思SenseAI】,原创/授权 发布于人人都是产品经理,未经许可,禁止转载。
题图来自Unsplash,基于 CC0 协议。
- 目前还没评论,等你发挥!

起点课堂会员权益




