这篇文档按一个真实场景来讲 Codex 的 MultiAgent V2:

用户:把 Codex 的 memory、skills、context compaction 三块并行分析,然后汇总成一篇技术文档。

阅读源码时会遇到三个词:agentsubagenttask。它们不是同一层概念,但也不应该被讲成三套互不相干的系统。

本文采用下面的口径:

agent / subagent:
  在 MultiAgent V2 场景里,spawn_agent 创建出来的 agent,本质上就是一个 ThreadSpawn subagent。
  用户和模型看到的是 agent;协议和 session source 里记录的是 SubAgent(ThreadSpawn)。

special subagent:
  /review、compact、memory consolidation、CSV worker 等也是 subagent,但不是 MultiAgent V2 可对话 agent。
  它们是 Codex 内部工作流创建的特殊子会话,不需要单独当成“另一种 agent 系统”理解。

task:
  Codex runtime 内部驱动一次 turn/workflow 的执行对象,例如 RegularTask、ReviewTask、CompactTask。
  它不是模型计划里的 todo,也不是 spawn_agent.task_name。

一句话总结:

MultiAgent V2 的 agent = 可管理、可通信、可等待、可关闭的 ThreadSpawn subagent。
task = runtime 跑一次 turn/workflow 的执行单元。

下面从“模型为什么会 spawn、child 怎么启动、父子怎么通信、状态怎么恢复”四条线讲清楚。

场景一:用户明确要求并行,模型为什么会调用 spawn_agent

用户输入:

把 memory、skills、context compaction 三块并行分析,最后由你汇总。

Codex 不会在 runtime 里自动拆任务。模型只有在当前 turn 里看到了 MultiAgent V2 tools,才可能调用 spawn_agent。工具可见性来自 feature/config/tool plan;调用决定来自模型读到的用户意图、tool schema、usage hint、developer instructions。

模型看到的核心工具是 spawn_agent

相关 Tool Schema / spawn_agent

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: spawn_agent
description:
  Spawns an agent to work on the specified task. If your current task is `/root/task1`
  and you spawn_agent with task_name "task_3" the agent will have canonical task name
  `/root/task1/task_3`.
  You are then able to refer to this agent as `task_3` or `/root/task1/task_3`
  interchangeably. However an agent `/root/task2/task_3` would only be able to
  communicate with this agent via its canonical name `/root/task1/task_3`.
  The spawned agent will have the same tools as you and the ability to spawn its own subagents.
  Spawned agents inherit your current model by default. Omit `model` to use that preferred
  default; set `model` only when an explicit override is needed.
  It will be able to send you and other running agents messages, and its final answer will be
  provided to you when it finishes.
  The new agent's canonical task name will be provided to it along with the message.
  This session is configured with `max_concurrent_threads_per_session = <limit>` for
  concurrently open agent threads.

required: ["task_name", "message"]

parameters:
  task_name:
    description: Task name for the new agent. Use lowercase letters, digits, and underscores.
  message:
    description: Initial plain-text task for the new agent.
  agent_type:
    description:
      Optional type name for the new agent. If omitted, `default` is used.
      Available roles:
      default: { Default agent. }
      explorer: { Use `explorer` for specific codebase questions... }
      worker: { Use for execution and production work... }
  fork_turns:
    description:
      Optional number of turns to fork. Defaults to `all`. Use `none`, `all`, or a positive
      integer string such as `3` to fork only the most recent turns.
  model:
    description:
      Optional model override for the new agent. Leave unset to inherit the same model as the
      parent, which is the preferred default.
  reasoning_effort:
    description:
      Optional reasoning effort override for the new agent. Replaces the inherited reasoning effort.
  service_tier:
    description:
      Optional service tier override for the new agent. Leave unset unless the user explicitly
      asks for one.

中文对照:

名称:spawn_agent
描述:
  创建一个 agent 去处理指定任务。如果当前任务是 `/root/task1`,并用 task_name
  "task_3" 调用 spawn_agent,新 agent 的规范任务名就是 `/root/task1/task_3`。
  之后可以用 `task_3` 或 `/root/task1/task_3` 指代它。但 `/root/task2/task_3`
  只能通过规范名 `/root/task1/task_3` 与它通信。
  新 agent 拥有和当前 agent 相同的工具,也可以继续创建自己的 subagents。
  新 agent 默认继承当前模型。省略 `model` 会使用推荐默认值。
  它可以给你和其他运行中的 agent 发送消息;完成时,最终回答会提供给你。
  新 agent 的规范任务名会随初始 message 一起提供给它。
  当前 session 配置了 `max_concurrent_threads_per_session = <limit>`,限制并发打开的 agent thread 数量。

必填字段:["task_name", "message"]

参数:
  task_name:新 agent 的任务名。使用小写字母、数字和下划线。
  message:给新 agent 的初始纯文本任务。
  agent_type:新 agent 的可选类型名。省略时使用 default。
  fork_turns:要 fork 的 turn 数。默认 all,可用 none、all 或正整数字符串。
  model:可选模型覆盖。默认继承父级模型。
  reasoning_effort:可选 reasoning effort 覆盖。
  service_tier:可选 service tier 覆盖。

这段 schema 让模型知道五件事:

1. agent 是有路径的工作线程,不是普通函数。
2. agent 默认继承父模型和工具。
3. fork_turns 决定 child 启动时带多少父上下文。
4. agent_type 是 role/config 选择,不是另一个 runtime。
5. agent 完成后,结果会回到父 agent。

一个合理的模型生成 tool call 是:

相关 Tool Call Example / 模型生成的 tool call 示例

{
  "task_name": "memory_scan",
  "agent_type": "explorer",
  "fork_turns": "3",
  "message": "只读分析 Codex 的 memory 系统。你只需要参考最近 3 轮上下文,输出关键文件、数据流和不确定点,不要修改代码。"
}

这是模型生成的调用示例,不是 schema。handler 收到后,会创建一个 child thread/session,并把它登记成 SubAgentSource::ThreadSpawn

相关 Implementation Note / ThreadSpawn 身份

英文原文:

ThreadSpawn { parent_thread_id, depth, agent_path, agent_nickname, agent_role }

中文对照:

ThreadSpawn {
  parent_thread_id:父 thread 是谁
  depth:在 agent 树里的深度
  agent_path:规范路径,例如 /root/memory_scan
  agent_nickname:用户可见昵称
  agent_role:角色,例如 explorer 或 worker
}

这就是为什么可以说“agent 和 subagent 在这里基本是一回事”:模型/用户层叫 agent,session source 里叫 ThreadSpawn subagent。

场景二:agent_type 到底注入了哪些提示词

用户可能会问:

worker 的协作规则是哪里来的?是子 agent 的系统提示词吗?

不是。agent_type 首先出现在 spawn_agent 的参数 schema 里,是父 agent 在决定怎么派工时看到的文本。

agent_type 相关的模型可见文本有五类。

第一类是参数总描述。

相关 Tool Parameter Description / agent_type 总描述

英文原文:

Optional type name for the new agent. If omitted, `default` is used.
Available roles:
...

中文对照:

新 agent 的可选类型名。省略时使用 `default`。
可用角色:
...

第二类是内置 role 描述。

相关 Tool Parameter Description / built-in roles

英文原文:

default: {
Default agent.
}

explorer: {
Use `explorer` for specific codebase questions.
Explorers are fast and authoritative.
They must be used to ask specific, well-scoped questions on the codebase.
Rules:
- In order to avoid redundant work, you should avoid exploring the same problem that explorers have already covered.
- You are encouraged to spawn up multiple explorers in parallel when you have multiple distinct questions to ask about the codebase that can be answered independently.
- Reuse existing explorers for related questions.
}

worker: {
Use for execution and production work.
Typical tasks:
- Implement part of a feature
- Fix tests or bugs
- Split large refactors into independent chunks
Rules:
- Explicitly assign ownership of the task (files / responsibility).
- Always tell workers they are not alone in the codebase, and they should not revert the edits made by others, and they should adjust their implementation to accommodate the changes made by others.
}

中文对照:

default:默认 agent。

explorer:用于具体代码库问题。适合快速、权威、范围清晰的只读探索。
规则包括避免重复探索、可并行探索多个独立问题、复用已有 explorer。

worker:用于执行和生产工作,例如实现功能、修测试/bug、拆分重构。
规则包括明确文件/职责所有权,并提醒 worker 不要回滚其他人的并行改动。

这就是文档里“它不是文件锁机制,而是让模型主动做协作约束”的来源:父 agent 看到 worker 描述后,应该在 message 里写清楚文件/职责边界。

第三类是用户自定义 role 描述。配置里可以写:

[agents.researcher]
description = "Research role"
config_file = "./agents/researcher.toml"
nickname_candidates = ["Hypatia", "Noether"]

这会让 researcher 出现在 agent_type 的 Available roles 里。用户自定义 role 排在内置 role 前面;同名时,用户定义覆盖内置同名描述。

第四类是 role 锁定配置提示。如果 role config 里写了 modelmodel_reasoning_effortservice_tier,schema 会额外提示模型这些设置不能被 spawn 参数覆盖。

相关 Tool Parameter Description / role 锁定设置说明

英文原文:

- This role's model is set to `<model>` and cannot be changed.
- This role's reasoning effort is set to `<reasoning_effort>` and cannot be changed.
- This role's service tier is set to `<service_tier>`. If it is supported by the resolved model, it takes precedence over a valid spawn request service tier.

中文对照:

- 这个 role 的模型被设置为 `<model>`,不能修改。
- 这个 role 的 reasoning effort 被设置为 `<reasoning_effort>`,不能修改。
- 这个 role 的 service tier 被设置为 `<service_tier>`。如果最终模型支持它,它会优先于 spawn 请求里的 service tier。

第五类才是 child 启动时真正叠加的 role config。比如 role config 文件可以写 developer_instructionsmodelmodel_reasoning_effort、skills 或其他配置项。当前内置状态是:

explorer:
  有内置 explorer.toml,但内容为空。
  当前不会额外注入 developer_instructions,也不会改模型/权限。

worker:
  没有内置 config_file。
  当前也不会额外注入 developer_instructions。

所以内置 explorerworker 的差异,主要发生在 spawn_agent.agent_type schema 的角色描述层,而不是 child 自动收到两套不同隐藏 system prompt。

还可以用配置隐藏这部分 metadata:

[features.multi_agent_v2]
hide_spawn_agent_metadata = true

开启后,agent_typemodelreasoning_effortservice_tier 会从 spawn_agent schema 中移除,模型不能显式选择这些 spawn metadata。

场景三:usage hint 是怎么影响模型是否 spawn 的

agent_type 说明的是“如果要 spawn,选什么角色”。另一个问题是“什么时候应该 spawn”。

MultiAgent V2 里有三种 usage hint,注入位置不同。

当前默认配置是:

usage_hint_enabled = true
usage_hint_text = None
root_agent_usage_hint_text = None
subagent_usage_hint_text = None

也就是说,root_agent_usage_hint_textsubagent_usage_hint_text 不是内置固定文本,需要用户或部署方自己配置。本机当前 /home/tlinux/.codex/config.toml 里也没有匹配到这两个字段。

配置方式:

[features.multi_agent_v2]
enabled = true

# 追加到 spawn_agent 的 Tool Description 里,影响“什么时候该 spawn”。
usage_hint_text = "Only delegate work when it can run independently from your next local step."

# 作为 developer message 注入 root/user 入口会话。
root_agent_usage_hint_text = "You are the coordinating root agent. Keep ownership of the critical path and delegate only independent subtasks."

# 作为 developer message 注入 ThreadSpawn subagent。
subagent_usage_hint_text = "You are a delegated subagent. Complete the assigned task, report concise findings, and avoid spawning more agents unless necessary."

三条路径不要混淆:

usage_hint_text:
  追加到 spawn_agent Tool Description。
  父 agent 在读工具描述时看到它。

root_agent_usage_hint_text:
  作为 developer message 注入 root/user session。
  不改变 tool schema。

subagent_usage_hint_text:
  作为 developer message 注入 ThreadSpawn child session。
  不改变 tool schema。

相关 Runtime Prompt / root/subagent usage hint 注入规则

英文原文:

if !Feature::MultiAgentV2:
  None

SessionSource::SubAgent(SubAgentSource::ThreadSpawn { .. }):
  multi_agent_v2.subagent_usage_hint_text

SessionSource::Cli | VSCode | Exec | Mcp | Custom | Unknown:
  multi_agent_v2.root_agent_usage_hint_text

SessionSource::Internal(_) | SessionSource::SubAgent(_):
  None

中文对照:

如果 MultiAgentV2 没开启:
  不注入

如果当前会话是 ThreadSpawn subagent:
  注入 subagent_usage_hint_text

如果当前会话是 root/user 入口:
  注入 root_agent_usage_hint_text

如果是 Internal 或其他 SubAgent:
  不注入

注意旧版 V1 曾经有更强的默认提示:

相关 Tool Description / legacy V1 usage hint

英文原文:

Only use `spawn_agent` if and only if the user explicitly asks for sub-agents, delegation, or parallel agent work.
Requests for depth, thoroughness, research, investigation, or detailed codebase analysis do not count as permission to spawn.

中文对照:

只有当用户明确要求 sub-agents、委派或并行 agent 工作时,才使用 `spawn_agent`。
要求深入、彻底、研究、调查或详细代码库分析,并不等于授权 spawn。

这段是 V1 默认 usage hint,不应该误认为 V2 所有会话固定存在。V2 是否有类似强约束,要看 usage_hint_text 或 developer instructions 是否配置。

场景四:子 agent 启动时到底继承什么

用户可能会问:

spawn 出来的 child,是不是直接共用主 agent 的系统提示词和历史 message list?

答案是:不共享同一个 message list。spawn_agent 更像新开一个同事窗口,然后交给它三包材料:

第一包:工作规则
  base_instructions.text、developer instructions、permissions、skills/plugins/apps 说明等。

第二包:工作现场
  cwd、模型/provider、reasoning、sandbox/approval policy、shell 环境、工具集合等。

第三包:前文材料
  fork_turns 决定带多少父会话历史快照。

base_instructions.text 是什么

base_instructions.text 是 Responses API 请求里的基础 instructions 字段。它对应“你是 Codex,应该怎么作为编码代理工作”的那层文本。

相关 Base Instructions / 默认 instructions 字段开头

英文原文:

You are a coding agent running in the Codex CLI, a terminal-based coding assistant. Codex CLI is an open source project led by OpenAI. You are expected to be precise, safe, and helpful.

Your capabilities:

- Receive user prompts and other context provided by the harness, such as files in the workspace.
- Communicate with the user by streaming thinking & responses, and by making & updating plans.
- Emit function calls to run terminal commands and apply patches. Depending on how this specific run is configured, you can request that these function calls be escalated to the user for approval before running. More on this in the "Sandbox and approvals" section.

中文对照:

你是运行在 Codex CLI 中的编码 agent,Codex CLI 是一个基于终端的编码助手。Codex CLI 是 OpenAI 主导的开源项目。你应当准确、安全、有帮助。

你的能力:

- 接收用户提示,以及运行环境提供的其他上下文,例如工作区文件。
- 通过流式 thinking、回复,以及创建/更新计划与用户沟通。
- 发出函数调用来运行终端命令和应用补丁。根据当前运行配置,你可以在执行前请求用户批准升级权限。更多说明见 “Sandbox and approvals” 部分。

父 agent 创建 child 时,会复制当前已解析完成的 base_instructions.text

相关 Implementation Note / 基础指令复制

英文原文:

config.base_instructions = Some(base_instructions.text.clone())

中文对照:

把当前基础指令文本复制到 child agent 配置里。

这不会让底层模型失去泛化能力,但会把产品边界收窄到 Codex coding agent:

不是:
  每个 child 都可以拥有完全不同的 system identity。

而是:
  同一个 Codex coding-agent 外壳
  + agent_type / role description 做分工
  + spawn_agent.message 指定任务
  + skills / AGENTS.md / developer instructions 补充工作规范
  + fork_turns 提供必要前文

对 Codex 这种代码仓库协作工具来说,这个设计牺牲了一部分“任意角色平台”的自由度,换来的是所有 child 默认遵守同一套文件修改、权限、sandbox、AGENTS.md、工具使用和最终汇报规则。

AGENTS.md、skills、tools、权限和目录

AGENTS.md 不是 base_instructions.text 的一部分。默认 base instructions 只说明 AGENTS.md 的作用域和优先级;具体 AGENTS.md 内容会作为 contextual user fragment 注入。

相关 Context Fragment / AGENTS.md 内容注入格式

英文原文:

# AGENTS.md instructions for <directory>

<INSTRUCTIONS>
...
</INSTRUCTIONS>

中文对照:

# <directory> 的 AGENTS.md 指令

<INSTRUCTIONS>
...
</INSTRUCTIONS>

这段是 user 角色上下文片段。来源包括 $CODEX_HOME 下的全局 AGENTS 指令,以及项目内从根目录到 cwd 路径上适用的 AGENTS.md。

skills 和 tools 不是复制一份静态列表,而是用 child 的有效配置重新构建:

父 agent 当前有效配置
  -> 复制给 child
  -> 按 agent_type 叠加 role 配置
  -> child 新建自己的 turn
  -> 根据 child 配置重新加载 skills/plugins/tools

通常看起来“全部继承”,是因为 child 从父 agent 当前有效配置开始,cwd、权限、模型、provider、sandbox、shell 环境等都会带过去。但它不是绝对一样:role、feature flag、MCP、插件、模型能力和配置都可能改变最终工具列表。

fork_turns 决定带多少父历史

fork_turns 不是复制进程,也不是父子共享内存。它只决定 child 启动时从父 rollout/history 拷贝多少前文。

相关 Tool Parameter Description / fork_turns

英文原文:

Optional number of turns to fork. Defaults to `all`. Use `none`, `all`, or a positive
integer string such as `3` to fork only the most recent turns.

中文对照:

可选的 fork turn 数。默认是 `all`。可以使用 `none`、`all`,或正整数字符串,
例如 `3` 表示只 fork 最近几个 turn。

含义:

fork_turns="none":
  child 不带父会话前文,只收到 spawn_agent.message。

fork_turns="3":
  child 带最近 3 个工作轮次。

fork_turns="all":
  child 带可复制的完整父会话历史。

如果 fork_turns="all",当前实现要求 child 完整继承父 agent type、model 和 reasoning effort,因此不能同时指定 agent_typemodelreasoning_effort。想指定 explorerworker,通常用 fork_turns="3"fork_turns="none" 这类非 full-history fork。

启动后,父子不共享 message list:

父 agent /root
  自己的 history、工具调用、mailbox、active task。

child /root/memory_scan
  自己的 history、工具调用、mailbox、active task。

两者之间
  通过 send_message / followup_task / final notification 交换消息。

场景五:task_name、task、shell 并发到底是不是一回事

在多 agent 代码里,task 有两个容易混淆的含义。

第一种是模型可见的 spawn_agent.task_name

task_name = "memory_scan"
  -> 生成 child canonical path /root/memory_scan

第二种是 Codex runtime 内部的 SessionTask

RegularTask:
  普通用户 turn 或 child agent turn,会调用模型并处理工具调用。

ReviewTask:
  /review 专用工作流,会启动隔离 reviewer 子会话。

CompactTask:
  上下文压缩流程。

UserShellTask:
  用户直接触发 shell 命令时的流程。

相关 Implementation Note / task 的实现边界

英文原文:

Async task that drives a Session turn.

Implementations encapsulate a specific Codex workflow (regular chat,
reviews, ghost snapshots, etc.). Each task instance is owned by a
Session and executed on a background Tokio task.

中文对照:

驱动一个 Session turn 的异步任务。

不同实现封装不同的 Codex 工作流,例如普通聊天、review、快照等。每个 task 实例属于一个 Session,并在后台 Tokio task 中执行。

所以链路更准确地写成:

父模型生成 spawn_agent tool call
  -> handler 创建 child thread/session
  -> child 获得 task_name/agent_path
  -> handler 给 child 发送 initial message
  -> child session 因为收到输入而启动 RegularTask

shell 并发又是第三件事。模型在一个 RegularTask 里可以发出多个 shell/exec tool call;这些 tool call 可以并发执行,但它们不是新的 SessionTask

相关 Implementation Note / shell 工具支持并行

英文原文:

fn supports_parallel_tool_calls(&self) -> bool {
    true
}

中文对照:

fn supports_parallel_tool_calls(&self) -> bool {
    true
}

三层并发边界是:

同一个 Session:
  通常只有一个主活动 turn/task,避免两个模型 turn 同时改同一份会话状态。

同一个 turn:
  多个支持并行的 tool call 可以同时跑。

多个 agent thread/session:
  每个 agent 各自跑自己的 RegularTask 和工具调用,这就是 MultiAgent V2 的并行。

场景六:父 agent 和 child 怎么通信

child 已经创建后,父 agent 可能要补充一句:

顺便确认 compact 之后原始 rollout 是否还存在。

如果只是排队消息,不主动启动目标 turn,用 send_message

相关 Tool Schema / send_message

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: send_message
description:
  Send a message to an existing agent. The message will be delivered promptly.
  Does not trigger a new turn.

required: ["target", "message"]

parameters:
  target:
    description: Relative or canonical task name to message (from spawn_agent).
  message:
    description: Message text to queue on the target agent.

中文对照:

名称:send_message
描述:
  给已有 agent 发送消息。消息会及时投递。
  但不会触发新的 turn。

必填字段:["target", "message"]

参数:
  target:要发送消息的相对或规范任务名。
  message:要排队到目标 agent 的消息文本。

如果要排队消息并让目标 agent 继续跑一轮,用 followup_task

相关 Tool Schema / followup_task

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: followup_task
description:
  Send a message to an existing non-root target agent and trigger a turn in that target.
  If the target is currently mid-turn, the message is queued and will be used to start the
  target's next turn, after the current turn completes.

required: ["target", "message"]

parameters:
  target:
    description: Agent id or canonical task name to message (from spawn_agent).
  message:
    description: Message text to send to the target agent.

中文对照:

名称:followup_task
描述:
  给已有的非 root 目标 agent 发送消息,并在目标 agent 中触发一个 turn。
  如果目标正在 turn 中,这条消息会排队,并在当前 turn 完成后用于启动目标下一轮。

必填字段:["target", "message"]

参数:
  target:目标 agent id 或规范任务名。
  message:发送给目标 agent 的消息文本。

两者内部都是 inter-agent communication envelope:

相关 Implementation Message / agent 间通信进入模型时的形态

英文原文:

{
  "author": "/root",
  "recipient": "/root/memory_scan",
  "other_recipients": [],
  "content": "Please also check whether compacted sessions keep original rollout items.",
  "trigger_turn": true
}

中文对照:

{
  "author": "/root",
  "recipient": "/root/memory_scan",
  "other_recipients": [],
  "content": "也请确认压缩后的 session 是否保留原始 rollout items。",
  "trigger_turn": true
}

差别只有 trigger_turn

send_message:
  mailbox mail,trigger_turn=false。
  如果目标 idle,不主动启动 turn。

followup_task:
  mailbox mail,trigger_turn=true。
  如果目标 idle,尝试启动 RegularTask。

mailbox delivery 点怎么确定

mailbox 不是“来一封就马上改写正在生成的上下文”。Codex 用 turn 内状态机决定什么时候把 mailbox 邮件 drain 进模型输入。

相关 Source Code Comment / mailbox delivery 状态机

英文原文:

Whether mailbox deliveries should still be folded into the current turn.

State machine:
- A turn starts in `CurrentTurn`, so queued child mail can join the next
  model request for that turn.
- After user-visible terminal output is recorded, we switch to `NextTurn`
  to leave late child mail queued instead of extending an already shown
  answer.
- If the same task later gets explicit same-turn work again (a steered user
  prompt or a tool call after an untagged preamble), we reopen `CurrentTurn`
  so that pending child mail is drained into that follow-up request.

中文对照:

mailbox 投递是否还应该并入当前 turn。

状态机:
- turn 开始时处于 `CurrentTurn`,已排队的子 agent 邮件可以加入本 turn 的下一次模型请求。
- 记录用户可见的终端输出后,切到 `NextTurn`,让迟到邮件继续排队,而不是扩展一个已经展示的回答。
- 如果同一任务后面又出现明确的同 turn 工作,例如用户 steer 输入或模型发起 tool call,就重新打开 `CurrentTurn`,让待处理邮件进入后续请求。

实际投递链路:

1. 邮件进入目标 agent mailbox。
2. 目标 session 在启动 turn、或当前 turn 下一次模型请求前调用 get_pending_input。
3. 如果 mailbox phase 是 CurrentTurn,就 drain 成 ResponseInputItem。
4. 如果 phase 是 NextTurn,就留到下一轮。

这就是为什么 send_message 不是“直接注入当前正在生成的文本”,followup_task 也不是“强行打断目标 turn”。

场景七:父 agent 怎么等结果,以及完成通知是谁生成的

父 agent 派出多个 child 后,可以继续做本地工作。只有下一步依赖 child 结果时,才应该 wait_agent

相关 Tool Schema / wait_agent

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: wait_agent
description:
  Wait for a mailbox update from any live agent, including queued messages and final-status
  notifications. Does not return the content; returns either a summary of which agents have
  updates (if any), or a timeout summary if no mailbox update arrives before the deadline.

parameters:
  timeout_ms:
    description:
      Optional timeout in milliseconds. Defaults to <default>, min <min>, max <max>.

中文对照:

名称:wait_agent
描述:
  等待任何 live agent 的 mailbox 更新,包括排队消息和最终状态通知。
  它不直接返回内容;只返回哪些 agent 有更新的摘要,或者超时摘要。

参数:
  timeout_ms:可选超时时间,单位毫秒。

默认超时配置:

min_wait_timeout_ms     = 10_000
default_wait_timeout_ms = 30_000
max_wait_timeout_ms     = 3_600_000

child 完成时,父 agent 会收到程序组装的通知:

相关 Context Fragment / subagent notification 格式

英文原文:

<subagent_notification>
{
  "agent_path": "<agent_reference>",
  "status": <AgentStatus>
}
</subagent_notification>

中文对照:

<subagent_notification>
{
  "agent_path": "<子 agent 路径>",
  "status": <子 agent 状态>
}
</subagent_notification>

这不是 child 模型自由写出的 XML。来源链路是:

child 完成
  -> runtime 读取 child 最终 AgentStatus
  -> 程序组装 <subagent_notification>...</subagent_notification>
  -> 作为 inter-agent communication 发给父 agent
  -> trigger_turn=false,只通知,不强行叫醒父 agent

通知片段本身按 ContextualUserFragment 定义,语义上是 user-role 上下文;在 MultiAgent V2 父子通信路径里,它通常会被放进 inter-agent communication envelope,再以 assistant/commentary 输入项投递给父 agent。关键点是:结构由程序生成,模型只消费它。

场景八:<subagents>、context diff 和 list_agents 的可见性

完整初始上下文里,Codex 会把当前可见 child 列表放进 environment_context

相关 Context Fragment / environment_context

英文原文:

<environment_context>
  <cwd>/home/tlinux/sdb/work/learn_ai/ai_agent_projects/codex</cwd>
  <shell>bash</shell>
  <current_date>2026-05-27</current_date>
  <timezone>Asia/Shanghai</timezone>
  <subagents>
    - memory_scan
    - skill_scan
  </subagents>
</environment_context>

中文对照:

<environment_context>
  <cwd>当前工作目录</cwd>
  <shell>当前 shell</shell>
  <current_date>当前日期</current_date>
  <timezone>当前时区</timezone>
  <subagents>
    当前 agent 的直接子 agent 列表
  </subagents>
</environment_context>

这段是 user 角色上下文片段,不是 system,也不是 developer。它在构建完整 initial context 时注入。

后续 turn 通常不重复发完整上下文,而是走 diff:

reference_context_item 不存在:
  注入完整 initial context,其中可能包含 <subagents>。

reference_context_item 存在:
  比较模型、权限、collaboration mode、realtime/personality、环境等变化。
  如果有变化,就追加 developer/user diff message。
  然后更新新的 reference_context_item 作为下一轮 baseline。

环境 diff 会覆盖 cwd、网络、日期、时区等变化,但当前实现明确不会把 <subagents> 放进环境 diff。因此 <subagents> 不是每轮自动刷新的 live registry。

这通常可以接受,因为父模型自己调用 spawn_agent 时会看到 tool output,child 完成时也会收到 mailbox notification。模型在正常上下文没丢时自然知道自己刚创建过哪些 agent。

风险在压缩、历史裁剪、恢复或模型遗忘后:模型可能不可靠记得当前 live agent 树。此时应该用 list_agents 获取权威状态。

相关 Tool Schema / list_agents

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: list_agents
description:
  List live agents in the current root thread tree. Optionally filter by task-path prefix.

parameters:
  path_prefix:
    description:
      Optional task-path prefix (not ending with trailing slash). Accepts the same relative or
      absolute task-path syntax.

中文对照:

名称:list_agents
描述:
  列出当前 root thread tree 中的 live agents。可选按 task-path prefix 过滤。

参数:
  path_prefix:可选 task-path 前缀,支持相对或绝对 task-path 语法。

<subagents>list_agents 的区别:

<subagents>:
  只展示当前 agent 的直接子 agent。
  只在完整环境上下文里出现,不是每轮实时刷新。

list_agents:
  默认列出同一 root thread tree 里的 live agents。
  包括兄弟分支和子孙分支。
  可用 path_prefix 过滤。

回答“list 工具能看到兄弟的 subagent 吗”:

默认 list_agents:
  能看到同一 root tree 的 live agents,包括兄弟和子孙。

list_agents(path_prefix="worker"):
  path_prefix 会相对当前 agent 路径解析。
  在 /root/researcher 中调用时,worker 会解析成 /root/researcher/worker。

list_agents(path_prefix="/root/other_branch"):
  使用 canonical path 时,可以明确查询兄弟分支或其他子树。

场景九:多个 agent 并行为什么不会把会话搞乱

创建多个 child 后,结构是:

Root Session /root
  Active task: RegularTask
  AgentControl / AgentRegistry
    /root/memory_scan      -> child Session -> child RegularTask
    /root/skills_scan      -> child Session -> child RegularTask
    /root/compaction_scan  -> child Session -> child RegularTask

会话层不会乱,因为每个 agent 都有自己的:

thread/session
history
rollout
input queue
mailbox
active task
status

文件层不保证完全不会乱。多个 agent 默认可能共享同一个 cwd 和同一个 git worktree。Codex 当前不会自动给文件加锁,也不会自动解决两个 worker 同时编辑同一文件的语义冲突。

系统降低风险的方式是:

运行时隔离:
  对话、状态、mailbox、任务生命周期分开。

路径命名:
  每个 spawned agent 有唯一 canonical path,例如 /root/memory_scan。

并发上限:
  同一用户 session 下可打开的 agent thread 数量受 max_concurrent_threads_per_session 限制。

角色提示:
  worker 描述要求父 agent 明确分配文件/职责边界,并提醒 worker 不要回滚其他人的改动。

默认配置里:

max_concurrent_threads_per_session = 4

root thread 也算一个,因此默认最多同时打开 1 个 root + 3 个 spawned agent thread。

场景十:关闭 agent,避免后台线程长期占用资源

如果父 agent 不确定当前还有哪些 agent,先 list_agents。不需要某个 agent 后,调用 close_agent

相关 Tool Schema / close_agent

类型:Tool Schema(模型可见的工具调用说明)

英文原文:

name: close_agent
description:
  Close an agent and any open descendants when they are no longer needed, and return the target
  agent's previous status before shutdown was requested. Don't keep agents open for too long if
  they are not needed anymore.

required: ["target"]

parameters:
  target:
    description: Agent id or canonical task name to close (from spawn_agent).

中文对照:

名称:close_agent
描述:
  当某个 agent 不再需要时,关闭它以及所有打开的后代 agent,并返回 shutdown 前观察到的目标状态。
  不要让不再需要的 agent 长时间保持打开。

必填字段:["target"]

参数:
  target:要关闭的 agent id 或规范任务名。

关闭会释放 registry 中的 thread 计数和路径占用。否则即使 agent 已经没用,也会继续占用并发名额。

场景十一:/review、compact、CSV worker 这些特殊 subagent 怎么看

实现里还有一些 subagent,但它们不是 MultiAgent V2 可对话 agent。

可以把它们归为“特殊工作流子会话”:

/review:
  ReviewTask 内部启动 reviewer 子会话。
  来源是 SubAgentSource::Review。
  用户不能用 spawn_agent 返回的 task_name 去 send_message/wait_agent/close_agent 管理它。

compact:
  CompactTask 或远端 compaction 路径创建压缩子流程。
  目的是重写上下文,不是让模型管理一个 live agent。

memory consolidation:
  记忆整理子会话。
  属于内部维护流程。

spawn_agents_on_csv:
  按 CSV 行创建 worker sub-agent。
  每个 worker 用 report_agent_job_result 回填结果。
  这是批处理,不是 V2 可对话 agent。

所以不需要把 /review 单独讲成一个“和 agent 并列的概念”。它只是 SubAgentSource 的另一个来源类型。本文讨论的可管理 agent,默认指 SubAgentSource::ThreadSpawn

一张图总结完整流程

用户请求并行分析
  -> 当前 root Session 启动 RegularTask
  -> 模型看到 spawn_agent / send_message / followup_task / wait_agent / close_agent / list_agents
  -> 模型根据用户意图、tool schema、usage hint 决定 spawn

spawn_agent
  -> 创建 child thread/session
  -> SessionSource = SubAgent(ThreadSpawn)
  -> 复制 base_instructions.text 和当前有效运行配置
  -> 按 agent_type 叠加 role config
  -> fork_turns 决定初始历史快照
  -> initial message 作为 inter-agent communication 发送给 child
  -> child 启动 RegularTask

运行中
  -> 父和 child 各自有独立 history/input queue/mailbox/status
  -> send_message 只排队
  -> followup_task 排队并触发目标 turn
  -> mailbox delivery phase 决定是否进入当前 turn

完成后
  -> runtime 组装 <subagent_notification>
  -> 父 agent 通过 mailbox 收到通知
  -> 父 agent 汇总、继续 followup 或 close_agent

关键区别表

概念 所在层 谁能看到 主要用途 生命周期
agent MultiAgent V2 工具层 模型可见 并行分工、通信、等待、关闭 spawn 后 live,直到完成或 close
subagent Session source 层 Codex runtime、协议元数据 标记子会话来源 跟随子 session/thread
ThreadSpawn subagent MultiAgent V2 agent 实现层 runtime 可见,部分状态进模型上下文 可管理 agent 的实际来源类型 跟随 child thread
task_name Agent path 层 模型可见 给 spawned agent 命名和寻址 agent 生命周期内有效
SessionTask Runtime 执行层 主要是 Codex 内部 管理一次 turn/workflow 随 turn/workflow 开始、取消、完成
shell/exec tool call 工具执行层 模型可见 tool call,用户可见输出 在一个 turn 内执行命令 随 tool call 或后台 process/session_id 生命周期
plan/todo item 模型输出/协作层 用户可见 表示工作拆分或进度 文本/状态,不是 runtime task

最重要的一句:

spawn_agent 创建的 agent,在实现上就是 ThreadSpawn subagent。
但不是所有 subagent 都是 MultiAgent V2 可管理 agent。
task 则是另一个 runtime 执行层概念。

管理边界和失败模式

当前实现已经有一些保护:

并发数量:
  MultiAgent V2 默认 max_concurrent_threads_per_session = 4。

路径冲突:
  同一 root tree 下 agent_path 必须唯一。

等待超时:
  wait_agent 有 min/default/max timeout,避免高频 busy polling。

关闭释放:
  close_agent 会关闭目标和后代,并释放 registry 占用。

历史控制:
  fork_turns 可以选择 none、all 或最近 N 个 turn。

上下文恢复:
  reference_context_item 缺失时会重新注入完整 initial context。
  压缩或历史裁剪后,模型可用 list_agents 找回 live agent 状态。

仍然需要模型和用户管理的边界:

不要让多个 worker 写同一批文件。
不要把紧急阻塞任务交给 child 后原地等待。
不要重复派发同一个问题。
不要忘记关闭不再需要的 agent。
不要把“详细分析”自动等同为“用户授权开 agent”。

这也解释了 Codex 在多 agent 设计上的克制。

Codex 不是不想让用户用 multi-agent,而是不想让模型自动滥用 multi-agent。它把并行能力做成一个可控 primitive,而不是默认把复杂任务自动拆成一批后台 agent。原因很现实:Codex 面对的是代码仓库和真实文件系统,过度自动并行很容易带来重复探索、并发编辑冲突、上下文污染、成本和 token 暴涨,以及用户难以判断后台到底发生了什么。

这种克制体现在几个地方:

spawn_agent 不由 runtime 自动触发:
  必须由模型在看到 tool schema、用户意图和 usage hint 后主动调用。

V2 默认 usage hint 很轻:
  root_agent_usage_hint_text 和 subagent_usage_hint_text 默认都是 None。
  是否更积极地鼓励并行,要靠用户或部署方显式配置。

并发上限很低:
  默认 max_concurrent_threads_per_session = 4,而且 root thread 也计入。

role 差异主要暴露给父 agent:
  explorer / worker 的主要作用是指导父 agent 如何派工。
  内置 explorer / worker 当前不会自动给 child 换一套强隐藏系统提示词。

child 仍然是 Codex coding agent:
  base_instructions.text 仍然是同一套 Codex 编码代理基础身份。
  agent_type、message、skills、AGENTS.md 和 fork_turns 负责细化任务,而不是把 child 变成任意角色容器。

通信和恢复都要求显式管理:
  send_message 不触发 turn。
  followup_task 才触发目标继续工作。
  wait_agent / list_agents / close_agent 都需要模型按需调用。

所以更准确的结论是:

Codex 开放了多 agent 能力,但默认刹车很重。
它更偏向给高级用户和模型一个受控并发工具,
而不是把 multi-agent 做成复杂任务的默认自动策略。

Codex 多 agent 系统的本质是:

runtime 提供独立 thread/session、mailbox、registry 和任务生命周期;
tool schema 把并行能力暴露给模型;
模型根据用户目标、工具说明、usage hint 和上下文决定如何拆分、通信和收尾。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐