Skip to content

Codex Subagent 模块深度调研:架构、自定义实践与演进路径

视角:上下文工程(Context Engineering)+ Agent 编排 范围:OpenAI Codex(CLI / App / IDE Extension)官方公开信息 + 源码 + 社区分析 时间锚点:截至 2026-04(Codex 0.117+0.120+ 时代的稳定形态)


0. TL;DR:一句话理解 Codex 的 Subagent

Codex 的 Subagent 是由主 Agent 显式调用的"子线程级 Agent 实例",每个子 Agent 拥有独立的上下文、模型配置、沙箱策略、MCP 工具集和 Skills 集合,通过一组 tool-call 原语(spawn_agent / wait_agent / send_input / resume_agent / close_agent 以及批处理用的 spawn_agents_on_csv)由主 Agent 编排,用于把"嘈杂的中间工作"从主线程剥离出去,避免 context pollution / context rot

自定义子 Agent 的最小形态是一个放在 ~/.codex/agents/<name>.toml.codex/agents/<name>.toml 的 TOML 文件,必须包含 name / description / developer_instructions 三个字段,可选叠加 model / model_reasoning_effort / sandbox_mode / mcp_servers / skills.config / nickname_candidates 等任意 config.toml 支持的键。


1. 概念与边界:Subagent 在 Codex 上下文工程中的定位

1.1 与几个易混淆概念的区分

概念定位与 Subagent 的关系
Context Window单个 LLM 调用允许的 token 容量Subagent 是为了避免单一 context 被污染而拆出来的,不是扩容手段
Session / ThreadCodex 一次"对话会话",含多轮 turn 与历史Subagent 是 Session 内 spawn 出来的子 Thread,有独立 ThreadId
Agent SkillSKILL.md一组工具脚本 + 提示 的可复用能力包Skill 是子 Agent 能装载的能力;子 Agent 可以通过 skills.config 选择性启用
MCP Server外部工具协议服务端子 Agent 可独立挂载自己的 MCP 服务(如某个 docs MCP)
AGENTS.md项目/全局给所有 Agent 看的指令(项目记忆)AGENTS.md 写"项目共识",Subagent 写"角色专长";二者叠加
Memoriesfeatures.memories跨会话沉淀的长期记忆与 Subagent 正交:Memories 解决"跨 session 记得",Subagent 解决"本 session 不污染"
Profileprofiles.<name>启动时切换的整套配置预设Profile 切的是主 Agent 自己的配置;Subagent TOML 切的是被 spawn 的子角色

关键一句话区分:Profile 解决"我这次怎么干",Subagent 解决"我让谁去干"。

1.2 一张图看清架构

1.3 解决了什么核心问题

引用官方 Concepts 页与 Chroma 的 context rot 研究,Codex 把 Subagent 的存在意义压缩为两个失败模式的解药:

  • Context Pollution(污染):有用的指令被探索笔记、错误栈、命令输出"埋掉"。
  • Context Rot(腐烂):随着不相关细节累积,会话越来越不可靠。

子 Agent 的处方很朴素:让脏活在别的 thread 里干,只把摘要回传给主线程


2. Subagent 的运行时协议:5+1 个 Tool 原语

Codex 在 codex-rs/core/src/tools/handlers/multi_agents.rs 把"主 Agent 调度子 Agent"这件事建模成一组模型可见的 function-calling 工具,受 features.multi_agent(默认开启)控制。

Tool作用关键字段
spawn_agent启动一个子 Agent threadname(角色名,如 reviewer)、task(任务描述)、可选 model 等覆盖项
wait_agent阻塞等待一组子 Agent 完成并取回结果agent_ids: [ThreadId]
send_input向运行中的子 Agent 发送追加指令(steer)agent_id, input
resume_agent恢复一个被挂起/关闭过的子 Agentagent_id
close_agent关闭子 Agent thread 释放槽位agent_id
spawn_agents_on_csv(experimental)按 CSV 批量 fan-out 子 Agent,每行一个 workercsv_path, instruction, id_column, output_schema, output_csv_path, max_concurrency, max_runtime_seconds

源码层面这些 Handler 都使用统一的 ToolHandler trait,并发出 CollabAgentSpawnBeginEvent / CollabAgentInteractionBeginEvent / CollabAgentSpawnEndEvent 等协议事件,TUI / IDE 借此渲染 /agent 视图。

几个容易踩坑的运行时语义(来自官方 Subagents 页):

  1. 不会自动 spawn:除非用户在 Prompt 里显式说"用子 Agent / 并行 / spawn one per point",主 Agent 都不会调用 spawn_agent。这是为了控制 token 成本。
  2. runtime override 跟随父级:父 turn 在交互中改过的 --yolo/approvals、sandbox 设置会重新覆盖子 Agent TOML 里的默认值。即使子 Agent 文件里写了 sandbox_mode = "read-only",父级被设成 danger-full-access 时子 Agent 也会被父级的运行时覆盖。
  3. 审批跨线程冒泡:子线程触发的 approval 弹窗会浮到父 TUI,按 o 可跳到对应子线程上下文再决定。非交互(codex exec)时无法 approval 的请求会被直接拒绝并把错误回传父级。
  4. CSV worker 必须显式上交结果:每个 spawn_agents_on_csv 的 worker 必须调用 report_agent_job_result 一次;漏调会被标记 error 写入输出 CSV。

3. 内建子 Agent 与三角色分工

Codex 出厂自带三个角色,定义角色职责而非具体模型:

角色定位典型调用语
default通用兜底,能力最全"用 default 帮我把这个改完并跑测试"
worker实施型:写代码 / 改 bug / 跑命令"把方案分给两个 worker 并行实现"
explorer只读探索:通读代码、定位实现"spawn 一个 explorer 把 settings 模块的调用链梳理出来"

重要规则:自定义 Agent 与内建同名时,自定义优先。这意味着你可以"重写 explorer"——比如换更便宜的模型、加上你私有的 docs MCP——而无需改 Prompt 称呼。


4. 自定义 Subagent 完整指南(重点章节)

4.1 文件位置与加载顺序

路径作用域适用场景
~/.codex/agents/<name>.toml用户全局跨项目复用的角色("我的安全审查员")
.codex/agents/<name>.toml当前项目仅本仓库需要的角色("针对该 monorepo 的 PR explorer")

加载机制有几个工程上要记住的点:

  • 每个文件只能定义一个 Agent("one file = one agent")。
  • Codex 通过文件内的 name 字段唯一标识 Agent;文件名和 name 不强制一致,但官方推荐一致以便人脑映射。
  • 项目级文件只在该项目被标记 trustedprojects.<path>.trust_level = "trusted")时才被加载。
  • 同名时优先级:项目级 > 用户级 > 内建

4.2 必填三件套

toml
name = "reviewer"
description = "PR reviewer focused on correctness, security, and missing tests."
developer_instructions = """
Review code like an owner.
Prioritize correctness, security, behavior regressions, and missing test coverage.
Lead with concrete findings, include reproduction steps when possible.
"""
字段类型作用
namestring主 Agent 在 spawn_agent 时通过该名字定位角色;也是 description 之外模型唯一可见的"角色身份"
descriptionstring给主 Agent 看的"说明书",主 Agent 据此判断"这个任务该不该派给它"。等价于 Anthropic Subagent 的 description
developer_instructionsstring被注入子 Agent system prompt 的开发者指令——子 Agent 的"灵魂"。等价于 Claude Code 的 system prompt 字段

description 的工程经验:把它当成"路由提示",写成< 30 词、动词开头、明确"什么场景用我"。description 写得越具体,主 Agent 自动选对 Agent 的概率越高;写得越宽泛,越容易被错派。

developer_instructions 的工程经验

  • 窄、专、可拒绝:明确写"不做什么"。例:Do not make code changes. / Do not propose fixes unless asked.
  • 强制输出格式:给一个具体的回包结构("返回结构为 finding/severity/file:line/repro")。
  • 限定证据来源:约束工具("只用 ripgrep + 目标文件 read,禁止全仓扫描")。

4.3 可选叠加项(继承父 Session)

下表中的字段省略时继承自父 Session,写了就作为该子 Agent 的默认覆盖(再被运行时覆盖一次,见 §2 第 2 条)。

字段含义选型建议
model子 Agent 用的模型探索/扫读用 gpt-5.4-mini;审查/改 bug 用 gpt-5.4;快速迭代用 gpt-5.3-codex-spark(research preview)
model_reasoning_effort推理深度:minimal/low/medium/high/xhighreviewer/security 类用 high;mapper/scanner 类用 medium;fixer 类视复杂度选
sandbox_mode沙箱策略:read-only / workspace-write / danger-full-access探索类一律 read-only;唯一能写代码的子 Agent 才给 workspace-write
mcp_servers.<id>子 Agent 私有的 MCP 工具把"docs 检索"、"chrome devtools"等隔离到对应子 Agent,避免主 Agent 工具列表爆炸
skills.config子 Agent 启用/禁用的 Skills例如 ui_fixer 关闭 docs-editor Skill 以避免误触
nickname_candidates显示名候选池(仅 UI)同一角色 spawn 多个实例时给 ["Atlas","Delta","Echo"] 便于区分
developer_instructions 之外几乎所有 config.toml 顶层键包括 web_searchapproval_policypersonality谨慎使用,避免与父 Session 冲突

4.4 三个端到端真实例子

例 1:PR 评审三件套(官方推荐范式)

.codex/config.toml

toml
[agents]
max_threads = 6
max_depth = 1

.codex/agents/pr-explorer.toml

toml
name = "pr_explorer"
description = "Read-only codebase explorer for gathering evidence before changes are proposed."
model = "gpt-5.3-codex-spark"
model_reasoning_effort = "medium"
sandbox_mode = "read-only"
developer_instructions = """
Stay in exploration mode.
Trace the real execution path, cite files and symbols, and avoid proposing fixes unless the parent agent asks for them.
Prefer fast search and targeted file reads over broad scans.
"""

.codex/agents/reviewer.toml

toml
name = "reviewer"
description = "PR reviewer focused on correctness, security, and missing tests."
model = "gpt-5.4"
model_reasoning_effort = "high"
sandbox_mode = "read-only"
developer_instructions = """
Review code like an owner.
Prioritize correctness, security, behavior regressions, and missing test coverage.
Lead with concrete findings, include reproduction steps when possible, and avoid style-only comments unless they hide a real bug.
"""

.codex/agents/docs-researcher.toml

toml
name = "docs_researcher"
description = "Documentation specialist that uses the docs MCP server to verify APIs."
model = "gpt-5.4-mini"
model_reasoning_effort = "medium"
sandbox_mode = "read-only"
developer_instructions = """
Use the docs MCP server to confirm APIs, options, and version-specific behavior.
Return concise answers with links or exact references when available.
Do not make code changes.
"""

[mcp_servers.openaiDeveloperDocs]
url = "https://developers.openai.com/mcp"

触发 Prompt:

Review this branch against main. Have pr_explorer map the affected code paths,
reviewer find real risks, and docs_researcher verify the framework APIs that the patch relies on.

例 2:前端调试三件套(Mapper + Browser Debugger + Fixer)

亮点:只有 ui_fixer 拥有 workspace-write 权限,其余两个全部只读,从沙箱层面强约束写权限。

toml
# code-mapper.toml: 只读梳理
sandbox_mode = "read-only"
model = "gpt-5.4-mini"
toml
# browser-debugger.toml: 用 chrome_devtools MCP 复现
[mcp_servers.chrome_devtools]
url = "http://localhost:3000/mcp"
startup_timeout_sec = 20
sandbox_mode = "workspace-write"  # 复现可能写截图
toml
# ui-fixer.toml: 仅它能写代码
[[skills.config]]
path = "/Users/me/.agents/skills/docs-editor/SKILL.md"
enabled = false

例 3:CSV 批处理(experimental)

text
Create /tmp/components.csv with columns path,owner and one row per frontend component.

Then call spawn_agents_on_csv with:
- csv_path: /tmp/components.csv
- id_column: path
- instruction: "Review {path} owned by {owner}. Return JSON with keys path, risk, summary, follow_up via report_agent_job_result."
- output_csv_path: /tmp/components-review.csv
- output_schema: object with required string fields path, risk, summary, follow_up

输出 CSV 包含原始列 + job_id / item_id / status / last_error / result_json社区评价的局限:当前批次中途失败不可恢复,整批从头跑。

4.5 全局调度参数([agents] in config.toml

字段默认作用与坑
agents.max_threads6并发开启的子 thread 上限。设太高会让 token 与本地资源失控
agents.max_depth1嵌套层数(root=0,允许子 Agent 但子的子默认禁止)。调高时小心 fan-out 爆炸——max_threads 只能限并发但不能限"深度递归带来的 token 总量"
agents.job_max_runtime_seconds1800(仅 CSV 默认)CSV worker 单次超时

0.105.0 的发布说明专门强调了 max_depth 没有内置预算上限,"如果你的 agent 喜欢 spawn,开成大数会迅速烧钱"。

4.6 自定义 Subagent 与 Skills、MCP、AGENTS.md 的协同关系

设计哲学上的分工:

  • AGENTS.md:写所有 Agent 都该遵守的项目级铁律("禁止改 schema 不写 migration")。
  • developer_instructions:写这个角色才有的作风("reviewer 优先看安全风险")。
  • mcp_servers:决定这个角色能接触哪些外部世界(docs API / chrome devtools)。
  • skills.config:决定这个角色能展开哪些剧本式能力包(一组 SKILL.md 中的工具+Prompt)。

5. 演进时间线(核心章节)

把"Subagent"作为一条独立产品线看,它在 Codex 上经历了至少四个阶段:

【阶段 1】Pre-Subagent:单 Agent + AGENTS.md

  • 痛点:所有探索/构建/审查都挤在一个 thread 里,长任务 context 膨胀,效果劣化(context rot)。
  • 方案:仅靠 AGENTS.md 项目记忆 + Profile 切预设,没有 thread 隔离。
  • 取舍:简单,但任何"复杂多步"任务都要靠用户手动 /clear 重置。
  • 遗留问题:模型再大也喂不下"探索 + 决策 + 实现"全链路噪声。

【阶段 2】社区 PR #3655:第一版多 Agent 编排(2025-09,未合并)

  • 痛点:阶段 1 的 context rot 在长任务上越发明显,社区呼声极高(issue #2604)。
  • 方案:贡献者 metaphorics 提交 PR,把"agent registry + ~/.codex/agents.toml 单文件多 agent 配置 + 系统提示文件"灌入 core,初版只支持 1 层("prevent recursive agent spawning for system stability")。
  • 取舍:架构耦合度高,OpenAI 团队后来明确"目前只接受 bug 与安全 PR,不接受新功能",PR 在 10 月底被关闭。
  • 遗留问题:社区拿到了路径,但需要官方按自己的 roadmap 重做。

【阶段 3】Codex 0.105.0:完整 Subagent 系统重写(2026 早期)

  • 痛点:阶段 2 的实现没法和后续 Skills、MCP、Sandbox 联动,且 thread id 不可读、嵌套不可调、批量任务无法跑。
  • 方案
    1. 可读 Nickname:Spawn 出来不再是裸 ThreadId,而是 Atlas/Delta/Echo 这种 nickname_candidates
    2. 可配置嵌套深度agents.max_depth,允许 agent → sub-agent → sub-sub-agent。
    3. 角色化自定义:从"单 agents.toml 多角色"改为"agents/<name>.toml 一角色一文件",并和 config.toml 同 schema,复用 model / mcp_servers / skills.config 等所有字段。
    4. CSV Fan-outspawn_agents_on_csv 把 Subagent 推向"小批量数据处理"场景。
    5. TUI 接入/agent 切线、approval 跨线冒泡、"dead agents" 可审计。
  • 取舍:用户心智复杂度大幅上升(一角色一文件、TOML schema 与父 config 共享);token 成本曲线陡峭。
  • 遗留问题:嵌套深度无内置预算上限;CSV 批次不可恢复。

【阶段 4】Codex 0.117+ / 0.120+:可寻址 + 实时进度

  • 痛点:嵌套深度上来后,"在哪个子线程里发生了什么"难以定位;长跑 Subagent 缺乏实时反馈。
  • 方案
    1. Sub-agent 可寻址:路径式 /root/agent_a 引用,结构化跨线消息。
    2. Realtime V2 进度流:背景 Subagent 工作时实时回传进度条。
    3. TUI hook 活动扫描:Subagent 触发的 hook 活动在 TUI 中可观察。
  • 取舍:进一步把 Subagent 推向"长时间后台 worker"用法,离 IDE 集成更近。
  • 遗留问题:复杂度仍在堆积,需要更好的"预算/沙箱/审计"统一面板。

6. 关键设计原则提炼

从演进过程能抽出几条对自研 Coding Agent 直接可借鉴的原则:

  1. 显式 spawn 优于隐式自动委派 Codex 选择"绝不自动 spawn 子 Agent"——因为子 Agent 是 token 成本放大器,自动化最容易踩到"为不大的任务付一倍钱"。**"你说 spawn 才 spawn"**比"AI 自己判断"更可控。

  2. 角色配置与运行时配置同 schema 子 Agent TOML 复用 config.toml 的所有键,不发明新 manifest。代价是"看起来重",收益是任何用户在 config.toml 学到的东西在 Agent 文件里直接生效,且未来扩展自动同步。

  3. 沙箱与权限按 Agent 维度收敛 "只有 ui_fixer 能写文件"是从沙箱层面而不是从 Prompt 层面强约束——Prompt 会被绕过,沙箱不会。最小写权限原则应是子 Agent 设计的第一性约束。

  4. runtime override 总是赢过文件默认 父 Session 的 --yolo 等运行时变更会重新覆盖子 Agent 的默认值——保证"用户当下的意图 > 文件里的旧约定"。代价是用户必须意识到这层覆盖。

  5. 可读性优于 ID Nickname 池、/agent 切线、可寻址路径,都是为了让人在多 thread 并行时不迷路——多 Agent 系统的工程瓶颈往往不是模型,而是 UX。

  6. 批量场景下"摘要回传"比"原始日志回传"重要spawn_agents_on_csv 强制每个 worker 调一次 report_agent_job_result,只把结构化结果回传父级;任何"想看 raw log"的需求由 SQLite 后台状态承接,而不是污染 conversation。


7. 横向对比:Codex Subagent vs Claude Code Subagents vs Cursor Tasks

维度Codex SubagentClaude Code Sub-agentsCursor Task Subagent
角色定义文件~/.codex/agents/<name>.toml(或 .codex/agents/~/.claude/agents/*.md(YAML frontmatter + Markdown)~/.cursor/... 内置 subagent_type 列表(explore/shell/browser-use/...)
必填字段name / description / developer_instructionsname / description / system prompt body内建枚举,不可自定义新类型
模型/推理可调model + model_reasoning_effort 可逐角色固定✅ frontmatter 可指定 model可选传 model 参数
沙箱/权限粒度✅ 逐角色 sandbox_mode + 父级 runtime 覆盖工具白名单 + 项目权限内置 readonly 标志 + Cursor 全局沙箱
MCP 私有挂载✅ 角色级 mcp_servers.*✅ 角色级工具白名单MCP 由主端共享,不按 subagent 隔离
嵌套深度agents.max_depth(默认 1)❌ 不允许嵌套(一层)通过显式再次 Task 调用,约束较弱
触发方式必须显式在 Prompt 说"spawn / 用子 Agent"主 Agent 可根据 description 自动 dispatch主 Agent 主动调 Task 工具
CSV / 批处理spawn_agents_on_csv(experimental)best-of-n-runner 类批量
可读 Nickname / Thread 寻址nickname_candidates + /root/agent_a 路径❌(线程 id 为主)内置 UI 抽屉,无需寻址
审批跨线冒泡✅ 父 TUI overlay 显示子线程来源项目权限统一处理由 IDE 弹窗统一

一句话差异

  • Codex 把 Subagent 当生产级别的 Agent 编排原语(多原语、多角色、可批量、可嵌套、有沙箱)。
  • Claude Code 把 Subagent 当轻量人格(Markdown + YAML 一文件搞定,强调"易写")。
  • Cursor 把 Subagent 当预置工具箱(不让用户自定义类型,靠内建几种类型保证质量与一致性)。

8. 落地启示:如果你要给自己的 Agent 设计 Subagent,借鉴清单

  1. 协议层:至少实现 spawn / wait / send_input / close 四个原语;resume 与 CSV 批处理可后置。子 Agent 的开始/结束/交互全部走结构化事件(参考 Codex CollabAgent*Event),便于 TUI/IDE 渲染。
  2. 配置层让子 Agent 配置 schema = 主 Agent 配置 schema。不要发明新 DSL,否则升级时维护成本翻倍。
  3. 目录约定:双层加载——全局 ~/<tool>/agents/ + 项目 .<tool>/agents/,且项目级仅在 trusted 项目内加载
  4. 必填三件套保持最小:name / description / system_promptdescription 是路由提示,写得越具体被错派概率越低
  5. 沙箱用文件而非 Prompt 约束:探索类强制 read-only,唯一允许写的子 Agent 显式标注。
  6. 触发显式化:默认禁止主 Agent 自动 spawn(成本不可控);用 Prompt 模板/Skill 教用户如何"喊"。
  7. 嵌套带默认上限(如 max_depth = 1)+ 并发上限(如 max_threads = 6),并在文档里强调没有内置 token 预算
  8. Nickname:从一开始就给子 Agent 一个可读名,否则 3 个并发就开始乱。
  9. 审批跨线:子 Agent approval 必须冒泡到父 UI 并标注源线程;非交互模式下直接拒绝并把错误回传父级。
  10. 批处理走外部状态:CSV/批量 fan-out 的中间结果写 SQLite/文件,不要进 conversation;只把摘要回主线程。

9. 当前局限(批判性视角)

  • 成本曲线陡:Subagent 是 token 放大器;新手很容易在 max_depth=2 上烧钱。没有内建 token 预算是短板。
  • CSV 批不可恢复:单条失败要整批重跑(社区已点出)。
  • Skills 与 Subagent 的协同还在演进skills.config 是按 Agent 启用,但 Skill 之间的依赖、Skill 调 Subagent 的反向链路尚未文档化。
  • 跨 Subagent 共享上下文困难:当前模型是"父发任务、子返摘要",Subagent 之间不能直接通信;复杂工作流仍需主 Agent 做中转,主 Agent 反而又会被摘要堆积(部分回到了 context rot 的老路)。
  • 可观测性仍偏 TUI/agent 视图、Nickname、/root/agent_a 寻址都很好,但跨 session 的 Subagent 调用日志/成本审计还需用户自行接 OTEL 落库。

10. 参考资料

官方文档

源码

  • codex-rs/core/src/tools/handlers/multi_agents.rs(Subagent 工具协议入口)
  • codex-rs/core/src/tools/handlers/multi_agents_v2.rs(v2 演进路径)
  • 早期社区实现 PR #3655(已关闭,可作为架构演进参考)

社区与第三方分析

凡涉及 0.117+ 之后的 Subagent 寻址、Realtime V2 进度等内容,参考自社区聚合源,OpenAI 官方文档对应章节仍在持续更新;如有最新差异以官方 changelog 为准。