OpenAI Codex 的 sub-agent 协作机制在多 Agent 架构中设计得相当成熟。本文基于 Codex 源码中的 multi-agent v2,从提示词设计、六个协作工具到多 Agent 文件冲突处理,拆解其 sub-agent 的完整工作机制。
此前已分析过主流 harness 的 memory、compaction 和 goal 机制,这次聚焦 sub-agent 的几个核心问题:主 agent 与子 agent 的提示词差异、子 agent 创建时能看到多少历史、sub-agent 之间如何通信、多 agent 如何处理冲突。
一、主 Agent 与 Sub-agent 的提示词
主 Agent
Codex 仓库内置的主 agent 基础提示词核心内容如下:
/root,主 agent,负责协调团队完成用户目标。可以创建 sub-agent、追加任务和发送消息,子 agent 也可以继续派生。团队成员具备同等协作能力,使用相同工具集。
关键对象包括:任务路径、协作工具、继承的上下文,以及接收消息的格式。agent 之间通过 analysis channel 通信,消息类型包括 MESSAGE 和 FINAL_ANSWER。
Sub-agent
子 agent 的基础提示词与主 agent 非常接近,核心差异在于:
- 身份定位:主 agent 明确为
/root,子 agent 路径可能是/root/research等 - 工作定位:主 agent 完成用户的总体目标,子 agent 完成分配给自己的任务
- 最终结果:主 agent 面向主会话输出,子 agent 明确要求回传直接父 agent
子 agent 并不是简化执行器,它会继承父 turn 的主要配置、权限环境和工作目录,拥有自己的对话上下文和执行循环。
默认不主动派生子 agent
Codex v2 的默认策略是:除非用户或 AGENTS.md/skill 明确要求,否则不创建 sub-agent。这是因为 Bash tool 等执行层工具本身已有并发控制能力,日常任务中 sub-agent 上场的必要性其实不高。
切换到 Ultra 模式时会启用 Proactive 策略:只要能通过委派并行工作来节省时间或提高质量,就应当使用协作工具分发任务。
二、六个 Sub-agent 协作工具
sub-agent 相关的协作工具位于 collaboration namespace 下,共六个:
| 工具 | 主要用途 |
|---|---|
spawn_agent |
创建子 agent,分配初始任务 |
send_message |
agent 之间传递信息,不唤醒目标 |
followup_task |
追加任务,必要时唤醒目标 |
wait_agent |
等待消息、完成通知或其他唤醒事件 |
interrupt_agent |
中断目标当前 turn |
list_agents |
查询团队成员和状态 |
这里的 turn 指 agent 接收一轮任务后,从推理、调用工具到最终结束的完整执行过程。一个 turn 内可有多次 LLM 请求。
1. spawn_agent:创建子 Agent
最重要的三个参数:
task_name:任务名,使用小写字母、数字和下划线message:分给子 agent 的初始任务说明fork_turns:从父 agent 继承多少历史,默认 "all"
另外可暴露 model、reasoning_effort,用于覆盖模型和推理强度。
如果调用者是 /root,新 agent 路径为 /root/check_tests。它再创建子 agent,路径变成 /root/check_tests/edge_cases。路径形成逻辑树,但内存组织通过 Map 维护,不需要逐层嵌套。
fork_turns:继承的历史范围
| 取值 | 继承范围 |
|---|---|
"all" |
继承可用的父模型历史,经过过滤与清理 |
"none" |
不继承父对话历史,但仍保留基础指导、工具定义、环境信息和任务说明 |
"3" 等正整数 |
继承最近 N 个可识别的 turn |
对于普通顶层历史记录,代码会保留用户消息、assistant 的最终回复,以及 system/developer 等指导消息;普通 tool call、tool result、reasoning 和 assistant 中间进度消息会被过滤。如果父历史已经压缩,fork 会处理现有的压缩 checkpoint,不会为了创建子 agent 重新展开所有压缩前原文。
目前 Codex v2 的设计无法让子 agent 利用 kv cache,这点与其他 sub-agent 设计不同。
子 Agent 完成后的通知机制
子 agent 正常完成时,最后回复会被运行时包装为完成消息,自动送到直接父 agent 的 mailbox。模型不需要额外调用"提交结果"工具。但只有最后一句消息会被自动回复,中间分析和工具结果不会全部进入父上下文。需要提前汇报的发现,应使用 send_message。
2. send_message:消息投递
参数只有 target 和 message。target 可以是 agent 标识或任务路径。底层投递分为五步:解析目标找到 thread ID、确认运行实例已加载、提交 InterAgentCommunication、接收方放入 mailbox(加锁的内存队列)、执行循环消费消息加入上下文。
关键区别:发送成功只说明完成了投递,不等于模型已经读到。消息先入队,不会取消正在执行的工具。工具调用返回后,下一次 LLM 请求通常能同时看到测试结果和补充消息。
普通 send_message 不会主动让空闲 agent 开始新 turn。空闲时没有"当前工具返回后"的处理机会,消息可能继续留在信箱里;需要它接着工作,应使用 followup_task。
3. followup_task:追加任务
参数同样是 target 和 message,与 send_message 的关键差异在于 trigger_turn:前者为 false,followup_task 为 true。子 agent 已交回分析后,主 agent 想让它继续工作,就调用 followup_task。如果子 agent 仍在工作,这次 followup 不会强制取消当前工具,消息可以随工具返回进入下一次 LLM 请求。
4. wait_agent:等待
可选参数 timeout_ms,默认 30 秒、最小 10 秒、最大 1 小时。父 agent 派发任务后可以继续做自己的工作,也可以调用它等待子 agent。普通消息、子任务完成通知、用户新输入或超时,都可能结束等待。
5. interrupt_agent:中断
参数只有 target,返回目标中断前的状态。会向目标线程发送中断操作,取消当前 turn。agent 的身份和历史仍保留,后续可继续接任务。发现方向错误时,先 interrupt,再用 followup_task 告诉它改做什么。
6. list_agents:查询
可选参数 path_prefix 过滤任务分支。常见状态包括初始化、运行、完成、中断、错误,以及实例关闭或查询不到目标。Completed 表示当前任务完成,agent 还可通过 followup 继续工作。容量不足时,运行时可卸载空闲 agent 释放资源;并发额度由团队共享,子 agent 再派生也受同一团队限制。
三、多 Agent 编辑同一文件的冲突处理
Codex 的公共提示词中明确说明:所有 agent 共享同一个容器和文件系统,使用相同的工作目录。A 修改文件后,B 再读能看到修改,但 B 之前读进上下文的旧内容不会自动更新。文件共享与上下文同步,是两回事。
如果两个 agent 同时修改同一文件,存在冲突风险。这条默认路径没有跨 agent 的文件所有权锁,也没有自动分支合并。因此分工时最好显式划分文件或模块范围,共享接口先约定;需要修改同一处时,让一个 agent 负责落盘,其他通过消息提出修改。
总结
Codex 的 sub-agent 设计相当成熟,主 agent 与子 agent 基本处于同等级别,通信机制设计完善。默认不派发子 agent 是因为工具并发能力已经足够好。Codex 并没有在 harness 层对多 agent 做冲突控制,需要主 agent 自行规划好子 agent 的行为边界。