跳至主要內容

Codex Multi-agent(2026)

Kevin 吴嘉文大约 5 分钟知识笔记LLMAgentScaling laws

Codex Multi-Agent

它本质上是在原有的 Codex Thread / Session / Agent Loop 之上,加了一层 Agent orchestration / control plane ,让一个 Agent 可以创建和控制其他独立的 Codex Thread。

从 Parent Agent 看,Subagent 首先表现为一个 Tool

Codex 给模型暴露了 multi-agent tools,例如当前代码中的:

  • spawn_agent
  • send_input / V2 的 send_message
  • wait_agent
  • list_agents
  • close_agent
  • resume_agent
  • 以及 V2 的 follow-up / interrupt 等能力

Agent 相关工具

SpawnAgent

spawn agent tool 对应的 description 和 参数(Options):

# `spawn_agent_tool` 使用的 description
f"""
        {agent_role_guidance}
        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.
{inherited_model_guidance}
Only call this tool for a concrete, bounded subtask that can run independently alongside useful local work; otherwise continue locally.
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.

Note that passing `fork_turns="none"` will not pass any surrounding context to the spawned subagent, which may cause the agent to lack the context it needs to complete its task, whereas `fork_turns="all"` will provide the subagent with all surrounding context."""
    

需要提供一下参数:

pub struct SpawnAgentToolOptions {
    pub available_models: Vec<ModelPreset>,
    pub agent_type_description: String,
    pub expose_agent_type: bool,
    pub hide_agent_type_model_reasoning: bool,
    pub expose_spawn_agent_model_overrides: bool,
    pub multi_agent_version: MultiAgentVersion,
    pub usage_hint_text: Option<String>,
}

available_models 类似:

Available model overrides (optional; inherited parent model is preferred):
- `gpt-5.6`: Best model for complex coding and reasoning. Reasoning efforts: low, medium (default), high. Service tiers: default, fast.
- `gpt-5.6-mini`: Fast model for lightweight tasks. Reasoning efforts: low (default), medium. Service tiers: default.

agent_type_description 类似:

Available 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.
}

usage_hint_text 类似:

usage_hint_text = """
Prefer spawning agents for independent research tasks.
Avoid spawning agents for trivial sequential work.
"""

SendMessage

"""
Send a message to an existing agent.

The message is queued and delivered to the target agent promptly.

Important:
- This is normal inter-agent communication.
- It does NOT start a new turn for an idle target agent.
- Use a relative task name when the target is addressable from the
  current agent, otherwise use its canonical task path.
"""

参数

send_message(
    target: str,
    message: str,
)

FollowupTask

send_message 发送到 queue 中,但这个 tool 会直接 trigger 一个 task。

"""
Send a follow-up task to an existing non-root agent.

If the target agent is idle:
    start a new turn.

If the target agent is currently running:
    deliver the task at an appropriate message boundary,
    or after its pending tool call finishes.

Use this when the message represents additional work,
rather than ordinary communication.
"""

参数

followup_task(
    target: str,
    message: str,
)

WaitAgent

"""
Wait for activity in the multi-agent mailbox.

The wait may finish when:
- an agent sends a message;
- an agent produces a final-status notification;
- new user input is steered into the current turn;
- the timeout expires.

The tool does NOT return the actual agent message content.
It only reports that relevant activity occurred.
"""

参数:

wait_agent(
    timeout_ms: int | None = None,
)

WaitAgentToolOptions

"""
Wait for a mailbox update from any live agent, including queued messages
and final-status notifications.

The wait also ends early when new user input is steered into the active turn.

Does not return the content; returns either:
- a summary of which agents have updates,
- an interruption summary for steered input,
- or a timeout summary if no activity arrives before the deadline.
"""

multi-agent 的额外 usage guidance 里还有专门针对 wait_agent 的指令:

Call wait_agent very sparingly.
Only call wait_agent when you need the result immediately for the next
critical-path step and you are blocked until it returns.

Do not redo delegated subagent tasks yourself...

While the subagent is running in the background,
do meaningful non-overlapping work immediately.

Do not repeatedly wait by reflex.

等待的机制类似消息队列:

async fn wait_for_activity(
    activity_rx,
    pending_activity,
    deadline,
) {
    // 已经有未处理事件就立即返回
    if let Some(activity) = pending_activity {
        return match activity {
            Mailbox => MailboxActivity,
            Steer   => Steered,
        };
    }

    // 没有事件时异步挂起
    match timeout_at(
        deadline,
        activity_rx.changed()
    ).await {
        activity => ...,
        timeout  => TimedOut,
    }
}

ListAgents

"""
List live agents belonging to the current root agent tree.

Optionally restrict results to agents whose canonical
task path starts with a specified path prefix.
"""

参数

list_agents(
    path_prefix: str | None = None,
)

InterruptAgent

"""
Interrupt the target agent's current turn.

If the agent is currently working, stop that turn.

The agent itself is not destroyed or closed:
it remains available for future messages and follow-up tasks.

Return the status that the agent had before interruption.
"""

参数

interrupt_agent(
    target: str,
)

AgentControl

  • 多个 agent 之间实时通信实现方式,通过本地文件状态共享?
  • 一个 agent 使用一个 thread 有局限性,explore 是否有其他优雅的方式。

AgentControl 管理所有 Agent,如共享信息给所有 subagent


spawn_agent_tool 等工具已经在输入的 tools 列表中

模型返回 tool call,Toolrouter 找到 handle_spawn_agent,任务交到 SpawnHandler

# 流程伪代码
async def handle_spawn_agent(parent, args):

    # 1. Parse intent
    fork_mode = parse_fork_mode(args.fork_turns)

    # 2. Derive child runtime configuration
    config = build_child_config(parent)

    apply_role(config, args.agent_type)
    apply_model(config, args.model)

    # 3. Build identity / topology
    source = ThreadSpawnSource(
        parent_thread_id=parent.thread_id,
        depth=parent.depth + 1,
        task_name=args.task_name,
    )

    child_path = source.agent_path

    # 4. Build agent-to-agent task message
    message = InterAgentCommunication(
        sender=parent.agent_path,
        receiver=child_path,
        content=args.message,
        trigger_turn=True,
    )

    # 5. Runtime control plane
    return await agent_control.spawn(
        config=config,
        source=source,
        initial_message=message,
        fork_mode=fork_mode,
    )

其中

# 伪代码
async def spawn(...):

    check_execution_capacity()

    reservation = reserve_spawn_slot()

    inheritance = derive_runtime_inheritance(parent)

    if fork_mode:
        child = await spawn_forked_thread(...)
    else:
        child = await spawn_new_thread(...)

    reservation.commit(child.id)

    register_agent(child)
    persist_spawn_edge(parent, child)

    await send_initial_message(
        child,
        trigger_turn=True,
    )

    return child

config 的内容大致为:

Config(
    # ----- Model -----
    model="gpt-5.6-codex",
    model_provider_id="openai",
    model_provider=ModelProviderInfo(...),

    model_context_window=200_000,
    model_auto_compact_token_limit=160_000,

    model_reasoning_effort="high",
    model_reasoning_summary="auto",
    service_tier="default",
    personality=None,

    # ----- Instructions -----
    base_instructions="""
        You are Codex...
        ...
    """,

    developer_instructions="""
        You are a subagent...
        ...
    """,

    include_permissions_instructions=True,
    include_apps_instructions=True,
    include_collaboration_mode_instructions=True,
    include_skill_instructions=True,
    include_environment_context=True,

    # ----- Execution -----
    cwd="/repo/project",

    workspace_roots=[
        "/repo/project"
    ],

    # ----- Permissions -----
    permissions=Permissions(
        approval_policy="on-request",
        permission_profile=...
    ),

    approvals_reviewer=...,

    # ----- MCP -----
    mcp_servers={
        "github": McpServerConfig(...),
        "database": McpServerConfig(...)
    },

    # ----- Skills / Orchestration -----
    orchestrator_skills_enabled=True,
    orchestrator_mcp_enabled=True,
    skill_max_context_tokens=...,

    # ----- Multi-agent -----
    agents_enabled=True,
    agent_max_threads=6,
    agent_default_subagent_reasoning_effort="medium",

    agent_roles={
        "explorer": ...,
        "worker": ...,
        "reviewer": ...
    },

    # ----- Persistence / context management -----
    history=...,
    ephemeral=False,
    tool_output_token_limit=...,

    # ----- Other global/runtime config -----
    config_layer_stack=...,
    codex_home="~/.codex",
    ...
)