Accueil / Articles / Agents spécialisés, un routage de mots-clés et une fonction d’interruption : Un coach LangGraph

Agents spécialisés, un routage de mots-clés et une fonction d’interruption : Un coach LangGraph

Concevez un assistant LangGraph à deux spécialistes doté d’un routage déterministe, d’un état partagé qui persiste lors des transferts, ainsi que d’un point de contrôle humain basé sur les interruptions et les commandes.

2462 mots

Un seul prompt d’assistant censé couvrir deux domaines non liés ne parvient généralement pas à les traiter correctement dans les deux cas. Cet article propose plutôt un coach en fitness et nutrition sous forme de petit système LangGraph : deux agents spécialisés, un routage déterministe qui décide qui répond, un état partagé transmettant les contraintes entre les échanges, ainsi qu’un point de contrôle humain qui pause le système après chaque réponse. À la fin, vous saurez comment fonctionne chacune de ces composantes, pourquoi le système est conçu de cette manière, et comment réutiliser cette structure pour n’importe quel duo d’experts.

Le scénario et la conversation de test

Le produit visé est un assistant intégré à une application de fitness qui aide tant pour l’entraînement que pour l’alimentation. La conception a été validée à l’aide d’une conversation en trois étapes : une demande de plan d’entraînement, une question complémentaire sur l’alimentation, et une contrainte diététique qui doit modifier les conseils alimentaires :

Scenario: A fitness app wants an assistant that helps with workouts and nutrition.

Test conversation flow (3 turns):
"Build muscle, lose fat — suggest a workout plan"
"What should I eat to support this workout plan?"
"I'm vegetarian, adjust the meal suggestion"

Toute décision de conception ci-dessous vise à garantir que ces trois échanges fonctionnent correctement et de manière prévisible.

Pourquoi un chatbot générique ne suffit pas

Lorsque l’on demande à un chatbot polyvalent un plan d’entraînement suivi d’un régime adapté, les réponses sont souvent superficielles. Le modèle doit gérer toutes les personnalités au sein d’une seule instruction système, ce qui fait perdre en précision les conseils d’entraînement et simplifie excessivement les aspects diététiques. Cette faiblesse provient du fait que trois exigences distinctes s’appliquent simultanément à la même instruction :

  • Séparation des domaines. La planification des exercices et la gestion nutritionnelle relèvent de domaines d’expertise différents ; les conseils dans l’un ne doivent ni s’infiltrer ni contredire ceux de l’autre.
  • Pertinence du contexte au fil des échanges. Une contrainte mentionnée précédemment, comme une blessure au genou évoquée il y a trois messages, doit continuer d’influencer le plan alimentaire ou d’entraînement élaboré par la suite.
  • Gestion par un humain. Le système ne doit pas fonctionner de manière entièrement autonome ; il nécessite un point de contrôle où une personne peut valider, ajouter des détails ou changer de direction avant que quoi que ce soit d’autre ne soit généré.
  • Au lieu de demander à une seule instruction d’effectuer trois tâches, l’architecture attribue à chaque exigence son propre mécanisme : des spécialistes pour la séparation des domaines, un état partagé pour le contexte, et un nœud humain pour la supervision.

    Les cinq éléments de base

    Le système se compose de cinq parties, chacune ayant une seule responsabilité :

    • Conseiller en exercices : un agent chargé des routines d’exercice, de la répartition des entraînements et des modifications en cas de blessure.
    • Conseiller en nutrition : un agent chargé des plans alimentaires, des macronutriments et des restrictions diététiques telles que les régimes végétarien, végan ou liés à des allergies.
    • Routeur : une étape de décision légère qui examine le dernier message de l’utilisateur et choisit quel conseiller répond en premier.
    • Nœud humain : une pause intentionnelle pendant laquelle le graphe attend la personne plutôt que de générer davantage de résultats.
    • État partagé : un enregistrement officiel du dialogue jusqu’à présent, ainsi que l’identité du conseiller qui a répondu en dernier.

    C’est un modèle hiérarchique, ou d’orchestration. Un coordinateur délègue des tâches à des sous-agents spécialisés, au lieu d’un seul agent qui tenterais de jouer tous les rôles en même temps.

    La règle qui assure une transmission prévisible

    Une règle structurelle rend tout le système fiable : un conseiller ne transfère jamais directement le contrôle à l’autre conseiller. Chaque sortie d’agent passe par le nœud humain, puis par le routeur. Ainsi, deux agents ne peuvent jamais échanger silencieusement le contrôle, et chaque transition est à la fois visible et interrompable.

    Comment un message se déplace dans le graphe

    Suivez un seul message d’utilisateur. Il arrive d’abord sur le nœud humain puis est transmis au routeur, qui lit le texte du message et, si ce dernier ne suffit pas à se prononcer, consulte le champ last_active_agent de l’état partagé afin de choisir entre le Conseiller d’entraînement et le Conseiller nutritionnel. Le conseiller choisi peut alors appeler son outil correspondant, avant de restituer le contrôle au nœud humain, qui s’arrête à nouveau et attend le message suivant. Le cycle est donc toujours : nœud humain, routeur, conseiller, outil optionnel, nœud humain.

    Le modèle : un LLM à poids ouverts sur Groq

    Les deux conseillers utilisent le modèle à poids ouverts openai/gpt-oss-120b fourni par Groq, configuré avec temperature=0 :

    • Pourquoi ce modèle : il dispose d’une capacité solide à appeler des outils natifs, essentielle au flux de travail basé sur les outils.
  • Pourquoi une température de 0 : cela rend les sorties aussi reproductibles que possible, de sorte que le graphique respecte ses règles prévues au lieu de varier d’une exécution à l’autre. Cela est important lorsque quelqu’un teste ou évalue la même conversation à plusieurs reprises. En pratique, même une température de 0 ne garantit pas un déterminisme strict avec les modèles hébergés, donc les tests devraient se concentrer sur le routage et la structure plutôt que sur les formulations exactes.
  • Pourquoi Groq : son matériel d’inférence LPU maintient une faible latence, ce qui est crucial lorsque chaque réponse de l’utilisateur déclenche plusieurs étapes (nœud humain, routeur, conseiller, outil, nœud humain) avant que la réponse ne apparaisse.
  • Rien dans le graphique ne dépend spécifiquement de Groq. Tout modèle de chat capable d’appeler des outils fonctionne, par exemple Google Gemini ou Anthropic Claude, à condition de fournir la clé API correspondante (telle que GEMINI_API_KEY ou ANTHROPIC_API_KEY) et d’orienter l’enveloppe du modèle vers ce fournisseur. La disponibilité des modèles sur les plateformes hébergées évolue avec le temps, il convient donc de vérifier l’identifiant du modèle dans le catalogue actuel du fournisseur.

    État partagé : où réside réellement la mémoire

    Chaque nœud lit dans et écrit sur un objet d’état de type MultiAgentState, qui comporte deux champs :

    • messages : l’intégralité de la conversation en cours, héritée de MessagesState de LangGraph ; cet objet fournit également le réducteur qui ajoute de nouveaux messages au lieu d’écraser la liste existante.
  • last_active_agent : doit être défini sur "workout_advisor" ou "nutrition_advisor" ; le routeur y a recours chaque fois qu’il ne parvient pas à déterminer à quel domaine appartient une demande de suivi telle que "donnez-moi plus d’informations">.
  • C’est ce mécanisme qui explique pourquoi une contrainte énoncée lors d’un tour reste en vigueur dans un tour ultérieur, même si c’est un nœud différent qui génère la réponse. Aucun résumé ni explication supplémentaire n’est fourni au modèle. Chaque conseiller reçoit simplement la même liste accumulée de messages à chaque exécution, et un point de contrôle permet de conserver l’état entre les tours d’une même conversation.

    Le choix de conception clé réside dans le fait que la mémoire est centralisée, et non limitée à chaque agent. Si chaque conseiller conservait son propre historique privé, le Conseiller en nutrition n’apprendrait jamais rien sur un objectif que l’utilisateur aurait indiqué au Conseiller en entraînement.

    Un seul outil restreint par conseiller

    Chaque agent ne peut utiliser qu’un seul outil, et cet outil appartient à son domaine. Dans cette version, les outils sont basés sur des règles et correspondent à des mots-clés, ce qui rend le comportement prévisible et facile à démontrer. En production, on pourrait les remplacer par une véritable API de fitness ou de nutrition, ou laisser le modèle générer entièrement la réponse. Comme le reste du graphe ne dépend que de l’interface de l’outil, changer son implémentation n’exige aucune autre modification.

    Rendre chaque conseiller spécialisé

    Un outil dédié ne suffit pas pour maintenir un agent sur le sujet ; le modèle doit également savoir à quoi il ne doit pas répondre. Chaque instruction système définit donc une limite explicite :

    • L’Assistant d’entraînement reçoit l’instruction de ne pas donner de conseils alimentaires ou nutritionnels, car le graphe enverra ces demandes à l’Assistant de nutrition.
  • L’Conseiller en nutrition a pour instruction de ne pas concevoir de routines d’entraînement, car cela relève de la compétence de l’Conseiller en entraînement.
  • Le principe à l’origine de cela est que le modèle ne s’oriente pas lui-même. Les indications de limite font en sorte que chaque agent attend des instructions plutôt que d’improviser en dehors de son domaine de compétence, et c’est le graphe, et non le LLM, qui prend explicitement toutes les décisions de routage.

    Un routeur basé sur des mots-clés, et non sur une autre appel à un LLM

    Avec les conseillers restreints, le routeur n’a qu’à choisir le prochain interlocuteur, et il le fait grâce à une simple correspondance de mots-clés :

    • Des mots tels que gym, reps, cardio ou muscle sont dirigés vers l’Conseiller en entraînement.
    • Des mots tels que diet, food, eat, protein ou vegetarian sont dirigés vers l’Conseiller en nutrition.
    • Tout ce qui ne correspond à aucune des listes est envoyé à l’agent stocké dans last_active_agent.

    Le routage déterministe est prévisible et facilement testable : le même message produit toujours le même saut, et il est possible de tester en unité le routeur sans appeler de modèle. Le compromis réside dans sa fragilité. Les listes de mots-clés manquent de synonymes, et un message mentionnant les deux domaines (une question sur la nourriture avant l’entraînement) nécessite une règle explicite de résolution des conflits. Lorsque la formulation devient trop variée pour les mots-clés, une petite opération de classification constitue une amélioration raisonnable, mais elle entraîne une perte partielle de cette prévisibilité.

    Intervention humaine avec interruption et commande

    La décision de conception la plus importante n’est pas le routeur, mais le nœud humain. Après chaque tour de l’assistant, le graphe s’arrête délibérément. Il ne devine pas la prochaine question de l’utilisateur ni ne continue à générer du contenu. Deux primitives LangGraph permettent d’implémenter cette pause :

    • interrupt(...) arrête l’exécution à l’endroit où il est appelé et restitue le contrôle à celui qui l’a invoqué, ainsi que la valeur que vous lui avez transmise.
    • Command(resume=...) reprend l’exécution du graphe interrompu, en utilisant les entrées fournies par l’humain comme valeur de retour de l’appel interrupt au sein du même nœud.

    Les interruptions reposent sur la création de points de contrôle : le graphe doit enregistrer son état au moment de l’arrêt afin de pouvoir reprendre plus tard, c’est pourquoi le graphe compilé a besoin d’un point de contrôle ainsi que d’une identifiant de thread.

    Pour un assistant en matière de fitness et de nutrition, cette pause n’est pas simplement une commodité d’interface utilisateur. C’est le moment où l’utilisateur peut ajouter une contrainte liée à la sécurité, telle qu’une blessure, une allergie ou une restriction alimentaire, avant que la conversation ne continue, plutôt qu’après que le système ait déjà donné des conseils qui l’ignorent.

    Décortiquer les trois échanges

    Échange 1 : lancement du dialogue

    L’utilisateur énonce deux objectifs, gagner en muscles et perdre du gras, et demande un plan d’entraînement. Un nouveau dialogue commence toujours avec le Conseiller en entraînement. Celui-ci retient les termes « muscles » et « gras », utilise son outil correspondant, et renvoie un programme structuré, par exemple un plan en quatre jours divisé en exercices pour les parties supérieures et inférieures du corps, avec 8 à 12 répétitions par série sur 3 à 4 séries, combinant trois jours d’entraînement de force avec deux sessions d’aérobic.

    Échange 2 : transfert vers la nutrition

    Ensuite, l’utilisateur souhaite savoir quels aliments pourraient soutenir ce plan. Le nœud humain transmet le message, le routeur correspond à « manger », et l’exécution est transférée à l’Conseiller en nutrition. Celui-ci renvoie un plan quotidien d’environ 2 500 kcal, axé sur les protéines maigres et les glucides complexes, avec des repas tous les trois à quatre heures.

    Tour 3 : application d’une contrainte provenant de l’état partagé

    Finalement, l’utilisateur mentionne qu’il est végétarien et demande que la suggestion de repas soit ajustée, avec l’ajout d’un en-cas. Le message est à nouveau envoyé à l’Conseiller en nutrition, et deux mécanismes s’accordent ici : le terme « végétarien » est lui-même un mot-clé lié à la nutrition, et last_active_agent indique toujours la catégorie nutrition, de sorte qu’une demande plus vague comme « ajustez cela, et ajoutez un en-cas » aboutit au même endroit. Le conseiller relit l’historique complet, respecte toujours l’objectif fixé lors du premier échange, ajoute la contrainte végétarienne par-dessus, et élabore un plan sans viande pour construire de la masse musculaire basé sur le tofu, le paneer et les lentilles, avec des en-cas tels que des haricots edamame et des bols de hummus. Personne n’a eu besoin de reformuler l’objectif, car il était déjà présent dans l’état actuel des informations.

    Cette étape représente le fruit d’un État centralisé. Un nœud différent de celui qui a reçu l’objectif initial produit la réponse, tout en disposant du contexte complet.

    Montage de la machine d’État

    Sur le plan structurel, on obtient un petit graphe d’États composé de trois nœuds (les deux conseillers et le nœud humain), ainsi que d’un routeur qui assure la logique conditionnelle entre eux, avec un point d’entrée fixe :

    • Chaque nouvelle thread commence par le Conseiller d’entraînement. Ce choix par défaut reflète les besoins des utilisateurs : ceux qui ouvrent une application de fitness demandent généralement des informations sur l’entraînement avant de s’intéresser à l’alimentation.
    • Après cette première réponse, le nœud humain et le routeur décident de chaque étape suivante.
  • Le graphe est compilé avec un point de contrôle MemorySaver et chaque conversation est identifiée par un thread_id. Chaque fois que l’appelant réutilise le même thread_id, le graphe restaure automatiquement les messages et le last_active_agent ; l’appelant n’a jamais besoin de renvoyer l’historique.
  • MemorySaver conserve les points de contrôle en mémoire de processus, ce qui est idéal pour un notebook, mais tout est perdu à la redémarrage. Une version déployée devrait utiliser un point de contrôle persistant soutenu par une base de données.

    La mise en œuvre complète et fonctionnelle est disponible sous forme de notebook d’accompagnement. Pour découvrir d’autres façons de structurer les étapes de routage et d’approbation, consultez cinq modèles LangGraph couvrant le routage, la diffusion, la critique et l’approbation.

    Réutiliser le modèle au-delà de la condition physique

    Rien ici n’est spécifique aux exercices ou aux repas. Les mêmes trois éléments — des agents spécialisés, un routage géré par un état partagé, et un point de contrôle humain utilisant des fonctionnalités d’interruption et de reprise — s’adaptent à tout système disposant de :

    • plus d’un domaine de compétence distinct dont une conversation pourrait avoir besoin,
  • La nécessité de s’arrêter en cours de route pour que quelqu’un donne son avis ou confirme, que ce soit pour des raisons de conformité, de sécurité ou de personnalisation,
  • un contexte établi dans une partie de la conversation qui doit influencer correctement un autre spécialiste par la suite.
  • En remplaçant les deux conseillers par un autre couple d’experts du domaine, la forme du graphe reste identique. Seuls les outils, les prompts et les mots-clés de routage changent.

    Principes de conception

    • Orchestration plutôt que monolithes. Utilisez le graphe pour imposer des voies d’expertise distinctes au lieu de compter sur un seul agent pour tout gérer.
    • Centraliser l’état. Un MultiAgentState persisté par un checkpointer est ce qui permet des transferts sans heurt et des contraintes persistantes.
  • Le contrôle est essentiel. Évitez les boucles entièrement autonomes en production ; utilisez interrupt() pour maintenir une intervention humaine aux points importants.
  • Points clés

    • Divisez le travail par domaine et laissez la logique de routage déterministe du graphe prendre les décisions au lieu du modèle.
    • Maintenez un historique commun des messages ainsi que la variable last_active_agent afin qu’une contrainte établie tôt influence les réponses produites bien plus tard par un autre agent.
    • Considérez interrupt() et Command(resume=...) comme les mécanismes permettant à une personne d’ajouter des contraintes de sécurité avant la prochaine réponse, et n’oubliez pas qu’elle a besoin d’un point de contrôle ainsi que d’une identifiant de thread.
    • Maintenez des outils spécialisés derrière une interface stable afin que la logique basée sur des règles puisse évoluer ultérieurement en une véritable API ou un raisonnement de modèle complet sans toucher au graphe.
    • Le moyen le plus rapide d’intégrer ce modèle est de choisir deux spécialistes bien définis parmi vos propres équipes et de mettre en place un graphe fonctionnel complet ; la topologie reste inchangée, tandis que les prompts et les outils sont les éléments que vous adaptez.

    Lectures complémentaires