多智能体系统

通过将专业化的 Agent 组合成团队来构建复杂的应用程序。

Agent 层次结构概述

Rendering architecture…

您将构建什么

在本指南中,您将创建一个客户服务系统,其中协调器将查询路由给专家:

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

关键概念:

  • Coordinator - 接收所有请求,决定由谁处理
  • Specialists - 专注于特定领域的 Agent
  • Transfer - 从 coordinator 到 specialist 的无缝交接

快速开始

1. 创建您的项目

cargo new multi_agent_demo
cd multi_agent_demo

将依赖项添加到 Cargo.toml

[dependencies]
adk-rust = "2.0.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-2.5-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...

多 Agent 传输的工作原理

概览

当您向父代理添加子代理时,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 tool当存在子代理时自动注入
Agent 描述帮助 LLM 决定由哪个 Agent 处理任务
Runner检测转交事件并调用目标 Agent
共享会话在转交过程中保留状态和历史记录

添加子代理前后

不使用子代理 - 一个代理完成所有任务:

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

使用子代理 - 专家处理各自的领域:

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

分层多代理系统

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

可视化:3级内容团队

                    ┌─────────────────────┐
                    │  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-2.5-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

示例提示:

  • “创建一篇关于医疗保健中AI的博客文章” → PM → 内容创作者 → 作家
  • “研究电动汽车” → PM → 内容创作者 → 研究员

子代理配置

使用 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()?;

要点:

  • 每个 Agent 可以有多个子 Agent
  • 子 Agent 可以有自己的子 Agent(多级层次结构)
  • Agent 名称在层次结构中必须是唯一的
  • 描述有助于 LLM 决定转移到哪个 Agent

编写有效的转移指令

为了成功进行 Agent 转移,请提供清晰的指令和描述:

父 Agent 指令

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()?;

子 Agent 描述

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()?;

最佳实践:

  • 使用描述性的 Agent 名称,清晰地表明其目的
  • 编写详细的描述 - LLM 使用这些来决定转移
  • 在描述中包含与可能的用户请求相匹配的特定关键词
  • 在父 Agent 指令中给出明确的委托规则
  • 在 Agent 描述中使用一致的术语

测试您的多 Agent 系统

运行示例

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

示例测试提示

客户服务:

  • "我有一个关于我上一个账单的问题" → 应路由到 billing_agent
  • "应用程序一直崩溃" → 应路由到 support_agent
  • "我如何升级我的计划?" → 应路由到 billing_agent
  • "你好,我需要帮助" → 应保留在 coordinator 以进行澄清

分层:

  • "撰写一篇关于医疗保健中人工智能的博客文章" → 项目经理 → 内容创作者 → 作家
  • "研究电动汽车的历史" → 项目经理 → 内容创作者 → 研究员
  • "我们当前项目的状态是什么?" → 应保留在 project_manager

调试传输问题

如果传输未按预期工作:

  1. 检查 agent 名称 - 必须与 transfer 调用中的名称完全匹配
  2. 审查描述 - 使其更具体、更富含关键词
  3. 澄清指令 - 明确何时进行 transfer
  4. 测试边缘情况 - 尝试模糊请求以观察路由行为
  5. 寻找 transfer 指示器 - [Agent: name] 显示哪个 agent 正在响应

全局指令

基本用法

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()?;

全局指令与 Agent 指令

  • 全局指令: 应用于层次结构中的所有 agent,设置整体个性和上下文
  • Agent 指令: 特定于每个 agent,定义其特定角色和行为

这两种指令都包含在对话历史中,其中全局指令首先出现。

动态全局指令

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

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 (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 封装为 Tool 时,子 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 会在将其返回给父级之前累积其完整响应。这可以防止部分流式传输块产生空结果的问题。

这在使用 AgentTool 模式时,实现了父 Agent 和子 Agent 之间的数据无缝流动。

运行多智能体系统

使用 Launcher

Launcher 提供了一种运行和测试多智能体系统的简便方法:

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: coordinator]
  • 转移可视化:显示转移事件 🔄 [Transfer requested to: billing_agent]
  • 无缝交接:目标智能体在转移后立即响应
  • 对话历史:在智能体转移过程中保持上下文

测试转移

要验证您的多智能体系统是否正常工作:

  1. 检查智能体名称在响应时是否出现在括号中
  2. 查找转移指示器 (🔄) 当智能体交接时
  3. 验证目标智能体的即时响应,无需重新提示
  4. 测试不同的请求类型以确保正确路由
  5. 检查边缘情况,例如转移到不存在的智能体

调试传输问题

如果传输不起作用:

  • 验证子代理是否已添加 通过 .sub_agent()
  • 检查代理描述 - LLM 使用这些来决定传输
  • 审查指令 - 父级应提及何时进行传输
  • 检查代理名称 - 必须与传输调用中的名称完全匹配
  • 启用日志记录 以在事件流中查看传输操作

最佳实践

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

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