> ## Documentation Index
> Fetch the complete documentation index at: https://oma-codex-339-workspace-permissions.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 多智能体编排

> 在单个会话中协调多个智能体。

"Multiagent orchestration"（多智能体编排）允许一个智能体与其他智能体协调以完成复杂的工作。各智能体可以在各自隔离的上下文中并行运行，这有助于提高输出质量，同时也能缩短完成时间。

不确定多智能体设置是否适合您的问题？请参阅何时使用多智能体系统（以及何时不使用）。

<Note>
  托管智能体 API 请求需要 `managed-agents-2026-04-01` Beta 请求头，但记忆存储端点除外，它们使用 `agent-memory-2026-07-22`。SDK 会自动设置正确的 Beta 请求头。请参阅[Beta 请求头](/docs/zh/api/versioning-beta)。
</Note>

## 工作原理

所有智能体共享同一个沙箱、文件系统和[密钥库凭据](/docs/zh/vaults)，但每个智能体都在自己的**会话线程**（session thread）中运行，这是一个上下文隔离的事件流，拥有自己的对话历史。协调器在**主线程**（primary thread）中报告活动（主线程与会话级[事件流](/docs/zh/events-and-streaming)相同）；当协调器委派工作时，会在运行时生成额外的线程。

线程是持久化的：协调器可以向之前调用过的智能体发送后续消息，该智能体会保留其之前所有轮次的内容。

每个智能体使用自己的配置：模型、系统提示、工具、MCP 服务器和技能。会话级[智能体配置覆盖](/docs/zh/sessions#override-agent-configuration-for-a-session)是例外情况；这些覆盖适用于协调器及其 `self` 副本。工具、MCP 服务器和上下文不会共享。

### 适合委派的任务

多智能体协调最适合需要跨多个领域开展工作的复杂任务，或者由多个范围明确的子任务共同构成总体目标的场景。

效果良好的模式：

* **并行化：** 同时分发独立的子任务（搜索多个来源、分析不同的文件），然后由协调器综合结果。
* **专业化：** 将任务路由到具有特定领域系统提示和工具的智能体，例如安全智能体或文档智能体，而不是让单个智能体承载所有能力。
* **升级处理：** 针对部分复杂子任务，咨询能力更强的智能体或模型。

## 配置协调器

在[定义智能体](/docs/zh/agent-setup)时，设置 `multiagent` 以声明协调器可以委派任务的智能体名单：

<CodeGroup defaultLanguage="CLI">
  ```bash cURL theme={null}
  coordinator=$(curl -fsS http://localhost:38080/v1/agents \
    -H "x-api-key: $OMA_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "name": "Engineering Lead",
    "model": "claude-opus-5",
    "system": "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    "tools": [
      {
        "type": "agent_toolset_20260401"
      }
    ],
    "multiagent": {
      "type": "coordinator",
      "agents": [
        {"type": "agent", "id": "$REVIEWER_AGENT_ID"},
        {"type": "agent", "id": "$TEST_WRITER_AGENT_ID"}
      ]
    }
  }
  EOF
  )
  ```

  ```bash CLI theme={null}
  ant beta:agents create <<YAML
  name: Engineering Lead
  model: claude-opus-5
  system: You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.
  tools:
    - type: agent_toolset_20260401
  multiagent:
    type: coordinator
    agents:
      - type: agent
        id: $REVIEWER_AGENT_ID
      - type: agent
        id: $TEST_WRITER_AGENT_ID
  YAML
  ```

  ```python Python theme={null}
  coordinator = client.beta.agents.create(
      name="Engineering Lead",
      model="claude-opus-5",
      system="You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
      tools=[
          {"type": "agent_toolset_20260401"},
      ],
      multiagent={
          "type": "coordinator",
          "agents": [
              {"type": "agent", "id": reviewer_agent.id},
              {"type": "agent", "id": test_writer_agent.id},
          ],
      },
  )
  ```

  ```typescript TypeScript theme={null}
  const coordinator = await client.beta.agents.create({
    name: "Engineering Lead",
    model: "claude-opus-5",
    system:
      "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    tools: [{ type: "agent_toolset_20260401" }],
    multiagent: {
      type: "coordinator",
      agents: [
        { type: "agent", id: reviewerAgent.id },
        { type: "agent", id: testWriterAgent.id },
      ],
    },
  });
  ```

  ```csharp C# theme={null}
  var coordinator = await client.Beta.Agents.Create(new()
  {
      Name = "Engineering Lead",
      Model = BetaManagedAgentsModel.ClaudeOpus5,
      System = "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
      Tools =
      [
          new BetaManagedAgentsAgentToolset20260401Params
          {
              Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
          },
      ],
      Multiagent = new BetaManagedAgentsMultiagentParams
      {
          Type = BetaManagedAgentsMultiagentParamsType.Coordinator,
          Agents = [reviewerAgent.ID, testWriterAgent.ID],
      },
  });
  ```

  ```go Go theme={null}
  coordinator, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
      Name:   "Engineering Lead",
      Model:  anthropic.BetaManagedAgentsModelConfigParams{ID: anthropic.BetaManagedAgentsModelClaudeOpus5},
      System: anthropic.String("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent."),
      Tools: []anthropic.BetaAgentNewParamsToolUnion{{
          OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
              Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
          },
      }},
      Multiagent: anthropic.BetaManagedAgentsMultiagentParams{
          Type: anthropic.BetaManagedAgentsMultiagentParamsTypeCoordinator,
          Agents: []anthropic.BetaManagedAgentsMultiagentRosterEntryParamsUnion{
              {OfString: anthropic.String(reviewerAgent.ID)},
              {OfString: anthropic.String(testWriterAgent.ID)},
          },
      },
  })
  if err != nil {
      panic(err)
  }
  ```

  ```java Java theme={null}
  var coordinator = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("Engineering Lead")
          .model(BetaManagedAgentsModel.CLAUDE_OPUS_5)
          .system("You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.")
          .addTool(
              BetaManagedAgentsAgentToolset20260401Params.builder()
                  .type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
                  .build()
          )
          .multiagent(BetaManagedAgentsMultiagentParams.builder()
              .type(BetaManagedAgentsMultiagentParams.Type.COORDINATOR)
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(reviewerAgent.id())
                  .build())
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(testWriterAgent.id())
                  .build())
              .build())
          .build()
  );
  ```

  ```php PHP theme={null}
  $coordinator = $client->beta->agents->create(
      name: 'Engineering Lead',
      model: 'claude-opus-5',
      system: 'You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.',
      tools: [
          ['type' => 'agent_toolset_20260401'],
      ],
      multiagent: [
          'type' => 'coordinator',
          'agents' => [
              ['type' => 'agent', 'id' => $reviewerAgent->id],
              ['type' => 'agent', 'id' => $testWriterAgent->id],
          ],
      ],
  );
  ```

  ```ruby Ruby theme={null}
  coordinator = client.beta.agents.create(
    name: "Engineering Lead",
    model: "claude-opus-5",
    system: "You coordinate engineering work. Delegate code review to the reviewer agent and test writing to the test agent.",
    tools: [
      {type: "agent_toolset_20260401"}
    ],
    multiagent: {
      type: "coordinator",
      agents: [
        {type: "agent", id: reviewer_agent.id},
        {type: "agent", id: test_writer_agent.id}
      ]
    }
  )
  ```
</CodeGroup>

`multiagent.agents` 可以接受以下任意形式：

* `{"type": "agent", "id": agent.id}` 通过 ID 引用先前创建的 `agent`。如果未指定 `version`，则该引用会固定到协调器创建时该智能体的最新版本。
* `{"type": "agent", "id": agent.id, "version": agent.version}` 固定到特定的智能体版本。
* `{"type": "self"}` 允许协调器生成自身的副本。如果会话是使用[智能体配置覆盖](/docs/zh/sessions#override-agent-configuration-for-a-session)创建的，这些覆盖也会应用于这些副本；通过 ID 引用的名单条目不受影响。
* `{"type": "advisor", "model": "<model id>"}` 为会话的主线程提供一个可在轮次中途咨询的顾问。每个名单最多包含一个顾问条目。请参阅[为会话提供顾问](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)。

协调器的配置（包括其 `multiagent.agents` 名单）会在协调器创建或更新时生成快照。被引用的智能体会固定在当时解析出的版本上，不会自动获取其定义的后续更新。如需委派给被引用智能体的更新版本，请[更新协调器](/docs/zh/agent-setup#update-an-agent)，使其名单引用该版本。

协调器只能委派给一层智能体；如果引用的智能体本身具有 `multiagent.agents` 名单，创建或更新请求将因验证错误而失败。`multiagent.agents` 中最多可列出 20 个不同的智能体，但协调器可以调用每个智能体的多个副本。

当智能体固定了推理地理位置（[智能体定义](/docs/zh/agent-setup)中的 `model.inference_geo`）时，协调器的固定值和每个名单成员的固定值必须全部设置为相同的值，或者全部不设置。不匹配的名单会被拒绝并返回 400 验证错误，无论是在保存智能体时，还是在[会话创建覆盖](/docs/zh/sessions#override-agent-configuration-for-a-session)更改任何固定值时。

### 为会话提供顾问

`multiagent.agents` 中的顾问条目为会话的主线程提供一个**顾问**（advisor）：一个可在轮次中途咨询以获取策略指导的模型，例如规划方法、摆脱困境或在完成前审查工作。该条目恰好包含两个字段，`type` 和 `model`：

```bash cURL theme={null}
curl -fsS http://localhost:38080/v1/agents \
  -H "x-api-key: $OMA_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  -H "content-type: application/json" \
  -d '{
    "name": "Backend engineer",
    "model": "claude-sonnet-5",
    "system": "You implement backend features end to end. Consult the advisor before major backend design decisions.",
    "multiagent": {
      "type": "coordinator",
      "agents": [
        {"type": "advisor", "model": "claude-opus-5"}
      ]
    }
  }'
```

一个名单最多可包含一个顾问条目，可与任何其他名单形式并存。该条目占用保留的名单名称 `anthropic.advisor`：如果名单同时列出了顾问条目和字面名称为 `anthropic.advisor` 的成员，则会被拒绝并返回 400 验证错误。在响应中，无论提交时顾问条目位于什么位置，它都会在名单中最后回显。

顾问模型必须满足最低能力要求，且智能体自身的模型能力不得高于其顾问；能力相当的模型可以配对。无效的配对会在保存智能体时被拒绝并返回 400 验证错误。有效的配对遵循顾问工具的[模型兼容性](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)表。

顾问也可作为 [消息 API 上的服务器工具](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)使用。托管智能体界面在配置和交付方式上有所不同：名单条目没有 `max_uses`、`max_tokens` 或 `caching` 字段，建议通过线程事件而非 `advisor_tool_result` 块传递。

#### 咨询的工作方式

每次咨询都作为一个由平台生成的、名为 `anthropic.advisor` 的线程运行，该线程在咨询完成时自行终止，建议会作为 `agent.thread_message_received` 事件传递到主线程。一次咨询会发出标准的线程事件，通过保留名称 `anthropic.advisor` 进行标识（线程生命周期事件将其作为 `agent_name` 携带，建议传递事件将其作为 `from_agent_name` 携带），通常按以下顺序：

1. `session.thread_created`
2. `session.thread_status_running`
3. `agent.thread_message_received`（建议内容）
4. `session.thread_status_idle`（`stop_reason: end_turn`）
5. `session.thread_status_terminated`

咨询不会发出 `agent.tool_use` 事件，会话的事件流上也不会出现 `agent.thread_message_sent` 事件，因为咨询输入是由平台组合的，而非由智能体发送。如果您列出顾问线程自身的事件，建议内容也会在那里以 `agent.thread_message_sent` 事件的形式出现。建议传递（事件 3）不保证在顾问线程的 idle 和 terminated 事件之前到达，因此不要将这些事件视为建议已传递的信号。

您的客户端能否读取建议内容取决于顾问模型的策略，这与消息 API 顾问工具上的[结果变体](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)划分相对应。在那里返回明文结果的顾问模型，在此处会将建议作为可读文本内容传递；在那里返回脱敏结果的顾问模型，在此处会在所有客户端界面上将 `[{"type": "redacted"}]` 占位符作为消息内容传递，而智能体本身仍会在服务器端读取完整的建议。在前面的示例中，`claude-opus-5` 是一个脱敏结果顾问，因此您的客户端会看到占位符，而智能体会读取完整的建议；如果您希望在事件流上可读取建议内容，请改为选择 `claude-opus-4-8` 作为顾问。顾问的思考过程永远不会显示。客户端不能自行发送 `redacted` 块；包含此类块的事件会被拒绝并返回 400 验证错误。

失败或被中断的咨询永远不会导致智能体的轮次失败：智能体会在收到咨询失败的通用通知后继续执行。咨询期间的会话级 `user.interrupt` 会终止顾问线程且不传递任何建议；带有顾问线程 `session_thread_id` 的 `user.interrupt` 仅放弃该次咨询。

#### 顾问线程

顾问不是名单智能体：它对协调器的 `list_agents` 工具不可见，无法通过 `send_to_agent` 向其发送消息，且只有会话的主线程可以咨询它。名单智能体不能咨询顾问。

顾问线程不受并发线程限制的约束。它们会出现在会话的[线程列表](/docs/zh/multiagent-orchestration#threads)中，其 `agent` 设置为与配置完全一致的顾问形式（`{"type": "advisor", "model": ...}`），`parent_thread_id` 设置为主线程。

顾问端的提示缓存是自动的；无需任何配置。咨询按顾问模型的费率计费，其令牌会出现在顾问线程的用量和会话的用量总计中。

#### 移除顾问

要移除顾问，请使用不再包含顾问条目的名单[更新智能体](/docs/zh/agent-setup#update-an-agent)。如果顾问是名单中唯一的条目，请通过设置 `"multiagent": null` 完全清空名单。

## 创建会话

创建一个引用协调器的会话。协调器会根据需要委派给其名单中的智能体。

<CodeGroup>
  ```bash cURL theme={null}
  session=$(curl -fsSL http://localhost:38080/v1/sessions \
    -H "x-api-key: $OMA_API_KEY" \
    -H "anthropic-version: 2023-06-01" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d @- <<EOF
  {
    "agent": "$COORDINATOR_ID",
    "environment_id": "$ENVIRONMENT_ID"
  }
  EOF
  )
  SESSION_ID=$(jq -r '.id' <<< "$session")
  ```

  ```bash CLI theme={null}
  ant beta:sessions create \
    --agent "$COORDINATOR_ID" \
    --environment-id "$ENVIRONMENT_ID"
  ```

  ```python Python theme={null}
  session = client.beta.sessions.create(
      agent=coordinator.id,
      environment_id=environment.id,
  )
  ```

  ```typescript TypeScript theme={null}
  const session = await client.beta.sessions.create({
    agent: coordinator.id,
    environment_id: environment.id,
  });
  ```

  ```csharp C# theme={null}
  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = coordinator.ID,
      EnvironmentID = environment.ID,
  });
  ```

  ```go Go theme={null}
  session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
      Agent: anthropic.BetaSessionNewParamsAgentUnion{
          OfString: anthropic.String(coordinator.ID),
      },
      EnvironmentID: environment.ID,
  })
  if err != nil {
      panic(err)
  }
  ```

  ```java Java theme={null}
  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(coordinator.id())
      .environmentId(environment.id())
      .build());
  ```

  ```php PHP theme={null}
  $session = $client->beta->sessions->create(
      agent: $coordinator->id,
      environmentID: $environment->id,
  );
  ```

  ```ruby Ruby theme={null}
  session = client.beta.sessions.create(
    agent: coordinator.id,
    environment_id: environment.id
  )
  ```
</CodeGroup>

## 将智能体连接到 MCP 服务器

MCP 服务器的作用域是智能体级别的（每个智能体定义声明自己的服务器和工具），而密钥库凭据的作用域是会话级别的（会话创建时传递的 `vault_ids` 适用于每个线程）。这对您的集成有两个影响：

* 要对 MCP 服务器进行身份验证，请为所有智能体使用的每个 MCP 服务器包含一个密钥库凭据。
* 要限制某个智能体的访问权限，请在其智能体定义中仅声明它所需的服务器。

会话创建时的[智能体配置覆盖](/docs/zh/sessions#override-agent-configuration-for-a-session)可以替换协调器及其 `self` 副本的 MCP 服务器。

<CodeGroup>
  ```bash cURL theme={null}
  research_agent_id=$(curl --fail-with-body -sS "$BASE/v1/agents" "${H[@]}" --data @- <<'EOF' | jq -er '.id'
  {
    "name": "researcher",
    "model": "claude-haiku-4-5",
    "mcp_servers": [{"type": "url", "name": "github", "url": "https://api.githubcopilot.com/mcp/"}],
    "tools": [{"type": "mcp_toolset", "mcp_server_name": "github"}]
  }
  EOF
  )

  coordinator_id=$(curl --fail-with-body -sS "$BASE/v1/agents" "${H[@]}" --data @- <<EOF | jq -er '.id'
  {
    "name": "coordinator",
    "model": "claude-opus-5",
    "tools": [{"type": "agent_toolset_20260401"}],
    "multiagent": {
      "type": "coordinator",
      "agents": [{"type": "agent", "id": "$research_agent_id"}]
    }
  }
  EOF
  )

  session_id=$(curl --fail-with-body -sS "$BASE/v1/sessions" "${H[@]}" --data @- <<EOF | jq -er '.id'
  {
    "agent": "$coordinator_id",
    "environment_id": "$environment_id",
    "vault_ids": ["$vault_id"]
  }
  EOF
  )
  echo "$session_id"
  ```

  ```bash CLI theme={null}
  research_agent_id=$(ant beta:agents create --transform id --raw-output <<YAML
  name: researcher
  model: claude-haiku-4-5
  mcp_servers:
    - type: url
      name: github
      url: https://api.githubcopilot.com/mcp/
  tools:
    - type: mcp_toolset
      mcp_server_name: github
  YAML
  )

  coordinator_id=$(ant beta:agents create --transform id --raw-output <<YAML
  name: coordinator
  model: claude-opus-5
  tools:
    - type: agent_toolset_20260401
  multiagent:
    type: coordinator
    agents:
      - type: agent
        id: $research_agent_id
  YAML
  )

  session_id=$(ant beta:sessions create \
    --agent "$coordinator_id" \
    --environment-id "$environment_id" \
    --vault-id "$vault_id" \
    --transform id --raw-output)
  echo "$session_id"
  ```

  ```python Python theme={null}
  research_agent = client.beta.agents.create(
      name="researcher",
      model="claude-haiku-4-5",
      mcp_servers=[
          {"type": "url", "name": "github", "url": "https://api.githubcopilot.com/mcp/"},
      ],
      tools=[{"type": "mcp_toolset", "mcp_server_name": "github"}],
  )

  coordinator = client.beta.agents.create(
      name="coordinator",
      model="claude-opus-5",
      tools=[{"type": "agent_toolset_20260401"}],
      multiagent={
          "type": "coordinator",
          "agents": [{"type": "agent", "id": research_agent.id}],
      },
  )

  session = client.beta.sessions.create(
      agent=coordinator.id,
      environment_id=environment.id,
      vault_ids=[vault.id],
  )
  print(session.id)
  ```

  ```typescript TypeScript theme={null}
  const researchAgent = await client.beta.agents.create({
    name: "researcher",
    model: "claude-haiku-4-5",
    mcp_servers: [
      { type: "url", name: "github", url: "https://api.githubcopilot.com/mcp/" },
    ],
    tools: [{ type: "mcp_toolset", mcp_server_name: "github" }],
  });

  const coordinator = await client.beta.agents.create({
    name: "coordinator",
    model: "claude-opus-5",
    tools: [{ type: "agent_toolset_20260401" }],
    multiagent: {
      type: "coordinator",
      agents: [{ type: "agent", id: researchAgent.id }],
    },
  });

  const session = await client.beta.sessions.create({
    agent: coordinator.id,
    environment_id: environment.id,
    vault_ids: [vault.id],
  });
  console.log(session.id);
  ```

  ```csharp C# theme={null}
  var researchAgent = await client.Beta.Agents.Create(new()
  {
      Name = "researcher",
      Model = BetaManagedAgentsModel.ClaudeHaiku4_5,
      McpServers =
      [
          new()
          {
              Type = BetaManagedAgentsUrlMcpServerParamsType.Url,
              Name = "github",
              Url = "https://api.githubcopilot.com/mcp/",
          },
      ],
      Tools =
      [
          new BetaManagedAgentsMcpToolsetParams
          {
              Type = BetaManagedAgentsMcpToolsetParamsType.McpToolset,
              McpServerName = "github",
          },
      ],
  });

  var coordinator = await client.Beta.Agents.Create(new()
  {
      Name = "coordinator",
      Model = BetaManagedAgentsModel.ClaudeOpus5,
      Tools =
      [
          new BetaManagedAgentsAgentToolset20260401Params
          {
              Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
          },
      ],
      Multiagent = new()
      {
          Type = BetaManagedAgentsMultiagentParamsType.Coordinator,
          Agents =
          [
              new BetaManagedAgentsAgentParams
              {
                  Type = BetaManagedAgentsAgentParamsType.Agent,
                  ID = researchAgent.ID,
              },
          ],
      },
  });

  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = coordinator.ID,
      EnvironmentID = environment.ID,
      VaultIds = [vault.ID],
  });
  Console.WriteLine(session.ID);
  ```

  ```go Go theme={null}
  researcher, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
      Name:  "researcher",
      Model: anthropic.BetaManagedAgentsModelConfigParams{ID: anthropic.BetaManagedAgentsModelClaudeHaiku4_5},
      MCPServers: []anthropic.BetaManagedAgentsURLMCPServerParams{{
          Type: anthropic.BetaManagedAgentsURLMCPServerParamsTypeURL,
          Name: "github",
          URL:  "https://api.githubcopilot.com/mcp/",
      }},
      Tools: []anthropic.BetaAgentNewParamsToolUnion{{
          OfMCPToolset: &anthropic.BetaManagedAgentsMCPToolsetParams{
              Type:          anthropic.BetaManagedAgentsMCPToolsetParamsTypeMCPToolset,
              MCPServerName: "github",
          },
      }},
  })
  if err != nil {
      panic(err)
  }

  coordinator, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
      Name:  "coordinator",
      Model: anthropic.BetaManagedAgentsModelConfigParams{ID: anthropic.BetaManagedAgentsModelClaudeOpus5},
      Tools: []anthropic.BetaAgentNewParamsToolUnion{{
          OfAgentToolset20260401: &anthropic.BetaManagedAgentsAgentToolset20260401Params{
              Type: anthropic.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
          },
      }},
      Multiagent: anthropic.BetaManagedAgentsMultiagentParams{
          Type: anthropic.BetaManagedAgentsMultiagentParamsTypeCoordinator,
          Agents: []anthropic.BetaManagedAgentsMultiagentRosterEntryParamsUnion{{
              OfBetaManagedAgentsAgents: &anthropic.BetaManagedAgentsAgentParams{
                  Type: anthropic.BetaManagedAgentsAgentParamsTypeAgent,
                  ID:   researcher.ID,
              },
          }},
      },
  })
  if err != nil {
      panic(err)
  }

  session, err := client.Beta.Sessions.New(ctx, anthropic.BetaSessionNewParams{
      Agent: anthropic.BetaSessionNewParamsAgentUnion{
          OfString: anthropic.String(coordinator.ID),
      },
      EnvironmentID: environment.ID,
      VaultIDs:      []string{vault.ID},
  })
  if err != nil {
      panic(err)
  }
  fmt.Println(session.ID)
  ```

  ```java Java theme={null}
  var researcher = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("researcher")
          .model(BetaManagedAgentsModel.CLAUDE_HAIKU_4_5)
          .addMcpServer(BetaManagedAgentsUrlMcpServerParams.builder()
              .name("github")
              .type(BetaManagedAgentsUrlMcpServerParams.Type.URL)
              .url("https://api.githubcopilot.com/mcp/")
              .build())
          .addTool(BetaManagedAgentsMcpToolsetParams.builder()
              .type(BetaManagedAgentsMcpToolsetParams.Type.MCP_TOOLSET)
              .mcpServerName("github")
              .build())
          .build()
  );

  var coordinator = client.beta().agents().create(
      AgentCreateParams.builder()
          .name("coordinator")
          .model(BetaManagedAgentsModel.CLAUDE_OPUS_5)
          .addTool(BetaManagedAgentsAgentToolset20260401Params.builder()
              .type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
              .build())
          .multiagent(BetaManagedAgentsMultiagentParams.builder()
              .type(BetaManagedAgentsMultiagentParams.Type.COORDINATOR)
              .addAgent(BetaManagedAgentsAgentParams.builder()
                  .type(BetaManagedAgentsAgentParams.Type.AGENT)
                  .id(researcher.id())
                  .build())
              .build())
          .build()
  );

  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(coordinator.id())
      .environmentId(environment.id())
      .vaultIds(List.of(vault.id()))
      .build());
  IO.println(session.id());
  ```

  ```php PHP theme={null}
  $researchAgent = $client->beta->agents->create(
      name: 'researcher',
      model: 'claude-haiku-4-5',
      mcpServers: [
          ['type' => 'url', 'name' => 'github', 'url' => 'https://api.githubcopilot.com/mcp/'],
      ],
      tools: [
          ['type' => 'mcp_toolset', 'mcp_server_name' => 'github'],
      ],
  );

  $coordinator = $client->beta->agents->create(
      name: 'coordinator',
      model: 'claude-opus-5',
      tools: [
          ['type' => 'agent_toolset_20260401'],
      ],
      multiagent: [
          'type' => 'coordinator',
          'agents' => [
              ['type' => 'agent', 'id' => $researchAgent->id],
          ],
      ],
  );

  $session = $client->beta->sessions->create(
      agent: $coordinator->id,
      environmentID: $environment->id,
      vaultIDs: [$vault->id],
  );
  echo "{$session->id}\n";
  ```

  ```ruby Ruby theme={null}
  research_agent = client.beta.agents.create(
    name: "researcher",
    model: "claude-haiku-4-5",
    mcp_servers: [
      {type: "url", name: "github", url: "https://api.githubcopilot.com/mcp/"}
    ],
    tools: [
      {type: "mcp_toolset", mcp_server_name: "github"}
    ]
  )

  coordinator = client.beta.agents.create(
    name: "coordinator",
    model: "claude-opus-5",
    tools: [
      {type: "agent_toolset_20260401"}
    ],
    multiagent: {
      type: "coordinator",
      agents: [
        {type: "agent", id: research_agent.id}
      ]
    }
  )

  session = client.beta.sessions.create(
    agent: coordinator.id,
    environment_id: environment.id,
    vault_ids: [vault.id]
  )
  puts session.id
  ```
</CodeGroup>

在此示例中，只有研究员智能体声明了 GitHub MCP 服务器，因此协调器没有访问权限。会话的 `vault_ids` 将 GitHub 凭据提供给研究员的线程。

<Tip>
  如果在声明服务器后，某个智能体的 MCP 调用身份验证失败，请确认凭据的 `mcp_server_url` 与智能体的 `mcp_servers[].url` 指向同一服务器。两个 URL 在匹配前都会进行规范化处理（协议和主机名转为小写，去除默认端口和尾部斜杠），因此主机名大小写、默认端口或尾部斜杠的差异不会阻止匹配；但不同的路径、子域名或非默认端口会导致不匹配。
</Tip>

## 线程

**会话级事件流**（`/v1/sessions/{session_id}/events/stream`）被视为**主线程**，包含所有线程中所有活动的精简视图。您不会看到子智能体的完整活动，但可以看到它们工作的开始和结束，以及工具权限请求等阻塞事件。

**会话线程**是您深入查看特定智能体活动的地方。

会话 `status` 是所有智能体活动的聚合；如果至少有一个线程处于 `running` 状态，则整个会话状态也为 `running`。

[会话预算](/docs/zh/budgets)是会话所有线程共享的单一上限。当达到上限时，各线程会独立暂停，每个线程的费用按该线程自身所使用的模型定价。

<Note>
  最多支持 25 个并发线程。协调器可以调用名单中单个智能体的多个副本，从而创建与一个 `agent` 关联的多个线程。[顾问](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)咨询线程不受此限制约束。
</Note>

<Tabs>
  <Tab title="列出线程">
    按如下方式列出与会话关联的所有线程：

    <CodeGroup>
      ```bash cURL theme={null}
      curl -fsS "http://localhost:38080/v1/sessions/$SESSION_ID/threads" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        | jq -r '.data[] | "[\(.agent.name)] \(.status)"'
      ```

      ```bash CLI theme={null}
      ant beta:sessions:threads list --session-id "$SESSION_ID"
      ```

      ```python Python theme={null}
      for thread in client.beta.sessions.threads.list(session.id):
          print(f"[{thread.agent.name}] {thread.status}")
      ```

      ```typescript TypeScript theme={null}
      for await (const thread of client.beta.sessions.threads.list(session.id)) {
        console.log(`[${thread.agent.name}] ${thread.status}`);
      }
      ```

      ```csharp C# theme={null}
      await foreach (var thread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
      {
          Console.WriteLine($"[{thread.Agent.Name}] {thread.Status}");
      }
      ```

      ```go Go theme={null}
      threads := client.Beta.Sessions.Threads.ListAutoPaging(ctx, session.ID, anthropic.BetaSessionThreadListParams{})
      for threads.Next() {
          thread := threads.Current()
          fmt.Printf("[%s] %s\n", thread.Agent.Name, thread.Status)
      }
      if err := threads.Err(); err != nil {
          panic(err)
      }
      ```

      ```java Java theme={null}
      for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
          var name = thread.agent().isAgent() ? thread.agent().asAgent().name() : "advisor";
          IO.println("[" + name + "] " + thread.status());
      }
      ```

      ```php PHP theme={null}
      foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
          echo "[{$thread->agent->name}] {$thread->status}\n";
      }
      ```

      ```ruby Ruby theme={null}
      client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
        puts "[#{thread.agent.name}] #{thread.status}"
      end
      ```
    </CodeGroup>

    完整列表包含主线程。主线程的 `parent_thread_id` 为 null。
  </Tab>

  <Tab title="中断会话线程">
    发送带有 `session_thread_id` 的 `user.interrupt` 以停止特定线程。省略 `session_thread_id` 会中断会话中所有未归档的线程，包括主线程。

    <CodeGroup>
      ```bash cURL theme={null}
      curl -fsS "http://localhost:38080/v1/sessions/$SESSION_ID/events?beta=true" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        -H "content-type: application/json" \
        -d "{\"events\": [{\"type\": \"user.interrupt\", \"session_thread_id\": \"$THREAD_ID\"}]}"
      ```

      ```bash CLI theme={null}
      ant beta:sessions:events send \
        --session-id "$SESSION_ID" \
        --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
      ```

      ```python Python theme={null}
      client.beta.sessions.events.send(
          session.id,
          events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
      )
      ```

      ```typescript TypeScript theme={null}
      await client.beta.sessions.events.send(session.id, {
        events: [{ type: "user.interrupt", session_thread_id: thread.id }],
      });
      ```

      ```csharp C# theme={null}
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserInterruptEventParams
              {
                  Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
                  SessionThreadID = thread.ID,
              },
          ],
      });
      ```

      ```go Go theme={null}
      if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
          Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
              OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
                  Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
                  SessionThreadID: anthropic.String(thread.ID),
              },
          }},
      }); err != nil {
          panic(err)
      }
      ```

      ```java Java theme={null}
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
                  .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
                  .sessionThreadId(thread.id())
                  .build())
              .build());
      ```

      ```php PHP theme={null}
      $client->beta->sessions->events->send(
          $session->id,
          events: [
              ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
          ],
      );
      ```

      ```ruby Ruby theme={null}
      client.beta.sessions.events.send_(
        session.id,
        events: [{type: "user.interrupt", session_thread_id: thread.id}]
      )
      ```
    </CodeGroup>

    对于阻塞在 `requires_action` 状态的子线程，中断会以错误工具结果（"Tool execution was interrupted before completion. Please retry."）关闭每个待处理的工具调用，并直接重新发出带有 `stop_reason: end_turn` 的 `session.thread_status_idle`；不会对模型进行采样。对于已处于 `idle` 状态的线程，中断是无操作的。
  </Tab>

  <Tab title="归档会话线程">
    当会话线程完成其工作后，可以选择将其归档。这会释放一个线程名额，以便不超过 25 个线程的限制。

    <CodeGroup>
      ```bash cURL theme={null}
      curl -fsS -X POST "http://localhost:38080/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI theme={null}
      ant beta:sessions:threads archive \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python theme={null}
      archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
      print(archived.status, archived.archived_at)
      ```

      ```typescript TypeScript theme={null}
      const archived = await client.beta.sessions.threads.archive(thread.id, {
        session_id: session.id,
      });
      console.log(archived.status, archived.archived_at);
      ```

      ```csharp C# theme={null}
      var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
      Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
      ```

      ```go Go theme={null}
      archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
          SessionID: session.ID,
      })
      if err != nil {
          panic(err)
      }
      fmt.Println(archived.Status, archived.ArchivedAt)
      ```

      ```java Java theme={null}
      var archived = client.beta().sessions().threads().archive(
          thread.id(),
          ThreadArchiveParams.builder()
              .sessionId(session.id())
              .build());
      IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
      ```

      ```php PHP theme={null}
      $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
      echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
      ```

      ```ruby Ruby theme={null}
      archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
      puts "#{archived.status} #{archived.archived_at}"
      ```
    </CodeGroup>

    只有当线程处于 `idle` 状态时，归档才会成功。停留在 `requires_action` 状态的线程视为空闲，可以直接归档；只有正在运行的线程必须先中断：

    <CodeGroup>
      ```bash cURL theme={null}
      # 中断该线程，然后将其归档
      curl -fsS "http://localhost:38080/v1/sessions/$SESSION_ID/events?beta=true" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        -H "content-type: application/json" \
        -d "{\"events\": [{\"type\": \"user.interrupt\", \"session_thread_id\": \"$THREAD_ID\"}]}"

      curl -fsS -X POST "http://localhost:38080/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01"
      ```

      ```bash CLI theme={null}
      ant beta:sessions:events send \
        --session-id "$SESSION_ID" \
        --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"

      ant beta:sessions:threads archive \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python theme={null}
      client.beta.sessions.events.send(
          session.id,
          events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
      )
      archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
      print(archived.status, archived.archived_at)
      ```

      ```typescript TypeScript theme={null}
      await client.beta.sessions.events.send(session.id, {
        events: [{ type: "user.interrupt", session_thread_id: thread.id }],
      });
      const archived = await client.beta.sessions.threads.archive(thread.id, {
        session_id: session.id,
      });
      console.log(archived.status, archived.archived_at);
      ```

      ```csharp C# theme={null}
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserInterruptEventParams
              {
                  Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
                  SessionThreadID = thread.ID,
              },
          ],
      });
      archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
      Console.WriteLine($"{archived.Status} {archived.ArchivedAt}");
      ```

      ```go Go theme={null}
      if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
          Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
              OfUserInterrupt: &anthropic.BetaManagedAgentsUserInterruptEventParams{
                  Type:            anthropic.BetaManagedAgentsUserInterruptEventParamsTypeUserInterrupt,
                  SessionThreadID: anthropic.String(thread.ID),
              },
          }},
      }); err != nil {
          panic(err)
      }

      archived, err := client.Beta.Sessions.Threads.Archive(ctx, thread.ID, anthropic.BetaSessionThreadArchiveParams{
          SessionID: session.ID,
      })
      if err != nil {
          panic(err)
      }
      fmt.Println(archived.Status, archived.ArchivedAt)
      ```

      ```java Java theme={null}
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
                  .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
                  .sessionThreadId(thread.id())
                  .build())
              .build());

      archived = client.beta().sessions().threads().archive(
          thread.id(),
          ThreadArchiveParams.builder()
              .sessionId(session.id())
              .build());
      IO.println(archived.status() + " " + archived.archivedAt().orElseThrow());
      ```

      ```php PHP theme={null}
      $client->beta->sessions->events->send(
          $session->id,
          events: [['type' => 'user.interrupt', 'session_thread_id' => $thread->id]],
      );
      $archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
      echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
      ```

      ```ruby Ruby theme={null}
      client.beta.sessions.events.send_(
        session.id,
        events: [{type: "user.interrupt", session_thread_id: thread.id}]
      )
      archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
      puts "#{archived.status} #{archived.archived_at}"
      ```
    </CodeGroup>
  </Tab>
</Tabs>

### 主线程事件

这些事件在 `/v1/sessions/{session_id}/events/stream` 的主线程上呈现多智能体活动。消息方向事件的命名是相对于其所在的线程流而言的：`agent.thread_message_received` 表示有消息从另一个线程到达此线程，`agent.thread_message_sent` 表示此线程发送了一条消息。例如，协调器委派的任务会在子线程自己的流上以 `agent.thread_message_received` 事件的形式到达。

| 类型                                 | 描述                                                                                 |
| ---------------------------------- | ---------------------------------------------------------------------------------- |
| `session.thread_created`           | 创建了一个线程。包含 `session_thread_id` 和 `agent_name`。                                     |
| `session.thread_status_running`    | 线程开始活动。                                                                            |
| `session.thread_status_idle`       | 与该线程关联的智能体正在等待输入。包含一个 `stop_reason`，指示智能体停止的原因。                                    |
| `session.thread_status_terminated` | 线程已归档或遇到终止性错误。                                                                     |
| `agent.thread_message_received`    | 在主线程上，某个智能体向协调器发送了报告或问题。包含 `from_session_thread_id`、`from_agent_name` 和 `content`。 |
| `agent.thread_message_sent`        | 在主线程上，协调器向另一个智能体发送了任务或后续消息。包含 `to_session_thread_id`、`to_agent_name` 和 `content`。  |

顾问咨询会以保留名称 `anthropic.advisor` 发出这些相同的线程事件（在线程生命周期事件中作为 `agent_name`，在建议传递事件中作为 `from_agent_name`）；有关事件顺序，请参阅[为会话提供顾问](/docs/zh/multiagent-orchestration#give-the-session-an-advisor)。

### 会话线程事件

关键事件会被代理到主线程。但是，您可能仍希望调查特定智能体的推理过程和工具调用。为此，请流式传输或列出关联会话线程的事件。

每个会话线程在 `/v1/sessions/{session_id}/threads/{thread_id}/stream` 都有自己的事件流，并且接受与会话级流相同的 `event_deltas[]` 参数，因此您可以在模型生成文本时预览子智能体的文本。一个连接只预览它正在读取的线程：子线程的预览永远不会出现在会话级流上，因此要实时观察子智能体，请打开其自己的线程流。有关启用、累积和协调预览的信息，请参阅[预览会话线程事件](/docs/zh/events-and-streaming#preview-session-thread-events)。

<Tabs>
  <Tab title="流式传输会话线程事件">
    <CodeGroup>
      ```bash cURL theme={null}
      curl -fsSN "http://localhost:38080/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" |
        while IFS= read -r line; do
          [[ $line == data:* ]] || continue
          json=${line#data: }
          case $(jq -r '.type' <<<"$json") in
            agent.message)
              printf '%s' "$(jq -j '.content[] | select(.type == "text") | .text' <<<"$json")"
              ;;
            session.thread_status_idle)
              break
              ;;
          esac
        done
      ```

      ```bash CLI theme={null}
      ant beta:sessions:threads:events stream \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python theme={null}
      with client.beta.sessions.threads.events.stream(
          thread.id,
          session_id=session.id,
      ) as stream:
          for event in stream:
              match event.type:
                  case "agent.message":
                      for block in event.content:
                          if block.type == "text":
                              print(block.text, end="")
                  case "session.thread_status_idle":
                      break
      ```

      ```typescript TypeScript theme={null}
      const stream = await client.beta.sessions.threads.events.stream(thread.id, {
        session_id: session.id,
      });

      for await (const event of stream) {
        if (event.type === "agent.message") {
          for (const block of event.content) {
            if (block.type === "text") {
              process.stdout.write(block.text);
            }
          }
        } else if (event.type === "session.thread_status_idle") {
          break;
        }
      }
      ```

      ```csharp C# theme={null}
      await foreach (var evt in client.Beta.Sessions.Threads.Events.StreamStreaming(thread.ID, new() { SessionID = session.ID }))
      {
          if (evt.Value is BetaManagedAgentsAgentMessageEvent message)
          {
              foreach (var block in message.Content)
              {
                  if (block.Type == "text")
                  {
                      Console.Write(block.Text);
                  }
              }
          }
          else if (evt.Value is BetaManagedAgentsSessionThreadStatusIdleEvent)
          {
              break;
          }
      }
      ```

      ```go Go theme={null}
          stream := client.Beta.Sessions.Threads.Events.StreamEvents(ctx, thread.ID, anthropic.BetaSessionThreadEventStreamParams{
              SessionID: session.ID,
          })
          defer stream.Close()

      loop:
          for stream.Next() {
              event := stream.Current()
              switch event.Type {
              case "agent.message":
                  for _, block := range event.AsAgentMessage().Content {
                      if block.Type == "text" {
                          fmt.Print(block.Text)
                      }
                  }
              case "session.thread_status_idle":
                  break loop
              }
          }
          if err := stream.Err(); err != nil {
              panic(err)
          }
      ```

      ```java Java theme={null}
      try (var streamResponse = client.beta().sessions().threads().events().streamStreaming(
          thread.id(),
          EventStreamParams.builder().sessionId(session.id()).build()
      )) {
          for (var event : (Iterable<BetaManagedAgentsStreamSessionThreadEvents>) streamResponse.stream()::iterator) {
              if (event.isAgentMessage()) {
                  for (var block : event.asAgentMessage().content()) {
                      block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                  }
              } else if (event.isSessionThreadStatusIdle()) {
                  break;
              }
          }
      }
      ```

      ```php PHP theme={null}
      $stream = $client->beta->sessions->threads->events->streamStream(
          $thread->id,
          sessionID: $session->id,
      );

      foreach ($stream as $event) {
          if ($event->type === 'agent.message') {
              foreach ($event->content as $block) {
                  if ($block->type === 'text') {
                      echo $block->text;
                  }
              }
          } elseif ($event->type === 'session.thread_status_idle') {
              break;
          }
      }
      ```

      ```ruby Ruby theme={null}
      client.beta.sessions.threads.events.stream_events(thread.id, session_id: session.id).each do |event|
        case event.type
        when :"agent.message"
          event.content.each do |block|
            print block.text if block.type == :text
          end
        when :"session.thread_status_idle"
          break
        end
      end
      ```
    </CodeGroup>
  </Tab>

  <Tab title="列出会话线程事件">
    列出所有过去的会话线程事件以获取完整历史记录。

    <CodeGroup>
      ```bash cURL theme={null}
      curl -fsS "http://localhost:38080/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        | jq -r '.data[] | "[\(.type)] \(.processed_at)"'
      ```

      ```bash CLI theme={null}
      ant beta:sessions:threads:events list \
        --session-id "$SESSION_ID" \
        --thread-id "$THREAD_ID"
      ```

      ```python Python theme={null}
      for event in client.beta.sessions.threads.events.list(
          thread.id,
          session_id=session.id,
      ):
          print(f"[{event.type}] {event.processed_at}")
      ```

      ```typescript TypeScript theme={null}
      for await (const event of client.beta.sessions.threads.events.list(thread.id, {
        session_id: session.id,
      })) {
        console.log(`[${event.type}] ${event.processed_at}`);
      }
      ```

      ```csharp C# theme={null}
      var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
      await foreach (var evt in page.Paginate())
      {
          Console.WriteLine($"[{evt.Type}] {evt.ProcessedAt}");
      }
      ```

      ```go Go theme={null}
      pager := client.Beta.Sessions.Threads.Events.ListAutoPaging(ctx, thread.ID, anthropic.BetaSessionThreadEventListParams{
          SessionID: session.ID,
      })
      for pager.Next() {
          event := pager.Current()
          fmt.Printf("[%s] %s\n", event.Type, event.ProcessedAt)
      }
      if err := pager.Err(); err != nil {
          panic(err)
      }
      ```

      ```java Java theme={null}
      for (var event : client.beta().sessions().threads().events().list(
              thread.id(),
              EventListParams.builder().sessionId(session.id()).build()
          ).autoPager()) {
          var type = event._json().orElseThrow() instanceof JsonObject json
              ? json.values().get("type").asStringOrThrow()
              : "unknown";
          var processedAt = event.processedAt().map(OffsetDateTime::toString).orElse("pending");
          IO.println("[" + type + "] " + processedAt);
      }
      ```

      ```php PHP theme={null}
      foreach (
          $client->beta->sessions->threads->events->list(
              $thread->id,
              sessionID: $session->id,
          )->pagingEachItem() as $event
      ) {
          echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
      }
      ```

      ```ruby Ruby theme={null}
      client.beta.sessions.threads.events.list(
        thread.id,
        session_id: session.id
      ).auto_paging_each do |event|
        puts "[#{event.type}] #{event.processed_at}"
      end
      ```
    </CodeGroup>
  </Tab>
</Tabs>

### 工具权限和自定义工具

如果子智能体需要从您的客户端获取某些内容，例如运行 `always_ask` 工具的[权限](/docs/zh/events-and-streaming#tool-confirmation)，或[自定义工具的结果](/docs/zh/events-and-streaming#handling-custom-tool-calls)，该事件会被交叉发布到**主线程**，并带有标识发起会话线程的 `session_thread_id`。

```json theme={null}
{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sth_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}
```

发布 `user.tool_confirmation`（带有 `tool_use_id`）或 `user.custom_tool_result`（带有 `custom_tool_use_id`）；服务器会自动将响应路由到正确的线程。

以下示例扩展了[工具确认处理程序](/docs/zh/events-and-streaming#tool-confirmation)以路由回复。相同的模式也适用于 `user.custom_tool_result`。

<CodeGroup>
  ```bash cURL theme={null}
  while IFS= read -r event_id; do
    jq -n --arg id "$event_id" \
      '{events: [{type: "user.tool_confirmation", tool_use_id: $id, result: "allow"}]}' |
      curl -fsS "http://localhost:38080/v1/sessions/$SESSION_ID/events?beta=true" \
        -H "x-api-key: $OMA_API_KEY" \
        -H "anthropic-version: 2023-06-01" \
        -H "anthropic-beta: managed-agents-2026-04-01" \
        -H "content-type: application/json" \
        -d @-
  done < <(jq -r '.stop_reason.event_ids[]' <<<"$data")
  ```

  ```bash CLI theme={null}
  # 此工作流不太适合用一次性的 shell 命令来表达。
  # 请改用此代码组中的某个 SDK 示例。
  ```

  ```python Python theme={null}
  for event_id in stop.event_ids:
      client.beta.sessions.events.send(
          session.id,
          events=[
              {
                  "type": "user.tool_confirmation",
                  "tool_use_id": event_id,
                  "result": "allow",
              }
          ],
      )
  ```

  ```typescript TypeScript theme={null}
  for (const eventId of stop.event_ids) {
    await client.beta.sessions.events.send(session.id, {
      events: [
        {
          type: "user.tool_confirmation",
          tool_use_id: eventId,
          result: "allow",
        },
      ],
    });
  }
  ```

  ```csharp C# theme={null}
  foreach (var eventId in requiresAction.EventIds)
  {
      await client.Beta.Sessions.Events.Send(session.ID, new()
      {
          Events =
          [
              new BetaManagedAgentsUserToolConfirmationEventParams
              {
                  Type = BetaManagedAgentsUserToolConfirmationEventParamsType.UserToolConfirmation,
                  ToolUseID = eventId,
                  Result = BetaManagedAgentsUserToolConfirmationEventParamsResult.Allow,
              },
          ],
      });
  }
  ```

  ```go Go theme={null}
  for _, eventID := range stopReason.EventIDs {
      params := anthropic.BetaManagedAgentsUserToolConfirmationEventParams{
          Type:      anthropic.BetaManagedAgentsUserToolConfirmationEventParamsTypeUserToolConfirmation,
          ToolUseID: eventID,
          Result:    anthropic.BetaManagedAgentsUserToolConfirmationEventParamsResultAllow,
      }
      if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, anthropic.BetaSessionEventSendParams{
          Events: []anthropic.BetaManagedAgentsEventParamsUnion{{OfUserToolConfirmation: &params}},
      }); err != nil {
          panic(err)
      }
  }
  ```

  ```java Java theme={null}
  for (var eventId : pendingToolUseIds) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                  .toolUseId(eventId)
                  .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                  .build())
              .build()
      );
  }
  ```

  ```php PHP theme={null}
  foreach ($event->stopReason->eventIDs as $eventId) {
      $client->beta->sessions->events->send($session->id, events: [[
          'type' => 'user.tool_confirmation',
          'tool_use_id' => $eventId,
          'result' => 'allow',
      ]]);
  }
  ```

  ```ruby Ruby theme={null}
  event_ids.each do |event_id|
    client.beta.sessions.events.send_(session.id, events: [{
      type: "user.tool_confirmation",
      tool_use_id: event_id,
      result: "allow"
    }])
  end
  ```
</CodeGroup>
