$catMANUAL||~31 分钟

Codex CLI Subagent 实战:把一个 AI 变成一支团队,三个真实场景全跑通

advertisement

最近 OpenAI 偷偷更新了 Codex CLI 的一个关键功能,叫 Subagent。说"偷偷"是因为 7 月初我打开 codex 时没注意,直到看到 codex features list 的输出里多了几个 flag,顺手翻官方文档才发现新大陆。

Subagent 是什么?说人话就是:你告诉 Codex 干一件事,它会自动拆给几个专门的子 agent 一起干,每个子 agent 跑自己的模型、自己的工具、自己的上下文。最后把结果汇总回来。

听起来跟"AI Agent 团队"的概念差不多,但这次是真的能用。官方文档 7 月 6 日才更新(codex/subagents),我对着文档跑了三个真实场景,今天把踩坑和细节都写下来。

为什么 Subagent 值得专门写一篇

之前我写 Codex CLI 基础教程的时候,提到过"Codex 适合单线程干一件事"。但实际工作里,很多任务根本不是单线程:

  • PR 审查:你可能想同时看安全性、代码质量、测试覆盖、文档准确性。单线程只能一个个问。
  • 前端 UI 调试:浏览器里复现 bug、追踪代码定位、最后改 bug。三个步骤天然可以并行起步。
  • 批量审计:100 个组件要挨个看风险等级。让一个 agent 串行跑 3 小时太慢。

Subagent 就是为这种"任务天然可以并行"的情况设计的。每个子 agent 跑独立的上下文,互不污染,最后 Codex 把结果汇总。

有个细节容易忽略:subagent 跑起来之后,每个子 agent 用的 token 是独立计算的。如果你开 6 个子 agent 跑同一个 GPT-5.5,一轮下来 token 消耗是单 agent 的 5-6 倍。这不是 bug,是 feature——子 agent 之间的上下文隔离就是按这个代价换来的。

三个内置 Subagent:default、worker、explorer

Codex 默认带三个内置 agent,写在源码里(不用自己配置):

  • default:通用 fallback agent,啥都能干
  • worker:执行型 agent,专门负责实现和修复
  • explorer:只读的代码库探索 agent

调用方式很简单。在交互式 TUI 里直接说:

text
1
I would like to review the following points on the current PR (this branch vs main).
2
Spawn one agent per point, wait for all of them, and summarize the result for each point.
3
1. Security issue
4
2. Code quality
5
3. Bugs
6
4. Race
7
5. Test flakiness
8
6. Maintainability of the code

注意这句话的关键结构:"Spawn one agent per point, wait for all of them"。你不显式说"用 subagent",Codex 就只会单线程跑。必须明确点出来。

跑起来之后,TUI 里会显示多线程的进度。可以用 /agent 在线程之间切换,看看每个子 agent 跑到哪了。

自定义 Subagent:从 TOML 到跑通

内置 agent 够用一阵子。但很快你会想自己定义,比如一个专门查文档的、一个专门写测试的。

配置文件位置

text
1
~/.codex/agents/    # 个人 agent,所有项目都能用
2
.codex/agents/      # 项目级 agent,只在当前项目生效

每个 agent 一个 .toml 文件,文件名建议跟 agent 的 name 字段一致。最少要有三个字段:

toml
1
name = "reviewer"
2
description = "PR reviewer focused on correctness, security, and missing tests."
3
developer_instructions = """
4
Review code like an owner.
5
Prioritize correctness, security, behavior regressions, and missing test coverage.
6
"""

可选字段:

  • model:用 gpt-5.5gpt-5.4gpt-5.4-minigpt-5.3-codex-spark
  • model_reasoning_effortlow / medium / high / extra high
  • sandbox_moderead-only / workspace-write / danger-full-access
  • mcp_servers:agent 专属的 MCP server 配置
  • skills.config:加载特定的 skill 文件
  • nickname_candidates:UI 上显示的友好名字(推荐加,多个 subagent 同时跑时区分方便)

全局配置:决定资源上限

不管你定义多少个 agent,全局限制在 config.toml[agents] 段:

toml
1
[agents]
2
max_threads = 6           # 同时打开的子 agent 线程上限,默认 6
3
max_depth = 1             # 嵌套深度,根 session 从 0 开始,默认 1
4
job_max_runtime_seconds = 1800  # spawn_agents_on_csv 每个 worker 的默认超时

max_depth = 1 意味着根 agent 可以派生子 agent,但子 agent 不能再生孙子。我第一次跑的时候不知道这个,开到 3,结果 token 烧得飞快——递归派生的代价你扛不住。除非你真的需要(比如多轮 code review),否则别动它。

场景一:PR 审查,三 Subagent 并行

官方文档给的最经典场景就是 PR 审查。我在我自己的项目上跑了一遍,配置如下。

项目配置

.codex/config.toml

toml
1
[agents]
2
max_threads = 6
3
max_depth = 1

.codex/agents/pr-explorer.toml

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

注意我用 gpt-5.3-codex-spark 而不是默认的 gpt-5.5。这个 agent 干的是"读代码、找证据"这种相对简单的活,spark 够用,省钱。sandbox_mode = "read-only" 也强制它只能读不能改——避免它一不小心把文件改了。

.codex/agents/reviewer.toml

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

这个用 gpt-5.4high reasoning,reviewer 是要"挑毛病"的活,便宜模型容易漏问题。

.codex/agents/docs-researcher.toml

toml
1
name = "docs_researcher"
2
description = "Documentation specialist that uses the docs MCP server to verify APIs and framework behavior."
3
model = "gpt-5.4-mini"
4
model_reasoning_effort = "medium"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Use the docs MCP server to confirm APIs, options, and version-specific behavior.
8
Return concise answers with links or exact references when available.
9
Do not make code changes.
10
"""
11
 
12
[mcp_servers.openaiDeveloperDocs]
13
url = "https://developers.openai.com/mcp"

这个 agent 配了一个 MCP server(OpenAI 官方文档的),专门负责查 API。

启动 prompt

在 TUI 里输入:

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

跑起来之后,TUI 会出现三个子线程。我观察到:

  • pr_explorer 大概 30 秒返回一份"涉及的文件 + 关键函数 + 调用关系"
  • reviewer 大概 1-2 分钟返回一份"风险点列表 + 复现步骤"
  • docs_researcher 大概 20 秒返回"API 验证结果 + 引用链接"

最后 Codex 把三份结果汇总,给我一个完整的 review 报告。如果中间某个子 agent 卡住,/agent 切过去看具体进度。

我的小改进

官方 demo 那个 PR review prompt 是英文的。我试了两次中文 prompt,Codex 也认,但分线程的时候偶尔出现"边界模糊",比如 reviewer 跑去干 docs_researcher 的活。

后来我学乖了,每个 agent 的 description 写得功能互斥。比如 reviewer 的 description 明确写 "Do not check documentation; rely on docs_researcher for that"。这样主 agent 调度的时候不容易搞混。

还有一个坑:如果你希望子 agent 严格按"read-only"工作,必须在 sandbox_mode 里写死。光在 developer_instructions 里写"don't modify files"是拦不住的,模型有时候会忘记。

场景二:UI 调试,Code Mapper + Browser Debugger + UI Fixer

PR review 是只读场景。改 bug 就要让某些子 agent 能写文件了。

官方给的第二个 demo 是前端集成调试。我把配置改了一下:

.codex/agents/code-mapper.toml

toml
1
name = "code_mapper"
2
description = "Read-only codebase explorer for locating the relevant frontend and backend code paths."
3
model = "gpt-5.4-mini"
4
model_reasoning_effort = "medium"
5
sandbox_mode = "read-only"
6
developer_instructions = """
7
Map the code that owns the failing UI flow.
8
Identify entry points, state transitions, and likely files before the worker starts editing.
9
"""

.codex/agents/browser-debugger.toml

toml
1
name = "browser_debugger"
2
description = "UI debugger that uses browser tooling to reproduce issues and capture evidence."
3
model = "gpt-5.4"
4
model_reasoning_effort = "high"
5
sandbox_mode = "workspace-write"
6
developer_instructions = """
7
Reproduce the issue in the browser, capture exact steps, and report what the UI actually does.
8
Use browser tooling for screenshots, console output, and network evidence.
9
Do not edit application code.
10
"""
11
 
12
[mcp_servers.chrome_devtools]
13
url = "http://localhost:3000/mcp"
14
startup_timeout_sec = 20

注意 sandbox_mode = "workspace-write"——它需要写文件保存截图和日志。但 developer_instructions 里明确说"不要改应用代码"。这种"能给权限但限制用途"的配置模式比"完全 read-only"灵活,但你要信任这个 agent 不会跑偏。

.codex/agents/ui-fixer.toml

toml
1
name = "ui_fixer"
2
description = "Implementation-focused agent for small, targeted fixes after the issue is understood."
3
model = "gpt-5.3-codex-spark"
4
model_reasoning_effort = "medium"
5
developer_instructions = """
6
Own the fix once the issue is reproduced.
7
Make the smallest defensible change, keep unrelated files untouched, and validate only the behavior you changed.
8
"""

ui-fixer 不写 sandbox_mode,会继承父 session 的 sandbox 策略。gpt-5.3-codex-spark 配上 medium reasoning,"改小 bug"这种活它干得足够。

启动 prompt

text
1
Investigate why the settings modal fails to save.
2
Have browser_debugger reproduce it,
3
code_mapper trace the responsible code path,
4
and ui_fixer implement the smallest fix once the failure mode is clear.

我自己在自己的 Next.js 项目上跑了一次,场景是"settings 页面保存按钮点了没反应"。

跑下来流程是这样:

  1. browser_debugger 用 Chrome DevTools MCP 复现 bug,截图保存到 ~/.codex/sessions/.../screenshots/
  2. code_mapper 同时去翻代码,定位到 app/settings/page.tsx 里的 onSave 回调
  3. 两者都返回后,主 agent 决定让 ui_fixer
  4. ui_fixer 改完,Codex 自动跑 lint + test,把结果反馈回来

整个过程大概 3-4 分钟。如果让一个 agent 串行干同样的事,至少 8 分钟。

容易踩的坑

坑 1:Chrome DevTools MCP 启动超时startup_timeout_sec = 20 在我的项目上不够,因为 DevTools MCP 要等 Vite 编译完成。改成 60 才好。如果你的启动时间更长,可能得用更复杂的健康检查。

坑 2:三个 agent 同时跑时 sandbox 冲突。理论上 sandbox 是隔离的,但 workspace-write agent 写文件时如果跟 read-only agent 同时读同一个文件,read-only agent 可能看到半写状态。我没碰到实际数据损坏,但偶尔会出现"文件读到了临时内容"的警告。

坑 3:MCP server 路径在子 agent 里要重新解析。父 session 启动时加载的 MCP server,子 agent 不一定继承。我得在每个子 agent 的 toml 里重新写一遍 [mcp_servers.xxx]。官方文档说"会继承",但实测不太稳定,保险起见每个 agent 都写上。

场景三:批量审计,spawn_agents_on_csv

前两个场景都是"少而精"的 subagent 调用。Codex 还提供了一个专门的批量工具 spawn_agents_on_csv,适合"很多相似任务"的情况。

它干啥

你给一个 CSV,指定哪一列是 ID、哪一列是输入字段、worker 跑完输出什么 JSON。Codex 自动派 N 个 worker,每个 worker 处理一行,最后把所有结果汇总到另一个 CSV。

典型用例:

  • 一次性审查 50 个前端组件的风险等级
  • 给 100 个 PR 各自生成结构化摘要
  • 批量分析日志文件的异常模式

参数

  • csv_path:源 CSV 路径
  • instruction:worker 的 prompt 模板,用 {column_name} 占位符
  • id_column:用哪一列做稳定 ID
  • output_schema:每个 worker 必须返回的 JSON shape
  • output_csv_path:结果 CSV 路径
  • max_concurrency:最大并发 worker 数
  • max_runtime_seconds:单 worker 超时

每个 worker 必须调一次 report_agent_job_result,把结果报上去。如果 worker 跑飞了(没调这个),那一行在结果 CSV 里会被标记为 error。

实操示例

假设我有一个 CSV /tmp/components.csv

csv
1
path,owner
2
src/components/Settings.tsx,alice
3
src/components/Profile.tsx,bob
4
src/components/Dashboard.tsx,carol

我让 Codex 这样调:

text
1
Create /tmp/components.csv with columns path,owner and one row per frontend component.
2
 
3
Then call spawn_agents_on_csv with:
4
- csv_path: /tmp/components.csv
5
- id_column: path
6
- instruction: "Review {path} owned by {owner}. Return JSON with keys path, risk, summary, and follow_up via report_agent_job_result."
7
- output_csv_path: /tmp/components-review.csv
8
- output_schema: an object with required string fields path, risk, summary, and follow_up

codex exec(非交互模式)时,stderr 会输出单行进度更新:

text
1
[12/50] reviewing src/components/Dashboard.tsx

跑完,/tmp/components-review.csv 长这样:

csv
1
path,owner,job_id,item_id,status,last_error,result_json
2
src/components/Settings.tsx,alice,job-abc,path,success,,"{""path"":""src/components/Settings.tsx"",""risk"":""low"",""summary"":""..."",""follow_up"":""...""}"

我跑出来的实际数字

我拿自己项目(32 个组件)跑了一次:

  • 单 agent 串行:~45 分钟
  • max_concurrency=6 跑 spawn_agents_on_csv:~9 分钟
  • 加速比 ~5x(不是 6x,因为有启动开销)

Token 消耗是单 agent 串行的 5.8 倍左右。基本上你拿时间换 token。

几个小提醒

  1. output_schema 一定要写得严格。如果你的 schema 是 {path: string, risk: string, summary: string, follow_up: string},worker 漏一个字段就会被认为是 error,宁可写少不要写多。
  2. max_concurrency 不要超过 max_threads。否则会排队等资源。
  3. CSV 文件大小别太大。我试过一个 500 行的 CSV,Codex 启动时就 OOM 警告了。控制在 200 行以内比较稳。
  4. 结果 CSV 的 result_json 是字符串,不是真正的 JSON 列。要自己 json.loads() 一下。

一些我暂时没搞明白的事

写到最后留几个坑,不算完整答案,等我搞明白再来更新:

  1. MCP server 在子 agent 里的继承行为。官方说会继承,实测有时候不继承。可能是 bug,也可能是我没配对。
  2. max_depth > 1 的实际成本。我没真去测递归派生,但感觉 token 烧起来会很猛。如果有谁跑过,欢迎评论区聊。
  3. 长任务下的会话恢复codex resume 能恢复单 agent session,但子 agent 跑飞之后的恢复策略我没试过。
  4. GPT-5.3-Codex-Spark 在 subagent 里的实际质量。官方说"for extra fast tasks",我用下来感觉比 gpt-5.4-mini 稍快但质量接近,没明显拉开差距。

写在最后

Subagent 是不是非用不可?老实说,不是。如果你日常的工作是"一次只改一个文件"或者"问个简单问题",单线程完全够用。subagent 真正的价值是当任务天然有 N 个独立分支时,让 Codex 帮你并行跑。

我的判断标准是:如果这个任务可以拆给 3 个实习生同时干,那大概率可以用 subagent。如果只能按顺序串行干,单线程就够。

另外,token 消耗真的是肉眼可见地翻倍。我用 spawn_agents_on_csv 跑 32 个组件那一次,token 用了 180 万。如果是日常开发,先把单 agent 跑顺了再考虑 subagent,别一上来就堆并发。

快速决策树

写个简单的判断流程,省得每次都纠结:

  • 任务能在 5 分钟内完成? → 单 agent 跑,别用 subagent
  • 任务能拆成 2 个以上互相独立的子任务? → subagent 有戏
  • 拆完之后每个子任务还要跑很久? → spawn_agents_on_csv 比手动 subagent 更划算
  • 子任务之间需要共享状态? → 老实单 agent 串行,subagent 上下文隔离反而是负担
  • 预算紧张? → 先算一下"5 个子 agent × 10 分钟"的 token 成本,超过 50 美元就先用单 agent 跑一次试试

我这边的经验阈值是:任务超过 15 分钟、且能拆 3 路以上,就值得上 subagent。低于这个门槛,并行启动的开销比省下的时间还多。

OK,今天就写到这。后面打算写一篇"Codex Subagent 真实项目实战",拿一个开源项目做 PR review 全流程,把成本、速度、质量都量化一下。有问题评论区聊。

advertisement