Leçon 3 — Orchestrer : séquence, parallèle, boucle

SequentialAgent et ParallelAgent

5 min de lecture

SequentialAgent et ParallelAgent

SequentialAgent : la chaîne de montage

SequentialAgent(sub_agents=[a, b, c]) exécute a, puis b, puis c, avec le même contexte d'invocation : même session, même état. Chaque étape voit donc ce que les précédentes ont écrit. Dans le code installé (ADK 2.10), la boucle est littéralement un for sub_agent in self.sub_agents : pas de LLM, pas de surprise.

ParallelAgent : trois chercheurs à la fois

Les trois recherches (registre, finances, réputation) sont indépendantes : aucune n'a besoin du résultat d'une autre. Les lancer en parallèle divise le temps de cette phase par trois environ, puisque chaque chercheur passe l'essentiel de son temps à attendre le modèle.

files/agent.py (extrait)
def _chercheur(nom: str, outil, cle: str, model) -> LlmAgent:
    return LlmAgent(
        name=nom, model=model, tools=[outil], output_key=cle,
        description=f"Collecte les informations via {outil.__name__}.",
        instruction=(f"Tu es analyste achats. Appelle l'outil {outil.__name__} UNE seule fois avec le nom exact "
                     "du fournisseur cité dans la demande, puis résume le résultat en 5 puces factuelles au plus. "
                     f"N'invente rien. {CONSIGNE_DONNEES}"),
        **CALLBACKS,
    )

recherche = ParallelAgent(name="recherche", sub_agents=[
    _chercheur("chercheur_registre", consulter_registre, "notes_registre", model),
    _chercheur("chercheur_finances", consulter_finances, "notes_finances", model),
    _chercheur("chercheur_reputation", consulter_reputation, "notes_reputation", model),
])

Une fabrique _chercheur évite de copier trois fois la même configuration. Elle crée un agent neuf à chaque appel, ce qui compte : dans ADK, un agent ne peut avoir qu'un seul parent. Réutiliser la même instance dans deux workflows lève une erreur de validation.

gantt
  title "Phase de recherche (ordre de grandeur)"
  dateFormat ss
  axisFormat %S s
  section Séquentiel
  registre :a1, 00, 3s
  finances :a2, after a1, 3s
  reputation :a3, after a2, 3s
  section Parallèle
  registre :b1, 00, 3s
  finances :b2, 00, 3s
  reputation :b3, 00, 3s

Ce que voit chaque branche

ParallelAgent donne à chaque sous-agent une branche (recherche.chercheur_finances). Un agent ne reçoit dans son historique que les événements de sa propre branche et de ses ancêtres : la demande de l'utilisateur, oui ; les échanges des deux autres chercheurs, non. C'est voulu : chaque chercheur reste concentré, et ses tokens ne sont pas gaspillés à lire le travail des autres.

En revanche, l'état est commun. C'est pourquoi chaque branche écrit sous sa propre clé (leçon précédente).

Le chercheur_finances peut-il lire dans son historique le résumé produit par chercheur_registre pendant la phase parallèle ?

Quelles étapes du pipeline peuvent tourner en parallèle sans changer le résultat ?

Exercice : un pipeline à deux vitesses

Vous voulez qu'un fournisseur inconnu ne déclenche pas l'analyse et la rédaction (inutile de payer cinq appels au modèle pour un dossier vide). Proposez un mécanisme, sachant que SequentialAgent ignore escalate.

Voir la solution

Un before_agent_callback sur l'analyste et sur la boucle : s'il renvoie un types.Content, ADK saute l'agent et utilise ce contenu comme réponse.

agent.py (variante)
def sauter_si_indetermine(callback_context):
    score = callback_context.state.get("score_risque") or {}
    if score.get("niveau") == "indéterminé":
        return types.Content(role="model", parts=[types.Part(
            text="Dossier incomplet : " + "; ".join(score.get("motifs", [])))])
    return None  # None = l'agent s'exécute normalement

analyste = LlmAgent(..., before_agent_callback=sauter_si_indetermine)
boucle = LoopAgent(..., before_agent_callback=sauter_si_indetermine)

Vérifié avec le faux modèle des tests : pour « Société Imaginaire », l'analyste et la boucle répondent Dossier incomplet : données manquantes : registre, finances, reputation et le modèle n'est appelé que 6 fois (les 3 chercheurs) au lieu de 11 pour un dossier complet. Ajoutez cette assertion à test_pipeline_fournisseur_inconnu.

Résultat obtenu

Extrait du journal d'adk eval du 2026-10-15 (modèle gemini-3.5-flash) : les trois requêtes des chercheurs partent dans la même dizaine de millisecondes, puis les trois outils sont appelés dans l'ordre où les réponses arrivent, différent de l'ordre de la liste.

16:37:06,743 - google_llm.py - Sending out request, model: gemini-3.5-flash
16:37:06,748 - google_llm.py - Sending out request, model: gemini-3.5-flash
16:37:06,751 - google_llm.py - Sending out request, model: gemini-3.5-flash
16:37:08,380 - agent.py - {"agent": "chercheur_finances", "outil": "consulter_finances", …}
16:37:08,609 - agent.py - {"agent": "chercheur_reputation", "outil": "consulter_reputation", …}
16:37:08,642 - agent.py - {"agent": "chercheur_registre", "outil": "consulter_registre", …}

Je sais faire

  • Enchaîner des étapes avec SequentialAgent en sachant qu'il ignore escalate
  • Paralléliser des étapes indépendantes avec ParallelAgent
  • Expliquer ce que voit une branche (historique isolé, état commun)
  • Créer des agents par fabrique pour respecter la règle « un seul parent »
Suivant