Sistemas multiagente
Crea aplicaciones sofisticadas componiendo agentes especializados en equipos.
Descripción general de la jerarquía de agentes
Lo que crearás
En esta guía, crearás un Sistema de Atención al Cliente en el que un coordinador dirige las consultas a especialistas:
┌─────────────────────┐
User Query │ │
────────────────▶ │ COORDINATOR │
│ "Route to expert" │
└──────────┬──────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ BILLING AGENT │ │ SUPPORT AGENT │
│ │ │ │
│ 💰 Payments │ │ 🔧 Tech Issues │
│ 📄 Invoices │ │ 🐛 Bug Reports │
│ 💳 Subscriptions│ │ ❓ How-To │
└──────────────────┘ └──────────────────┘
Conceptos clave:
- Coordinador: recibe todas las solicitudes y decide quién las gestiona
- Especialistas: agentes enfocados que destacan en dominios específicos
- Transferencia: cambio fluido del coordinador al especialista
TeamSpec portable
Usa TeamSpec cuando el mismo equipo deba ejecutarse de forma idéntica en proyectos generados, CLI, la vista previa de Studio y las implementaciones de Enterprise. La especificación son datos serializables; compile vincula cada nombre declarado con cualquier implementación Agent existente y devuelve una raíz CompiledTeam ejecutable. No añade un nuevo tipo de agente atómico.
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),
])?;
Para usar directamente el crate, habilita la característica team-tools de adk-agent cuando la especificación contenga Delegate. Con el crate paraguas, habilita tools (o el nivel standard). Las especificaciones que solo usan transferencias se compilan con el conjunto de características base adk-agent.
| Relación | Comportamiento del control | Mecanismo reducido |
|---|---|---|
Handoff | El llamador transfiere el control y no lo recupera | transfer_to_agent del ejecutor |
Delegate | El llamador invoca a un miembro, recibe su resultado y reanuda la ejecución | AgentTool |
Los objetivos son exactos para cada miembro. El compilador rechaza miembros y aristas duplicados, puntos finales ausentes, ciclos, miembros inalcanzables, límites no válidos, vinculaciones de agentes ausentes y vinculaciones adicionales. Las aristas de delegación pueden transportar esquemas de entrada/salida JSON, proyección del contexto y del historial, claves de estado exactas, requisitos de tiempo de espera y aprobación, comportamiento de reintento/reserva y un interruptor de circuito. Las aristas de transferencia admiten reintentos/reservas de inicio acotados; los campos exclusivos de la delegación se rechazan en lugar de ignorarse silenciosamente. Las reservas deben declararse como aristas independientes; la validación nunca amplía su autoridad.
La política de todo el equipo establece límites para la profundidad de
transferencia/delegación, la concurrencia, los eventos, las llamadas al modelo
y a las herramientas, los tokens, el coste, el número de relaciones y el tiempo
de ejecución. Las reglas de terminación pueden detenerse ante una escalada, un
autor final exacto o un marcador de texto. Cada evento incluye un comprobante
de ejecución serializable en __adk_team_execution_v1, que contiene la lista congelada de
miembros, las aristas causales de delegación/transferencia, el uso agregado y
el estado terminal. Usa CompiledTeam::execution_snapshot para inspeccionarlo y restore_execution_snapshot antes de
reanudar con el mismo ID de invocación.
Una transferencia activa se reanuda en su objetivo congelado. Una delegación
no resuelta no se reproduce automáticamente porque el contrato genérico
Agent no puede demostrar que una llamada a una herramienta sea idempotente;
en su lugar, el entorno de ejecución devuelve un error accionable. Reanuda esa
delegación mediante su propio mecanismo de puntos de control o falla/reinicia
explícitamente la operación.
Para arquitecturas reutilizables, TeamArchitectureTemplate reduce los ajustes preestablecidos de supervisor, enrutador y jerárquicos a datos explícitos de TeamSpec. WorkflowArchitectureTemplate compila ajustes preestablecidos secuenciales, paralelos, de distribución y agregación, y de bucle de revisión acotado a partir de agentes de flujo de trabajo existentes. La resolución basada en capacidades está disponible mediante TeamAgentRegistry; la selección es determinista, puede requerir etiquetas de estado, versión, resumen y confianza, y la lista seleccionada queda congelada en el comprobante de ejecución.
Capacidades del plano de ejecución
La compilación comprueba la AgentCapabilities declarada por el agente de origen. Un origen delegado debe consumir herramientas de tiempo de ejecución limitadas al ámbito de la invocación, un origen de transferencia debe emitir transferencias y un delegado aprobado debe admitir la confirmación de llamada exacta. LlmAgent y CodeActAgent declaran sus capacidades de tiempo de ejecución compatibles; los agentes personalizados conservan valores predeterminados conservadores y pueden sobrescribir capabilities().
Los equipos conservan el comportamiento estándar de ADK: los complementos de Runner, las devoluciones de llamada de agente/modelo/herramienta, las barreras de protección, la cancelación, la identidad de la solicitud, la memoria, los artefactos, las sesiones y la persistencia de eventos siguen envolviendo los agentes miembro existentes. compile_with_hooks añade devoluciones de llamada asíncronas TeamLifecycleHook ordenadas para los límites del equipo, de los miembros y de las relaciones exactas. Los hooks pueden observar el éxito o el fallo, o denegar una operación pendiente sin ampliar su destino. Runner también llama al hook asíncrono govern_transfer de la raíz inmediatamente antes de una transferencia validada.
Los spans de trazado nativos de team.run, team.member.run y team.relationship.execute transportan la identidad del equipo/miembro/borde, el estado y la duración de la relación mediante el exportador normal de adk-telemetry. Los fallos de tiempo de ejecución utilizan códigos estructurados estables como agent.team.policy_denied, agent.team.budget_exceeded y agent.team.resume_unsafe.
Los contratos de delegación distinguen las lecturas de estado (stateKeys) de las escrituras
(stateWriteKeys). stateMerge: rejectConflicts rechaza una escritura secundaria cuando el
valor principal cambió durante la delegación, y artifactPrefixes limita los nombres
de los artefactos. AgentTool independiente expone los mismos controles. Su valor
predeterminado sigue siendo que gana la última escritura por compatibilidad; las
relaciones de Team rechazan los conflictos de forma predeterminada.
CompiledTeam::resume_plan describe la única acción siguiente segura después de restaurar
un recibo: coordinador, objetivo de transferencia, delegado consciente de los
puntos de control o delegación insegura con cierre ante fallos. validate_team_replay verifica la
integridad causal del recibo, mientras que analyze_execution informa sobre la cobertura de la
topología independiente del proveedor, los fallos y la profundidad causal máxima
para las pruebas y las canalizaciones de evaluación.
BlackboardSpec abarca el chat grupal gobernado por turnos y controlado por un selector. Establece
límites para los turnos y la proyección de la transcripción, y las transiciones del
selector utilizan listas de permitidos exactas. La composición existente de
sub_agent sigue siendo compatible con versiones anteriores, pero es un árbol en
memoria en lugar de una definición de equipo portátil.
Inicio rápido
1. Crea tu proyecto
cargo new multi_agent_demo
cd multi_agent_demo
Añade dependencias a Cargo.toml:
[dependencies]
adk-rust = "2.1.0"
tokio = { version = "1", features = ["full"] }
dotenvy = "0.15"
Crea .env con tu clave de API:
echo 'GOOGLE_API_KEY=your-api-key' > .env
2. Ejemplo de atención al cliente
Este es un ejemplo completo y funcional:
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(())
}
Interacción de ejemplo:
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...
Cómo funciona la transferencia entre múltiples agentes
La visión general
Cuando añades subagentes a un agente principal, LLM adquiere la capacidad de transferir el control:
┌─────────────────────┐
User Message │ │
─────────────────▶ COORDINATOR │
│ │
└──────────┬──────────┘
│
"This is a billing question..."
│
┌────────────────┴────────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ billing_agent │ │ support_agent │
│ 💰 Payments │ │ 🔧 Tech Issues │
│ 📄 Invoices │ │ 🐛 Bug Reports │
└──────────────────┘ └──────────────────┘
Flujo de transferencia paso a paso
Esto es exactamente lo que ocurre cuando un usuario hace una pregunta sobre facturación:
┌──────────────────────────────────────────────────────────────────────┐
│ 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! │
│ │
└──────────────────────────────────────────────────────────────────────┘
Qué hace que funcione
| Componente | Función |
|---|---|
.sub_agent() | Registra especialistas bajo el principal |
transfer_to_agent herramienta | Se inyecta automáticamente cuando existen subagentes |
| Descripciones de agentes | Ayuda a LLM a decidir qué agente gestiona cada tarea |
| Ejecutor | Detecta eventos de transferencia e invoca al agente objetivo |
| Sesión compartida | El estado y el historial se conservan durante las transferencias |
Antes y después de añadir subagentes
Sin subagentes: un agente lo hace todo:
User ──▶ coordinator ──▶ Response (handles billing AND support)
Con subagentes: los especialistas se encargan de su área:
User ──▶ coordinator ──▶ billing_agent ──▶ Response (billing expert)
──▶ support_agent ──▶ Response (tech expert)
Sistemas multiagente jerárquicos
Para escenarios complejos, puedes crear jerarquías de varios niveles. Cada agente puede tener sus propios subagentes, formando un árbol:
Visualización: equipo de contenido de 3 niveles
┌─────────────────────┐
│ 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 │
└──────────────────┘ └──────────────────┘
Cómo fluyen las solicitudes hacia abajo
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 │
└─────────────────────────────────────────────────────────────┘
Código de ejemplo completo
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(())
}
Jerarquía de agentes:
project_manager
└── content_creator
├── researcher
└── writer
Ejemplos de prompts:
- "Crea una publicación de blog sobre la IA en la atención sanitaria" → PM → Creador de contenido → Redactor
- "Investiga los vehículos eléctricos" → PM → Creador de contenido → Investigador
Configuración de subagentes
Añade subagentes a cualquier LlmAgent mediante el método de construcción sub_agent():
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()?;
Puntos clave:
- Cada agente puede tener varios subagentes
- Los subagentes pueden tener sus propios subagentes (jerarquías de varios niveles)
- Los nombres de los agentes deben ser únicos dentro de la jerarquía
- Las descripciones ayudan a LLM a decidir a qué agente transferir la solicitud
Redacción de instrucciones de transferencia eficaces
Para lograr transferencias de agentes correctas, proporciona instrucciones y descripciones claras:
Instrucciones del agente principal
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()?;
Descripciones de los subagentes
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()?;
Prácticas recomendadas:
- Usa nombres de agentes descriptivos que indiquen claramente su propósito
- Escribe descripciones detalladas; LLM las utiliza para decidir las transferencias
- Incluye palabras clave específicas en las descripciones que coincidan con las solicitudes probables de los usuarios
- Proporciona reglas claras de delegación en las instrucciones del agente principal
- Usa terminología coherente en las descripciones de los agentes
Prueba de tu sistema multiagente
Ejecución de ejemplos
cargo run --manifest-path examples/tier_examples/enterprise/Cargo.toml --bin 14-enterprise-multi-agent
Prompts de prueba de ejemplo
Atención al cliente:
- "Tengo una pregunta sobre mi última factura" → Debe dirigirse a
billing_agent - "La aplicación sigue fallando" → Debe dirigirse a
support_agent - "¿Cómo actualizo mi plan?" → Debe dirigirse a
billing_agent - "Hola, necesito ayuda" → Debe permanecer con
coordinatorpara aclaraciones
Jerárquico:
- "Crea una publicación de blog sobre la IA en la atención sanitaria" → PM → Content Creator → Writer
- "Investiga la historia de los vehículos eléctricos" → PM → Content Creator → Researcher
- "¿Cuál es el estado de nuestros proyectos actuales?" → Debe permanecer con
project_manager
Depuración de problemas de transferencia
Si las transferencias no funcionan como se esperaba:
- Comprueba los nombres de los agentes - Deben coincidir exactamente en las llamadas de transferencia
- Revisa las descripciones - Hazlas más específicas y ricas en palabras clave
- Aclara las instrucciones - Sé explícito sobre cuándo realizar la transferencia
- Prueba casos límite - Intenta solicitudes ambiguas para observar el comportamiento del enrutamiento
- Busca indicadores de transferencia -
[Agent: name]muestra qué agente está respondiendo
Instrucción global
Uso básico
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()?;
Instrucción global frente a instrucción del agente
- Instrucción global: Se aplica a todos los agentes de la jerarquía y establece la personalidad y el contexto generales
- Instrucción del agente: Específica para cada agente; define su función y comportamiento particulares
Ambas instrucciones se incluyen en el historial de la conversación, y la instrucción global aparece primero.
Instrucciones globales dinámicas
Para escenarios más avanzados, puedes utilizar un proveedor de instrucciones globales que calcule la instrucción dinámicamente:
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()?;
Inyección de variables de estado
Tanto las instrucciones globales como las de los agentes admiten la inyección de variables de estado mediante la sintaxis {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()?;
El framework inyecta automáticamente los valores del estado de la sesión en las plantillas de instrucciones.
Patrones comunes multiagente
Patrón de coordinador/despachador
Un agente central dirige las solicitudes a subagentes especializados:
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()?;
Conversación de ejemplo:
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...
Puntos clave:
- El coordinador analiza la solicitud y transfiere al agente de facturación
- El agente de facturación responde inmediatamente en el mismo turno
- Los mensajes posteriores continúan con el agente de facturación
- Los indicadores de transferencia (
🔄) muestran cuándo se producen las transferencias
Descomposición jerárquica de tareas
Jerarquías de varios niveles para desglosar tareas complejas:
// 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()?;
Combinación con agentes de flujo de trabajo
Los sistemas multiagente funcionan bien con agentes de flujo de trabajo (secuenciales, paralelos y de bucle):
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()?;
Comunicación entre agentes
Los agentes de una jerarquía se comunican mediante el estado de sesión compartido:
// 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()?;
La configuración output_key guarda automáticamente la respuesta final de un agente en el estado de la sesión, lo que la hace disponible para los agentes posteriores.
AgentTool Reenvío de estado y artefactos
Al usar AgentTool para envolver agentes como herramientas, los cambios de estado y los artefactos de los subagentes se reenvían automáticamente al contexto del agente principal:
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 ejecuta internamente los subagentes en modo no streaming (StreamingMode::None), por lo que el subagente acumula su respuesta completa antes de devolverla al agente principal. Esto evita problemas en los que fragmentos parciales del streaming podrían generar resultados vacíos.
Esto permite un flujo de datos fluido entre los agentes principales y secundarios al usar el patrón AgentTool.
Ejecución de sistemas multiagente
Uso del lanzador
Launcher proporciona una forma sencilla de ejecutar y probar sistemas multiagente:
use adk_rust::Launcher;
let coordinator = /* your multi-agent setup */;
Launcher::new(Arc::new(coordinator))
.run()
.await?;
Modos de ejecución:
# 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
Características:
- Indicadores de agente: Muestra qué agente está respondiendo
[Agent: coordinator] - Visualización de transferencias: Muestra los eventos de transferencia
🔄 [Transfer requested to: billing_agent] - Traspasos fluidos: El agente de destino responde inmediatamente después de la transferencia
- Historial de conversación: Mantiene el contexto entre las transferencias de agentes
Probar transferencias
Para verificar que el sistema multiagente funciona correctamente:
- Comprueba que los nombres de los agentes aparezcan entre corchetes cuando respondan
- Busca indicadores de transferencia (
🔄) cuando los agentes cedan el control - Verifica las respuestas inmediatas de los agentes de destino sin volver a solicitar una respuesta
- Prueba diferentes tipos de solicitudes para garantizar un enrutamiento correcto
- Comprueba casos extremos, como transferencias a agentes inexistentes
Depurar problemas de transferencia
Si las transferencias no funcionan:
- Verifica que los subagentes se hayan añadido mediante
.sub_agent() - Comprueba las descripciones de los agentes: LLM las utiliza para decidir las transferencias
- Revisa las instrucciones: el agente principal debe indicar cuándo realizar la transferencia
- Comprueba los nombres de los agentes: deben coincidir exactamente en las llamadas de transferencia
- Activa el registro para ver las acciones de transferencia en el flujo de eventos
Prácticas recomendadas
- Descripciones claras: Escribe nombres y descripciones de agentes descriptivos para ayudar a LLM a tomar buenas decisiones de transferencia
- Instrucciones específicas: Proporciona a cada agente instrucciones claras y centradas para su función
- Usa una instrucción global: Establece una personalidad y un contexto coherentes en todos los agentes
- Gestión del estado: Usa
output_keyy variables de estado para la comunicación entre agentes - Limita la profundidad de la jerarquía: Mantén las jerarquías poco profundas (2-3 niveles) para facilitar el mantenimiento
- Prueba la lógica de transferencia: Verifica que los agentes transfieran las solicitudes a los subagentes correctos
Relacionado
- LLM Agente - Configuración básica del agente
- Agentes de flujo de trabajo - Agentes secuenciales, paralelos y de bucle
- Sesiones - Gestión del estado de la sesión
Anterior: ← Agentes de flujo de trabajo | Siguiente: Agentes de grafos →