多代理会话

在单个会话中协调多个代理。


多代理编排让一个代理与其他代理协调以完成复杂的工作。代理可以并行运行,拥有自己的隔离上下文,这有助于提高输出质量,也可以改善完成时间。

Note

所有托管代理 API 请求都需要 managed-agents-2026-04-01 beta 头。SDK 会自动设置该 beta 头。

工作原理

所有代理共享相同的容器、文件系统和保险库凭据,但每个代理在自己的会话线程中运行,这是一个具有自己对话历史的上下文隔离的事件流。协调器在主线程中报告活动(这与会话级别的事件流相同);附加线程在协调器委派工作时在运行时生成。

线程是持久的:协调器可以向之前调用的代理发送后续消息,该代理保留之前所有轮次的内容。

每个代理使用其创建时定义的配置(模型、系统提示、工具、MCP 服务器和技能)。工具、MCP 服务器和上下文不共享。

委派什么

多代理协调最适合复杂任务,这些任务要么需要跨多个方面的工作,要么多个范围明确的任务共同贡献于一个整体目标。

适合的模式:

  • **并行化:**同时分散独立的子任务(搜索多个来源、分析单独的文件),让协调器综合结果。
  • **专业化:**路由到具有领域特定系统提示和工具的代理,例如安全代理或文档代理,而不是将所有功能加载到单个代理中。
  • **升级:**为一部分复杂子任务咨询更强大的代理或模型。

配置协调器

定义代理时,设置 multiagent 以声明协调器可以委派的代理名单:

coordinator=$(curl -fsS https://api.anthropic.com/v1/agents \
  -H "x-api-key: $ANTHROPIC_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-4-7",
  "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
)
ant beta:agents create <<YAML
name: Engineering Lead
model: claude-opus-4-7
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
coordinator = client.beta.agents.create(
    name="Engineering Lead",
    model="claude-opus-4-7",
    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},
        ],
    },
)
const coordinator = await client.beta.agents.create({
  name: "Engineering Lead",
  model: "claude-opus-4-7",
  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 },
    ],
  },
});
var coordinator = await client.Beta.Agents.Create(new()
{
    Name = "Engineering Lead",
    Model = BetaManagedAgentsModel.ClaudeOpus4_7,
    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],
    },
});
coordinator, err := client.Beta.Agents.New(ctx, anthropic.BetaAgentNewParams{
	Name:   "Engineering Lead",
	Model:  anthropic.BetaManagedAgentsModelConfigParams{ID: anthropic.BetaManagedAgentsModelClaudeOpus4_7},
	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)
}
var coordinator = client.beta().agents().create(
    AgentCreateParams.builder()
        .name("Engineering Lead")
        .model(BetaManagedAgentsModel.CLAUDE_OPUS_4_7)
        .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()
);
$coordinator = $client->beta->agents->create(
    name: 'Engineering Lead',
    model: 'claude-opus-4-7',
    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],
        ],
    ],
);
coordinator = client.beta.agents.create(
  name: "Engineering Lead",
  model: "claude-opus-4-7",
  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}
    ]
  }
)

multiagent.agents 可以接受以下任一形式:

  • {"type": "agent", "id": agent.id} 通过 ID 引用先前创建的 agent。如果未指定 version,则默认固定到最新的代理版本。
  • {"type": "agent", "id": agent.id, "version": agent.version} 固定到特定代理版本。
  • {"type": "self"} 允许协调器生成自身的副本。

协调器只能委派到一层代理;深度 > 1 将被忽略。multiagent.agents 中最多可列出 20 个唯一代理,但协调器可以调用每个代理的多个副本。

创建会话

创建引用协调器的会话。协调器根据需要委派给其名单中的代理。

session=$(curl -fsSL https://api.anthropic.com/v1/sessions \
  -H "x-api-key: $ANTHROPIC_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")
ant beta:sessions create \
  --agent "$COORDINATOR_ID" \
  --environment-id "$ENVIRONMENT_ID"
session = client.beta.sessions.create(
    agent=coordinator.id,
    environment_id=environment.id,
)
const session = await client.beta.sessions.create({
  agent: coordinator.id,
  environment_id: environment.id,
});
var session = await client.Beta.Sessions.Create(new()
{
    Agent = coordinator.ID,
    EnvironmentID = environment.ID,
});
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)
}
var session = client.beta().sessions().create(SessionCreateParams.builder()
    .agent(coordinator.id())
    .environmentId(environment.id())
    .build());
$session = $client->beta->sessions->create(
    agent: $coordinator->id,
    environmentID: $environment->id,
);
session = client.beta.sessions.create(
  agent: coordinator.id,
  environment_id: environment.id
)

将代理连接到 MCP 服务器

MCP 服务器是代理范围的(每个代理定义声明自己的服务器和工具),而保险库凭据是会话范围的(会话创建时传递的 vault_ids 适用于每个线程)。对您的集成有两个影响:

  • 要认证 MCP 服务器,请为所有代理使用的所有 MCP 服务器包含保险库凭据。
  • 要限制代理的访问,请在其代理定义中仅声明其需要的服务器。
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-4-7",
  "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"
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-4-7
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"
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-4-7",
    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)
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-4-7",
  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);
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.ClaudeOpus4_7,
    Tools =
    [
        new BetaManagedAgentsAgentToolset20260401Params
        {
            Type = BetaManagedAgentsAgentToolset20260401ParamsType.AgentToolset20260401,
        },
    ],
    Multiagent = new()
    {
        Type = BetaManagedAgentsMultiagentParamsType.Coordinator,
        Agents =
        [
            new BetaManagedAgentsAgentParams
            {
                Type = Anthropic.Models.Beta.Sessions.Type.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);
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.BetaManagedAgentsModelClaudeOpus4_7},
	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)
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_4_7)
        .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());
$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-4-7',
    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";
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-4-7",
  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

在此示例中,只有 researcher 声明了 GitHub MCP 服务器,因此协调器没有访问权限。会话的 vault_ids 为 researcher 的线程提供 GitHub 凭据。

Tip

如果代理的 MCP 调用在您声明服务器后认证失败,请确认凭据的 mcp_server_url 与代理的 mcp_servers[].url 完全匹配,包括协议和尾部斜杠。

线程

会话级事件流/v1/sessions/:id/events/stream)被视为主线程,包含所有线程活动的浓缩视图。您不会看到子代理的完整活动,但会看到其工作的开始和结束,以及阻止事件(如工具权限请求)。

会话线程是您深入了解特定代理活动的地方。

会话 status 是所有代理活动的聚合;如果至少有一个线程是 running,那么整体会话状态也是 running

Note

最多支持 25 个并发线程。协调器可以调用名单中单个代理的多个副本,创建与一个 agent 关联的多个线程。

列出与会话关联的所有线程:

curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  | jq -r '.data[] | "[\(.agent.name)] \(.status)"'
ant beta:sessions:threads list --session-id "$SESSION_ID"
for thread in client.beta.sessions.threads.list(session.id):
    print(f"[{thread.agent.name}] {thread.status}")
for await (const thread of client.beta.sessions.threads.list(session.id)) {
  console.log(`[${thread.agent.name}] ${thread.status}`);
}
await foreach (var thread in (await client.Beta.Sessions.Threads.List(session.ID)).Paginate())
{
    Console.WriteLine({{CONTENT}}quot;[{thread.Agent.Name}] {thread.Status}");
}
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)
}
for (var thread : client.beta().sessions().threads().list(session.id()).autoPager()) {
    IO.println("[" + thread.agent().name() + "] " + thread.status());
}
foreach ($client->beta->sessions->threads->list($session->id)->pagingEachItem() as $thread) {
    echo "[{$thread->agent->name}] {$thread->status}\n";
}
client.beta.sessions.threads.list(session.id).auto_paging_each do |thread|
  puts "[#{thread.agent.name}] #{thread.status}"
end

完整列表包括主线程。主线程的 parent_thread_id 为 null。

发送带有 session_thread_iduser.interrupt 以停止特定线程。省略 session_thread_id 将针对主线程。

curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
  -H "x-api-key: $ANTHROPIC_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\"}]}"
ant beta:sessions:events send \
  --session-id "$SESSION_ID" \
  --event "{type: user.interrupt, session_thread_id: $THREAD_ID}"
client.beta.sessions.events.send(
    session.id,
    events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
)
await client.beta.sessions.events.send(session.id, {
  events: [{ type: "user.interrupt", session_thread_id: thread.id }],
});
await client.Beta.Sessions.Events.Send(session.ID, new()
{
    Events =
    [
        new BetaManagedAgentsUserInterruptEventParams
        {
            Type = BetaManagedAgentsUserInterruptEventParamsType.UserInterrupt,
            SessionThreadID = thread.ID,
        },
    ],
});
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)
}
client.beta().sessions().events().send(
    session.id(),
    EventSendParams.builder()
        .addEvent(BetaManagedAgentsUserInterruptEventParams.builder()
            .type(BetaManagedAgentsUserInterruptEventParams.Type.USER_INTERRUPT)
            .sessionThreadId(thread.id())
            .build())
        .build());
$client->beta->sessions->events->send(
    $session->id,
    events: [
        ['type' => 'user.interrupt', 'session_thread_id' => $thread->id],
    ],
);
client.beta.sessions.events.send_(
  session.id,
  events: [{type: "user.interrupt", session_thread_id: thread.id}]
)

对于在 requires_action 上阻塞的子线程,中断会将每个待处理的工具调用标记为已拒绝,并直接重新发出 session.thread_status_idle(包含 stop_reason: end_turn);模型不会被采样。对于已经处于 idle 的线程,中断是无操作。

可选地在会话线程完成工作后归档它。这会释放一个线程以应对 25 线程限制。

curl -fsS -X POST "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
ant beta:sessions:threads archive \
  --session-id "$SESSION_ID" \
  --thread-id "$THREAD_ID"
archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
print(archived.status, archived.archived_at)
const archived = await client.beta.sessions.threads.archive(thread.id, {
  session_id: session.id,
});
console.log(archived.status, archived.archived_at);
var archived = await client.Beta.Sessions.Threads.Archive(thread.ID, new() { SessionID = session.ID });
Console.WriteLine({{CONTENT}}quot;{archived.Status} {archived.ArchivedAt}");
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)
var archived = client.beta().sessions().threads().archive(
    thread.id(),
    ThreadArchiveParams.builder()
        .sessionId(session.id())
        .build());
IO.println(archived.status() + " " + archived.archivedAt());
$archived = $client->beta->sessions->threads->archive($thread->id, sessionID: $session->id);
echo "{$archived->status} {$archived->archivedAt->format(DATE_ATOM)}\n";
archived = client.beta.sessions.threads.archive(thread.id, session_id: session.id)
puts "#{archived.status} #{archived.archived_at}"

归档只有在线程处于 idle 状态时才能成功。如果线程正在运行或在 requires_action 上阻塞,请先中断它:

# Interrupt the thread, then archive it
curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
  -H "x-api-key: $ANTHROPIC_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 "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/archive" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01"
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"
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)
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);
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({{CONTENT}}quot;{archived.Status} {archived.ArchivedAt}");
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)
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());
$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";
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}"

主线程事件

这些事件在 /v1/sessions/:id/events/stream 的主线上显示多代理活动。

类型描述
session.thread_created线程已创建。包含 session_thread_idagent_name
session.thread_status_running线程开始了活动。
session.thread_status_idle与线程关联的代理正在等待输入。包含一个 stop_reason,指示代理停止的原因。
session.thread_status_terminated线程已归档或遇到终端错误。
agent.thread_message_received代理将其结果传递给协调器。包含 from_session_thread_idfrom_agent_namecontent
agent.thread_message_sent协调器向另一个代理发送后续消息。包含 to_session_thread_idto_agent_namecontent

会话线程事件

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

curl -fsSN "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/stream?beta=true" \
  -H "x-api-key: $ANTHROPIC_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
ant beta:sessions:threads:events stream \
  --session-id "$SESSION_ID" \
  --thread-id "$THREAD_ID"
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
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;
  }
}
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;
    }
}
	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)
	}
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()) {
                IO.print(block.text());
            }
        } else if (event.isSessionThreadStatusIdle()) {
            break;
        }
    }
}
$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;
    }
}
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

列出所有过去的会话线程事件以获取完整历史。

curl -fsS "https://api.anthropic.com/v1/sessions/$SESSION_ID/threads/$THREAD_ID/events" \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: managed-agents-2026-04-01" \
  | jq -r '.data[] | "[\(.type)] \(.processed_at)"'
ant beta:sessions:threads:events list \
  --session-id "$SESSION_ID" \
  --thread-id "$THREAD_ID"
for event in client.beta.sessions.threads.events.list(
    thread.id,
    session_id=session.id,
):
    print(f"[{event.type}] {event.processed_at}")
for await (const event of client.beta.sessions.threads.events.list(thread.id, {
  session_id: session.id,
})) {
  console.log(`[${event.type}] ${event.processed_at}`);
}
var page = await client.Beta.Sessions.Threads.Events.List(thread.ID, new() { SessionID = session.ID });
await foreach (var evt in page.Paginate())
{
    Console.WriteLine({{CONTENT}}quot;[{evt.Type}] {evt.ProcessedAt}");
}
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)
}
for (var event : client.beta().sessions().threads().events().list(
        thread.id(),
        EventListParams.builder().sessionId(session.id()).build()
    ).autoPager()) {
    var json = (Map<String, JsonValue>) event._json().orElseThrow().asObject().orElseThrow();
    var type = json.get("type").asStringOrThrow();
    var processedAt = json.containsKey("processed_at")
        ? json.get("processed_at").asStringOrThrow()
        : "pending";
    IO.println("[" + type + "] " + processedAt);
}
foreach (
    $client->beta->sessions->threads->events->list(
        $thread->id,
        sessionID: $session->id,
    )->pagingEachItem() as $event
) {
    echo "[{$event->type}] {$event->processedAt->format(DATE_RFC3339)}\n";
}
client.beta.sessions.threads.events.list(
  thread.id,
  session_id: session.id
).auto_paging_each do |event|
  puts "[#{event.type}] #{event.processed_at}"
end

工具权限和自定义工具

如果子代理需要您的客户端提供某些内容,例如运行 always_ask 工具的权限,或自定义工具的结果,该事件会交叉发布到主线程,其中 session_thread_id 标识原始会话线程。

{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sth_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["toolu_01XYZ..."]
  }
}

发布 user.tool_confirmation(带 tool_use_id)或 user.custom_tool_result(带 custom_tool_use_id);服务器会自动将响应路由到正确的线程。

以下示例扩展了工具确认处理器以路由回复。同样的模式适用于 user.custom_tool_result

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 "https://api.anthropic.com/v1/sessions/$SESSION_ID/events?beta=true" \
      -H "x-api-key: $ANTHROPIC_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")
# This workflow does not translate well to a one-off shell command.
# Use one of the SDK examples in this code group instead.
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",
            }
        ],
    )
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",
      },
    ],
  });
}
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,
            },
        ],
    });
}
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)
	}
}
for (var eventId : pendingToolUseIds) {
    client.beta().sessions().events().send(
        session.id(),
        EventSendParams.builder()
            .addEvent(BetaManagedAgentsUserToolConfirmationEventParams.builder()
                .toolUseId(eventId)
                .result(BetaManagedAgentsUserToolConfirmationEventParams.Result.ALLOW)
                .build())
            .build()
    );
}
foreach ($event->stopReason->eventIDs as $eventId) {
    $client->beta->sessions->events->send($session->id, events: [[
        'type' => 'user.tool_confirmation',
        'tool_use_id' => $eventId,
        'result' => 'allow',
    ]]);
}
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