跳转至

model-harness-fit-agent-harness

Ch01.217 model-harness-fit-agent-harness

📊 Level ⭐⭐ | 16.7KB | entities/model-harness-fit-agent-harness.md

Model-Harness-Fit:模型与壳的适配性

原文: https://mp.weixin.qq.com/s/TTe7IY_pjAuv4zA9krlYrQ Author: Nicolas Bustamante (Cursor/前OpenAI),编译自其博客 Score: value=7, confidence=7, product=49 ≥ 49 → PASS

核心论点:Model-Harness-Fit

"模型不是只针对API做post-training的,它是针对壳做的。" "同一批权重跑在三个壳上,其实是三个不同的模型——权重byte-for-byte一样,本能却被post-training条件化了。" — Nicolas Bustamante

让人不舒服的榜单

Terminal-Bench 2.0(2026年4月30日)前十名: | 排名 | Harness | 模型 | 分数 | |------|---------|------|------| | 1 | Codex | GPT-5.5 | 82.0% | | 2 | ForgeCode | GPT-5.4 | 81.8% | | 3 | TongAgents | Gemini 3.1 Pro | 80.2% | | 4 | 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分。 Cursor团队:同一个模型,没换权重,只改壳,榜单排名从Top 30冲到Top 5(一跳25位)。 结论:这一年壳带来的分数,比模型升级带来的分数还多。

为什么模型不是单独存在的

每个模型的post-training都包含了壳的细节:

  • 工具叫什么名字
  • 输入schema长什么样
  • 引用标签怎么写
  • skill文件放哪里
  • 计划协议什么格式 这些不是模型的通用能力,它们是byte级别的约定,被烤进了post-training

    "你把模型拖出它的壳,性能的损耗是你再也拿不回来的。除非你把另一边也重写一遍。" 这就是为什么每个号称"model agnostic"的agent都碰过同一堵墙——你没法就这么换个模型。 要干净地换模型,你得把壳也一起换掉:工具面、schema形状、skill里点名调哪些工具的主体、引用契约、记忆仪式、system prompt结构,有时连planning协议都得换。

三种壳,三种完全不同的协议

Codex:typed asynchronous protocol

  • 模型发出一个Submission,拿回一串typed Event
  • 协议定义在 codex-rs/protocol/src/protocol.rs,用Rust的serde枚举
  • 模型被训练成"发submission、读event"的思维模式
  • AGENTS.md里写着硬性组织纪律:每个Rust模块软上限500行,硬上限800行,新feature以新crate形式上——这是编译器工具链的组织纪律被拿来治理agent harness

Claude Code:direct typed conversation loop

  • ConversationRuntime::run_turn 每轮吃一个 Vec<AssistantEvent>
  • 模型在assistant message里直接打出工具调用,下一轮接上tool result
  • 记忆是两阶段流水线:
  • Phase 1:gpt-5.4-mini抽取原始记忆,写成严格JSON schema artifact
  • Phase 2:gpt-5.4对着git baseline做consolidation,prompt长达841行
  • 模型的记忆引用是包在 <oai-mem-citation> XML块里的

GitHub Copilot CLI:supervisor protocol- host不跑agent loop,spawn一个 @github/copilot 子进程,用vscode-jsonrpc走stdio

  • agent loop跑在子进程里
  • 是三个壳里唯一明确尝试跨模型路由
  • picker里有Sonnet、Opus、Haiku和GPT-5.x整条线

工具表:模型的方言

表面看前6个工具(read/write/bash/grep/glob)都一样,第7个开始发散:

Codex的工具差异化

  • apply_patch:自家diff格式,两种变体(Lark语法版本+JSON版本)
  • 8个subagent编排动词:spawn_agent_v1、spawn_agent_v2、wait_agent_v1、wait_agent_v2、send_message、close_agent_v1、close_agent_v2、resume_agent

Claude Code的工具

  • 只有一个Agent动词做subagent调度
  • Edit(old_string/new_string)格式

Copilot CLI的工具

  • read_agent/write_agent搞多轮subagent控制
  • task搞dispatch
  • 三动词的read_bash/write_bash/stop_bash

    Cursor壳团队说得最白:"OpenAI的模型被训练成用patch格式编辑文件,Anthropic的模型被训练成用字符串替换。两个模型都能用对方的工具,但是用陌生的那个,要多花推理token,而且出错率更高。" 这不是抽象偏好,这是可以测量的成本。

Skill:md文件格式一样,底下契约根本不一样

三个harness都用SKILL.md+YAML frontmatter,格式近到同一个skill body三个壳都能解析。但skill藏着隐式契约——它期待调用哪些工具,这不在frontmatter里,而在body里。 同一个skill(假设有TodoWrite步骤)在:

  • Claude Code里:TodoWrite是内建的,Agent是内建的,Explore有壳强制的工具白名单——完美工作
  • Codex里:TodoWrite不存在,最接近的是update_plan_tool但schema不一样,Agent没有这个shape——默默失败或跑出残血版本
  • Copilot CLI里:TodoWrite动词根本不存在,subagent调度走task或read_agent/write_agent——更惨

记忆层:最密集的碰撞面

三种记忆架构,三种完全不同的押注: | | Codex | Claude Code | Copilot CLI | |--|-------|------------|-------------| | 写入模式 | 延迟批量写,两阶段流水线 | 同步live write | 服务端记忆 | | 触发条件 | 6小时以上空闲 | 同步写入每条.md | store_memory工具 | | 格式 | 严格JSON schema | 每个记忆一个.md文件 | 远端后端 | | 引用信号 | <oai-mem-citation> XML块 | 每次body read的verification | 服务端处理 | | 衰减机制 | 30天无引用+Phase2淘汰 | usage_count+last_usage | 服务端衰减 |

:六个字符决定记忆生死

Codex的模型每次用到记忆后在message末尾挂<oai-mem-citation> XML块,带thread_id和raw_memory_id。壳的parser把这个剥掉,同时在SQLite里把对应记忆的usage_count和last_usage+1。 跨壳运行的结果

  • Codex模型扔进Claude Code:模型依然挂citation tag,Claude Code的壳不解析,直接显示给用户,记忆系统里没有衰减信号
  • Claude模型扔进Codex:模型内联用记忆不挂tag,Codex的usage_count永远是0,好记忆反而被当作没用的淘汰掉

    "六个字符的XML tag,决定了一个记忆系统是越用越好还是默默腐烂。"

Copilot CLI的诚实做法:真正的路由

Copilot CLI是唯一明确尝试跨模型路由的,三条路同时走: 1. per-model工具包含:v0.0.366 changelog:"Codex specific patch toolchain"——apply_patch只在当前模型是Codex家族时才加载 2. per-model工具搜索:v1.0.13——"Tool search for Claude models"——Claude系模型拿到deferred tool loading loop,OpenAI系模型拿到完整工具列表 3. Critic agent用互补模型:v1.0.18——Claude系实验模式下启用

"不是把所有东西翻译成公共方言,而是把对的方言喂给对的模型。" 代价是诚实——壳必须承认"Claude on Copilot CLI"和"GPT on Copilot CLI"是两个不同的产品

换模型的隐藏成本

三重同时崩溃

  1. 对话历史变out-of-distribution:前一个模型打出来的工具调用是它的方言,切换后新模型要对着一份它自己绝不会打出来的transcript推理
  2. prompt cache断了:cache是provider×model特定的,切换一次必然cache miss
  3. 工具表换了形状:走到subagent dispatch的一半,下一轮拿到的是一套不同的动词 Cursor做完所有缓解之后,给出的建议:除非有理由,一般建议一个会话里只用一个模型。最干净的变通是派一个subagent用另一个模型,而不是切换主对话。

Matched Pair会漂移

"harness里的每个组件都编码了一个'模型自己搞不定'的假设。这些假设会过期。" "想待在前沿,新模型发布时你得删掉你大部分代码。LLM把脚手架当早餐吃。" Rajasekaran给Sonnet 4.5造的壳(context reset、sprint拆分、激进压缩),在Opus 4.6身上变成了死重。他直接砍掉所有这些脚手架,跑了一个两小时连续会话。

深度分析

1. Model-Harness-Fit 的范式革命

传统观点认为 AI 模型是"大脑",Harness(壳)是"身体"——模型决定能力上限,壳决定如何调用。但 Nicolas Bustamante 揭示的 Model-Harness-Fit 范式彻底颠覆了这一认知:壳不是模型的附属品,而是模型行为不可分割的一部分。 这一论断的证据极具说服力:同一权重(byte-for-byte identical)在不同壳上展现出显著不同的性能表现(Claude Opus 4.6:ForgeCode 79.8% vs Capy 75.3%)。这种差异不是来源于模型能力,而是来源于post-training 中编码的隐式契约——工具命名、schema 格式、引用协议等细节。 这意味着,当我们评估一个 coding agent 的能力时,不能只看"模型是什么",必须看"壳是什么"——两者是一个整体。这对 AI 基础设施的构建方式产生了根本性影响。

2. 工具表作为"模型方言"的深层含义

文章提出"工具表=模型方言"这一概念,直击跨模型移植的本质困难。传统观点认为"工具是抽象接口,换模型时保持工具不变即可"。但 Bustamante 的分析表明:工具的具体实现(如 Edit 的 old_string/new_string vs apply_patch 的 diff 格式)是模型 post-training 的一部分。 当模型被训练成"用 patch 格式编辑文件",它对工具调用的预期格式是特定的。用陌生格式调用它,模型需要额外的推理 token(因为这不是它的本能反应),而且出错率更高。这不是实现细节,而是可以测量的性能差异。 这一洞见对 agent 框架设计的启示是:不应该追求"抽象掉模型的差异",而应该拥抱模型的特异性,为每个模型定制最合适的工具接口。Copilot CLI 的"per-model 工具包含"策略正是这一理念的体现。

3. 记忆系统的"隐性契约"问题

记忆层是 Model-Harness-Fit 理论最有力的证据:三种壳实现了三种完全不同的记忆架构——延迟批量写 vs 同步 live write vs 服务端记忆。更关键的是,这些架构之间的差异在表面上不可见(都叫"记忆"),但实际行为完全不同。 <oai-mem-citation> XML 块的案例尤其说明问题:六个字符的差异决定了记忆系统的生死——在 Codex 中这是衰减信号,在 Claude Code 中这是无意义的噪音。这意味着跨壳移植时,即使记忆的概念相同,隐式契约也不同。 这对构建长期记忆系统的 agent 框架提出了严峻挑战:当模型升级时,记忆系统可能需要完全重建。"build to delete"的建议正是基于这一现实:harness 的每个组件都会过期,必须准备好重建

4. "Matched Pair 漂移"的工程哲学

文章提出的"Matched Pair 会漂移"概念,揭示了 LLM 应用开发的一个根本性悖论:我们为模型构建的护栏和辅助结构,本质上是因为模型"自己搞不定"某些事情。但当模型能力提升时,这些护栏反而变成了限制。 Rajasekaran 的案例极具说明性:为 Sonnet 4.5 精心设计的 context reset、sprint 拆分、激进压缩等机制,在 Opus 4.6 身上变成了性能损耗。这不是框架的错,而是能力边界的相对性——护栏的必要性取决于模型当前的能力边界,而模型能力在快速演进。 "build to delete"的建议不仅是技术策略,更是一种工程哲学:对 LLM 应用保持谦逊,不要过度投资于当前能力边界的辅助结构,因为它们可能在新模型发布时变成债务

实践启示

给 Agent 平台开发者的建议

  1. 选模型+选壳,当成一个产品发

  2. 不要假装模型可以跨壳移植

  3. 不要假装壳是中立的
  4. "Claude on Copilot CLI"和"GPT on Copilot CLI"是两个不同的产品,需要不同的工具配置
  5. 建立 Matched Pair 的持续评估机制

  6. 每个新模型发布时,重新评估 harness 的每个组件

  7. 准备"build to delete"的心理预期——旧护栏可能变死重
  8. 用两小时连续会话测试旧 harness 在新模型上的表现,而不是假设兼容性存在
  9. 换模型要整个换

  10. 工具面 + citation 契约 + system prompt 结构 + 记忆仪式 一起换

  11. 试图只换一半(换模型但保留 harness)会导致三重同时崩溃
  12. 最干净的变通:派一个 subagent 用另一个模型,而不是切换主对话

给模型实验室的建议

  1. 壳是产品战略,不是基础设施

  2. Anthropic 的 system-reminder 注入机制

  3. Codex 的 citation tag
  4. Copilot CLI 的十节 system prompt 骨架
  5. 这些不是施工细节,是模型被雕塑的那个表面,是模型的护城河
  6. post-training 中编码壳细节是合理策略

  7. 模型不是"通用大脑",而是针对特定使用场景优化的产品

  8. 试图让模型适配所有壳,反而会让模型在每个壳上都表现平庸

给 Agent 应用开发者的建议

  1. 不要迷信"model agnostic"

  2. 任何声称可以无缝换模型的框架,都会碰壁

  3. 如果需要换模型,准备好重新调优整个 harness
  4. 优先投资于工具配置的精细化

  5. 在模型选择上的边际收益可能低于在 harness 调优上的边际收益

  6. Cursor 换壳 Top30→Top5 的案例说明:harness 优化可以带来质的飞跃
  7. 构建可观测性

  8. 监控 harness 中每个组件的有效性

  9. 建立"护栏有效性"的评估机制,在模型能力跃升时及时删除旧护栏
  10. 团队工具链选择策略

  11. 如果团队使用多个模型,为每个模型维护独立的 harness 配置

  12. 不要假设同一套 prompt 或工具配置在不同模型上表现相同
  13. 记录每个模型在特定任务上的表现差异,持续优化 match

来源

  • 原文:Model-Harness-Fit by Nicolas Bustamante (https://claude.com/blog)
  • 数据来源:Nicolas Bustamante on X, 2026-05-04
  • Terminal-Bench 2.0 leaderboard(2026-04-30)
  • Cursor研究团队harness engineering博客(2026-04-30)
  • Cursor Harness Model

相关实体