托管智能体 API 请求需要
managed-agents-2026-04-01 Beta 请求头,但记忆存储端点除外,它们使用 agent-memory-2026-07-22。SDK 会自动设置正确的 Beta 请求头。请参阅Beta 请求头。工作原理
所有智能体共享同一个沙箱、文件系统和密钥库凭据,但每个智能体都在自己的会话线程(session thread)中运行,这是一个上下文隔离的事件流,拥有自己的对话历史。协调器在主线程(primary thread)中报告活动(主线程与会话级事件流相同);当协调器委派工作时,会在运行时生成额外的线程。 线程是持久化的:协调器可以向之前调用过的智能体发送后续消息,该智能体会保留其之前所有轮次的内容。 每个智能体使用自己的配置:模型、系统提示、工具、MCP 服务器和技能。会话级智能体配置覆盖是例外情况;这些覆盖适用于协调器及其self 副本。工具、MCP 服务器和上下文不会共享。
适合委派的任务
多智能体协调最适合需要跨多个领域开展工作的复杂任务,或者由多个范围明确的子任务共同构成总体目标的场景。 效果良好的模式:- 并行化: 同时分发独立的子任务(搜索多个来源、分析不同的文件),然后由协调器综合结果。
- 专业化: 将任务路由到具有特定领域系统提示和工具的智能体,例如安全智能体或文档智能体,而不是让单个智能体承载所有能力。
- 升级处理: 针对部分复杂子任务,咨询能力更强的智能体或模型。
配置协调器
在定义智能体时,设置multiagent 以声明协调器可以委派任务的智能体名单:
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
)
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
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},
],
},
)
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 },
],
},
});
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],
},
});
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)
}
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()
);
$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],
],
],
);
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}
]
}
)
multiagent.agents 可以接受以下任意形式:
{"type": "agent", "id": agent.id}通过 ID 引用先前创建的agent。如果未指定version,则该引用会固定到协调器创建时该智能体的最新版本。{"type": "agent", "id": agent.id, "version": agent.version}固定到特定的智能体版本。{"type": "self"}允许协调器生成自身的副本。如果会话是使用智能体配置覆盖创建的,这些覆盖也会应用于这些副本;通过 ID 引用的名单条目不受影响。{"type": "advisor", "model": "<model id>"}为会话的主线程提供一个可在轮次中途咨询的顾问。每个名单最多包含一个顾问条目。请参阅为会话提供顾问。
multiagent.agents 名单)会在协调器创建或更新时生成快照。被引用的智能体会固定在当时解析出的版本上,不会自动获取其定义的后续更新。如需委派给被引用智能体的更新版本,请更新协调器,使其名单引用该版本。
协调器只能委派给一层智能体;如果引用的智能体本身具有 multiagent.agents 名单,创建或更新请求将因验证错误而失败。multiagent.agents 中最多可列出 20 个不同的智能体,但协调器可以调用每个智能体的多个副本。
当智能体固定了推理地理位置(智能体定义中的 model.inference_geo)时,协调器的固定值和每个名单成员的固定值必须全部设置为相同的值,或者全部不设置。不匹配的名单会被拒绝并返回 400 验证错误,无论是在保存智能体时,还是在会话创建覆盖更改任何固定值时。
为会话提供顾问
multiagent.agents 中的顾问条目为会话的主线程提供一个顾问(advisor):一个可在轮次中途咨询以获取策略指导的模型,例如规划方法、摆脱困境或在完成前审查工作。该条目恰好包含两个字段,type 和 model:
cURL
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 验证错误。有效的配对遵循顾问工具的模型兼容性表。
顾问也可作为 消息 API 上的服务器工具使用。托管智能体界面在配置和交付方式上有所不同:名单条目没有 max_uses、max_tokens 或 caching 字段,建议通过线程事件而非 advisor_tool_result 块传递。
咨询的工作方式
每次咨询都作为一个由平台生成的、名为anthropic.advisor 的线程运行,该线程在咨询完成时自行终止,建议会作为 agent.thread_message_received 事件传递到主线程。一次咨询会发出标准的线程事件,通过保留名称 anthropic.advisor 进行标识(线程生命周期事件将其作为 agent_name 携带,建议传递事件将其作为 from_agent_name 携带),通常按以下顺序:
session.thread_createdsession.thread_status_runningagent.thread_message_received(建议内容)session.thread_status_idle(stop_reason: end_turn)session.thread_status_terminated
agent.tool_use 事件,会话的事件流上也不会出现 agent.thread_message_sent 事件,因为咨询输入是由平台组合的,而非由智能体发送。如果您列出顾问线程自身的事件,建议内容也会在那里以 agent.thread_message_sent 事件的形式出现。建议传递(事件 3)不保证在顾问线程的 idle 和 terminated 事件之前到达,因此不要将这些事件视为建议已传递的信号。
您的客户端能否读取建议内容取决于顾问模型的策略,这与消息 API 顾问工具上的结果变体划分相对应。在那里返回明文结果的顾问模型,在此处会将建议作为可读文本内容传递;在那里返回脱敏结果的顾问模型,在此处会在所有客户端界面上将 [{"type": "redacted"}] 占位符作为消息内容传递,而智能体本身仍会在服务器端读取完整的建议。在前面的示例中,claude-opus-5 是一个脱敏结果顾问,因此您的客户端会看到占位符,而智能体会读取完整的建议;如果您希望在事件流上可读取建议内容,请改为选择 claude-opus-4-8 作为顾问。顾问的思考过程永远不会显示。客户端不能自行发送 redacted 块;包含此类块的事件会被拒绝并返回 400 验证错误。
失败或被中断的咨询永远不会导致智能体的轮次失败:智能体会在收到咨询失败的通用通知后继续执行。咨询期间的会话级 user.interrupt 会终止顾问线程且不传递任何建议;带有顾问线程 session_thread_id 的 user.interrupt 仅放弃该次咨询。
顾问线程
顾问不是名单智能体:它对协调器的list_agents 工具不可见,无法通过 send_to_agent 向其发送消息,且只有会话的主线程可以咨询它。名单智能体不能咨询顾问。
顾问线程不受并发线程限制的约束。它们会出现在会话的线程列表中,其 agent 设置为与配置完全一致的顾问形式({"type": "advisor", "model": ...}),parent_thread_id 设置为主线程。
顾问端的提示缓存是自动的;无需任何配置。咨询按顾问模型的费率计费,其令牌会出现在顾问线程的用量和会话的用量总计中。
移除顾问
要移除顾问,请使用不再包含顾问条目的名单更新智能体。如果顾问是名单中唯一的条目,请通过设置"multiagent": 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")
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 服务器包含一个密钥库凭据。
- 要限制某个智能体的访问权限,请在其智能体定义中仅声明它所需的服务器。
self 副本的 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-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"
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"
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)
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);
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);
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)
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());
$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";
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
vault_ids 将 GitHub 凭据提供给研究员的线程。
如果在声明服务器后,某个智能体的 MCP 调用身份验证失败,请确认凭据的
mcp_server_url 与智能体的 mcp_servers[].url 指向同一服务器。两个 URL 在匹配前都会进行规范化处理(协议和主机名转为小写,去除默认端口和尾部斜杠),因此主机名大小写、默认端口或尾部斜杠的差异不会阻止匹配;但不同的路径、子域名或非默认端口会导致不匹配。线程
会话级事件流(/v1/sessions/{session_id}/events/stream)被视为主线程,包含所有线程中所有活动的精简视图。您不会看到子智能体的完整活动,但可以看到它们工作的开始和结束,以及工具权限请求等阻塞事件。
会话线程是您深入查看特定智能体活动的地方。
会话 status 是所有智能体活动的聚合;如果至少有一个线程处于 running 状态,则整个会话状态也为 running。
会话预算是会话所有线程共享的单一上限。当达到上限时,各线程会独立暂停,每个线程的费用按该线程自身所使用的模型定价。
最多支持 25 个并发线程。协调器可以调用名单中单个智能体的多个副本,从而创建与一个
agent 关联的多个线程。顾问咨询线程不受此限制约束。- 列出线程
- 中断会话线程
- 归档会话线程
按如下方式列出与会话关联的所有线程:完整列表包含主线程。主线程的
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)"'
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($"[{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()) {
var name = thread.agent().isAgent() ? thread.agent().asAgent().name() : "advisor";
IO.println("[" + 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_id 的 user.interrupt 以停止特定线程。省略 session_thread_id 会中断会话中所有未归档的线程,包括主线程。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\"}]}"
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 状态的子线程,中断会以错误工具结果(“Tool execution was interrupted before completion. Please retry.”)关闭每个待处理的工具调用,并直接重新发出带有 stop_reason: end_turn 的 session.thread_status_idle;不会对模型进行采样。对于已处于 idle 状态的线程,中断是无操作的。当会话线程完成其工作后,可以选择将其归档。这会释放一个线程名额,以便不超过 25 个线程的限制。只有当线程处于
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"
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($"{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().orElseThrow());
$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 状态的线程视为空闲,可以直接归档;只有正在运行的线程必须先中断:# 中断该线程,然后将其归档
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"
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($"{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().orElseThrow());
$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/{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);有关事件顺序,请参阅为会话提供顾问。
会话线程事件
关键事件会被代理到主线程。但是,您可能仍希望调查特定智能体的推理过程和工具调用。为此,请流式传输或列出关联会话线程的事件。 每个会话线程在/v1/sessions/{session_id}/threads/{thread_id}/stream 都有自己的事件流,并且接受与会话级流相同的 event_deltas[] 参数,因此您可以在模型生成文本时预览子智能体的文本。一个连接只预览它正在读取的线程:子线程的预览永远不会出现在会话级流上,因此要实时观察子智能体,请打开其自己的线程流。有关启用、累积和协调预览的信息,请参阅预览会话线程事件。
- 流式传输会话线程事件
- 列出会话线程事件
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
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()) {
block.text().ifPresent(textBlock -> IO.print(textBlock.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 "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)"'
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($"[{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 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);
}
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": ["sevt_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 "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")
# 此工作流不太适合用一次性的 shell 命令来表达。
# 请改用此代码组中的某个 SDK 示例。
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: ¶ms}},
}); 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