Systèmes multi-agents
Créez des applications sophistiquées en composant des agents spécialisés au sein d’équipes.
Vue d’ensemble de la hiérarchie des agents
Ce que vous allez créer
Dans ce guide, vous allez créer un Système de service client dans lequel un coordinateur achemine les demandes vers des spécialistes :
┌─────────────────────┐
User Query │ │
────────────────▶ │ COORDINATOR │
│ "Route to expert" │
└──────────┬──────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ BILLING AGENT │ │ SUPPORT AGENT │
│ │ │ │
│ 💰 Payments │ │ 🔧 Tech Issues │
│ 📄 Invoices │ │ 🐛 Bug Reports │
│ 💳 Subscriptions│ │ ❓ How-To │
└──────────────────┘ └──────────────────┘
Concepts clés :
- Coordinateur - Reçoit toutes les demandes et décide qui les traite
- Spécialistes - Agents spécialisés qui excellent dans des domaines précis
- Transfert - Passage fluide du coordinateur au spécialiste
TeamSpec portable
Utilisez TeamSpec lorsque la même équipe doit s’exécuter de manière identique dans les
projets générés, CLI, l’aperçu Studio et les déploiements Enterprise. La
spécification est constituée de données sérialisables ; compile associe chaque nom
déclaré à toute implémentation Agent existante et renvoie une racine
CompiledTeam exécutable. Cela n’ajoute pas de nouveau type d’agent atomique.
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),
])?;
Pour utiliser directement la crate, activez la fonctionnalité team-tools de
adk-agent lorsque la spécification contient Delegate. Avec la crate parapluie, activez
tools (ou le niveau standard). Les spécifications utilisant uniquement des
transferts se compilent avec l’ensemble de fonctionnalités de base adk-agent.
| Relation | Comportement de contrôle | Mécanisme abaissé |
|---|---|---|
Handoff | L’appelant transfère le contrôle et ne reprend pas l’exécution | Runner transfer_to_agent |
Delegate | L’appelant invoque un membre, reçoit son résultat et reprend l’exécution | AgentTool |
Les cibles sont exactes pour chaque membre. Le compilateur rejette les membres et arêtes en double, les points de terminaison manquants, les cycles, les membres inaccessibles, les limites invalides, les liaisons d’agents manquantes et les liaisons supplémentaires. Les arêtes de délégation peuvent transporter des schémas d’entrée/sortie JSON, une projection du contexte et de l’historique, des clés d’état exactes, des exigences de délai d’expiration et d’approbation, un comportement de nouvelle tentative/de repli, ainsi qu’un disjoncteur. Les arêtes de transfert prennent en charge une nouvelle tentative/un repli de démarrage borné ; les champs réservés à la délégation sont rejetés plutôt que d’être ignorés silencieusement. Les replis doivent être déclarés comme des arêtes distinctes ; la validation n’élargit jamais leur autorité.
La politique applicable à toute l’équipe borne la profondeur des transferts et
délégations, la concurrence, les événements, les appels au modèle et aux outils,
les jetons, le coût, le nombre de relations et le temps écoulé. Les règles de
terminaison peuvent arrêter l’exécution en cas d’escalade, lorsqu’un auteur
final exact est atteint ou lorsqu’un marqueur textuel est rencontré. Chaque
événement contient un reçu d’exécution sérialisable dans __adk_team_execution_v1, notamment
la liste des membres figée, les arêtes causales de délégation/transfert,
l’utilisation agrégée et l’état terminal. Utilisez CompiledTeam::execution_snapshot pour l’inspecter, puis
restore_execution_snapshot avant de reprendre avec le même identifiant d’invocation.
Un transfert actif reprend au niveau de sa cible figée. Une délégation non
résolue n’est pas automatiquement rejouée, car le contrat générique
Agent ne peut pas prouver qu’un appel d’outil est idempotent ; l’exécution
renvoie à la place une erreur exploitable. Reprenez cette délégation via son
propre mécanisme de point de contrôle ou échouez/redémarrez explicitement
l’opération.
Pour les architectures réutilisables, TeamArchitectureTemplate réduit les préréglages de superviseur,
de routeur et hiérarchiques à des données TeamSpec explicites.
WorkflowArchitectureTemplate compile les préréglages séquentiels, parallèles, de répartition/agrégation
et de boucle de révision bornée à partir d’agents de workflow existants. La résolution
fondée sur les capacités est disponible via TeamAgentRegistry ; la sélection est déterministe,
peut exiger des étiquettes d’état, de version, de condensat et de confiance, et la
liste retenue est figée dans le reçu d’exécution.
Capacités du plan d’exécution
La compilation vérifie le AgentCapabilities déclaré par l’agent source. Une
source déléguée doit consommer les outils d’exécution associés à l’invocation, une
source de transfert doit émettre des transferts, et un délégué approuvé doit prendre
en charge la confirmation d’appel exact.
LlmAgent et CodeActAgent déclarent leurs capacités d’exécution prises en charge ;
les agents personnalisés conservent des valeurs par défaut prudentes et peuvent
remplacer capabilities().
Les équipes conservent le comportement ADK standard : les plugins Runner, les
rappels d’agent/modèle/outil, les garde-fous, l’annulation, l’identité de la
requête, la mémoire, les artefacts, les sessions et la persistance des événements
continuent d’envelopper les agents membres existants. compile_with_hooks
ajoute des rappels asynchrones TeamLifecycleHook ordonnés pour les limites de l’équipe, du
membre et de la relation exacte. Les hooks peuvent observer la réussite ou l’échec,
ou refuser une opération en attente sans élargir sa cible. Runner appelle également
le hook asynchrone govern_transfer de la racine immédiatement avant un transfert validé.
Les spans de traçage natifs team.run, team.member.run et team.relationship.execute
transportent l’identité de l’équipe, du membre et de l’arête, l’état et la durée de
la relation via l’exportateur adk-telemetry normal. Les échecs d’exécution utilisent des
codes structurés stables tels que agent.team.policy_denied, agent.team.budget_exceeded et
agent.team.resume_unsafe.
Les contrats de délégation distinguent les lectures d’état (stateKeys) des écritures
(stateWriteKeys). stateMerge: rejectConflicts rejette une écriture enfant lorsque la
valeur parente a changé pendant la délégation, et artifactPrefixes limite les noms
d’artefacts. Le AgentTool autonome expose les mêmes contrôles. Sa valeur par
défaut reste la priorité au dernier écrivain pour assurer la compatibilité ;
les relations d’équipe utilisent par défaut le rejet des conflits.
CompiledTeam::resume_plan décrit l’unique action suivante sûre après la restauration
d’un reçu : coordinateur, cible de transfert, délégué prenant en charge les
points de contrôle, ou délégation non sûre avec arrêt en cas d’échec. validate_team_replay
vérifie l’intégrité causale du reçu, tandis que analyze_execution rend compte de la
couverture de la topologie sans fournisseur, des échecs et de la profondeur
causale maximale pour les tests et les pipelines d’évaluation.
BlackboardSpec couvre la discussion de groupe régie en rotation et pilotée par
sélecteur. Elle limite le nombre de tours et la projection de la
transcription, et les transitions du sélecteur utilisent des listes
d’autorisation exactes. La composition sub_agent existante reste prise en
charge pour assurer la compatibilité ascendante, mais il s’agit d’un arbre
en mémoire plutôt que d’une définition d’équipe portable.
Démarrage rapide
1. Créer votre projet
cargo new multi_agent_demo
cd multi_agent_demo
Ajoutez les dépendances à Cargo.toml :
[dependencies]
adk-rust = "2.1.0"
tokio = { version = "1", features = ["full"] }
dotenvy = "0.15"
Créez .env avec votre clé API :
echo 'GOOGLE_API_KEY=your-api-key' > .env
2. Exemple de service client
Voici un exemple complet et fonctionnel :
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(())
}
Interaction d’exemple :
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...
Fonctionnement du transfert entre plusieurs agents
Vue d’ensemble
Lorsque vous ajoutez des sous-agents à un agent parent, le LLM acquiert la capacité de transférer le contrôle :
┌─────────────────────┐
User Message │ │
─────────────────▶ COORDINATOR │
│ │
└──────────┬──────────┘
│
"This is a billing question..."
│
┌────────────────┴────────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ billing_agent │ │ support_agent │
│ 💰 Payments │ │ 🔧 Tech Issues │
│ 📄 Invoices │ │ 🐛 Bug Reports │
└──────────────────┘ └──────────────────┘
Déroulement du transfert, étape par étape
Voici exactement ce qui se produit lorsqu’un utilisateur pose une question de facturation :
┌──────────────────────────────────────────────────────────────────────┐
│ 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! │
│ │
└──────────────────────────────────────────────────────────────────────┘
Ce qui permet son fonctionnement
| Composant | Rôle |
|---|---|
.sub_agent() | Enregistre les spécialistes sous le parent |
transfer_to_agent outil | Injecté automatiquement lorsque des sous-agents existent |
| Descriptions des agents | Aide LLM à décider quel agent gère quoi |
| Exécuteur | Détecte les événements de transfert et invoque l’agent cible |
| Session partagée | État et historique conservés lors des transferts |
Avant et après l’ajout de sous-agents
Sans sous-agents – Un seul agent s’occupe de tout :
User ──▶ coordinator ──▶ Response (handles billing AND support)
Avec des sous-agents – Des spécialistes gèrent leur domaine :
User ──▶ coordinator ──▶ billing_agent ──▶ Response (billing expert)
──▶ support_agent ──▶ Response (tech expert)
Systèmes multi-agents hiérarchiques
Pour les scénarios complexes, vous pouvez créer des hiérarchies à plusieurs niveaux. Chaque agent peut avoir ses propres sous-agents, formant ainsi un arbre :
Visuel : équipe de contenu à 3 niveaux
┌─────────────────────┐
│ 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 │
└──────────────────┘ └──────────────────┘
Comment les requêtes descendent dans la hiérarchie
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 │
└─────────────────────────────────────────────────────────────┘
Exemple de code complet
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(())
}
Hiérarchie des agents :
project_manager
└── content_creator
├── researcher
└── writer
Exemples de requêtes :
- « Créer un article de blog sur l’IA dans le domaine de la santé » → PM → Créateur de contenu → Rédacteur
- « Faire des recherches sur les véhicules électriques » → PM → Créateur de contenu → Chercheur
Configuration des sous-agents
Ajoutez des sous-agents à n’importe quel LlmAgent à l’aide de la méthode de construction 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()?;
Points clés :
- Chaque agent peut avoir plusieurs sous-agents
- Les sous-agents peuvent avoir leurs propres sous-agents (hiérarchies à plusieurs niveaux)
- Les noms des agents doivent être uniques au sein de la hiérarchie
- Les descriptions aident LLM à décider vers quel agent transférer la requête
Rédiger des instructions de transfert efficaces
Pour réussir les transferts entre agents, fournissez des instructions et des descriptions claires :
Instructions de l’agent parent
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()?;
Descriptions des sous-agents
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()?;
Bonnes pratiques :
- Utilisez des noms d’agents descriptifs qui indiquent clairement leur objectif
- Rédigez des descriptions détaillées – LLM les utilise pour décider des transferts
- Incluez des mots-clés spécifiques dans les descriptions, correspondant aux requêtes probables des utilisateurs
- Donnez des règles de délégation claires dans les instructions de l’agent parent
- Utilisez une terminologie cohérente dans les descriptions des agents
Tester votre système multi-agents
Exécuter les exemples
cargo run --manifest-path examples/tier_examples/enterprise/Cargo.toml --bin 14-enterprise-multi-agent
Exemples de requêtes de test
Service client :
- « J’ai une question concernant ma dernière facture » → Doit être dirigé vers
billing_agent - « L’application ne cesse de planter » → Doit être dirigé vers
support_agent - « Comment mettre à niveau mon forfait ? » → Doit être dirigé vers
billing_agent - « Bonjour, j’ai besoin d’aide » → Doit rester avec
coordinatorpour clarification
Hiérarchique :
- « Créer un article de blog sur l’IA dans le domaine de la santé » → PM → Content Creator → Writer
- « Faire des recherches sur l’histoire des véhicules électriques » → PM → Content Creator → Researcher
- « Quel est l’état d’avancement de nos projets actuels ? » → Doit rester avec
project_manager
Débogage des problèmes de transfert
Si les transferts ne fonctionnent pas comme prévu :
- Vérifiez les noms des agents - Ils doivent correspondre exactement dans les appels de transfert
- Examinez les descriptions - Rendez-les plus spécifiques et riches en mots-clés
- Clarifiez les instructions - Indiquez explicitement quand effectuer un transfert
- Testez les cas limites - Essayez des demandes ambiguës pour observer le comportement du routage
- Recherchez les indicateurs de transfert -
[Agent: name]indique quel agent répond
Instruction globale
Utilisation de base
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()?;
Instruction globale et instruction de l’agent
- Instruction globale : appliquée à tous les agents de la hiérarchie, elle définit la personnalité et le contexte généraux
- Instruction de l’agent : spécifique à chaque agent, elle définit son rôle et son comportement particuliers
Les deux instructions sont incluses dans l’historique de la conversation, l’instruction globale apparaissant en premier.
Instructions globales dynamiques
Pour des scénarios plus avancés, vous pouvez utiliser un fournisseur d’instructions globales qui calcule l’instruction dynamiquement :
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()?;
Injection de variables d’état
Les instructions globales et celles des agents prennent en charge l’injection de variables d’état à l’aide de la syntaxe {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()?;
Le framework injecte automatiquement les valeurs de l’état de la session dans les modèles d’instructions.
Modèles multi-agents courants
Modèle coordinateur/dispatcher
Un agent central achemine les requêtes vers des sous-agents spécialisés :
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()?;
Exemple de conversation :
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...
Points clés :
- Le coordinateur analyse la requête et la transfère à l’agent de facturation
- L’agent de facturation répond immédiatement au cours du même tour
- Les messages suivants continuent avec l’agent de facturation
- Les indicateurs de transfert (
🔄) indiquent quand les transferts ont lieu
Décomposition hiérarchique des tâches
Hiérarchies à plusieurs niveaux pour décomposer les tâches complexes :
// 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()?;
Combinaison avec des agents de workflow
Les systèmes multi-agents fonctionnent bien avec les agents de workflow (séquentiels, parallèles, en boucle) :
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()?;
Communication entre les agents
Les agents d’une hiérarchie communiquent par l’intermédiaire de l’état de session partagé :
// 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 configuration output_key enregistre automatiquement la réponse finale d’un agent dans l’état de la session, la rendant disponible pour les agents suivants.
Transfert de l’état et des artefacts AgentTool
Lors de l’utilisation de AgentTool pour encapsuler des agents en tant qu’outils, les modifications d’état et les artefacts des sous-agents sont automatiquement transférés au contexte parent :
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 exécute les sous-agents en mode non diffusé (StreamingMode::None) en interne, afin que le sous-agent accumule l’intégralité de sa réponse avant de la renvoyer au parent. Cela évite les problèmes où des fragments de diffusion partiels pourraient produire des résultats vides.
Cela permet un flux de données fluide entre les agents parent et enfant lors de l’utilisation du modèle AgentTool.
Exécution de systèmes multi-agents
Utilisation du lanceur
Le Launcher offre un moyen simple d’exécuter et de tester des systèmes multi-agents :
use adk_rust::Launcher;
let coordinator = /* your multi-agent setup */;
Launcher::new(Arc::new(coordinator))
.run()
.await?;
Modes d’exécution :
# 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
Fonctionnalités :
- Indicateurs d’agent : indique quel agent répond
[Agent: coordinator] - Visualisation des transferts : affiche les événements de transfert
🔄 [Transfer requested to: billing_agent] - Passages transparents : l’agent cible répond immédiatement après le transfert
- Historique de la conversation : conserve le contexte lors des transferts entre agents
Tester les transferts
Pour vérifier que votre système multi-agent fonctionne correctement :
- Vérifiez que les noms des agents apparaissent entre crochets lorsqu’ils répondent
- Recherchez les indicateurs de transfert (
🔄) lorsque les agents passent la main - Vérifiez les réponses immédiates des agents cibles, sans nouvelle invite
- Testez différents types de requêtes pour garantir un routage approprié
- Vérifiez les cas limites, comme le transfert vers des agents inexistants
Déboguer les problèmes de transfert
Si les transferts ne fonctionnent pas :
- Vérifiez que les sous-agents sont ajoutés via
.sub_agent() - Vérifiez les descriptions des agents : le LLM les utilise pour décider des transferts
- Examinez les instructions : le parent doit indiquer quand effectuer un transfert
- Vérifiez les noms des agents : ils doivent correspondre exactement dans les appels de transfert
- Activez la journalisation pour voir les actions de transfert dans le flux d’événements
Bonnes pratiques
- Descriptions claires : rédigez des noms et des descriptions d’agents explicites pour aider le LLM à prendre de bonnes décisions de transfert
- Instructions précises : donnez à chaque agent des instructions claires et ciblées pour son rôle
- Utilisez l’instruction globale : définissez une personnalité et un contexte cohérents pour tous les agents
- Gestion de l’état : utilisez
output_keyet des variables d’état pour la communication entre agents - Limitez la profondeur de la hiérarchie : gardez des hiérarchies peu profondes (2 à 3 niveaux) pour faciliter la maintenance
- Testez la logique de transfert : vérifiez que les agents transfèrent les différentes requêtes vers les sous-agents appropriés
Références associées
- LLM Agent - Configuration de l’agent principal
- Agents de workflow - Agents séquentiels, parallèles et en boucle
- Sessions - Gestion de l’état de la session
Précédent : ← Agents de workflow | Suivant : Agents de graphe →