Multi-Agent-Systeme

Erstelle anspruchsvolle Anwendungen, indem du spezialisierte Agenten zu Teams kombinierst.

Übersicht über die Agentenhierarchie

Rendering architecture…

Was du erstellen wirst

In diesem Leitfaden erstellst du ein Kundenservice-System, in dem ein Koordinator Anfragen an Spezialisten weiterleitet:

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

Wichtige Konzepte:

  • Koordinator – Empfängt alle Anfragen und entscheidet, wer sie bearbeitet
  • Spezialisten – Fokussierte Agenten, die in bestimmten Fachgebieten besonders leistungsfähig sind
  • Weiterleitung – Nahtlose Übergabe vom Koordinator an einen Spezialisten

Portable TeamSpec

Verwende TeamSpec, wenn dasselbe Team in generierten Projekten, im CLI, in der Studio-Vorschau und in Enterprise-Bereitstellungen identisch ausgeführt werden muss. Die Spezifikation besteht aus serialisierbaren Daten; compile verknüpft jeden deklarierten Namen mit einer beliebigen vorhandenen Agent- Implementierung und gibt einen ausführbaren CompiledTeam-Stamm zurück. Sie fügt keine neue Art atomarer Agenten hinzu.

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),
])?;

Für die direkte Verwendung des Crates aktiviere das Feature team-tools von adk-agent, wenn die Spezifikation Delegate enthält. Aktiviere beim übergeordneten Crate tools (oder die Stufe standard). Spezifikationen, die nur Weiterleitungen enthalten, werden mit dem Basis-Feature-Set adk-agent kompiliert.

BeziehungSteuerungsverhaltenAbgesenkter Mechanismus
HandoffDer Aufrufer überträgt die Kontrolle und nimmt die Ausführung nicht wieder aufRunner transfer_to_agent
DelegateDer Aufrufer ruft ein Mitglied auf, erhält dessen Ergebnis und nimmt die Ausführung wieder aufAgentTool

Ziele gelten exakt pro Mitglied. Der Compiler lehnt doppelte Mitglieder und Kanten, fehlende Endpunkte, Zyklen, nicht erreichbare Mitglieder, ungültige Grenzen, fehlende Agentenbindungen und zusätzliche Bindungen ab. Delegationskanten können Eingabe-/Ausgabe-JSON-Schemas, Kontext- und Verlaufsprojektion, exakte Zustandsschlüssel, Anforderungen an Zeitüberschreitungen und Genehmigungen, Wiederholungs-/Fallback-Verhalten sowie einen Circuit Breaker enthalten. Übergabekanten unterstützen begrenzte Wiederholungen/Fallbacks beim Start; ausschließlich für Delegationen vorgesehene Felder werden abgelehnt, anstatt stillschweigend ignoriert zu werden. Fallbacks müssen als separate Kanten deklariert werden; die Validierung erweitert deren Berechtigungen niemals.

Richtlinien auf Teamebene begrenzen die Tiefe von Übergaben/Delegationen, die Nebenläufigkeit, Ereignisse, Modell- und Tool-Aufrufe, Token, Kosten, Beziehungsanzahlen und die verstrichene Zeit. Beendigungsregeln können bei einer Eskalation, einem exakt festgelegten finalen Autor oder einer Textmarkierung abbrechen. Jedes Ereignis enthält in __adk_team_execution_v1 einen serialisierbaren Ausführungsnachweis, einschließlich des eingefrorenen Mitgliederbestands, der kausalen Delegations-/Übergabekanten, der aggregierten Nutzung und des terminalen Status. Verwenden Sie CompiledTeam::execution_snapshot, um ihn zu untersuchen, und restore_execution_snapshot, bevor Sie mit derselben Aufruf-ID fortfahren.

Eine aktive Übergabe wird am eingefrorenen Ziel fortgesetzt. Eine nicht aufgelöste Delegation wird nicht automatisch erneut abgespielt, da der generische Vertrag Agent nicht nachweisen kann, dass ein Tool-Aufruf idempotent ist; die Laufzeit gibt stattdessen einen handlungsorientierten Fehler zurück. Setzen Sie diese Delegation über ihren eigenen Checkpoint-Mechanismus fort oder schlagen Sie die Operation ausdrücklich fehl bzw. starten Sie sie neu.

Für wiederverwendbare Architekturen reduziert TeamArchitectureTemplate Supervisor-, Router- und hierarchische Voreinstellungen auf explizite TeamSpec-Daten. WorkflowArchitectureTemplate kompiliert sequenzielle, parallele, Fan-out/Fan-in- und begrenzte Review-Schleifen-Voreinstellungen aus vorhandenen Workflow-Agenten. Eine fähigkeitsbasierte Auflösung ist über TeamAgentRegistry verfügbar; die Auswahl ist deterministisch, kann Integritäts-, Versions-, Digest- und Vertrauenskennzeichnungen voraussetzen, und die ausgewählte Zusammenstellung wird im Ausführungsbeleg eingefroren.

Fähigkeiten der Ausführungsebene

Die Kompilierung überprüft die vom Quellagenten deklarierten AgentCapabilities. Eine Delegate-Quelle muss aufrufungsbezogene Laufzeitwerkzeuge verwenden, eine Handoff-Quelle muss Transfers ausgeben, und ein genehmigter Delegate muss eine Bestätigung für exakte Aufrufe unterstützen. LlmAgent und CodeActAgent deklarieren ihre unterstützten Laufzeitfähigkeiten; benutzerdefinierte Agenten behalten konservative Standardwerte bei und können capabilities() überschreiben.

Teams behalten das standardmäßige ADK-Verhalten bei: Runner-Plugins, Agenten-/Modell-/Werkzeug-Callbacks, Leitplanken, Abbruch, Anfrageidentität, Speicher, Artefakte, Sitzungen und Ereignispersistenz umschließen weiterhin die vorhandenen Mitgliedsagenten. compile_with_hooks fügt geordnete asynchrone TeamLifecycleHook-Callbacks für die Team-, Mitglieds- und exakten Beziehungsgrenzen hinzu. Hooks können Erfolg oder Fehler beobachten oder eine ausstehende Operation ablehnen, ohne deren Zielbereich zu erweitern. Runner ruft außerdem den asynchronen govern_transfer-Hook der Wurzel unmittelbar vor einer validierten Übergabe auf.

Native team.run-, team.member.run- und team.relationship.execute-Tracing-Spans übertragen Team-/Mitglieds-/Kantenidentität, Status und Beziehungsdauer über den normalen adk-telemetry-Exporter. Laufzeitfehler verwenden stabile strukturierte Codes wie agent.team.policy_denied, agent.team.budget_exceeded und agent.team.resume_unsafe.

Delegationsverträge unterscheiden Zustandslesevorgänge (stateKeys) von Schreibvorgängen (stateWriteKeys). stateMerge: rejectConflicts weist einen Schreibvorgang eines untergeordneten Agents zurück, wenn sich der übergeordnete Wert während der Delegation geändert hat, und artifactPrefixes begrenzt Artefaktnamen. Das eigenständige AgentTool stellt dieselben Steuerungsmöglichkeiten bereit. Die Standardeinstellung bleibt aus Kompatibilitätsgründen „der letzte Schreibvorgang gewinnt“; Beziehungen zwischen Teams verwenden standardmäßig die Ablehnung von Konflikten.

CompiledTeam::resume_plan beschreibt die einzig sichere nächste Aktion nach der Wiederherstellung einer Empfangsbestätigung: Koordinator, Übergabeziel, prüfpunktbewusster Delegate oder eine unsichere, bei Fehlern geschlossene Delegation. validate_team_replay überprüft die Integrität der kausalen Empfangsbestätigung, während analyze_execution die providerfreie Abdeckung der Topologie, Fehler und die maximale kausale Tiefe für Tests und Evaluierungspipelines meldet.

BlackboardSpec behandelt gesteuertes Round-Robin und durch einen Selektor gesteuerten Gruppenchat. Es begrenzt Runden und die Projektion des Transkripts, und bei Selektorübergängen werden exakte Zulassungslisten verwendet. Die bestehende sub_agent-Komposition wird aus Gründen der Abwärtskompatibilität weiterhin unterstützt, ist jedoch ein In-Memory-Baum und keine portable Teamdefinition.


Schnellstart

1. Projekt erstellen

cargo new multi_agent_demo
cd multi_agent_demo

Füge Abhängigkeiten zu Cargo.toml hinzu:

[dependencies]
adk-rust = "2.1.0"
tokio = { version = "1", features = ["full"] }
dotenvy = "0.15"

Erstelle .env mit deinem API-Schlüssel:

echo 'GOOGLE_API_KEY=your-api-key' > .env

2. Beispiel für einen Kundenservice

Hier ist ein vollständiges, funktionierendes Beispiel:

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(())
}

Beispielinteraktion:

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

So funktioniert die Übergabe zwischen mehreren Agents

Der Überblick

Wenn du einem übergeordneten Agent untergeordnete Agents hinzufügst, erhält der LLM die Möglichkeit, die Kontrolle zu übergeben:

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

Ablauf der Übergabe Schritt für Schritt

Hier ist genau beschrieben, was geschieht, wenn ein Benutzer eine Frage zur Abrechnung stellt:

┌──────────────────────────────────────────────────────────────────────┐
│ 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!                  │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘

Was macht dies möglich

KomponenteRolle
.sub_agent()Registriert Spezialisten unter dem übergeordneten Element
transfer_to_agent WerkzeugWird automatisch eingefügt, wenn Unteragenten vorhanden sind
AgentenbeschreibungenHilft LLM zu entscheiden, welcher Agent welche Aufgabe übernimmt
AusführerErkennt Übertragungsereignisse und ruft den Zielagenten auf
Gemeinsame SitzungStatus und Verlauf bleiben bei Übertragungen erhalten

Vorher vs. Nachher beim Hinzufügen von Unteragenten

Ohne Unteragenten – Ein Agent erledigt alles:

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

Mit Unteragenten – Spezialisten kümmern sich um ihren jeweiligen Bereich:

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

Hierarchische Multi-Agenten-Systeme

Für komplexe Szenarien können Sie mehrstufige Hierarchien erstellen. Jeder Agent kann eigene Unteragenten haben, wodurch ein Baum entsteht:

Visualisierung: Content-Team mit 3 Ebenen

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

So werden Anfragen weitergeleitet

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                                │
└─────────────────────────────────────────────────────────────┘

Vollständiger Beispielcode

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(())
}

Agentenhierarchie:

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

Beispielanfragen:

  • „Erstelle einen Blogbeitrag über KI im Gesundheitswesen“ → Projektmanager → Content-Ersteller → Autor
  • „Recherchiere Elektrofahrzeuge“ → Projektmanager → Content-Ersteller → Rechercheur

Konfiguration von Unteragenten

Fügen Sie jedem LlmAgent mit der sub_agent()-Builder-Methode Unteragenten hinzu:

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

Wichtige Punkte:

  • Jeder Agent kann mehrere Unteragenten haben
  • Unteragenten können eigene Unteragenten haben (mehrstufige Hierarchien)
  • Agentennamen müssen innerhalb der Hierarchie eindeutig sein
  • Beschreibungen helfen LLM bei der Entscheidung, an welchen Agenten die Anfrage weitergeleitet werden soll

Effektive Anweisungen zur Weiterleitung verfassen

Stellen Sie für erfolgreiche Agentenweiterleitungen klare Anweisungen und Beschreibungen bereit:

Anweisungen für den übergeordneten Agenten

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

Beschreibungen der Unteragenten

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

Bewährte Vorgehensweisen:

  • Verwenden Sie aussagekräftige Agentennamen, die ihren Zweck klar erkennen lassen
  • Verfassen Sie detaillierte Beschreibungen – LLM verwendet diese, um über Weiterleitungen zu entscheiden
  • Fügen Sie spezifische Schlüsselwörter in die Beschreibungen ein, die zu wahrscheinlichen Benutzeranfragen passen
  • Geben Sie in den Anweisungen des übergeordneten Agenten klare Delegierungsregeln an
  • Verwenden Sie in den Agentenbeschreibungen eine einheitliche Terminologie

Testen Ihres Multi-Agenten-Systems

Beispiele ausführen

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

Beispiel-Testanfragen

Kundenservice:

  • „Ich habe eine Frage zu meiner letzten Rechnung“ → Sollte an billing_agent weitergeleitet werden
  • „Die App stürzt ständig ab“ → Sollte an support_agent weitergeleitet werden
  • „Wie kann ich meinen Tarif upgraden?“ → Sollte an billing_agent weitergeleitet werden
  • „Hallo, ich brauche Hilfe“ → Sollte zur Klärung bei coordinator bleiben

Hierarchisch:

  • „Erstelle einen Blogbeitrag über KI im Gesundheitswesen“ → PM → Content Creator → Writer
  • „Recherchiere die Geschichte von Elektrofahrzeugen“ → PM → Content Creator → Researcher
  • „Wie ist der Status unserer aktuellen Projekte?“ → Sollte bei project_manager bleiben

Probleme bei der Weiterleitung beheben

Wenn Weiterleitungen nicht wie erwartet funktionieren:

  1. Agentennamen überprüfen – Sie müssen in Weiterleitungsaufrufen exakt übereinstimmen
  2. Beschreibungen überprüfen – Formuliere sie spezifischer und mit relevanten Schlüsselwörtern
  3. Anweisungen präzisieren – Lege ausdrücklich fest, wann weitergeleitet werden soll
  4. Grenzfälle testen – Probiere mehrdeutige Anfragen aus, um das Weiterleitungsverhalten zu beobachten
  5. Auf Weiterleitungsindikatoren achten – [Agent: name] zeigt, welcher Agent antwortet

Globale Anweisung

Grundlegende Verwendung

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

Globale Anweisung vs. Agentenanweisung

  • Globale Anweisung: Wird auf alle Agenten in der Hierarchie angewendet und legt die übergreifende Persönlichkeit sowie den Kontext fest
  • Agentenanweisung: Ist für jeden Agenten spezifisch und definiert dessen jeweilige Rolle und sein Verhalten

Beide Anweisungen werden in den Gesprächsverlauf aufgenommen, wobei die globale Anweisung zuerst erscheint.

Dynamische globale Anweisungen

Für fortgeschrittenere Szenarien kannst du einen Anbieter für globale Anweisungen verwenden, der die Anweisung dynamisch berechnet:

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

Injektion von Zustandsvariablen

Sowohl globale als auch Agentenanweisungen unterstützen die Injektion von Zustandsvariablen mithilfe der Syntax {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()?;

Das Framework fügt automatisch Werte aus dem Sitzungszustand in die Vorlagen der Anweisungen ein.

Allgemeine Muster für mehrere Agenten

Koordinator-/Dispatcher-Muster

Ein zentraler Agent leitet Anfragen an spezialisierte Subagenten weiter:

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

Beispielkonversation:

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

Wichtige Punkte:

  • Der Koordinator analysiert die Anfrage und übergibt sie an den Abrechnungsagenten
  • Der Abrechnungsagent antwortet sofort in derselben Runde
  • Nachfolgende Nachrichten werden mit dem Abrechnungsagenten fortgesetzt
  • Übergabeindikatoren (🔄) zeigen an, wann Übergaben stattfinden

Hierarchische Aufgabenzerlegung

Mehrstufige Hierarchien zur Aufteilung komplexer Aufgaben:

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

Kombination mit Workflow-Agenten

Multi-Agenten-Systeme funktionieren gut mit Workflow-Agenten (Sequenziell, Parallel, Schleife):

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

Kommunikation zwischen Agenten

Agenten in einer Hierarchie kommunizieren über den gemeinsamen Sitzungsstatus:

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

Die Konfiguration output_key speichert die endgültige Antwort eines Agenten automatisch im Sitzungsstatus, sodass sie nachfolgenden Agenten zur Verfügung steht.

AgentTool Status- und Artefaktweiterleitung

Bei Verwendung von AgentTool zum Verpacken von Agenten als Tools werden Statusänderungen und Artefakte von Subagenten automatisch an den übergeordneten Kontext weitergeleitet:

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 führt Subagenten intern im Nicht-Streaming-Modus (StreamingMode::None) aus, sodass der Subagent seine vollständige Antwort sammelt, bevor er sie an den übergeordneten Agenten zurückgibt. Dadurch werden Probleme verhindert, bei denen partielle Streaming-Blöcke leere Ergebnisse erzeugen könnten.

Dies ermöglicht einen nahtlosen Datenfluss zwischen übergeordneten und untergeordneten Agenten bei Verwendung des AgentTool-Musters.

Ausführen von Multi-Agenten-Systemen

Verwendung des Launchers

Launcher bietet eine einfache Möglichkeit, Multi-Agenten-Systeme auszuführen und zu testen:

use adk_rust::Launcher;

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

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

Ausführungsmodi:

# 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

Funktionen:

  • Agentenanzeigen: Zeigt, welcher Agent antwortet [Agent: coordinator]
  • Transfervisualisierung: Zeigt Transferereignisse an 🔄 [Transfer requested to: billing_agent]
  • Nahtlose Übergaben: Der Zielagent antwortet unmittelbar nach dem Transfer
  • Unterhaltungsverlauf: Bewahrt den Kontext über Agententransfers hinweg

Transfers testen

So überprüfen Sie, ob Ihr Multi-Agenten-System korrekt funktioniert:

  1. Agentennamen prüfen: Sie erscheinen in eckigen Klammern, wenn die Agenten antworten
  2. Nach Transferanzeigen suchen (🔄), wenn Agenten die Bearbeitung übergeben
  3. Unmittelbare Antworten der Zielagenten ohne erneute Aufforderung überprüfen
  4. Verschiedene Anfragearten testen, um eine korrekte Weiterleitung sicherzustellen
  5. Sonderfälle prüfen, z. B. die Übertragung an nicht vorhandene Agenten

Probleme bei Transfers beheben

Wenn Transfers nicht funktionieren:

  • Überprüfen, ob Subagenten hinzugefügt wurden über .sub_agent()
  • Agentenbeschreibungen prüfen – LLM verwendet diese, um Transfers zu entscheiden
  • Anweisungen überprüfen – der übergeordnete Agent sollte erwähnen, wann eine Übertragung erfolgen soll
  • Agentennamen prüfen – sie müssen in Transferaufrufen exakt übereinstimmen
  • Protokollierung aktivieren, um Transferaktionen im Ereignisstrom zu sehen

Best Practices

  1. Klare Beschreibungen: Verfassen Sie aussagekräftige Agentennamen und -beschreibungen, damit LLM gute Transferentscheidungen treffen kann
  2. Spezifische Anweisungen: Geben Sie jedem Agenten klare, fokussierte Anweisungen für seine Rolle
  3. Globale Anweisung verwenden: Legen Sie eine einheitliche Persönlichkeit und einen einheitlichen Kontext für alle Agenten fest
  4. Zustandsverwaltung: Verwenden Sie output_key und Zustandsvariablen für die Kommunikation zwischen Agenten
  5. Hierarchietiefe begrenzen: Halten Sie Hierarchien flach (2–3 Ebenen), um die Wartbarkeit zu verbessern
  6. Transferlogik testen: Überprüfen Sie, ob Agenten Anfragen an die richtigen Subagenten übertragen

Zurück: ← Workflow-Agenten | Weiter: Graph-Agenten →