Boucle reason-act
Comprendre le mécanisme interne qui transforme un modèle de langage en agent : la boucle think, act, observe, et les conditions qui doivent obligatoirement la stopper.
Table des matières
Pourquoi cette boucle est le cœur de tout agent
Un agentagentIASystème qui enchaîne des appels d'outils de façon autonome pour atteindre un objectif : il planifie, agit, observe, recommence. Sa fiabilité décroît exponentiellement avec le nombre d'étapes.Voir dans le glossaire LLMLLMIAGrand modèle de langage (Large Language Model) entraîné sur d'énormes corpus pour prédire et générer du texte.Voir dans le glossaire n'est pas un modèle qui répond une fois à une question : c'est un modèle qu'on fait tourner plusieurs fois de suite, en lui donnant à chaque tour la possibilité soit de répondre, soit d'agir. Ce mécanisme itératif porte un nom précis dans la littérature : ReAct, contraction de reasoning et acting. Le terme désigne une architecture, pas un produit — elle a été popularisée par un article de recherche en 2022 et depuis, tous les frameworks d'agents (function callingtool callingIACapacité d'un agent ou d'un LLM à invoquer des outils externes (API, calcul, recherche) pendant le raisonnement.Voir dans le glossaire natif des API, LangChain, outils propriétaires) en implémentent une variante.
Comprendre cette boucle en détail n'est pas un exercice académique. C'est ce qui vous permet de diagnostiquer pourquoi un agent boucle sur le même outil, pourquoi il s'arrête trop tôt, ou pourquoi une facture d'API explose sans qu'aucune erreur ne remonte. La boucle reason-act est un objet mécanique : on peut l'instrumenter, la borner et la déboguer comme n'importe quel autre système à états.
Un agent n'a pas de plan préétabli qui existerait indépendamment de la boucle. À chaque tour, le modèle recalcule sa décision à partir de l'historique accumulé. Il n'y a pas de mémoire persistante entre deux tours autre que le texte réinjecté dans le promptpromptIAConsigne ou contexte fourni à un modèle de langage pour orienter sa réponse. La qualité du prompt conditionne souvent la qualité du résultat.Voir dans le glossaire. Perdre cette réalité de vue conduit à surestimer la cohérence stratégique d'un agent sur de longues trajectoires.
Anatomie du cycle : think, act, observe
La boucle se décompose en trois temps qui se répètent.
Think. Le modèle reçoit l'historique complet de la conversation, y compris les résultats des actions précédentes, et produit un raisonnement suivi d'une décision : appeler un outil avec certains arguments, ou produire la réponse finale destinée à l'utilisateur. Dans les implémentations modernes, cette étape correspond à un appel API avec une liste d'outils déclarés (tools dans le schéma de function callingappel d'outilsIAMécanisme par lequel un modèle produit un appel de fonction structuré que le code environnant décide d'exécuter. Le modèle n'a jamais d'accès direct : il propose, le code dispose.Voir dans le glossaire). Le modèle renvoie soit du texte, soit une structure tool_calls avec le nom de la fonction et ses paramètres sérialisés en JSON.
Act. L'action décidée est exécutée — mais pas par le modèle lui-même. Le modèle ne fait qu'émettre une intention structurée ; c'est le code applicatif qui appelle réellement la fonction, la requête HTTP, la commande shell ou la requête base de donnéesdonnéesIAEnsemble d'informations structurées ou non utilisées pour entraîner, évaluer ou alimenter un modèle. La qualité, la quantité et la représentativité des données sont les facteurs décisifs pour les performances en apprentissage automatique.Voir dans le glossaire correspondante. Cette séparation est fondamentale : elle signifie que tout contrôle de sécurité (validation des arguments, autorisation, sandboxing) se situe côté application, jamais côté modèle.
Observe. Le résultat de l'action — succès, erreur, données renvoyées — est sérialisé et réinjecté dans l'historique sous forme d'un message de rôle tool. Ce message devient partie intégrante du contextefenêtre de contexteIAQuantité de texte qu'un modèle peut prendre en compte simultanément : question, documents fournis et historique. Au-delà, les éléments les plus anciens sortent du champ.Voir dans le glossaire pour le prochain appel au modèle. C'est cette réinjection qui rend la boucle itérative : le modèle du tour suivant « voit » ce qui s'est passé et ajuste sa décision en conséquence.
Le cycle recommence ensuite depuis Think, avec un historique augmenté d'un échange action/observation supplémentaire. Il se termine lorsque le modèle choisit de répondre en texte libre sans appeler d'outil, ou lorsqu'une condition d'arrêt externe interrompt la boucle.
L'historique de trajectoire n'est pas un détail d'implémentation
À chaque tour, l'intégralité de l'historique — message utilisateur initial, tours de raisonnement, appels d'outils, résultats — est renvoyée au modèle. Ce n'est pas une mémoire au sens cognitif : c'est un contexte texte qui grossit à chaque itération. Deux conséquences pratiques en découlent directement.
D'abord, le coût croît avec le nombre de tours, et pas linéairement dans les architectures qui refacturent tout le contexte à chaque appel : un agent qui effectue dix appels d'outils sur une trajectoire longue paie dix fois un contexte de plus en plus volumineux. Ensuite, la qualité de décision dépend directement de ce qui a été mis dans l'historique : un résultat d'outil verbeux, mal structuré ou tronqué dégrade la capacité du modèle à raisonner correctement au tour suivant, exactement comme n'importe quelle information de mauvaise qualité insérée dans un prompt.
Si un outil renvoie un document de plusieurs milliers de mots, ne le réinjectez pas tel quel dans l'historique à chaque tour. Résumez ou tronquez côté application avant réinjection, et conservez le document complet ailleurs (fichier, base) si le modèle doit pouvoir y revenir via un second appel d'outil. Cela limite la dérivedériveIADégradation progressive des performances d'un modèle après son déploiement, causée par l'évolution des comportements ou du contexte. Elle impose surveillance et réentraînement.Voir dans le glossaire de contexte et le coût cumulé.
Les conditions d'arrêt : le point le plus souvent négligé
Une boucle reason-act n'a, par construction, aucune raison intrinsèque de s'arrêter. Le modèle peut décider d'enchaîner les appels d'outils indéfiniment, en particulier si un outil renvoie une erreur que le modèle interprète comme « il faut réessayer » ou « il faut essayer autrement ». C'est le pattern d'échec le plus fréquent en production : un outil mal conçu, une erreur ambiguë, et l'agent multiplie les tentatives sans jamais converger.
Quatre familles de conditions d'arrêt doivent être posées avant tout déploiement.
Réponse finale explicite. Le cas nominal : le modèle produit du texte sans appel d'outil. C'est la sortie attendue, mais elle ne doit jamais être la seule ligne de défense.
Budget d'itérations (max_steps). Un compteur d'itérations, incrémenté à chaque passage par Think, qui interrompt la boucle au-delà d'un seuil fixé — typiquement entre 5 et 15 selon la complexité de la tâche. C'est la garde-fou la plus simple et la plus indispensable : sans elle, un agent mal conçu peut consommer un budget de tokenstokenIAFragment de texte manipulé par un modèle de langage, généralement plus court qu'un mot — trois à quatre caractères en français. La tarification et la limite de contexte se comptent en tokens.Voir dans le glossaire illimité sur une seule requête utilisateur.
Budget de coût ou de temps. Un plafond en tokens consommés ou en durée d'exécution, particulièrement utile quand plusieurs outils ont des latences très différentes (un appel base de données local contre un appel à une API tierce lente).
Détection de répétition. Un mécanisme qui compare l'action courante aux actions précédentes de la trajectoire et interrompt la boucle si le même outil est appelé avec des arguments identiques ou quasi identiques plusieurs fois de suite. C'est le filet qui attrape les boucles où le modèle « s'entête » sur une stratégie qui ne fonctionne pas.
Quand le budget d'itérations est épuisé sans réponse finale, ne renvoyez jamais un message générique masquant l'échec (« Voici le résultat » suivi de rien d'utile). Remontez explicitement l'état : trajectoire interrompue, dernière observation connue, et si possible une réponse partielle construite à partir de ce qui a été observé. Un échec silencieux dans un agent en production est plus coûteux qu'une erreur visible.
| Condition d'arrêt | Détecte | Risque si absente |
|---|---|---|
| Réponse finale du modèle | Fin de tâche nominale | — (cas de base) |
| max_steps (budget d'itérations) | Trajectoire trop longue | Consommation de tokens sans borne |
| Budget de coût / temps | Dérive économique ou latence | Facture ou délai imprévisibles |
| Détection de répétition | Blocage sur une même action | Boucle sur un outil en échec |
| Erreur non récupérable | Panne d'un outil critique | Boucle sur une erreur systémique |
Un agent chargé de répondre à une question à partir d'une base documentaire interne appelle un outil
search_docs. La requête initiale ne renvoie aucun résultat pertinent. Sans garde-fou, le modèle reformule la requête et rappellesearch_docsune deuxième fois, puis une troisième, en variant à peine les termes — jusqu'à ce que max_steps interrompe la boucle au bout de dix appels quasi identiques. Avec une détection de répétition fondée sur la similarité des arguments, l'agent aurait pu être forcé, dès le troisième échec, à répondre honnêtement qu'aucune information pertinente n'a été trouvée plutôt que de s'épuiser sur la même stratégie.
Erreurs d'outils : un cas à traiter à part
Toutes les observations ne se valent pas. Un résultat d'outil peut être un succès avec des données exploitables, un échec métier attendu (« aucun résultat trouvé », ce qui est une information légitime), ou une erreur technique (timeout, erreur d'authentification, service indisponible). Ces trois cas doivent être distingués dans le message d'observation renvoyé au modèle, sinon celui-ci ne peut pas adapter correctement sa stratégie.
Une pratique robuste consiste à structurer systématiquement les observations avec un champ de statut explicite plutôt que de renvoyer une simple chaîne de caractères. Le modèle raisonne mieux sur {"status": "error", "error_type": "timeout", "retryable": true} que sur un message d'erreur brut noyé dans une pile d'exceptions.
Le fait qu'une erreur soit techniquement récupérable (retryable) est une information factuelle que l'application doit fournir, pas une inférenceinférenceIAUtilisation d'un modèle déjà entraîné sur une donnée nouvelle. Peu coûteuse à l'unité mais répétée à chaque requête, elle constitue le coût récurrent d'exploitation.Voir dans le glossaire que le modèle doit deviner à partir du texte de l'erreur. Sans ce signal explicite, deux comportements erronés sont fréquents : réessayer indéfiniment une erreur définitive, ou abandonner après une erreur transitoire qui aurait réussi au deuxième essai.
Idempotence et effets de bord des actions
Un point souvent sous-estimé : dans une boucle qui peut répéter une action plusieurs fois (par exemple après une erreur de timeout suivie d'un nouvel essai), toutes les actions ne se comportent pas de la même façon face à la répétition. Une lecture (get_ticket, search_docs) est idempotente par nature : l'appeler deux fois de suite avec les mêmes arguments ne produit aucun effet indésirable, seulement une requête redondante. Une écriture (add_comment, charge_payment, send_email) ne l'est pas forcément : la rejouer après un timeout peut créer un doublon, facturer deux fois, ou envoyer le même message deux fois à l'utilisateur final.
Cette distinction doit être prise en compte dès la conception des outils, pas ajoutée après coup. Deux approches complémentaires existent. La première consiste à rendre les actions sensibles elles-mêmes idempotentes côté application, par exemple en exigeant une clé d'idempotence générée pour chaque tentative logique et vérifiée côté serveur avant exécution. La seconde consiste à interdire explicitement au modèle de rejouer une action à effet de bord après une erreur ambiguë (timeout sans confirmation du résultat), en le forçant à interroger l'état réel du système avant de décider s'il faut réessayer.
Un détail à ne pas négliger : un timeout signifie seulement que l'application n'a pas reçu de réponse dans le délai imparti, pas que l'action a échoué côté serveur distant. Un agent qui réessaie systématiquement une action à effet de bord après un timeout, sans vérifier l'état réel, peut produire des doublons silencieux difficiles à détecter en aval. Pour toute action non idempotente, une vérification de l'état avant nouvelle tentative doit faire partie du contrat de l'outil.
Trace d'exécution commentée
Prenons un agent outillé pour consulter un système de tickets et y créer une entrée. L'utilisateur demande : « Le ticket #482 est-il résolu ? Si non, ajoute un commentaire pour relancer. »
- Think (tour 1) — Le modèle identifie qu'il doit d'abord consulter l'état du ticket. Il émet un appel
get_ticket(id=482). - Act — L'application exécute la requête vers l'API de tickets.
- Observe — Résultat :
{"status": "open", "last_update": "..."}réinjecté dans l'historique. - Think (tour 2) — Le ticket est ouvert, donc non résolu. Le modèle décide d'appeler
add_comment(id=482, text="Relance..."). - Act — Commentaire ajouté via l'API.
- Observe — Résultat :
{"status": "success", "comment_id": 9931}. - Think (tour 3) — Aucune action supplémentaire n'est nécessaire. Le modèle produit la réponse finale : confirmation à l'utilisateur.
Trois tours, deux actions, une réponse finale explicite. C'est le déroulé nominal. Le travail de conception se concentre sur ce qui se passe quand le tour 3 renvoie une erreur d'API au lieu d'un succès — c'est là que les conditions d'arrêt entrent en jeu.
Checklist avant mise en production
- Un budget d'itérations (max_steps) est fixé et testé en le déclenchant volontairement.
- Chaque outil renvoie un statut structuré (succès / échec métier / erreur technique), pas une chaîne libre.
- Un mécanisme de détection de répétition existe, même simple (comparaison des derniers appels).
- Le comportement en cas d'arrêt forcé est défini explicitement, pas laissé à l'improvisation du modèle.
- Les observations volumineuses sont résumées ou tronquées avant réinjection dans l'historique.
- Un budget de coût ou de temps existe en complément du budget d'itérations, en particulier si des outils ont des latences variables.
- La trajectoire complète (tours, appels, observations) est journalisée pour permettre le débogage a posteriori.
Aucune de ces mesures n'est optionnelle au-delà d'un prototype. La boucle reason-act est puissante précisément parce qu'elle laisse le modèle décider de la suite des opérations — ce qui signifie que sans garde-fous externes, c'est le modèle qui décide seul quand s'arrêter. Les chapitres suivants détaillent la conception des outils eux-mêmes et la gestion des erreurs qui alimentent cette boucle.
L'essentiel à retenir
Ce chapitre détaille la boucle reason-act (ReAct), le mécanisme central qui permet à un agent LLM d'alterner raisonnement et appels d'outils jusqu'à produire une réponse. Il explique le rôle de chaque étape — think, act, observe — et pourquoi l'historique de trajectoire conditionne la qualité des décisions suivantes. Une large partie est consacrée aux conditions d'arrêt, sans lesquelles un agent peut boucler indéfiniment ou consommer un budget de tokens disproportionné. Le chapitre se termine par une trace d'exécution commentée et une checklist de mise en production.
Questions fréquentes
Quelle est la différence entre ReAct et un simple appel de fonction unique ?
Combien de tours (max_steps) faut-il autoriser en pratique ?
Que se passe-t-il si le modèle appelle un outil qui n'existe pas ?
Faut-il toujours limiter le nombre d'outils accessibles à chaque tour ?
La détection de répétition peut-elle bloquer des appels légitimes identiques ?
Comment débogue-t-on une boucle qui ne s'arrête jamais en production ?
La boucle reason-act est-elle spécifique à un fournisseur de modèle en particulier ?
Progression sauvegardée dans votre navigateur.
Quiz de validation
Quiz Player
Quiz de validation
Plusieurs réponses possibles — validez ensuite.
Vrai ou faux.
Quiz indisponible (données invalides).