10713 words
54 minutes
Codex 与 Claude Code 为什么如此不同:两种 AI Harness 设计思路

最近 Anthropic 发布了 Claude Opus 5。模型能力大幅提升的同时,Anthropic 也给 Claude Code 带来了一项值得关注的变化:针对 Claude Opus 5、Claude Fable 5 等新模型,将 Claude Code 的 system prompt 缩减了 80% 以上,并且在其 coding evaluations 中没有观察到可测量的性能下降。

这一举措一方面说明,随着 Opus 5 自身模型能力的提升,过去为了约束模型行为而不断加入的提示词,其中相当一部分已经变得不再必要,甚至可能开始限制模型自身的判断;另一方面也引导我们重新理解 AI harness:它并不是一套设计完成后长期不变的框架,而应该随着模型能力的变化,不断调整模型与 harness 之间的职责边界。

借此机会,我也想分享一下自己一段时间以来对 Claude Code 和 Codex 设计理念差异的观察。尤其是在 AI harness 的设计上,OpenAI 和 Anthropic 表现出了明显不同的倾向。

总的来说,我的看法是:

Codex 更像是把模型当成一个能够直接操作计算机的工程师,harness 主要负责给它提供高表达力的工具、反馈和安全边界;Claude Code 则更像是把模型放进一套受控的工程工作流,harness 除了提供工具和边界,也规定了相当一部分工作程序。

这不是“一个有约束、一个没有约束”的区别。Codex 的 sandbox、approval、patch parser、tool router 都相当严格;Claude Code 也绝不是只会机械执行状态机。两者真正不同的地方,是哪些决定交给模型,哪些不变量由 harness 直接保证。

本文主要从 Edit Tool、Plan Mode、Agent Loop、LLM API 与 KV Cache、Reasoning、Skills、MCP、Memory、Subagent 和 Multi-Agent 几个方面展开。

产品一直在变化,本文讨论的是这些版本所呈现的设计倾向,而不是某个产品永远不变的结论。

什么是 AI Harness#

今天评价一个 coding agent,已经不能只看模型 benchmark 了。

同一个模型放到不同 harness 中,会表现出非常不同的行为:它能看到哪些上下文,以什么格式调用工具,修改文件之前是否必须读取,失败以后收到什么反馈,计划如何保存,context window 不够时留下什么,子 agent 能继承多少状态,这些都会直接影响最后的成功率。

如果用一个不太严谨但比较直观的公式表达:

Agent 能力 ≈ 模型能力
× 上下文组织
× 工具的表达能力
× 状态恢复能力
× 反馈与约束

AI Harness 由模型能力、上下文组织、工具表达、状态察觉以及反馈与约束共同构成

LLM 只是其中的决策核心。把模型包装成一个可以长时间工作的 agent 的其余部分,都可以被视作 AI harness。

这里有一个很重要的区别:harness 可以约束操作边界,也可以约束工作方法。

例如,“不能写 workspace 以外的文件”“执行危险命令需要用户批准”,属于操作边界;“修改文件前必须 Read”“一个任务必须先进入 Plan Mode”“完成三个 task 时必须启动 verifier”,则已经开始进入工作方法。

操作边界越明确通常越好,因为它们对应安全、权限和不可逆影响。工作方法却不一定如此。它可能弥补当前模型的能力缺陷,也可能在模型升级以后成为过时的脚手架。

从这个角度看,Codex 的 harness 可以概括为:语义上较薄,操作上很厚。它较少规定模型应该如何理解和分解任务,但会严格处理 patch 语法、沙箱、审批、工具执行和错误回传。

Claude Code 则在语义和操作两个层面都更厚。除了安全边界,它还把 Read/Edit 的前置条件、plan 的生命周期、task 状态、hooks、compaction 恢复和 agent 角色等内容做进了客户端运行时。

两种路线在 Edit Tool 上表现得最明显。

Edit Tool:输出最终动作,还是声明局部意图#

Codex 的完整 patch 与 Claude Code 的局部 edit 工具对比

Codex:让模型直接写 patch#

Codex 的 apply_patch 是一个 freeform tool。模型不需要先构造一组 JSON 参数,而是直接输出完整 patch:

*** Begin Patch
*** Update File: src/example.ts
@@
-const timeout = 1000
+const timeout = 3000
*** End Patch

在源码里,这个工具通过一份 Lark grammar 约束输入格式;注释中还直接写着这套 freeform 形式很适合 GPT-5 系列模型。工具描述只告诉模型 patch 的语法,真正的文件修改内容全部由模型生成。

这种接口的单次动作表达能力很强。一个 patch 可以同时新增、删除、移动和修改多个文件,也可以一次完成多个不相邻的变更。模型已经理解了最终代码应该是什么,harness 没有必要再把这个决定拆成大量低带宽的 old_string/new_string 调用。

但“相信模型生成 patch”不等于直接执行模型输出。

Codex 会先解析 patch,根据当前文件系统验证路径和上下文,再进入 sandbox 与 approval 流程。验证失败不会被静默忽略,而会作为结构化错误返回给模型,让模型重新观察并修复 patch。

所以 Codex 信任的是模型对修改内容的判断,而不是模型输出的每一个字节都一定正确。harness 仍然保证 patch 必须符合语法、必须能在当前文件状态上应用,也不能越过权限边界。

这是一种很典型的设计:

模型负责:我要把代码改成什么
Harness 负责:这个动作是否合法、能否在当前环境执行

Claude Code:让模型提交一个受约束的 edit#

Claude Code 的 FileEditTool 采用了另一种接口:

{
file_path,
old_string,
new_string,
replace_all
}

模型不是直接输出最终 diff,而是声明“把这段旧字符串替换成这段新字符串”。

真正值得注意的并不是 JSON 形式,而是工具背后的状态约束。FileEditTool 会检查:

  • 模型是否已经完整 Read 过这个文件;
  • Read 之后文件是否又被其他进程或用户修改;
  • old_string 是否存在;
  • 它是否只匹配一个位置;
  • 多次匹配时,模型是否显式设置了 replace_all

这些检查在实际写入前还会在原子化的 read-modify-write 临界区内再次执行。写入成功以后,工具还会更新文件历史、read state,并通知 LSP。

从效果上看,这已经很接近一个带版本检查的文件事务。模型只负责表达局部修改意图,harness 决定这次意图是否仍然可以安全地落到当前文件上。

它能非常确定地阻止一类常见问题:模型读完文件以后,用户又在 IDE 中改了同一文件;模型基于旧内容继续写,覆盖了用户的新修改。也能阻止一个看似合理的 old_string 意外改到三个位置。

代价同样很直观。模型往往需要先 Read,再发起一次或多次 Edit;多文件、大范围、不规则重构会产生更多 tool call,也会把很多中间文件内容重新带进上下文。相比之下,Codex 的一个 patch 可能已经把整个动作表达完了。

因此两者不是简单的“diff 比字符串替换高级”。真正的区别是动作边界:

Codex:模型提交最终动作,harness 验证动作能否执行
Claude Code:模型提交局部意图,harness 根据内部状态构造并约束动作

Codex 优先提高模型的动作带宽,允许模型一次表达更多东西;Claude Code 优先减少状态不一致,让每次写入都满足一组明确前置条件。

这也解释了二者不同的错误分布。Codex 的语法错误和上下文不匹配会在 apply patch 时发现,但更高层的语义错误通常要在修改之后,通过编译、测试或者 review 才会暴露。Claude Code 会在写入前消除 stale edit、重复匹配等一部分确定性风险,却要为此维护更多客户端状态。

Plan Mode:对话中的认知产物,还是可恢复的运行时状态#

Edit Tool 体现的是一次动作如何发生,Plan Mode 体现的则是一个长任务如何被组织。

Codex 的对话式计划与 Claude Code 的可恢复运行时计划对比

Codex:Plan 是一次对话承诺,不是一份持久化状态#

Codex 在 Plan Mode 上最大、或许也最出乎大家意料的一个做法,是它并没有真正建立一个独立的 Plan 状态。

模型规划完成以后,这份计划不会作为一份拥有独立生命周期的 plan file 落盘。程序中也没有一个类似 current_plan、脱离对话上下文而存在并且可以在后续被稳定查询的状态。换句话说,Codex 没有为 plan 建立专门的保存、读取、版本管理和恢复流程。

那么 Codex 的 Plan Mode 实际上做了什么?

首先,它向模型注入一套 developer instructions,要求模型在这个阶段探索代码、澄清问题、形成完整方案,并保持只读。等模型认为计划已经完成,再用 <proposed_plan> 标签把最终方案返回给用户。

Codex 随后会从 assistant message 中提取 <proposed_plan> 的文本,并将其包装成一个 PlanItem 放入 thread history。PlanItem 本身也非常简单,只有 idtext,没有文件路径、版本、当前执行位置或恢复策略。

因此,在没有发生上下文压缩的会话中,模型仍然可以像阅读其他历史消息一样看到此前的 plan;thread 本身被持久化时,这段文本也可能跟着被保存。但这只是对话历史的持久化,并不代表 Codex 持久化了一个独立的 Plan。

这个区别会在 context compaction 后真正表现出来。Codex 会使用保留下来的用户消息和模型生成的 summary 重建新的 history,原始 PlanItem 并不会通过某个 plan store 被重新读取并注入。如果 summary 没有完整保留计划中的约束和取舍,后续模型也就无法再次阅读那份原始 plan。

如果读者也去阅读 Codex 的源码,很快还会发现一个名为 update_plan 的工具。它看起来似乎就是用来保存上述 plan 的,但实际上两者没有直接关系。

Codex 明确把 update_plan 定义成任务执行过程中的 TODO/checklist。它记录的是若干简短步骤,以及每个步骤的 pendingin_progresscompleted 状态,用于向用户展示当前进度;它不会接收或保存 Plan Mode 最后生成的完整方案。

Codex 甚至会在 Plan Mode 中明确拒绝 update_plan,并向模型返回“它是 TODO/checklist 工具,不能在 Plan Mode 使用”的错误。这进一步说明,在 Codex 的实现里,Plan Mode 产生的方案和执行阶段的进度列表是两套互不相同的概念。

同样值得注意的是,至少在工具装配这一层,apply_patch 并不会因为进入 Plan Mode 就直接从工具列表中消失。Plan Mode 的只读要求主要来自 developer instructions,再叠加既有的 sandbox 和 approval 边界。

所以说 Codex 的 Plan 是模型与用户之间的一次契约会更加准确:模型先说明自己准备如何完成任务,并在之后的执行中遵守这份承诺;harness 没有把这份承诺进一步转换成一套持久化工作流。

这样的实现几乎没有计划状态同步的成本,也不会要求模型围绕 plan file 工作。但当任务经历 context compaction、长时间 resume、handoff 或多 agent 协作时,计划能否延续就会更依赖模型生成的摘要和当前上下文,计划漂移也更容易发生。

Claude Code:Plan 是工作流状态#

Claude Code 的 EnterPlanModeTool 会直接切换 permission mode。计划被写入 session 对应的独立 plan 文件,ExitPlanModeTool 会读取这份文件,并在需要时进入 leader approval 流程。

这份 plan 不是普通的对话文本。Claude Code 会给每个 session 生成 plan slug;resume 时恢复原 plan,远端 plan 文件丢失时尝试从 snapshot 或消息历史重建,fork session 时再复制一份新的 plan。

甚至在 context compaction 之后,运行时也会把 plan 内容和当前 plan mode 重新注入上下文。

因此 Claude Code 的 plan 既是模型的认知产物,也是 harness 的运行时状态。

这样设计很适合长时间任务、跨 session 恢复、leader/teammate 协作和多人 handoff。即使自然语言对话被压缩,系统仍然知道当前正在 plan mode,也能重新拿到此前批准或等待批准的计划。

它的成本是状态同步。计划文件、对话内容、task 状态、permission mode 和真实代码可能彼此偏离,harness 需要处理恢复、分叉、缺失和冲突。原本只是一段模型输出的计划,现在变成了需要维护生命周期的数据结构。

所以 Codex 更接近“让模型记住自己承诺过什么”,Claude Code 更接近“让系统记住模型正处于哪个工作阶段”。

对于十分钟内可以完成、用户一直在场的交互式任务,前者通常更轻;对于会跨越数小时、发生 compaction、换 agent 或由多个角色协作的任务,后者会更可审计,也更容易恢复。

Agent Loop:相同的基本循环,不同的流程密度#

几乎所有 coding agent 的最小循环都一样:

组织上下文 → 调用模型 → 执行工具 → 返回结果 → 再次调用模型

Codex 的自主 Agent Loop 与 Claude Code 的显式状态机对比

Codex 的核心 turn loop 基本忠实地保留了这个结构。源码注释直接描述了两种情况:模型返回 function call,就执行并把结果送入下一次请求;模型只返回普通 assistant message,这个 turn 就结束。

当然,实现内部还要处理流式事件、并发工具调用、重试、审批、compaction、diff tracking 等大量工程细节。但这些机制大多服务于“让模型可靠地观察—行动—再观察”,没有试图替模型预先规定完整的解题过程。

Claude Code 的 query loop 则明显更像一个工作流状态机。除了消息和工具结果,它还持续维护 tool-use context、自动压缩、token 恢复、stop hooks、turn count、permission transition、queued commands、agent 状态、memory 和 skill discovery 等状态。

每轮调用之前和之后,都有一系列 harness 级判断:

  • 是否需要 micro-compaction 或 auto-compaction;
  • hook 是否要求停止或继续;
  • 是否有新的 memory、skill、command 和 agent 状态需要附加;
  • MCP tools 是否发生变化;
  • 是否达到 max turns;
  • 当前 continuation reason 应该进入哪个分支。

这让 Claude Code 更容易在模型输出之外实现确定性流程,也意味着客户端本身拥有更多可能出 bug 的状态。

Verification 是一个很好的例子#

Codex 的默认提示倾向于把验证留给模型判断:如果仓库有测试就考虑运行,并根据任务情况决定验证范围。它没有假设所有变更都必须经过同一套 verifier 流程。

Claude Code 则可以把验证做成结构化工作流。这里的 task 指 Claude Code 通过 Task 工具维护的工作项:模型可以把一个长任务拆成若干条 task,并分别标记为 pending、in progress 或 completed。当模型完成一个包含三个以上 task 的任务列表,其中却没有任何一项与验证有关时,TaskUpdateTool 会在工具结果中追加提醒,要求模型在总结前启动 verification agent。这个内置验证 agent 只有只读工具,必须实际运行测试或检查命令,最后输出明确的 PASS、FAIL 或 PARTIAL verdict。

前文提到 Anthropic 为新模型删除了 Claude Code system prompt 中 80% 以上的内容,其中也包括原本长期放在 system prompt 里的 verification 和 code review 指引。只看“删除”这个动作,很容易理解成 Claude Code 弱化甚至取消了验证流程;Anthropic 实际做的是改变这些规则的加载位置:把并非每次任务都需要的详细指引迁移到按需调用的 skills 中。

因此,被删除的是每轮调用都常驻上下文的验证说明,验证能力和运行时机制仍然存在。TaskUpdateTool 的结构性提醒、独立 verification agent 和按需加载的 verification skill 可以继续发挥作用,只是不再共同占据每一次模型请求的 system prompt。

这恰好说明 harness 的设计不只有“有约束”和“没约束”两个选项。规则可以放在常驻 prompt、tool schema、运行时状态机、hook、skill 或独立 verifier 中。放在哪里,决定了它的 token 成本、强制程度和更新成本。

LLM API、KV Cache 与 Reasoning:产品哲学之外的基础设施差异#

讨论两套 harness 时,还有一部分差异不能完全归因于 OpenAI 和 Anthropic 对 agent 的主观理念,因为底层 API 本身就在塑造客户端。

先澄清一个经常被混用的概念:Codex 和 Claude Code 都不直接管理推理服务中的原始 KV Cache。它们能做的是稳定 prompt prefix、设置 API cache 标记,或者利用服务端 continuation,让后续请求尽可能复用已有推理状态。

Codex:延续一个服务端 response#

Codex 的 Responses/WebSocket 路径会比较 model、instructions、tools、reasoning 配置和 cache key 等请求属性。如果前缀仍然兼容,它可以携带 previous_response_id,只发送本轮新增的输入,而不必每一轮都在客户端重建并重新提交完整历史。

这会自然鼓励一种较薄的客户端:服务端 response 是连续状态的一部分,客户端重点维护本轮新增内容和工具执行结果。

Codex 协议中还把 reasoning summary 和 reasoning content 作为独立 item 处理,并允许配置 reasoning effort。harness 的主要职责是传输、续接和展示这些状态,而不是在客户端实现一套复杂的“如何思考”流程。

Claude Code:精确维护完整消息前缀#

Claude Messages API 的使用方式让客户端承担了更多 transcript 组织工作。Claude Code 会在 system 和 message blocks 上插入 cache_control,维护唯一的 message cache marker,并谨慎修复 tool_use/tool_result 的配对关系。

工具定义也是缓存前缀的一部分,因此 Claude Code 会稳定排序 built-in tools 和 MCP tools,尽量避免无关的顺序变化破坏 cache prefix。

这里还涉及 Claude 的 extended thinking。它是 Claude 面向复杂任务提供的一种扩展推理机制:模型会在生成最终文本或决定工具调用时投入额外推理,API 则用独立的 thinking content block 承载这部分推理状态。在连续的工具调用中,客户端需要把这些 thinking blocks 原样传回 API,模型才能延续此前的推理过程。

每个 thinking block 还包含一个不透明的 signature 字段,用来证明这段推理状态确实由 Claude 生成,并携带服务端恢复完整 thinking 所需的加密信息。客户端不能自行修改或重新构造它,否则 API 会拒绝这段消息;Claude Code 因此需要在流式响应中同时收集 thinking_deltasignature_delta

当主模型不可用、Claude Code 需要 fallback 到另一个模型时,原模型生成的 thinking block 可能与备用模型不兼容,直接放进新请求会得到 400 错误。这份实现会在重试前移除历史中带签名的 thinking blocks,让备用模型基于清理后的消息继续工作。这也是 reasoning 状态进入 harness 的一个具体例子:一旦推理结果需要跨 API 请求延续,客户端就必须负责它的保存、完整性以及模型切换时的兼容处理。

因此,在 Codex 中,reasoning 更像 API 原生的连续状态;在 Claude Code 中,thinking block、signature、cache marker 和 transcript 合法性需要被客户端更显式地管理。

这并不直接说明谁更先进。它说明了一个更容易被忽略的事实:API 的状态模型,就是 agent harness 的地基。如果服务端替你维护 response continuation,客户端可以更轻;如果客户端每轮都要提交一份结构正确、前缀稳定的完整消息,它就必然长出更多 cache 和 transcript 管理逻辑。

Skills 与 MCP:给模型知识,还是给运行时增加一个模块#

Codex 的 MCP 工具连接与 Claude Code 的 Skills 能力封装对比

Skills 经常被理解成一份“告诉模型如何做某件事的 Markdown”。在 Codex 中,这个理解基本接近它的核心形态,但也不完全。

Codex skill 除了 name、description 和路径,还可以声明依赖工具、调用策略、scope 和 plugin 等元数据。harness 会把可用 skill 的说明放入 developer context,模型再根据任务选择并阅读具体 SKILL.md。

所以 Codex skill 更像一个提供给模型的“可发现操作手册”:harness 负责发现、注入、权限和依赖,如何把手册应用到当前任务,主要仍由模型判断。

Claude Code 的 skill frontmatter 能直接影响更多运行时行为,包括 allowed tools、model、effort、hooks、execution context、agent 和是否允许模型自动调用等。

一个 skill 甚至可以要求 context: fork,让 Claude Code 在隔离的 subagent 中执行,并拥有自己的模型、effort 和 token budget。

从这个意义上说,Claude Code 的 skill 不只是文档,也更接近一个声明式运行时模块。

但 Anthropic 最近的调整又让这里发生了有趣的变化。随着模型判断力增强,它开始把更多规则从常驻 system prompt 移到按需加载的 skill,同时删减 skill 中不必要的解释和例子。skill 的定位也随之变得更轻:它承担的职责逐渐从“详细教模型每一步怎么做”,转向“在需要时提供目标、边界和参考资料”。

MCP 也存在类似问题。MCP 只规定了工具、资源和提示如何被发现与调用,并不会自动解决上下文膨胀、权限或工具选择。

Codex 的 MCP 调用链重点处理动态刷新、tool binding、审批、sandbox、参数改写、结果清洗和截断。这延续了它一贯的边界设计:模型选择工具,harness 保证调用在受控环境中发生。

Claude Code 则把 MCP 更深地接入了 cache 和 agent 配置:MCP tools 要保持稳定顺序,可以在 turn 之间刷新,也可以只暴露给某个特定 agent。它更关心“当前工作流中的这个角色,此刻应该看到哪些工具”。

工具越多,单纯把所有 schema 永久塞进 prompt 的成本就越高。无论是 tool search、deferred loading 还是 skill discovery,本质上都在解决同一个问题:让模型拥有很多潜在能力,但每一轮只为当前相关的那部分付 context 和 cache 成本。

Memory 与 Compaction:任务连续性与长期经验#

长任务一定会遇到 context window。真正的问题不是会不会 compact,而是 compact 以后系统还记得什么。

Codex 的大上下文取舍与 Claude Code 的上下文提炼和压缩对比

Codex 的 compaction 会从历史中保留用户消息和模型生成的 summary,再重新注入初始上下文与当前 world state。源码也明确提醒,长 thread 和多次 compaction 会降低准确性。

这意味着过程连续性在较大程度上依赖模型把重要信息写入 summary:之前为什么放弃某个方案、计划进行到了哪一步、用户刚刚修改了什么。如果 summary 没保住这些语义,后续模型只能从压缩结果中重新推断。

Claude Code 除了 summary,还会显式重建一批运行时 attachments。compaction 完成以后,它会重新附加最近读取的文件、plan 内容、已调用的 skills、当前 plan mode 和异步 agent 状态。

这里的设计差异非常清楚:

Codex:尽量让压缩后的语义摘要延续任务
Claude Code:摘要之外,再由 harness 恢复关键操作状态

后者对 resume、handoff 和 multi-agent 更稳定,却需要定义哪些状态值得恢复、恢复到什么版本,以及它们和摘要冲突时相信谁。

上面讨论的是一次长会话经过 compaction 以后如何继续。到了跨会话 memory 这一层,Codex 和 Claude Code 又选择了两种目标不同的实现。

从产品目标上看,Codex 的 memory 更类似我们在 ChatGPT 网页端使用的记忆功能:它关注的是如何从多次使用中沉淀能够长期复用的经验和知识,例如稳定的用户偏好、反复出现的工作习惯、验证过的操作流程、仓库中的关键路径以及曾经遇到过的失败模式。它服务于未来的 agent,不只服务于生成这条记忆的当前会话。

这套 memory pipeline 会在 root session 启动时异步运行。第一阶段扫描近期符合条件的 rollout——也就是一次 agent 会话留下的完整执行记录——并让模型从每个 rollout 中提取结构化记忆;第二阶段再从多个会话的结果中选择高价值内容,由 consolidation subagent 合并成 MEMORY.mdmemory_summary.md 和可复用 skills。由于它使用的是用户级 memory workspace,输入可能来自不同会话和不同项目,最终记忆也会保留各自的项目或工作目录边界,避免把只适用于某个仓库的经验错误推广到其他任务。

claim、lease、并行提取、重试、敏感信息清理、全局锁和 heartbeat 等机制,都是围绕这套自动化长期记忆流水线产生的。它要从许多历史会话中持续提炼知识,同时处理多个 Codex session 并发启动的问题,因此需要解决重复提取、全局一致性和失败恢复。

Claude Code 的 memory 实现相对直接一些。每种 agent 可以选择 userprojectlocal 作用域,harness 为这个 agent 准备对应的 memory 目录和 MEMORY.md,再把读写说明及已有内容注入 agent 上下文:

  • user memory 保存跨项目通用的经验;
  • project memory 保存当前项目内、可以随版本库共享的知识;
  • local memory 保存只适用于当前项目和当前机器的内容。

不同 agent type 拥有各自的 memory 文件,因此 verifier、explorer 或其他自定义 agent 可以长期保留与自身职责相关的信息。

从这份实现看,Claude Code 没有采用 Codex 那种扫描大量历史 rollout、再进行两阶段全局整合的流水线。它更强调在明确的 agent 和作用域下保存一份可以直接阅读、修改和注入的文件化记忆,机制更简单,也更容易让用户或项目维护者理解记忆从哪里来、会作用到哪里。

因此二者虽然都叫 memory,解决的问题并不完全相同:

Codex Memory:从多个历史会话中自动沉淀长期经验和可复用知识
Claude Code Memory:为特定 agent 和作用域维护直接可用的持久化上下文

Codex 为自动提取、跨会话整合和全局复用付出了更高的后台工程成本;Claude Code 选择了更明确、更轻量的作用域文件。这里的复杂与简单主要反映设计目标的差异,并不直接代表哪一种记忆能力更强。

Subagent 与 Multi-Agent:上下文、角色与协作状态#

Codex 的最小协作与 Claude Code 的结构化多智能体团队对比

Codex 创建 subagent 的基础操作是启动一个 child thread,并向它发送任务。调用 spawn_agent 时,父 agent 可以选择让 child 继承全部历史、最近若干轮历史或完全不继承,接口还提供 model、reasoning effort 和 agent_type 等参数;其中 full-history fork 不允许再覆盖 role。

这里的 agent_type 就是 Codex 的 role。选中 role 后,Codex 会使用与 config.toml 相同的配置合并机制,把 role 作为高优先级配置层应用到 child thread。role 可以调整 developer instructions、model、reasoning、sandbox、skills 等设置,Codex 也内置了 explorer、awaiter 等常用角色。

因此,一个 Codex subagent 可以理解为“带有独立任务、选定上下文和 role 配置的 Codex thread”。父 agent 根据当前任务决定如何拆分工作、创建多少 child、为每个 child 选择什么上下文和角色,并在它们返回以后综合结果。

Claude Code 的 subagent 同样会进入正常的 query loop,并使用独立的 system prompt、上下文和 tool-use context。它的 agent definition 集中暴露了更完整的子代理参数,包括 tools、disallowed tools、model、effort、permission mode、MCP servers、hooks、max turns、skills、memory、background mode 和 worktree isolation。

这些参数基本覆盖了 Claude Code 主 Agent Loop 中已有的 harness 控制范围。subagent 沿用相同的工具执行、权限、hooks、MCP 和 turn limit 机制,agent definition 只是为某个角色预先固定其中一部分配置。

所以在单个 subagent 的抽象上,两者的差异没有看起来那么大:

Codex:spawn child thread → 选择上下文、模型和 role → role 配置覆盖 session config
Claude Code:spawn subagent → 加载 agent definition → 带角色配置进入同一套 query loop

更具体的区别在于参数的组织方式。Codex 把即时参数放在 spawn_agent 中,把角色能力放在可复用的 config layer 中;Claude Code 则在 agent definition 里直接集中声明工具白名单与黑名单、权限、hooks、memory 和隔离方式,更容易从一份定义中看到这个 subagent 的完整运行边界。

前面提到的 verification agent 就是这种配置方式的例子。它通过 agent definition 限制可用工具,再通过角色 prompt 规定验证步骤和 verdict 格式。Codex 也可以通过 role config 创建专门角色,只是相关约束会沿用 Codex 自身的配置层体系。

到了 Multi-Agent 协作层,两者的侧重点才进一步拉开。Codex 已经提供 child thread、agent communication 和状态查询等原语,任务如何拆分以及结果如何汇总主要由父 agent 判断。Claude Code 在这些调用能力之外,还把 plan approval、task ownership、mailbox 和 agent status 做成显式运行时状态,使权限、交接和验收过程更容易追踪。

模型主导的任务分解可以临时形成很灵活的协作方式,也可能产生重复分工、无价值 subagent 或 compaction 后的目标漂移。显式协调状态有助于长任务恢复和审计,同时增加了 task、角色、消息和权限之间的同步成本。这里更接近“模型负责多少协调、harness 保存多少协作状态”的取舍。

两种路线的最终权衡#

到这里可以看出,Codex 和 Claude Code 的区别并不只体现在某个工具的 schema 上。Edit、Plan、Compaction、Skills 和 Multi-Agent 其实都在回答同一个问题:

当模型已经能够理解任务并根据反馈纠错时,harness 还应该替它决定多少事情?

Codex 与 Claude Code 两种 AI Harness 设计路线的工程权衡

Codex 的优势#

  • 工具调用次数和 token 税更低。高表达力的 freeform patch 可以用一次动作完成复杂修改。
  • 单次动作表达能力强。模型可以直接表达最终代码变化,而不必把决策切碎成许多局部操作。
  • harness 不容易因为模型行为升级而迅速过时。模型变强以后,系统能直接获得更大收益,不需要先重写大量工作流规则。
  • frontier model 能力越强,整体收益越大。规划、工具选择和自我纠错能力的提升可以直接转化为 agent 能力。

它的风险也来自同一个地方:

  • 更依赖特定模型的质量。模型是否理解仓库、能否稳定生成 patch、会不会主动验证,直接决定系统表现。
  • 一部分语义错误要在动作之后才会发现。harness 能验证 patch 能否应用,却不能在写入前证明这个修改符合业务意图。
  • 长会话、compaction 和 resume 后更容易发生计划漂移,因为较多过程状态依赖对话与摘要延续。
  • 模型版本退化会更直接地反映为 agent 行为退化。过去由模型自己维持的习惯一旦变化,客户端没有足够多的结构性机制兜底。

Claude Code 的优势#

  • stale edit、重复匹配、计划恢复和人机并发等行为更确定,因为这些不变量由工具和状态机直接保证。
  • 对较弱模型和模型升级波动更稳健。模型忘记某个步骤时,tool schema、permission mode、hooks 和 task state 仍然可以阻止一部分错误。
  • 长任务、恢复、handoff 和 multi-agent 工作流更容易审计。plan、task、agent role 和 verification 都有显式状态。

这些保障也有明确成本:

  • Read/Edit/Task/Plan 状态带来更多 tool-call 税和 token 税。
  • 客户端状态机更复杂,harness 自己也会产生 bug,特别是 resume、fork 和 compaction 之后的状态同步问题。
  • 约束可能挡住模型本来正确而且更高效的做法。例如模型已经能安全生成一个完整重构,却仍需把它拆成很多局部 edit。
  • cache、hooks、permissions、attachments、skills 和 MCP 相互作用以后,维护与测试成本会快速上升。

因此我不认为存在一个脱离场景的绝对答案。

如果任务以本地代码修改为主,错误容易观察和回滚,用户又一直处于交互循环中,那么 Codex 式的高带宽工具通常更高效。尤其当模型已经处在 frontier 水平时,过细的流程约束可能只是在重复模型已经学会的能力。

如果任务要运行很久,需要断点恢复、多人或多 agent 协作,包含大量并发状态,或者会产生昂贵、不可逆的外部影响,那么 Claude Code 式的显式状态和流程不变量会更有价值。

模型强弱也不是唯一变量。真正应该决定约束强度的是错误的性质:

是否应该进入 harness
≈ 错误的不可逆性
× 状态的并发程度
× 恢复成本
× 审计要求

一次 patch 失败,模型看到错误后重试即可,没必要为它设计庞大的状态机;一次覆盖用户并发修改、错误发布生产环境或者丢失跨 agent 的任务所有权,则不应该只寄希望于模型“记得小心”。

删除 80% 提示词真正说明了什么#

回到开头 Anthropic 删除 80% system prompt 的事情。

我认为它最值得关注的地方,不是证明了“prompt engineering 已经没有意义”,也不是证明 Claude Code 正在变成 Codex,而是暴露了 AI harness 中一种很常见的技术债。

模型每出现一种失败行为,开发者就很自然地在 system prompt 中补一条规则:

模型忘记验证 → 加一句必须验证
模型过早结束 → 加一句不要过早结束
模型探索不足 → 加几段搜索示例
模型滥用工具 → 再解释一次工具边界

在当时,这些规则可能真的提升了效果。但 prompt 只会增加,很少有人在模型升级以后系统地检查旧规则是否仍然必要。

久而久之,system prompt 就变成一份历史模型缺陷的兼容层。新模型已经学会了某些能力,旧约束却还在要求它按旧模型需要的步骤工作;大量示例还可能收窄它的探索空间,让模型倾向于复制已有路径,而不是根据当前环境作出更好的判断。

Anthropic 这次清理 prompt,实际上做了三件事:

第一,承认约束有保质期。针对旧模型有效的 harness,不一定适合新模型。

第二,把常驻规则和按需知识分开。verification、review 和特定工作流仍然可以存在,但不必进入每一个请求的 system prompt,而可以通过 skill 渐进式加载。

第三,重新把一部分判断权交还给模型。新模型能够利用工具描述、仓库上下文和任务目标推断正确行为时,就不需要再用几十条互相重叠的指令规定过程。

这和 Codex 一直以来表现出的设计倾向非常接近:让工具接口拥有足够表达力,让模型看到真实环境和明确反馈,然后把可以低成本纠正的错误交给模型自己处理。

但它并不意味着所有约束都应该删除。Anthropic 删掉的主要是语义脚手架,而不是权限、文件一致性、cache 正确性或恢复机制。模型能力可以替代很多“应该怎么工作”的提示,却不能自动替代并发控制、安全边界和持久化协议。

那么,一个好的 AI Harness 应该做到什么#

经过上面的对比,一个好的 AI Harness 首先应该清楚划分模型与系统的职责:让模型负责需要理解上下文和权衡取舍的语义决策,让 harness 保证可以被确定性验证的工程不变量。

对于代码应该如何设计、任务应该如何拆分、接下来应该查看哪些文件等问题,harness 应该提供高带宽、可组合的工具和清晰的环境反馈,让模型充分发挥自身判断力。把当前模型的每个弱点都固化成工作流规则,会让 harness 随着模型升级迅速过时。

以下场景则适合由 harness 提供结构化约束:

  • 写入对象可能被用户或其他 agent 并发修改;
  • 动作不可逆,或者会影响外部真实系统;
  • 任务会跨 session、跨 context compaction 或跨 agent handoff;
  • 状态需要审计,不能只存在于模型生成的摘要中;
  • 某个不变量可以被系统低成本、确定性地验证。

因此,一个好的 harness 应该具有清晰的分层:

语义决策层:尽量相信模型
操作执行层:提供强反馈和可恢复性
安全与并发层:由系统保证不变量
长任务协调层:按需启用持久状态

例如,Edit Tool 可以保留 Codex patch 的高表达力,同时在实际写入前加入版本检查,防止覆盖用户并发修改;Plan 默认只是一段对话中的认知产物,但当任务需要 resume、handoff 或多人协作时,再升级为持久化 artifact;verification 默认由模型根据风险决定,高风险任务则自动进入只读 verifier。

这类设计的关键在于让约束与风险匹配。低成本、可观察、可恢复的失败,可以更多交给模型和反馈循环;涉及安全、并发、不可逆动作和跨会话一致性的问题,应该由 harness 直接保证

一个好的 harness 还需要具备随模型演进而调整的能力。system prompt、tool description、skill 和客户端状态机都应该定期通过新模型重新评估:仍然必要的约束继续保留,已经被模型能力覆盖的脚手架及时删除,需要时才有价值的知识则迁移到按需加载的位置。

结语#

Codex 和 Claude Code 代表了两种都很合理的 agent 工程路线。

Codex 更相信模型能力可以直接转化成 agent 能力,因此尽量保持工具高带宽、流程低假设。它的设计对 frontier model 更敏感,也更容易随着模型升级自然获得收益。

Claude Code 更关注模型行为的方差和长任务中的状态一致性,因此愿意用更多工具约束、运行时状态和角色协议换取确定性。它在模型不稳定、任务复杂或协作链路很长时更有韧性,但也承担了更高的 token 税和 harness 维护成本。

而 Claude Opus 5 之后删除 80% system prompt 的变化,说明这两条路线并不是固定不动的阵营。随着模型变强,Claude Code 也开始把一部分流程判断交还给模型;同样,当 Codex 面对跨会话 memory、安全审批和并发一致性时,它也会采用非常严格的状态机。

所以真正重要的问题不是 harness 应该有多少约束,而是:

哪些事情可以交给模型通过环境反馈自行修正,哪些不变量必须由系统保证?

这个边界不会有一次性的答案。模型每升级一代,工具能力、context window 和推理接口每发生一次变化,我们都应该重新检查它。

这也解释了为什么模型公司往往需要开发并长期维护自己的 Agent Harness。Harness 需要适配模型的工具调用方式、reasoning 状态、context 特性、cache 机制和常见失败模式,再随着模型迭代不断调整。OpenAI 可以让 Codex 的工具接口和 Responses API 一起演进,Anthropic 也可以围绕 Claude 的 thinking blocks、prompt cache 和行为特点持续修改 Claude Code。模型与 harness 共同设计,才能更充分地发挥这一代模型的能力。

一个优秀的 AI harness,不应该只是不断给模型增加护栏。它还应该知道,什么时候可以把已经不再必要的护栏拆掉。

AI Harness 没有一成不变的最佳设计,边界需要随模型能力持续演化

Codex 与 Claude Code 为什么如此不同:两种 AI Harness 设计思路
https://contrue.top/posts/codex-vs-claude-code-ai-harness/
Author
contrueCT
Published at
2026-07-27
License
CC BY-NC-SA 4.0