Sistemas multiagente

Crea aplicaciones sofisticadas componiendo agentes especializados en equipos.

Descripción general de la jerarquía de agentes

Rendering architecture…

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ónComportamiento del controlMecanismo reducido
HandoffEl llamador transfiere el control y no lo recuperatransfer_to_agent del ejecutor
DelegateEl llamador invoca a un miembro, recibe su resultado y reanuda la ejecuciónAgentTool

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

ComponenteFunción
.sub_agent()Registra especialistas bajo el principal
transfer_to_agent herramientaSe inyecta automáticamente cuando existen subagentes
Descripciones de agentesAyuda a LLM a decidir qué agente gestiona cada tarea
EjecutorDetecta eventos de transferencia e invoca al agente objetivo
Sesión compartidaEl 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 coordinator para 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:

  1. Comprueba los nombres de los agentes - Deben coincidir exactamente en las llamadas de transferencia
  2. Revisa las descripciones - Hazlas más específicas y ricas en palabras clave
  3. Aclara las instrucciones - Sé explícito sobre cuándo realizar la transferencia
  4. Prueba casos límite - Intenta solicitudes ambiguas para observar el comportamiento del enrutamiento
  5. 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:

  1. Comprueba que los nombres de los agentes aparezcan entre corchetes cuando respondan
  2. Busca indicadores de transferencia (🔄) cuando los agentes cedan el control
  3. Verifica las respuestas inmediatas de los agentes de destino sin volver a solicitar una respuesta
  4. Prueba diferentes tipos de solicitudes para garantizar un enrutamiento correcto
  5. 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

  1. Descripciones claras: Escribe nombres y descripciones de agentes descriptivos para ayudar a LLM a tomar buenas decisiones de transferencia
  2. Instrucciones específicas: Proporciona a cada agente instrucciones claras y centradas para su función
  3. Usa una instrucción global: Establece una personalidad y un contexto coherentes en todos los agentes
  4. Gestión del estado: Usa output_key y variables de estado para la comunicación entre agentes
  5. Limita la profundidad de la jerarquía: Mantén las jerarquías poco profundas (2-3 niveles) para facilitar el mantenimiento
  6. Prueba la lógica de transferencia: Verifica que los agentes transfieran las solicitudes a los subagentes correctos

Anterior: ← Agentes de flujo de trabajo | Siguiente: Agentes de grafos →