多智能体系统

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

您将构建什么

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

                        ┌─────────────────────┐
       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 = { version = "0.2", features = ["agents", "models", "cli"] }
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")?);

    // 专家:计费代理
    let billing_agent = LlmAgentBuilder::new("billing_agent")
        .description("处理计费问题:付款、发票、订阅、退款")
        .instruction("您是计费专家。帮助客户处理:\n\
                     - 发票问题和付款历史\n\
                     - 订阅计划和升级\n\
                     - 退款请求\n\
                     - 付款方式更新\n\
                     请保持专业并提供清晰的计费信息。")
        .model(model.clone())
        .build()?;

    // 专家:技术支持代理
    let support_agent = LlmAgentBuilder::new("support_agent")
        .description("处理技术支持:错误、故障、故障排除、操作方法问题")
        .instruction("您是技术支持专家。帮助客户处理:\n\
                     - 故障排除错误和故障\n\
                     - 关于产品使用方法的问题\n\
                     - 配置和设置问题\n\
                     - 性能问题\n\
                     请耐心并提供分步指导。")
        .model(model.clone())
        .build()?;

    // 协调员:路由到适当的专家
    let coordinator = LlmAgentBuilder::new("coordinator")
        .description("主要客户服务协调员")
        .instruction("您是客户服务协调员。分析每个客户请求:\n\n\
                     - 对于计费问题(付款、发票、订阅、退款):\n\
                       转接给 billing_agent\n\n\
                     - 对于技术问题(错误、故障、操作方法、故障排除):\n\
                       转接给 support_agent\n\n\
                     - 对于一般问候或不明确的请求:\n\
                       自行回复并提出澄清问题\n\n\
                     转接时,简要地向客户确认并解释移交过程。")
        .model(model.clone())
        .sub_agent(Arc::new(billing_agent))
        .sub_agent(Arc::new(support_agent))
        .build()?;

    println!("🏢 客户服务中心");
    println!("   协调员 → 计费代理 | 支持代理");
    println!();

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

示例交互:

您:我有一个关于我上一张发票的问题

[Agent: coordinator]
助手:我将为您转接我们的计费专家,帮助您解决发票问题。

[Agent: billing_agent]
助手:您好!我可以帮助您处理发票问题。关于您上一张发票,您有什么具体问题?

您:为什么我被收取了两次费用?

[Agent: billing_agent]
助手:我理解您对重复收费的担忧。让我来帮助您调查这个问题...

Multi-Agent 转移工作原理

概览

当您向 parent agent 添加 sub-agents 时,LLM 获得了委派任务的能力:

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

逐步转移流程

当用户询问一个账单问题时,具体会发生什么:

┌──────────────────────────────────────────────────────────────────────┐
│ STEP 1: 用户发送消息                                           │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   User: "Why was I charged twice on my invoice?"                     │
│                                                                      │
│                              ↓                                       │
│                                                                      │
│   ┌──────────────────────────────────────┐                          │
│   │         COORDINATOR AGENT            │                          │
│   │  首先接收消息                          │                          │
│   └──────────────────────────────────────┘                          │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 2: LLM 分析并决定进行转移                                        │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   🧠 LLM 思考:"这是关于发票费用的..."                                │
│                  "发票 = 账单话题..."                                │
│                  "我应该转移到 billing_agent"                          │
│                                                                      │
│   📞 LLM 调用: transfer_to_agent(agent_name="billing_agent")        │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 3: Runner 检测到转移并调用目标                                   │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────┐     转移事件      ┌─────────────────┐           │
│   │ Runner  │ ─────────────────────▶  │  billing_agent  │           │
│   └─────────┘   (相同的用户消息)   └─────────────────┘           │
│                                                                      │
│   • Runner 在 agent 树中找到 "billing_agent"                       │
│   • 使用相同的用户消息创建新上下文                                     │
│   • 立即调用 billing_agent                                          │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────────┐
│ STEP 4: 目标 agent 响应                                               │
├──────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   ┌─────────────────────────────────────────┐                       │
│   │           billing_agent 响应            │                       │
│   │                                         │                       │
│   │  "我可以帮您处理重复扣费的问题。           │                       │
│   │   让我调查一下..."                      │                       │
│   └─────────────────────────────────────────┘                       │
│                                                                      │
│   ✅ 用户看到无缝响应 - 没有中断!                                     │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

实现原理

Component角色
.sub_agent()parent 下注册专家
transfer_to_agent toolsub-agents 存在时自动注入
Agent descriptions帮助 LLM 决定哪个 Agent 处理什么
Runner检测转移事件并调用目标 agent
共享 Session在跨转移中保留状态和历史记录

添加 sub-agents 前后的对比

没有 sub-agents - 一个 agent 完成所有任务:

User ──▶ coordinator ──▶ Response (处理账单和支持)

sub-agents - 专家处理各自的领域:

User ──▶ coordinator ──▶ billing_agent ──▶ Response (账单专家)
                    ──▶ support_agent ──▶ Response (技术专家)

分层多 Agent 系统

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

可视化: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(())
}

Agent 层次结构:

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

示例提示:

  • "Create a blog post about AI in healthcare" → 项目经理 → 内容创作者 → 作家
  • "Research electric vehicles" → 项目经理 → 内容创作者 → 研究员

子代理配置

使用 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 使用这些描述来决定转移
  • 在描述中包含与可能的用户请求匹配的特定关键词
  • 在父代理指令中给出明确的委托规则
  • 在所有代理描述中使用一致的术语

测试您的多代理系统

运行示例

# Run the customer service example
cargo run --bin customer_service

# Run the hierarchical example  
cargo run --bin hierarchical

示例测试提示

客户服务:

  • “我有一个关于我上张发票的问题” → 应该路由到 billing_agent
  • “应用程序一直崩溃” → 应该路由到 support_agent
  • “如何升级我的套餐?” → 应该路由到 billing_agent
  • “你好,我需要帮助” → 应该停留在 coordinator 以进行澄清

层次结构:

  • “撰写一篇关于医疗保健中 AI 的博客文章” → PM → Content Creator → Writer
  • “研究电动汽车的历史” → PM → Content Creator → Researcher
  • “我们当前项目的状态如何?” → 应该停留在 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()?;

全局指令与 Agent 指令

  • Global Instruction: 应用于层级结构中的所有 Agent,设定整体的个性/上下文
  • Agent Instruction: 特定于每个 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()?;

状态变量注入

全局指令和 Agent 指令都支持使用 {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()?;

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

常见的多智能体模式

协调器/调度器模式

一个中心智能体将请求路由到专门的子智能体:

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...

关键点:

  • 协调器分析请求并将其转交给 billing agent
  • billing agent 在同一轮次中立即响应
  • 后续消息继续与 billing 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()?;

与工作流智能体结合

多智能体系统与工作流智能体(SequentialAgent、ParallelAgent、LoopAgent)配合良好:

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

智能体之间的通信

层次结构中的智能体通过共享 Session 状态进行通信:

// 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 配置会自动将智能体的最终响应保存到 Session 状态,使其可供后续智能体使用。

运行多代理系统

使用 Launcher

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

use adk_rust::Launcher;

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

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

运行模式:

# Interactive console mode
cargo run --example multi_agent -- chat

# Web server mode with UI
cargo run --example multi_agent -- serve
cargo run --example multi_agent -- serve --port 3000

特性:

  • 代理指示器:显示哪个 Agent 正在响应 [Agent: coordinator]
  • 转移可视化:显示转移事件 🔄 [Transfer requested to: billing_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

相关


上一页← Workflow Agents | 下一页Graph Agents →