跳转到内容

多智能体模式

Awaken 支持多种 agent 组合方式,包括本地委托、远程 A2A agent、sub-agent 执行以及 handoff。

通过 AgentSpec.delegates 进行 agent 委托

Section titled “通过 AgentSpec.delegates 进行 agent 委托”

agent 可以通过 delegates 声明它允许委托的子 agent:

{
"id": "orchestrator",
"model_id": "gpt-4o",
"system_prompt": "You coordinate tasks across specialized agents.",
"delegates": ["researcher", "writer", "reviewer"]
}

解析时,运行时会为每个 delegate 生成一个 AgentTool,LLM 看见的是普通工具,例如 agent_run_researcher

AgentTool 持有 Arc<dyn AgentResolver>预选 backend。LLM 调用该工具时,execute()call timeresolver.resolve_execution(&agent_id),resolver 按每次调用决定:

  • 本地 agent(无 endpoint)解析为本地 ResolvedAgent,在同一 runtime 内联执行。
  • 远程 agent(有 endpoint)解析为 ResolvedBackendAgent,经配置的 ExecutionBackend(当前是 A2A)执行 —— 发 message:send,然后轮询返回的 task 直到完成。

因为解析推迟到 call time,通过配置 API 改 delegate 的 AgentSpec(例如翻转其 endpoint)在下一次工具调用立刻生效,无需重建父 agent。

如果 AgentSpec.endpoint 存在,该 delegate 会被当作远程 A2A agent:

{
"id": "remote-analyst",
"model_id": "unused-for-remote",
"system_prompt": "",
"endpoint": {
"backend": "a2a",
"base_url": "https://analyst.example.com/v1/a2a",
"auth": { "type": "bearer", "token": "token-abc" },
"target": "analyst",
"timeout_ms": 300000,
"options": {
"poll_interval_ms": 1000
}
}
}

A2aBackend 会发送 message:send,读取返回的 task.id 并轮询任务状态,再把最终结果包装成 BackendRunResult / ToolResult 返回给父 agent。远端超时、失败、等待输入、等待认证和取消会保留在 BackendRunStatus 中。

在工具里以代码方式调用 Sub-Agent

Section titled “在工具里以代码方式调用 Sub-Agent”

当你写一个自定义 Tool 需要委托给另一个 agent,特别是需要严格控制父 ↔ 子 state 流动时,使用 awaken_runtime::child_agentrun_child_agent。它是 AgentToolrun_streaming_subagent 共同依赖的底层辅助函数。

run_child_agent 通过 initial_state_seed: Option<PersistedState> 接受父→子的初始状态注入,并通过 BackendRunResult.state(一个 PersistedState)把子的终态返回给父工具去解码并以 StateCommand 形式塞进 ToolOutput。子→父 state 走的是普通工具就在用的 ToolOutput.command 通路,没有单独的”sub-agent 导出”机制。

state seeding 只对 Local backend 生效。它不是 BackendProfile 里的一个 capability flag:只要解析出的 ExecutionPlan 不是本地执行, validate_delegate_execution_request 就会拒绝 BackendDelegateRunRequest.state_seed。A2A 和自定义远程 backend 没有统一的 seed wire 协议,因此带 seed 的 delegate 请求会以 ExecutionBackendError 失败, 而不是静默丢弃 seed。只要 backend 返回了结果,子的 BackendRunResult.state 仍然可读,用于回写父侧。

Backend 实现者应使用 BackendProfile 表达 continuation、persistence、waits、 transcript shape 和 output shape 等 typed capabilities。父→子 state seed 是这个 profile 之外的本地执行规则。

Orchestrator -> researcher -> result
-> writer -> result
-> reviewer -> result

父 agent 按顺序调用子 agent,并根据前一步结果决定下一步。

如果 LLM 在同一轮推理里一次返回多个 delegate tool call,它们会使用和普通工具相同的 ToolExecutor。内置 resolver 默认安装 SequentialToolExecutor,因此委托默认逐个执行。如果这些 delegate call 相互独立,并且需要并发执行,可以通过自定义 resolver 或 ResolvedAgent::with_tool_executor(...) 安装 ParallelToolExecutor

orchestrator
-> team_lead (delegates: [dev_a, dev_b])
-> dev_a
-> dev_b

每一层都独立通过 AgentResolver 解析。理论上没有硬深度限制,但每层都会增加 token 和延迟成本。

handoff 会在同一 run 内把控制权切换给另一个 agent,而不是把它当成子任务调用。

机制:

  1. 插件或 handoff 扩展把新 agent ID 写入活动 agent 状态键
  2. loop runner 在下一个步骤边界检测到变化
  3. 重新通过 AgentResolver 解析 agent
  4. 在同一个 thread、同一条消息历史上继续执行
方面DelegationHandoff
控制流父 agent 调子 agent,拿回结果控制权直接切到新 agent
Thread 连续性子 agent 可以有独立上下文同一 thread、同一消息历史
返回路径结果回到父 agent不返回,后续由新 agent 接管
适用场景任务拆解、专长子任务角色切换、升级处理、路由

root execution 和 delegation 都基于 canonical ExecutionBackend

pub trait ExecutionBackend: Send + Sync {
fn capabilities(&self) -> BackendProfile;
async fn abort(&self, request: BackendAbortRequest<'_>)
-> Result<(), ExecutionBackendError>;
async fn execute_root(
&self,
request: BackendRootRunRequest<'_>,
) -> Result<BackendRunResult, ExecutionBackendError>;
async fn execute_delegate(
&self,
request: BackendDelegateRunRequest<'_>,
) -> Result<BackendRunResult, ExecutionBackendError>;
}

BackendRunResult 保留 agent ID、状态、终止原因、可选响应文本、结构化输出、run ID、inbox、run-scoped 持久化状态,以及可选的 thread-scoped 持久化状态。BackendRunStatus 包含 CompletedWaitingInputWaitingAuthSuspendedFailedCancelledTimeout

这也是实现自定义本地或远程执行后端的扩展点。awaken_runtime::extensions::a2a 仍然重新导出 AgentBackendAgentBackendFactoryDelegateRunResult 作为兼容别名,但新代码应使用 ExecutionBackend 命名。