第 07 章

Subagents 多代理

让 AI 团队并行工作,而不是一个人扛所有任务

从单兵作战到多代理协作的跨越

一个人扛不住

当任务越来越复杂,单个代理开始力不从心

角色混乱

你让同一个代理既写代码、又做审查、又跑测试。它忘了自己该专注什么——上下文越堆越多,质量越来越差。

上下文膨胀

一个会话塞了 50 个文件的修改历史。代理开始"遗忘"你最早说的话,输出变得前后矛盾。

审查走过场

让代理自己审查自己的代码?它当然说"没问题"。自己当自己的裁判,等于没有裁判。

安全风险

一个拥有全部权限的代理,一旦被误导,可能删库、泄露密钥。权限越大,风险越大。

解决方案:把大任务拆成小任务,分给多个专门的子代理并行处理。这就是 Subagents 的设计哲学。

开始之前

适合谁

  • 已经熟悉 Codex CLI 基本交互的开发者
  • 需要并行处理多个子任务(审查、测试、重构)的人
  • 想通过角色分离提升代码质量的团队
  • 希望降低单个代理权限暴露面的安全敏感用户

读完后你应该已经能

  • 理解 Subagent 的隔离机制与生命周期
  • 用 spawn_agent 工具创建并调度子代理
  • 配置 agent_type 与模型切换策略
  • 用多代理模式做并行安全审查

这些内容暂时先不碰

  • 自定义 agent_type 的深层扩展
  • 跨会话持久化代理(persistent agents)
  • SDK 层面的代理编排 API
  • 企业级多租户代理调度
前置知识:建议先完成第 01–05 章,尤其是 AGENTS.md 和 MCP 章节,因为子代理会继承这些配置。

Subagent 是什么

一个独立的 AI 助手,有自己的上下文和权限

Subagent(子代理)是由主代理(root agent)临时创建的 AI 工作单元。它像一个短期雇员:被分配任务、独立执行、交付结果、然后离开。

独立上下文

子代理看不到主代理的对话历史。它只拿到你通过 message 传入的任务描述。信息隔离 = 专注。

受限权限

子代理可以只拥有完成任务所需的最小工具集。比如一个"探索者"只需要读文件,不需要执行 Bash。最小权限 = 安全。

临时生命周期

子代理完成任务后自动关闭,不占用资源。没有"常驻后台"的概念。用完即走 = 节省。

一句话总结:Subagent = 隔离上下文 + 单一职责 + 最小权限 + 临时生命周期。

为什么需要多代理

单代理模式的三个根本瓶颈

瓶颈表现多代理解法
上下文窗口有限塞满后遗忘、前后矛盾每个子代理只看到自己需要的上下文
串行执行慢审查必须等写完才能开始写代码和做审查可以同时跑
角色冲突自己审自己 = 没有审查独立子代理做审查,视角客观
类比:一个全能程序员 vs 一个开发团队。前者什么都能做,但一次只能做一件事;后者可以并行,但需要协调。Subagents 就是后者。

Multi-Agent V2 架构

主代理协调,子代理执行

Codex 的 Multi-Agent V2 采用经典的主从架构(master-worker)。主代理是唯一的协调者,负责理解你的需求、拆分任务、分发给子代理、汇总结果。

主代理(Root Agent)

  • 直接与你对话
  • 理解需求、拆分任务
  • 通过 spawn_agent 创建子代理
  • 汇总子代理结果,给你最终回复

子代理(Child Agent)

  • 独立执行分配的任务
  • 拥有隔离的上下文
  • 完成后自动返回结果
  • 不能创建自己的子代理(depth=1)
注意:默认 agents.max_depth = 1,子代理不能再 spawn 子代理,防止无限递归。这是一个真实的配置参数——调大会允许更深嵌套,但也增加 token 消耗和 fan-out 风险(fan-out 即"扇出":每个子代理再 spawn 多个子代理,数量像树杈一样层层扩散,1 → 3 → 9 → 27……指数级增长,迅速耗尽并发上限和 token 预算)。

生命周期

spawn → 执行 → 返回 → 关闭

图:多代理交互流程——spawn、执行、返回、关闭
关键:子代理的生命周期由主代理管理。主代理决定何时创建、何时等待、何时关闭。

上下文隔离

子代理看不到主代理的对话历史

每个子代理拿到的是独立的上下文窗口。它不知道主代理之前聊了什么,不知道其他子代理的存在,只知道你通过 message 或 items 传入的任务。

好处

  • 避免上下文膨胀——每个子代理的窗口都很小
  • 防止信息泄露——子代理不会意外暴露主代理的敏感信息
  • 专注任务——子代理不会被无关对话干扰

代价

  • 子代理不知道全局背景
  • 你需要在 message 里写清楚所有必要信息
  • 子代理之间不能直接通信(必须通过主代理中转)
设计原则:宁可多写一点任务描述,也不要让子代理因为缺少上下文而做出错误判断。

单一职责

每个子代理只做一件事,把它做好

多代理的核心哲学是分工。不要创建一个"全能子代理",而是创建多个各司其职的专才。

职责agent_type典型任务
探索explorer读文件、搜索代码、理解架构
执行worker写代码、改文件、跑测试
审查default代码审查、安全扫描、质量评估
原则:如果一个子代理的任务描述超过 3 句话,考虑拆成两个。

最小权限

只给完成任务所需的最少工具

子代理的权限应该被限制在刚好够用的范围内。一个只需要读文件的子代理,不应该有 Bash 执行权限。

为什么重要

  • 防止子代理被 prompt injection 误导后执行危险操作
  • 减少意外修改的可能性
  • 符合安全最佳实践(principle of least privilege)

如何实现

  • 通过 agent_type 限制工具集
  • 在 message 中明确禁止某些操作
  • 配合沙箱模式进一步限制文件系统访问
安全提醒:永远不要让一个你不信任的子代理拥有 exec 或 write 权限。审查型任务应该用只读的 explorer。

并行执行

多个子代理同时跑,效率翻倍

当多个子任务之间没有依赖关系时,主代理可以同时 spawn 多个子代理并行执行。这是多代理相比单代理最大的效率优势。

图:并行执行示意图——主代理同时 spawn 三个子代理
并行上限:默认配置下最多 6 个并发子代理(max_threads = 6)。超过后自动排队,等正在运行的线程完成后继续。

spawn_agent 工具

创建子代理的核心工具

spawn_agent 是主代理用来创建子代理的工具。调用后,子代理立即开始独立执行,主代理可以继续做其他事或等待结果。

{
  "message": "审查 src/auth/ 目录下的所有文件,找出安全漏洞",
  "model": "gpt-5.4",
  "reasoning_effort": "medium"
}

必填参数

  • message — 任务描述(字符串)

可选参数

  • items — 多模态任务描述(文本+图片)
  • fork_context — 是否继承主代理上下文(布尔,默认 false)
  • model — 指定模型
  • reasoning_effort — 推理力度
  • service_tier — 服务层级
V1 vs V2 差异:当前大多数模型(gpt-5.4 及以下)使用 multi_agent_v1__spawn_agent,参数如上。gpt-5.5/5.6 会强制使用 V2,其 schema 不同——fork_context 变为 fork_turns("all"/"recent"/"none"),且 task_name 必填。详见 issue #31097。

参数:message 与 items

告诉子代理该做什么

message 是最常用的参数——一段纯文本任务描述。items 支持多模态,可以混入图片。

// 纯文本任务
{
  "message": "分析 src/utils/parser.js 的性能瓶颈,给出优化建议"
}

// 多模态任务(文本 + 截图)
{
  "items": [
    { "type": "text", "text": "这个错误发生在点击提交按钮时" },
    { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
  ]
}
写作技巧:任务描述要具体。不要写"审查代码",要写"审查 src/auth/login.ts,重点关注 SQL 注入和 XSS 漏洞"。

内置角色类型

三种内置 agent 定义了不同的能力边界

Codex 内置了三种角色,各有不同的工具权限和适用场景。选错类型 = 给审计员发了把锤子。

类型能力适用场景
default完整工具集(读/写/执行)通用任务,需要修改代码的场景
explorer只读工具(搜索/读取)代码探索、架构理解、安全审查
worker写工具(读/写/执行)代码修改、测试执行、构建任务
如何指定角色:在 V1 中,agent_type 不在正式 schema 中,但后端会接受。最可靠的方式是直接写进 message:"你是一个只读代码审查代理,只报告问题不修改文件"。V2 中则通过 .codex/agents/*.toml 自定义角色文件 + task_name 来激活。
选择建议:如果任务只需要"看"不需要"改",永远用 explorer 或在 message 中明确要求只读。这是最安全的默认选择。

参数:model

为不同任务选不同模型

子代理可以独立于主代理选择模型。这是成本优化的关键手段——简单任务用便宜模型,复杂任务用强模型。

主代理用强模型

$ codex -m gpt-5.5

主代理负责理解需求、拆分任务、汇总结果,需要最强的推理能力。

子代理用性价比模型

{
  "model": "gpt-5.4-mini",
  "message": "搜索所有 TODO 注释"
}

简单搜索任务不需要最强模型,用便宜模型省成本。

成本意识:一个典型多代理会话中,子代理的 token 消耗可能占 70%。选对模型能省大量费用。

参数:推理与服务

控制思考深度和服务质量

reasoning_effort

  • low — 快速响应,浅层推理
  • medium — 平衡(默认)
  • high — 深度推理,慢但更准

适合需要复杂逻辑分析的审查任务。

service_tier

  • default — 标准服务
  • priority — 优先处理(低延迟)
  • flex — 弹性处理(可能排队,但更便宜)

非紧急的后台审查任务可以用 flex 省钱。

{
  "reasoning_effort": "high",
  "service_tier": "flex",
  "message": "深度分析这个 PR 的架构影响"
}

参数:fork_context

是否继承主代理的会话上下文

fork_context 控制子代理是否能看到主代理的对话历史。

值行为适用场景
false(默认)完全隔离,只看到 message 传入的内容大多数场景,安全且专注
true继承主代理的对话历史作为背景子代理需要理解完整背景才能工作
慎用 fork_context: true:继承上下文会消耗子代理的上下文窗口,可能导致它"看到太多"而分心。大多数情况下,把必要信息直接写进 message 更好。
V2 演进:在 MultiAgentV2(gpt-5.5/5.6)中,此参数升级为 fork_turns,取值变为 "all" / "recent" / "none",默认值改为 "all"(全历史继承),行为有较大差异。

等待与汇总

Codex 自动等待所有子代理完成

当多个子代理在运行时,Codex 会自动等待所有请求的结果就绪,然后返回一份合并后的回复。你不需要手动调用等待工具——告诉 Codex "等全部跑完再汇总"就行。

自然语言驱动

审查这个 PR,为每个维度各开一个 agent,
等全部跑完后汇总每个结果:
1. 安全问题  2. 代码质量  3. Bug
4. 竞态条件  5. 测试稳定性  6. 可维护性

Codex 会自动 spawn、等待、汇总——你只需描述意图。

用 /agent 检查进度

  • 输入 /agent 切换到任意活跃线程
  • 查看每个子代理的实时输出
  • 在不打断的情况下监控执行状态
超时控制:对于长时间运行的命令,Codex 可以使用内置的 monitor 角色进行轮询等待,单次轮询窗口最长可达 1 小时。

运行中管理

用自然语言操控运行中的子代理

子代理在执行过程中,你可以随时追加指令、纠正方向或紧急停止。不需要手写工具调用——直接用自然语言告诉 Codex 即可。

使用场景

  • 子代理遗漏了某些文件 → "让审查 agent 也检查一下 src/api/ 目录"
  • 发现子代理跑偏了 → "纠正探索 agent,重点关注认证模块"
  • 紧急停止 → "停掉正在运行的 worker agent"
  • 关闭已完成的 → "关闭已完成的审查 agent"

管理手段

  • /agent — 切换到指定线程查看详情
  • 直接对 Codex 说 — "停掉 / 关闭 / 追加指令给某个 agent"
  • Codex 会将你的意图路由到正确的子代理线程
注意:中断子代理时,已完成的操作不会回滚,但后续调用会被取消。

生命周期收尾

子代理完成后会自动清理

子代理完成任务后通常会自动关闭。但在某些异常情况下(如主代理崩溃、网络中断),可能需要手动干预。

自动行为

  • 子代理交付结果后自动关闭
  • Codex 汇总所有结果后返回一份合并回复
  • 线程资源自动释放

手动管理

  • 用 /agent 查看所有活跃线程
  • 对 Codex 说 "关闭已完成的 agent"
  • 发现卡住的代理可以直接要求停止
最佳实践:在会话结束前,用 /agent 确认没有残留的子代理。

角色:default

全能型代理,什么都能做

default 类型的子代理拥有完整的工具集:读文件、写文件、执行命令、搜索代码。它像一个全栈工程师。

能力

  • Read / Write / Edit 文件
  • 执行 Bash 命令
  • 搜索代码库
  • 安装依赖、运行测试

适用场景

  • 需要修改代码的任务
  • 端到端的功能开发
  • 构建和测试执行
风险:default 代理权限最大,被 prompt injection 利用时危害也最大。只在信任的任务中使用。

角色:explorer

只读探索者,安全又高效

explorer 类型的子代理默认以只读模式运行——读文件、搜索代码、分析架构。它的 sandbox_mode 默认设为 read-only,是最安全的默认选择。

能力

  • Read 文件
  • 搜索代码(grep / glob)
  • 分析架构和依赖

不能做

  • ❌ 修改文件
  • ❌ 执行命令
  • ❌ 安装依赖
最佳实践:所有审查类任务(安全审查、代码审查、架构分析)都应该用 explorer。注意:子代理的沙箱策略默认继承父会话,在 --yolo 或 workspace-write 模式下可能被覆盖——如需确保只读,在自定义 agent 中显式设置 sandbox_mode = "read-only"。

角色:worker

代码修改的执行者

worker 类型的子代理可以读、写、执行,但通常被配置为专注于代码修改。它像一个只负责写代码的程序员。

与 default 的区别

功能上类似,但 worker 在语义上更强调"执行具体编码任务"。在配置中可以给它不同的工具限制或提示词。

适用场景

  • 按规格写代码
  • 重构指定模块
  • 修复明确的 bug
提示:如果你的任务描述非常具体("按这个接口实现这个函数"),worker 比 default 更合适——它不会自作主张。

配置概览

在 config.toml 中控制多代理行为

多代理的全局设置在 ~/.codex/config.toml 的 [agents] 段中配置。当前版本默认已启用子代理功能。

[agents]
max_threads = 6              # 最大并发子代理线程数(默认 6)
max_depth = 1                # 嵌套深度,root 从 0 开始(默认 1)
job_max_runtime_seconds = 1800  # CSV 批处理任务的单 worker 超时
注意:如果另外启用了 MultiAgentV2([features.multi_agent_v2]),则不能同时设置 agents.max_threads——V2 使用自己的 max_concurrent_threads_per_session 参数,二者冲突会报错。
核心配置:max_threads 控制并发数(默认 6),max_depth 控制嵌套深度(默认 1)。大多数情况下默认值就够了。

配置参数详解

每个参数的含义与调优建议

参数默认值含义
max_threads6最大并发子代理线程数。太高会触发 API 限流
max_depth1子代理嵌套深度(root 从 0 开始)。调大会允许更深递归,但也增加 token 消耗
job_max_runtime_seconds1800CSV 批处理任务的单 worker 超时
调优建议:如果 API 限流频繁,把 max_threads 降到 3-4。遇到 spawn 排队等待是正常行为——超过上限的请求会自动排队,等正在运行的线程完成后继续。

自定义 Agent 角色

用 TOML 文件定义专属子代理

除了三个内置角色,你还可以在 ~/.codex/agents/(个人级)或 .codex/agents/(项目级)目录下放置 TOML 文件来定义自己的 agent。

# .codex/agents/reviewer.toml
name = "reviewer"
description = "PR 审查员,关注正确性、安全性和测试覆盖"
developer_instructions = """
审查代码时像 owner 一样思考。
优先关注正确性、安全性和缺失的边界测试。
"""
model = "gpt-5.4"
model_reasoning_effort = "high"
sandbox_mode = "read-only"

必填字段

  • name — 角色名称
  • description — 何时使用此角色
  • developer_instructions — 角色的系统提示词

可选字段

  • model / model_reasoning_effort
  • sandbox_mode — 沙箱策略
  • mcp_servers — 专属 MCP 服务
  • nickname_candidates — 显示昵称池

实战:并行安全审查

同时跑安全审查和质量审查

假设你刚写完一个 PR,想同时做安全审查和质量审查。两个任务互不依赖,可以并行。

// 主代理同时 spawn 两个子代理
spawn_agent({
  "message": "你是一个只读安全审查代理。审查 src/auth/ 和 src/api/ 目录,找出所有安全漏洞(SQL注入、XSS、CSRF、认证绕过)",
  "model": "gpt-5.4-mini"
})

spawn_agent({
  "message": "你是一个只读代码质量审查代理。审查 src/auth/ 和 src/api/ 目录:命名规范、函数复杂度、重复代码、缺少测试",
  "model": "gpt-5.4-mini"
})
效果:两个审查同时运行,总耗时约等于单次审查时间,而不是两倍。

实战:等待与汇总

Codex 自动收集结果,给你一份综合报告

子代理完成后,Codex 自动汇总结果,生成一份结构化的审查报告。你只需在原始提示中说"等全部跑完后汇总"。

# 你只需要说:
"等所有审查 agent 跑完后,给我一份汇总报告"

# Codex 自动完成:
# 1. 等待所有子代理返回结果
# 2. 安全审查结果(3 个高危、5 个中危)
# 3. 质量审查结果(2 个函数过复杂、4 处重复代码)
# 4. 优先修复建议——交叉分析两份审查
主代理的价值:子代理各自为战,主代理负责全局视角。它能看到两个审查的交叉点——比如"这个函数既不安全又太复杂"。

实战:复杂任务拆分

把一个大需求拆成多个子代理并行处理

假设你要给项目添加用户认证模块。这个任务可以拆成多个并行子任务:

子代理任务角色定位
A分析现有代码结构,确定最佳集成点explorer
B编写数据库迁移脚本worker
C实现 JWT 认证中间件worker
D编写单元测试worker
注意:B、C、D 都修改文件,如果它们改同一个文件会冲突。确保每个子代理操作不同的文件。

实战:模型切换

主代理用强模型,子代理用性价比模型

成本优化的经典策略:让贵模型做决策,让便宜模型干活。

# 启动时用强模型
$ codex -m gpt-5.5
# 子代理用性价比模型
{
  "model": "gpt-5.4-mini",
  "message": "你是一个代码实现代理。按照规格实现 UserService 的 createUser 方法"
}

{
  "model": "gpt-5.4-mini",
  "message": "你是一个只读探索代理。搜索所有调用 UserService 的地方"
}
成本节省:假设主代理消耗 20% token,子代理消耗 80% token。子代理从 gpt-5.5 切换到 gpt-5.4-mini,总成本可降低 60%+。

错误:并发过高

一次 spawn 太多子代理,触发限流

看到并行很爽,于是一口气 spawn 10 个子代理。结果 API 返回 429 Rate Limited,全部失败。

错误表现

  • API 返回 429 Too Many Requests
  • 子代理启动失败或执行中断
  • 主代理收到大量错误通知

修复方法

  • 把 max_threads 降到 3-4
  • 分批 spawn:先跑 3 个,完成后再跑下一批
  • 用 service_tier: "flex" 降低优先级
经验法则:除非任务非常简单,否则并发数不要超过 4。质量 > 速度。

错误:超时太短

子代理还没跑完就被判了死刑

你让子代理审查一个大型代码库,但等了半天它还没返回结果——超时是 Codex 自动管理的,长时间运行的子代理可能被提前终止。

错误表现

  • 子代理运行时间过长被自动终止
  • /agent 查看状态显示超时或 error
  • 主代理拿到空结果或不完整结果

修复方法

  • 根据任务复杂度合理设置超时
  • 简单搜索:10-15 秒
  • 代码审查:30-60 秒
  • 复杂分析:120 秒+
建议:宁可超时设长一点(大不了多等一会儿),也不要设太短导致任务白跑。

错误:fork 误用

继承太多上下文,子代理反而表现更差

觉得子代理需要"完整背景",于是设了 fork_context: true。结果子代理的上下文窗口被主代理的对话历史占满了,真正该看的代码反而放不下。

什么时候不该用

  • 主代理的对话历史超过 100 行
  • 子代理只需要看几个特定文件
  • 任务描述可以自包含

什么时候可以用

  • 子代理需要理解之前讨论的设计决策
  • 任务涉及多个文件的交叉引用
  • 主代理的对话历史很短(少于 20 行)
默认选择:99% 的情况下,fork_context: false(默认值)是正确的。把必要信息写进 message 就好。

错误:描述模糊

"审查代码"不是任务,是废话

子代理不会读心术。你写"审查代码",它不知道审查什么、用什么标准、输出什么格式。

❌ 模糊描述✅ 具体描述
"审查代码""审查 src/auth/login.ts,重点关注 OWASP Top 10 漏洞,输出 JSON 格式"
"优化性能""分析 src/api/handlers.go 中处理 /users 请求的函数,找出 N+1 查询问题"
"写测试""为 src/utils/validator.js 中的 validateEmail 函数写单元测试,覆盖边界情况"
黄金法则:任务描述要具体到"一个不需要任何背景知识的人也能执行"的程度。

错误:忘记清理

资源泄漏,像不关水龙头

子代理完成任务后通常会自动关闭,但在某些异常情况下(如主代理崩溃、网络中断),子代理可能变成"僵尸"继续占用线程资源。

检测方法

/agent

用 /agent 列出所有活跃线程,查看有没有"卡住"的子代理。

清理方法

关闭已完成的 agent
停掉卡住的 worker agent

直接用自然语言告诉 Codex 关闭或停止指定的子代理。

最佳实践:在会话结束前,用 /agent 确认没有残留的子代理。

最佳实践

多代理使用的七条军规

  1. 默认用 explorer — 不需要写权限的任务一律用只读代理
  2. 任务描述要具体 — 文件路径、关注点、输出格式,一个都不能少
  3. 并发不要贪多 — 3-4 个足够,超过反而触发限流
  4. 子代理用便宜模型 — 主代理用强模型做决策,子代理用性价比模型干活
  5. 超时宁长勿短 — 任务白跑比多等一会儿更浪费
  6. fork_context 默认关 — 把信息写进 message,不要依赖上下文继承
  7. 审查用独立代理 — 永远不要让代理审查自己的代码

与 MCP 配合

子代理也能用 MCP 工具

子代理继承了主代理的 MCP server 配置。这意味着你可以让子代理调用外部工具——比如让一个子代理查数据库,另一个同时查日志。

场景示例

  • 子代理 A:用 database MCP 查表结构
  • 子代理 B:用 logging MCP 查错误日志
  • 子代理 C:用 github MCP 查相关 issue

注意事项

  • 确保 MCP server 支持并发调用
  • 注意 MCP 工具的速率限制
  • 子代理的 MCP 权限与主代理一致
回顾:如果你还不熟悉 MCP,回到第 05 章学习如何配置 MCP server。

调试技巧

当子代理不按预期工作时

1. 检查任务描述

子代理的输出质量 100% 取决于你的任务描述。先确认 message 是否足够具体。

2. 检查 agent_type

需要写文件但用了 explorer?需要只读但用了 worker?类型不匹配会静默失败。

3. 用 /agent 查看状态

切换到子代理线程,看它的实时输出。是 running、completed 还是 error?

4. 用自然语言追加指令

子代理跑偏了?直接告诉 Codex "纠正 xxx agent,让它关注 yyy",而不是杀掉重来。

第 07 章

小结

你已经掌握了 Codex 多代理的核心概念和实战技巧

本章要点

  • Subagent = 隔离上下文 + 单一职责 + 最小权限 + 临时生命周期
  • 主代理协调,子代理执行;spawn → 执行 → 返回 → 关闭
  • 三种 agent_type:default(全能)、explorer(只读)、worker(执行)
  • 通过 model 参数实现成本优化:主代理强模型,子代理性价比模型
  • 配置 max_threads 控制并发(默认 6),max_depth 控制嵌套深度(默认 1)
  • 常见陷阱:并发过高、超时太短、fork_context 滥用、任务描述模糊
下一步:第 08 章 Hooks —— 在代理生命周期的关键节点插入自定义逻辑,实现自动化工作流。