多智能体系统
通过将专业 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 tool | 当 sub-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
调试转移问题
如果转移未按预期工作:
- 检查代理名称 - 必须与转移调用中的名称精确匹配
- 审阅描述 - 使其更具体,包含更多关键词
- 澄清指令 - 明确何时进行转移
- 测试边缘情况 - 尝试模糊请求以查看路由行为
- 查找转移指示器 -
[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 转移之间保持上下文
测试转移
要验证您的多代理系统是否正常工作:
- 检查 Agent 名称,确保它们在响应时出现在括号中
- 查找转移指示器 (
🔄),当 Agent 进行切换时 - 验证目标 Agent 的即时响应,无需重新提示
- 测试不同请求类型,以确保正确路由
- 检查边缘情况,例如转移到不存在的 Agent
调试转移问题
如果转移不起作用:
- 验证子 Agent 是否已添加,通过
.sub_agent() - 检查 Agent 描述 - LLM 使用这些来决定转移
- 审查说明 - 父 Agent 应该提及何时进行转移
- 检查 Agent 名称 - 必须与转移调用中的名称完全匹配
- 启用日志记录 以查看事件流中的转移操作
最佳实践
- 清晰的描述:编写描述性的 Agent 名称和描述,以帮助 LLM 做出良好的转移决策
- 具体说明:为每个 Agent 提供清晰、集中的角色说明
- 使用全局指令:在所有 Agent 中设置一致的个性和上下文
- 状态管理:使用
output_key和状态变量进行 Agent 通信 - 限制层级深度:保持层级结构扁平(2-3级),以便更好地维护
- 测试转移逻辑:验证 Agent 针对不同请求转移到正确的子 Agent
相关
- LLM Agent - 核心 Agent 配置
- Workflow Agents - SequentialAgent, ParallelAgent 和 LoopAgent
- Sessions - Session 状态管理
上一页:← Workflow Agents | 下一页:Graph Agents →