Featured image of post Codex 的 Main Agent 和 Subagent 是怎么通信的

Codex 的 Main Agent 和 Subagent 是怎么通信的

把 Codex 多 agent 的通信模型收成一条闭环:main agent 如何派任务、subagent 如何回消息、wait_agent 在等什么,以及这些环节在源码里分别落在哪些文件。

很多人第一次看 Codex 的多 agent 机制时,会下意识把 subagent 理解成 “main agent 调了一个内部函数”。这个理解不对。

在 Codex 里,subagent 更接近于:主线程之外又开了一个独立聊天线程。它有自己的 thread id、上下文、工具调用过程、执行状态和 mailbox。main agent 和 subagent 不共享完整上下文,它们靠 Codex runtime 转发内部消息协作。

这篇文章不讲 Rust 语法,只讲一条闭环:

1
2
3
4
5
main agent 怎么派任务
-> runtime 怎么投递消息
-> subagent 怎么独立执行
-> 结果怎么回到 main agent
-> main agent 怎么继续往下走

先看总览图:

Codex main agent 与 subagent 通信总览图

先记住一个模型

可以先把 Codex 的 subagent 理解成下面这个结构:

1
2
3
main agent 所在主聊天线程
  另外开了一个独立聊天线程
  这个独立线程就是 subagent

所以 subagent 不是 main agent 内部的一个函数。它有自己的:

  • thread id
  • 上下文
  • 工具调用过程
  • 执行状态
  • mailbox

这件事决定了 main agent 和 subagent 的关系不是同步函数调用,而是多个独立线程之间通过内部信件协作。

先看通信闭环

main agent 能控制 subagent 的方式,不是直接读写它的内存,而是调用一组协作工具:

1
2
3
4
5
6
spawn_agent      创建子 agent,并给第一条任务
send_message     给已有 agent 发消息,但不强制它马上跑
followup_task    给已有 agent 发后续任务,并在空闲时触发它继续跑
wait_agent       等待有 agent 消息或状态变化
interrupt_agent  打断某个 agent 当前执行
list_agents      查看当前有哪些 agent

这些工具本质上都是 function call。模型输出 function call,Codex runtime 收到后执行内部逻辑。

把完整流程压成一条主链路,大致就是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
用户提问
  -> main agent 决定拆任务
  -> main agent 调 spawn_agent
  -> runtime 创建 child thread
  -> 第一条任务进入 child thread
  -> child agent 独立执行
  -> main agent 调 wait_agent
  -> child agent 调 send_message(target=root)
  -> root mailbox 收到结果
  -> wait_agent 被唤醒
  -> main agent 下一轮读到这条消息
  -> main agent 汇总后回答用户

这里最重要的三点是:

  1. subagent 是真的独立 thread,不是内部函数。
  2. send_messagefollowup_task 都是在投递内部消息,只是后者会顺带触发目标 agent 开始下一轮执行。
  3. wait_agent 等的是 mailbox 活动,不是同步拿子函数返回值。

例如 main agent 创建 subagent 时,可能只是发出这样一个调用:

1
2
3
4
spawn_agent({
  task_name: "research",
  message: "你去分析 app-server 里的 subagent 通信逻辑"
})

但 runtime 真正做的是:

1
2
3
4
5
创建 child thread
记录 parent_thread_id
记录 task_name
投递第一条任务
启动 child agent 独立执行

而 subagent 回结果时,走的也不是“返回值”,而是再发一封内部信:

1
2
3
4
send_message({
  target: "root",
  message: "我查完了。结论是:Codex 通过 InterAgentCommunication 和 mailbox 通信。"
})

所以从运行时视角看,Codex 的多 agent 更像:

1
2
多个独立聊天线程
  通过 runtime 维护的内部邮箱互相发消息

wait_agent 为什么经常被误解

wait_agent 最容易被误解成“同步等待 subagent 返回值”,其实它更像:

1
main agent 暂停一下,等 mailbox 有没有新消息

比如:

1
2
3
wait_agent({
  timeout_ms: 30000
})

它的意思是:

1
2
3
最多等 30 秒
如果收到 agent 消息或相关活动,就返回
如果 30 秒没有,就返回 timed_out

超时返回类似:

1
2
3
4
{
  "message": "Wait timed out.",
  "timed_out": true
}

这里一定要分清:

1
2
3
wait_agent 超时
!=
subagent 被杀掉

子 agent 可能还在继续跑。runtime 不会自动替 main agent 做决策,它不会自动:

  • kill 子 agent
  • interrupt 子 agent
  • 判定子 agent 失败
  • 自动重派任务

它只是把 timed_out=true 交还给 main agent。接下来 main agent 自己决定下一步:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
继续等:
  wait_agent({ timeout_ms: 30000 })

打断:
  interrupt_agent({ target: "research" })

催促:
  followup_task({ target: "research", message: "收敛一下,直接给结论" })

先回答用户:
  基于已有信息组织答案

所以 main agent 在这套机制里,本质上更像调度者。

这条闭环在源码里怎么落地

如果只看一张源码总览图,可以先记住这条链:

1
2
3
4
5
6
7
模型看到工具定义
  -> 模型发起 function call
  -> tool handler 接住 function call
  -> AgentControl 负责创建 / 发消息 / 等待 / 中断
  -> session handlers 和 InputQueue 负责 mailbox
  -> turn loop 把 mailbox 消息放进模型上下文
  -> app-server / TUI 把事件展示给客户端

按这个链路往下看,源码可以分成 6 个位置。

第一层:工具暴露给模型

文件:

1
2
codex-rs/core/src/tools/spec_plan.rs
codex-rs/core/src/tools/handlers/multi_agents_spec.rs

这里分别负责两件事:

  • spec_plan.rs 决定当前 turn 要不要把多 agent 工具暴露给模型
  • multi_agents_spec.rs 定义这些 function call 的参数 schema

也就是说,main agent 能不能调用 spawn_agentsend_messagewait_agent,第一步先看这里。

第二层:main agent 的控制入口

文件:

1
2
3
4
5
6
7
codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs
codex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs
codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs
codex-rs/core/src/tools/handlers/multi_agents_v2/interrupt_agent.rs
codex-rs/core/src/agent/control.rs
codex-rs/core/src/agent/control/spawn.rs
codex-rs/core/src/agent/registry.rs

这层可以理解成 “main agent 的控制台”:

  • spawn.rs 负责接住 spawn_agent
  • message_tool.rs 负责接住 send_messagefollowup_task
  • wait.rs 负责 wait_agent
  • interrupt_agent.rs 负责打断入口
  • AgentControl 是统一控制中心
  • spawn_agent_internal(...) 真正创建 child thread
  • AgentRegistry 记住有哪些 agent、各自的 thread id 和 path

所以“main agent 怎么找到 subagent、怎么把任务发过去”,主要都在这层。

第三层:内部消息的数据结构

文件:

1
codex-rs/protocol/src/protocol.rs

关键结构:

1
InterAgentCommunication

它可以直接理解成一封内部信,里面最关键的是:

  • author
  • recipient
  • content
  • trigger_turn

例如:

1
2
3
4
from: root
to: research
content: 继续查一下 thread_history.rs
trigger_turn: true

第四层:消息如何进入 mailbox

文件:

1
2
codex-rs/core/src/session/handlers.rs
codex-rs/core/src/session/input_queue.rs

这里是真正的 mailbox 落点:

  • handlers.rs 里的 inter_agent_communication(...) 收到 Op::InterAgentCommunication
  • input_queue.rs 里的 enqueue_mailbox_communication(...) 把消息放进 mailbox
  • 如果 trigger_turn=true,runtime 会尝试启动目标 agent 的新一轮 turn

所以 send_messagefollowup_task 的本质区别,就落在 trigger_turn 上。

第五层:mailbox 怎么进入模型上下文

文件:

1
2
codex-rs/core/src/session/turn.rs
codex-rs/core/src/session/mod.rs

这里负责两件事:

  • turn.rs 在每轮请求模型前,从 InputQueue 取 pending input
  • mod.rs 里的 record_inter_agent_communication(...) 把消息写进历史和 rollout

也就是说,agent 间消息不只是内存里短暂经过,它还会:

  • 进入下一轮模型上下文
  • 写入会话历史
  • 被客户端看到

第六层:状态和展示

文件:

1
2
3
4
5
codex-rs/core/src/agent/status.rs
codex-rs/app-server-protocol/src/protocol/thread_history.rs
codex-rs/tui/src/app/loaded_threads.rs
codex-rs/tui/src/app/agent_navigation.rs
codex-rs/tui/src/app/thread_routing.rs

这层负责把运行中的事实变成用户能看到的东西:

  • status.rs 把事件映射成 RunningCompletedErrored 等状态
  • thread_history.rs 把协作事件转成客户端能展示的 ThreadItem
  • TUI 相关文件负责找出主线程下有哪些 subagent,以及怎么在界面里切换和缓存它们

所以你在 app-server 或 TUI 里看到的“创建了哪个 agent”“谁给谁发了消息”“wait 有没有超时”,最终都依赖这层映射和展示。

最短源码定位表

你想看什么看哪个文件
工具怎么暴露给模型codex-rs/core/src/tools/spec_plan.rs
function call 参数怎么定义codex-rs/core/src/tools/handlers/multi_agents_spec.rs
spawn_agent 入口codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs
创建 child threadcodex-rs/core/src/agent/control/spawn.rs
main 如何控制 agentcodex-rs/core/src/agent/control.rs
agent 列表和 metadatacodex-rs/core/src/agent/registry.rs
send_message / followup_taskcodex-rs/core/src/tools/handlers/multi_agents_v2/message_tool.rs
内部信件结构codex-rs/protocol/src/protocol.rs
mailbox 队列codex-rs/core/src/session/input_queue.rs
mailbox 进入模型上下文codex-rs/core/src/session/turn.rs
通信记录进历史codex-rs/core/src/session/mod.rs
wait_agent 超时逻辑codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs
agent 状态codex-rs/core/src/agent/status.rs
app-server 展示codex-rs/app-server-protocol/src/protocol/thread_history.rs
TUI subagent 导航codex-rs/tui/src/app/agent_navigation.rs

最后收一句

如果只记一句话,我建议记这句:

1
Codex 的 main agent 和 subagent 不是同步函数调用,而是多个独立 thread 通过 runtime 维护的 mailbox 互相发消息

从原理上看,这是一个“多线程聊天 + 内部邮箱”的协作模型;从源码上看,这条链路则落在 tool handler、AgentControl、session mailbox、turn loop、app-server 和 TUI 几层里,刚好首尾闭环。

本文采用 CC BY-NC-SA 4.0 协议,转载请注明出处。
最后更新于 2026-06-25
记录工程实践和排障过程。
使用 Hugo 构建
主题 StackJimmy 设计