多智能体系统

通过将专门的智能体组合成团队,构建复杂的应用程序。

智能体层级概览

Rendering architecture…

你将构建的内容

在本指南中,你将创建一个客户服务系统,其中协调器会将查询路由给专业智能体:

                        ┌─────────────────────┐
       User Query       │                     │
      ────────────────▶ │    COORDINATOR      │
                        │  "Route to expert"  │
                        └──────────┬──────────┘
                                   │
                   ┌───────────────┴───────────────┐
                   │                               │
                   ▼                               ▼
        ┌──────────────────┐            ┌──────────────────┐
        │  BILLING AGENT   │            │  SUPPORT AGENT   │
        │                  │            │                  │
        │  💰 Payments     │            │  🔧 Tech Issues  │
        │  📄 Invoices     │            │  🐛 Bug Reports  │
        │  💳 Subscriptions│            │  ❓ How-To       │
        └──────────────────┘            └──────────────────┘

核心概念:

  • 协调器 - 接收所有请求,并决定由谁处理
  • 专业智能体 - 专注于特定领域并在其中表现出色的智能体
  • 转交 - 从协调器无缝交接给专业智能体

可移植的 TeamSpec

当同一团队必须在生成的项目、CLI、Studio 预览环境和 Enterprise 部署中以完全相同的方式执行时,请使用 TeamSpec。该规范是可序列化的数据;compile 将每个声明的名称绑定到任意现有的 Agent 实现,并返回一个可执行的 CompiledTeam 根节点。它不会新增原子智能体类型。

use adk_agent::{
    Agent, RelationshipKind, TeamMemberSpec, TeamRelationship, TeamSpec,
};
use std::sync::Arc;

let spec = TeamSpec {
    name: "support_team".into(),
    description: "Customer support routing".into(),
    coordinator: "coordinator".into(),
    members: vec![
        TeamMemberSpec::new("coordinator"),
        TeamMemberSpec::new("billing_agent"),
        TeamMemberSpec::new("research_agent"),
    ],
    relationships: vec![
        TeamRelationship::new(
            "coordinator", "billing_agent", RelationshipKind::Handoff,
        ),
        TeamRelationship::new(
            "coordinator", "research_agent", RelationshipKind::Delegate,
        ),
    ],
    policy: Default::default(),
};

let root = spec.compile(vec![
    Arc::new(coordinator) as Arc<dyn Agent>,
    Arc::new(billing_agent),
    Arc::new(research_agent),
])?;

直接使用 crate 时,如果规范包含 Delegate,请启用 adk-agent 的 team-tools 特性。使用 umbrella crate 时,请启用 tools(或 standard 层级)。仅包含转交的规范使用基础 adk-agent 特性集即可编译。

关系控制行为降低后的机制
Handoff调用方转移控制权且不会恢复执行运行器 transfer_to_agent
Delegate调用方调用成员、接收其结果并恢复执行AgentTool

每个成员的目标都是精确的。编译器会拒绝重复的成员和边、缺失的端点、循环、不可达成员、无效边界、缺失的 agent 绑定以及多余的绑定。委托边可以携带输入/输出 JSON 模式、上下文和历史投影、精确的状态键、超时和审批要求、重试/回退行为以及熔断器。 交接边支持有界的启动重试/回退;仅限委托的字段会被拒绝,而不是被静默忽略。回退必须作为单独声明的边;验证绝不会扩大其权限。

团队范围的策略会限制交接/委托深度、并发数、事件数、模型和工具调用次数、令牌数、成本、关系数量以及墙钟时间。终止规则可以在发生升级、最终作者精确匹配或出现文本标记时停止。每个事件都会在 __adk_team_execution_v1 中携带可序列化的执行回执,其中包括冻结的成员名单、因果委托/交接边、汇总用量和终止状态。使用 CompiledTeam::execution_snapshot 检查该回执,并在使用相同调用 ID 恢复之前使用 restore_execution_snapshot。 活动中的交接会在其冻结的目标处恢复。未解决的委托不会被自动重放,因为通用的 Agent 契约无法证明工具调用具有幂等性;运行时会返回可执行的错误。应通过其自身的检查点机制恢复该委托,或明确失败/重新启动该操作。

对于可复用的架构,TeamArchitectureTemplate 会将监督器、路由器和层级预设降级为显式的 TeamSpec 数据。 WorkflowArchitectureTemplate 会基于现有工作流代理编译顺序、并行、扇出/扇入以及有界审查循环预设。基于能力的解析可通过 TeamAgentRegistry 使用;选择过程具有确定性,可以要求健康状态、版本、摘要和信任标签,并且选定的成员清单会被冻结在执行回执中。

执行平面能力

编译会检查源代理声明的 AgentCapabilities。委托源必须使用调用作用域内的运行时工具,移交源必须发出转移,而获批准的委托必须支持精确调用确认。 LlmAgent 和 CodeActAgent 声明其支持的运行时能力;自定义代理保留保守默认值,并且可以覆盖 capabilities()。

团队保留标准的 ADK 行为:Runner 插件、代理/模型/工具回调、护栏、取消、请求身份、记忆、工件、会话和事件持久化仍会包装现有成员代理。compile_with_hooks 为团队、成员以及精确关系边界添加有序的异步 TeamLifecycleHook 回调。钩子可以观察成功/失败,或拒绝待处理的操作,而不会扩大其目标范围。Runner 还会在经过验证的移交之前立即调用根代理的异步 govern_transfer 钩子。

原生 team.run、team.member.run 和 team.relationship.execute 跟踪跨度会通过常规的 adk-telemetry 导出器传递团队/成员/边的身份、状态和关系持续时间。运行时故障使用稳定的结构化代码,例如 agent.team.policy_denied、agent.team.budget_exceeded 和 agent.team.resume_unsafe。

委托契约区分状态读取(stateKeys)和写入(stateWriteKeys)。当父级值在委托期间发生变化时,stateMerge: rejectConflicts 会拒绝子级写入,而 artifactPrefixes 会限制工件名称。独立的 AgentTool 提供相同的控制。为保持兼容性,其默认行为仍为后写入者胜出;Team 关系默认拒绝冲突。

CompiledTeam::resume_plan 描述了恢复回执后唯一安全的下一步操作:协调器、交接目标、支持检查点的委托,或不安全的故障闭合委托。validate_team_replay 验证因果回执完整性,而 analyze_execution 报告无提供商拓扑覆盖率、失败情况以及用于测试和评估流水线的最大因果深度。

BlackboardSpec 涵盖受治理的轮询和选择器驱动的群聊。它限制轮次和转录投影,并且选择器转换使用精确的允许列表。现有的 sub_agent 组合方式仍受支持,以保持向后兼容,但它是内存中的树,而不是可移植的团队定义。


快速开始

1. 创建项目

cargo new multi_agent_demo
cd multi_agent_demo

将依赖项添加到 Cargo.toml:

[dependencies]
adk-rust = "2.1.0"
tokio = { version = "1", features = ["full"] }
dotenvy = "0.15"

使用你的 API 密钥创建 .env:

echo 'GOOGLE_API_KEY=your-api-key' > .env

2. 客户服务示例

以下是一个完整的可运行示例:

use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?);

    // Specialist: Billing Agent
    let billing_agent = LlmAgentBuilder::new("billing_agent")
        .description("Handles billing questions: payments, invoices, subscriptions, refunds")
        .instruction("You are a billing specialist. Help customers with:\n\
                     - Invoice questions and payment history\n\
                     - Subscription plans and upgrades\n\
                     - Refund requests\n\
                     - Payment method updates\n\
                     Be professional and provide clear information about billing matters.")
        .model(model.clone())
        .build()?;

    // Specialist: Technical Support Agent
    let support_agent = LlmAgentBuilder::new("support_agent")
        .description("Handles technical support: bugs, errors, troubleshooting, how-to questions")
        .instruction("You are a technical support specialist. Help customers with:\n\
                     - Troubleshooting errors and bugs\n\
                     - How-to questions about using the product\n\
                     - Configuration and setup issues\n\
                     - Performance problems\n\
                     Be patient and provide step-by-step guidance.")
        .model(model.clone())
        .build()?;

    // Coordinator: Routes to appropriate specialist
    let coordinator = LlmAgentBuilder::new("coordinator")
        .description("Main customer service coordinator")
        .instruction("You are a customer service coordinator. Analyze each customer request:\n\n\
                     - For BILLING questions (payments, invoices, subscriptions, refunds):\n\
                       Transfer to billing_agent\n\n\
                     - For TECHNICAL questions (errors, bugs, how-to, troubleshooting):\n\
                       Transfer to support_agent\n\n\
                     - For GENERAL greetings or unclear requests:\n\
                       Respond yourself and ask clarifying questions\n\n\
                     When transferring, briefly acknowledge the customer and explain the handoff.")
        .model(model.clone())
        .sub_agent(Arc::new(billing_agent))
        .sub_agent(Arc::new(support_agent))
        .build()?;

    println!("🏢 Customer Service Center");
    println!("   Coordinator → Billing Agent | Support Agent");
    println!();

    Launcher::new(Arc::new(coordinator)).run().await?;
    Ok(())
}

交互示例:

You: I have a question about my last invoice

[Agent: coordinator]
Assistant: I'll connect you with our billing specialist to help with your invoice question.

[Agent: billing_agent]
Assistant: Hello! I can help you with your invoice. What specific question do you have about your last invoice?

You: Why was I charged twice?

[Agent: billing_agent]
Assistant: I understand your concern about the duplicate charge. Let me help you investigate this...

多智能体交接的工作原理

整体概览

当你将子智能体添加到父智能体后,LLM 将获得交接控制权的能力:

                    ┌─────────────────────┐
    User Message    │                     │
   ─────────────────▶    COORDINATOR      │
                    │                     │
                    └──────────┬──────────┘
                               │
           "This is a billing question..."
                               │
              ┌────────────────┴────────────────┐
              │                                 │
              ▼                                 ▼
   ┌──────────────────┐              ┌──────────────────┐
   │  billing_agent   │              │  support_agent   │
   │  💰 Payments     │              │  🔧 Tech Issues  │
   │  📄 Invoices     │              │  🐛 Bug Reports  │
   └──────────────────┘              └──────────────────┘

逐步交接流程

以下是用户提出账单问题时的具体处理过程:

┌──────────────────────────────────────────────────────────────────────┐
│ STEP 1: User sends message                                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   User: "Why was I charged twice on my invoice?"                     │
│                                                                      │
│                              ↓                                       │
│                                                                      │
│   ┌──────────────────────────────────────┐                          │
│   │         COORDINATOR AGENT            │                          │
│   │  Receives message first              │                          │
│   └──────────────────────────────────────┘                          │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 2: LLM analyzes and decides to transfer                         │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   🧠 LLM thinks: "This is about an invoice charge..."                │
│                  "Invoice = billing topic..."                        │
│                  "I should transfer to billing_agent"                │
│                                                                      │
│   📞 LLM calls: transfer_to_agent(agent_name="billing_agent")        │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 3: Runner detects transfer and invokes target                   │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────┐     transfer event      ┌─────────────────┐           │
│   │ Runner  │ ─────────────────────▶  │  billing_agent  │           │
│   └─────────┘   (same user message)   └─────────────────┘           │
│                                                                      │
│   • Runner finds "billing_agent" in agent tree                       │
│   • Creates new context with SAME user message                       │
│   • Invokes billing_agent immediately                                │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 4: Target agent responds                                        │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────────────────────────────────────┐                       │
│   │           billing_agent responds        │                       │
│   │                                         │                       │
│   │  "I can help with your duplicate        │                       │
│   │   charge. Let me investigate..."        │                       │
│   └─────────────────────────────────────────┘                       │
│                                                                      │
│   ✅ User sees seamless response - no interruption!                  │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

实现这一过程的关键因素

组件作用
.sub_agent()在父级下注册专用代理
transfer_to_agent 工具存在子代理时自动注入
Agent descriptions帮助 LLM 决定由哪个 agent 处理什么
Runner检测转移事件并调用目标 agent
Shared session在转移过程中保留状态和历史记录

添加子代理前后对比

没有子代理 - 一个代理完成所有工作:

User ──▶ coordinator ──▶ Response (handles billing AND support)

使用子代理 - 专业代理负责各自的领域:

User ──▶ coordinator ──▶ billing_agent ──▶ Response (billing expert)
                    ──▶ support_agent ──▶ Response (tech expert)

分层多代理系统

对于复杂场景,你可以创建多级层次结构。每个代理都可以拥有自己的子代理,从而形成一棵树:

可视化:三级内容团队

                    ┌─────────────────────┐
                    │  PROJECT MANAGER    │  ← Level 1: Top-level coordinator
                    │  "Manage projects"  │
                    └──────────┬──────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │  CONTENT CREATOR    │  ← Level 2: Mid-level coordinator  
                    │  "Coordinate R&W"   │
                    └──────────┬──────────┘
                               │
              ┌────────────────┴────────────────┐
              │                                 │
              ▼                                 ▼
   ┌──────────────────┐              ┌──────────────────┐
   │   RESEARCHER     │              │     WRITER       │  ← Level 3: Specialists
   │                  │              │                  │
   │  📚 Gather facts │              │  ✍️ Write content │
   │  🔍 Analyze data │              │  📝 Polish text  │
   │  📊 Find sources │              │  🎨 Style & tone │
   └──────────────────┘              └──────────────────┘

请求如何向下流转

User: "Create a blog post about electric vehicles"
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│  PROJECT MANAGER: "This is a content task"                  │
│  → transfers to content_creator                             │
└─────────────────────────────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│  CONTENT CREATOR: "Need research first, then writing"       │
│  → transfers to researcher                                  │
└─────────────────────────────────────────────────────────────┘
                        │
                        ▼
┌─────────────────────────────────────────────────────────────┐
│  RESEARCHER: "Here's what I found about EVs..."             │
│  → provides research summary                                │
└─────────────────────────────────────────────────────────────┘

完整示例代码

use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?);

    // Level 3: Leaf specialists
    let researcher = LlmAgentBuilder::new("researcher")
        .description("Researches topics and gathers comprehensive information")
        .instruction("You are a research specialist. When asked to research a topic:\n\
                     - Gather key facts and data\n\
                     - Identify main themes and subtopics\n\
                     - Note important sources or references\n\
                     Provide thorough, well-organized research summaries.")
        .model(model.clone())
        .build()?;

    let writer = LlmAgentBuilder::new("writer")
        .description("Writes polished content based on research")
        .instruction("You are a content writer. When asked to write:\n\
                     - Create engaging, clear content\n\
                     - Use appropriate tone for the audience\n\
                     - Structure content logically\n\
                     - Polish for grammar and style\n\
                     Produce professional, publication-ready content.")
        .model(model.clone())
        .build()?;

    // Level 2: Content coordinator
    let content_creator = LlmAgentBuilder::new("content_creator")
        .description("Coordinates content creation by delegating research and writing")
        .instruction("You are a content creation lead. For content requests:\n\n\
                     - If RESEARCH is needed: Transfer to researcher\n\
                     - If WRITING is needed: Transfer to writer\n\
                     - For PLANNING or overview: Handle yourself\n\n\
                     Coordinate between research and writing phases.")
        .model(model.clone())
        .sub_agent(Arc::new(researcher))
        .sub_agent(Arc::new(writer))
        .build()?;

    // Level 1: Top-level manager
    let project_manager = LlmAgentBuilder::new("project_manager")
        .description("Manages projects and coordinates with content team")
        .instruction("You are a project manager. For incoming requests:\n\n\
                     - For CONTENT creation tasks: Transfer to content_creator\n\
                     - For PROJECT STATUS or general questions: Handle yourself\n\n\
                     Keep track of overall project goals and deadlines.")
        .model(model.clone())
        .sub_agent(Arc::new(content_creator))
        .build()?;

    println!("📊 Hierarchical Multi-Agent System");
    println!();
    println!("   project_manager");
    println!("       └── content_creator");
    println!("               ├── researcher");
    println!("               └── writer");
    println!();

    Launcher::new(Arc::new(project_manager)).run().await?;
    Ok(())
}

代理层次结构:

project_manager
└── content_creator
    ├── researcher
    └── writer

示例提示词:

  • “创建一篇关于医疗保健领域人工智能的博客文章” → 项目经理 → 内容创作者 → 写作者
  • “研究电动汽车” → 项目经理 → 内容创作者 → 研究员

子代理配置

使用 sub_agent() 构建器方法向任意 LlmAgent 添加子代理:

let parent = LlmAgentBuilder::new("parent")
    .description("Coordinates specialized tasks")
    .instruction("Route requests to appropriate specialists.")
    .model(model.clone())
    .sub_agent(Arc::new(specialist_a))
    .sub_agent(Arc::new(specialist_b))
    .build()?;

要点:

  • 每个代理都可以拥有多个子代理
  • 子代理可以拥有自己的子代理(多级层次结构)
  • 代理名称在层次结构中必须唯一
  • 描述有助于 LLM 决定将请求转交给哪个代理

编写有效的转交指令

要实现成功的代理转交,请提供清晰的指令和描述:

父代理指令

let coordinator = LlmAgentBuilder::new("coordinator")
    .description("Main customer service coordinator")
    .instruction("You are a customer service coordinator. Analyze each request:\n\n\
                 - For BILLING questions (payments, invoices, subscriptions):\n\
                   Transfer to billing_agent\n\n\
                 - For TECHNICAL questions (errors, bugs, troubleshooting):\n\
                   Transfer to support_agent\n\n\
                 - For GENERAL greetings or unclear requests:\n\
                   Respond yourself and ask clarifying questions")
    .model(model.clone())
    .sub_agent(Arc::new(billing_agent))
    .sub_agent(Arc::new(support_agent))
    .build()?;

子代理描述

let billing_agent = LlmAgentBuilder::new("billing_agent")
    .description("Handles billing questions: payments, invoices, subscriptions, refunds")
    .instruction("You are a billing specialist. Help with payment and subscription issues.")
    .model(model.clone())
    .build()?;

let support_agent = LlmAgentBuilder::new("support_agent")
    .description("Handles technical support: bugs, errors, troubleshooting, how-to questions")
    .instruction("You are a technical support specialist. Provide step-by-step guidance.")
    .model(model.clone())
    .build()?;

最佳实践:

  • 使用描述性代理名称,清楚表明其用途
  • 编写详细描述 - LLM 会使用这些描述来决定转交
  • 在描述中加入与用户可能请求相匹配的具体关键词
  • 在父代理指令中提供明确的委派规则
  • 在各代理描述中使用一致的术语

测试你的多代理系统

运行示例

cargo run --manifest-path examples/tier_examples/enterprise/Cargo.toml --bin 14-enterprise-multi-agent

示例测试提示词

客户服务:

  • “我对上一张发票有疑问” → 应路由至 billing_agent
  • “应用一直崩溃” → 应路由至 support_agent
  • “如何升级我的套餐?” → 应路由至 billing_agent
  • “您好,我需要帮助” → 应由 coordinator 继续处理以进行澄清

层级式:

  • “撰写一篇关于医疗保健领域 AI 的博客文章” → 项目经理 → 内容创作者 → 撰稿人
  • “研究电动汽车的历史” → 项目经理 → 内容创作者 → 研究员
  • “我们当前项目的状态如何?” → 应由 project_manager 继续处理

调试转移问题

如果转移未按预期工作:

  1. 检查代理名称 - 在转移调用中必须完全匹配
  2. 检查描述 - 使其更加具体并包含丰富的关键词
  3. 明确指令 - 明确说明何时进行转移
  4. 测试边界情况 - 尝试含义模糊的请求,观察路由行为
  5. 查找转移指示器 - [Agent: name] 会显示哪个代理正在响应

全局指令

基本用法

let agent = LlmAgentBuilder::new("assistant")
    .description("A helpful assistant")
    .global_instruction(
        "You are a professional assistant for Acme Corp. \
         Always maintain a friendly but professional tone. \
         Our company values are: customer-first, innovation, and integrity."
    )
    .instruction("Help users with their questions and tasks.")
    .model(model.clone())
    .build()?;

全局指令与代理指令

  • 全局指令:应用于层级结构中的所有代理,设置总体个性和上下文
  • 代理指令:针对每个代理,定义其特定角色和行为

这两种指令都会包含在对话历史中,其中全局指令位于最前面。

动态全局指令

对于更高级的场景,可以使用全局指令提供程序动态计算指令:

use adk_core::GlobalInstructionProvider;

let provider: GlobalInstructionProvider = Arc::new(|ctx| {
    Box::pin(async move {
        // Access context information
        let user_id = ctx.user_id();
        
        // Compute dynamic instruction
        let instruction = format!(
            "You are assisting user {}. Tailor your responses to their preferences.",
            user_id
        );
        
        Ok(instruction)
    })
});

let agent = LlmAgentBuilder::new("assistant")
    .description("A personalized assistant")
    .global_instruction_provider(provider)
    .model(model.clone())
    .build()?;

状态变量注入

全局指令和代理指令都支持使用 {variable} 语法注入状态变量:

// Set state in a previous agent or tool
// state["company_name"] = "Acme Corp"
// state["user_role"] = "manager"

let agent = LlmAgentBuilder::new("assistant")
    .global_instruction(
        "You are an assistant for {company_name}. \
         The user is a {user_role}."
    )
    .instruction("Help with {user_role}-level tasks.")
    .model(model.clone())
    .build()?;

框架会自动将会话状态中的值注入指令模板。

常见的多代理模式

协调器/分发器模式

中央 agent 将请求路由到专用子 agent:

let billing = LlmAgentBuilder::new("billing")
    .description("Handles billing and payment questions")
    .model(model.clone())
    .build()?;

let support = LlmAgentBuilder::new("support")
    .description("Provides technical support")
    .model(model.clone())
    .build()?;

let coordinator = LlmAgentBuilder::new("coordinator")
    .instruction("Route requests to billing or support agents as appropriate.")
    .sub_agent(Arc::new(billing))
    .sub_agent(Arc::new(support))
    .model(model.clone())
    .build()?;

示例对话:

User: I have a question about my last invoice

[Agent: coordinator]
Assistant: I'll connect you with our billing specialist.
🔄 [Transfer requested to: billing]

[Agent: billing]
Assistant: Hello! I can help you with your invoice. 
What specific question do you have?

User: Why was I charged twice?

[Agent: billing]
Assistant: Let me investigate that duplicate charge for you...

要点:

  • 协调器分析请求并将其转交给计费 agent
  • 计费 agent 在同一轮中立即响应
  • 后续消息继续由计费 agent 处理
  • 转交指示器(🔄)显示何时发生交接

分层任务分解

用于分解复杂任务的多级层次结构:

// Low-level specialists
let researcher = LlmAgentBuilder::new("researcher")
    .description("Researches topics and gathers information")
    .model(model.clone())
    .build()?;

let writer = LlmAgentBuilder::new("writer")
    .description("Writes content based on research")
    .model(model.clone())
    .build()?;

// Mid-level coordinator
let content_creator = LlmAgentBuilder::new("content_creator")
    .description("Creates content by coordinating research and writing")
    .sub_agent(Arc::new(researcher))
    .sub_agent(Arc::new(writer))
    .model(model.clone())
    .build()?;

// Top-level manager
let project_manager = LlmAgentBuilder::new("project_manager")
    .description("Manages content creation projects")
    .sub_agent(Arc::new(content_creator))
    .model(model.clone())
    .build()?;

与工作流 Agent 结合

多 agent 系统与工作流 agent(Sequential、Parallel、Loop)配合使用效果良好:

use adk_agent::workflow::{SequentialAgent, ParallelAgent};

// Create specialized agents
let validator = LlmAgentBuilder::new("validator")
    .instruction("Validate the input data.")
    .output_key("validation_result")
    .model(model.clone())
    .build()?;

let processor = LlmAgentBuilder::new("processor")
    .instruction("Process data if {validation_result} is valid.")
    .output_key("processed_data")
    .model(model.clone())
    .build()?;

// Combine in a sequential workflow
let pipeline = SequentialAgent::new(
    "validation_pipeline",
    vec![Arc::new(validator), Arc::new(processor)]
);

// Use the pipeline as a sub-agent
let coordinator = LlmAgentBuilder::new("coordinator")
    .description("Coordinates data processing")
    .sub_agent(Arc::new(pipeline))
    .model(model.clone())
    .build()?;

Agent 之间的通信

层次结构中的 agent 通过共享的会话状态进行通信:

// Agent A saves data to state
let agent_a = LlmAgentBuilder::new("agent_a")
    .instruction("Analyze the topic and save key points.")
    .output_key("key_points")  // Automatically saves output to state
    .model(model.clone())
    .build()?;

// Agent B reads data from state
let agent_b = LlmAgentBuilder::new("agent_b")
    .instruction("Expand on the key points: {key_points}")
    .model(model.clone())
    .build()?;

output_key 配置会自动将 agent 的最终响应保存到会话状态中,使其可供后续 agent 使用。

AgentTool 状态与工件转发

使用 AgentTool 将 agent 封装为工具时,子 agent 的状态更改和工件会自动转发到父上下文:

use adk_tool::AgentTool;

// Create a sub-agent that modifies state
let data_processor = LlmAgentBuilder::new("data_processor")
    .instruction("Process the data and save results.")
    .output_key("processed_data")
    .model(model.clone())
    .build()?;

// Wrap as a tool - state_delta and artifact_delta are forwarded
let processor_tool = AgentTool::new(Arc::new(data_processor));

// Parent agent can use the tool and see state changes
let coordinator = LlmAgentBuilder::new("coordinator")
    .instruction("Use the data_processor tool, then access {processed_data}.")
    .model(model.clone())
    .tool(Arc::new(processor_tool))
    .build()?;

AgentTool 在内部以非流式模式(StreamingMode::None)运行子 agent,因此子 agent 会先累积完整响应,然后再将其返回给父 agent。这样可以避免部分流式数据块产生空结果的问题。

这使得使用 AgentTool 模式时,父 agent 与子 agent 之间能够实现无缝的数据流转。

运行多 Agent 系统

使用启动器

Launcher 提供了一种运行和测试多 agent 系统的简便方式:

use adk_rust::Launcher;

let coordinator = /* your multi-agent setup */;

Launcher::new(Arc::new(coordinator))
    .run()
    .await?;

运行模式:

# Interactive console mode
cargo run --manifest-path examples/tier_examples/enterprise/Cargo.toml --bin 14-enterprise-multi-agent

# Use a generated API project when you need HTTP serving
cargo adk new multi_agent_api --template api

功能:

  • Agent 指示器:显示正在响应的 agent [Agent: coordinator]
  • 转移可视化:显示转移事件 🔄 [Transfer requested to: billing_agent]
  • 无缝交接:转移后目标 agent 会立即响应
  • 对话历史:在 agent 转移过程中保持上下文

测试转移

验证多 agent 系统是否正常运行:

  1. 检查 agent 名称:它们响应时应显示在方括号中
  2. 查找转移指示器:agent 交接时应显示 (🔄)
  3. 验证立即响应:目标 agent 无需重新提示即可响应
  4. 测试不同的请求类型:确保路由正确
  5. 检查边界情况:例如转移到不存在的 agent

调试转移问题

如果转移无法正常工作:

  • 验证子 agent 是否已添加:通过 .sub_agent()
  • 检查 agent 描述:LLM 使用这些描述来决定转移
  • 查看指令:父 agent 应说明何时进行转移
  • 检查 agent 名称:必须与转移调用中的名称完全匹配
  • 启用日志记录:在事件流中查看转移操作

最佳实践

  1. 清晰的描述:编写描述性的 agent 名称和描述,帮助 LLM 做出良好的转移决策
  2. 具体的指令:为每个 agent 提供清晰、专注于其角色的指令
  3. 使用全局指令:在所有 agent 之间设置一致的个性和上下文
  4. 状态管理:使用 output_key 和状态变量进行 agent 间通信
  5. 限制层级深度:保持层级较浅(2-3 层),以便更好地维护
  6. 测试转移逻辑:验证 agent 是否会针对不同请求转移到正确的子 agent

上一页:← 工作流代理 | 下一页:图代理 →

多智能体系统 - ADK-Rust 文档 | ADK-Rust