Le problème
La requête semble simple : trouver le statut d'une commande et renvoyer une réponse courte.
Dans les logs, on voit que l'agent répète le même cycle :
plan → call_tool → analyze → plan → call_tool → analyze
Il y a une semaine, ce type de tâche se terminait en 3-4 étapes.
Maintenant, la même demande peut tourner sur 20+ étapes et finir en timeout.
En 15 minutes, l'agent peut faire 60+ étapes et dépenser environ 12 $ pour une tâche qui coûte d'habitude ~0.08 $.
Le système ne tombe pas immédiatement.
Il brûle simplement du temps, des tokens et de l'argent.
Analogie : imagine un GPS qui dit à chaque carrefour "faites demi-tour", même quand c'est déjà fait. La voiture avance, mais tu ne te rapproches pas de la destination. Une boucle infinie d'agent fonctionne pareil : il y a des actions, pas de progrès.
Pourquoi cela arrive
Les agents LLM sont des systèmes stochastiques. Même un petit changement de prompt, tool output ou contexte peut déplacer l'ordre des étapes. Si runtime ne vérifie pas le progrès réel, la boucle se bloque facilement.
En production, cela ressemble souvent à ceci :
- le LLM propose l'action suivante ;
- l'agent appelle un
tool; - il reçoit une observation, mais sans nouveau signal ;
- il revient encore au même reasoning loop.
Une infinite loop apparaît non pas quand l'agent "réfléchit trop longtemps", mais quand runtime ne distingue pas travail utile et répétition sans progrès.
Quels échecs arrivent le plus souvent
Pour rester simple, dans un scénario d'infinite loop on voit surtout quatre patterns.
Boucle dure (Hard loop)
L'agent appelle le même tool avec les mêmes arguments de nombreuses fois.
Cause typique : pas de dedupe sur tool+args, ou répétitions sans limite.
Boucle douce (Soft loop)
L'agent fait la même action avec de petits changements d'arguments : par exemple, ajoute un mot à la recherche et réessaie.
Cause typique : absence de vérification "est-ce que quelque chose de nouveau est apparu".
Tempête de retries (Retry storm)
L'outil échoue, et en même temps retries sont faits côté gateway et côté agent. Résultat : le nombre d'appels se multiplie.
Cause typique : logique de retry dispersée sur plusieurs couches sans policy unique.
Boucle sémantique (Semantic loop)
L'agent paraît actif, mais n'avance pas : il reformule le plan, re-synthétise les mêmes données, ou redemande ce qui est déjà connu.
Cause typique : pas de critère clair de progrès dans runtime.
Comment détecter ces problèmes
Une boucle infinie se voit mieux par combinaison de signaux, pas via une seule métrique.
| Métrique | Signal de boucle | Que faire |
|---|---|---|
steps_per_task | hausse brutale des étapes sans fin | ajouter un max_steps dur et un stop reason |
repeated_tool_signature_rate | répétitions tool+args dans un même run | activer dedupe et limiter les répétitions |
no_progress_steps | plusieurs étapes sans faits/artifacts nouveaux | arrêter le run via règle no-progress window |
stop_reason_distribution | beaucoup de timeout et max_steps_reached | vérifier retry policy et runtime gates |
tokens_per_task | coût en hausse, qualité stable | limiter context/tool output et ajouter progress check |
Comment distinguer un échec d'une tâche vraiment complexe
Un run long ne signifie pas toujours loop. La question clé : est-ce qu'un nouveau signal utile apparaît.
Normal si :
- chaque 1-2 étapes apporte des faits ou artifacts nouveaux ;
- les appels
toolchangent de manière substantielle, pas cosmétique ; - l'agent se rapproche progressivement de
final_answer.
Dangereux si :
- 3-5 étapes d'affilée n'apportent rien de nouveau ;
- le même
toolse répète (ou la même intention se répète) ; - les coûts montent et la qualité de réponse ne s'améliore pas.
Comment arrêter ces échecs
Le but est simple : ne pas continuer le run à tout prix, mais le terminer de manière contrôlée.
En pratique :
- définir des limites runtime dures :
max_steps,timeout,max_tool_calls,max_tokens; - ajouter dedupe sur
tool+argset limite de répétitions ; - arrêter le run s'il n'y a pas de progrès pendant N étapes ;
- renvoyer un stop reason contrôlé et un résultat partiel, pas une erreur "silencieuse".
Loop-guard minimal en runtime :
class LoopGuard:
def __init__(self):
self.max_steps = 12
self.max_repeat = 3
self.max_flat_steps = 4
self.steps = 0
self.flat_steps = 0
self.seen = {}
def on_step(self):
self.steps += 1
if self.steps > self.max_steps:
return "max_steps_reached"
return None
def on_tool_call(self, signature: str):
self.seen[signature] = self.seen.get(signature, 0) + 1
if self.seen[signature] >= self.max_repeat:
return "loop_detected:repeated_tool_signature"
return None
def on_progress(self, has_new_signal: bool):
self.flat_steps = 0 if has_new_signal else self.flat_steps + 1
if self.flat_steps >= self.max_flat_steps:
return "loop_detected:no_progress"
return None
Important : dans chaque itération, appelle d'abord on_step(), puis on_tool_call(...), et après analyse du résultat appelle on_progress(...).
Ce guard ne "soigne" pas l'agent. Il empêche juste la boucle de devenir un incident de production.
Où cela s'implémente dans l'architecture
Dans les systèmes de production, le contrôle de boucle se trouve généralement non pas dans l'agent lui-même, mais dans des couches d'architecture séparées.
Agent Runtime est responsable de l'execution loop de l'agent : limites (max_steps, timeout, max_tokens), stop reasons et fin forcée du run. C'est ici qu'on implémente généralement LoopGuard et la vérification de progrès.
Tool Execution Layer est responsable de l'exécution sûre de tool_call : dedupe des appels, retry policy et normalisation des erreurs. Beaucoup de boucles - retry storm, repeated tool calls et tool spam - apparaissent ici quand il n'y a pas de retry policy unifiée ou de deduplication.
Auto-vérification
Vérification rapide avant release. Coche les points et regarde le statut ci-dessous.
C'est un sanity-check court, pas un audit formel.
Progression: 0/8
⚠ Il y a des signaux de risque
Il manque des contrôles de base. Fermez les points clés de la checklist avant release.
FAQ
Q : Passer à un modèle plus puissant résout-il infinite loop ?
R : Parfois ça aide partiellement, mais ça ne résout pas la racine. Sans runtime gates, même un modèle fort peut boucler.
Q : Comment choisir max_steps au départ ?
R : Commence avec une limite basse conservatrice, et augmente-la seulement là où tu vois un gain de qualité confirmé.
Q : Faut-il toujours faire des retries ?
R : Non. Pour 401/403 et erreurs de validation stables, retries aggravent généralement la boucle.
Q : Que montrer à l'utilisateur quand le run est arrêté ?
R : La raison d'arrêt, ce qui a déjà été essayé, et un résultat partiel. Cela réduit les redémarrages sans changement.
Infinite loop ressemble rarement à une grosse panne. C'est une dégradation lente qui consomme budget et temps. Donc un agent de production a besoin non seulement d'un modèle "intelligent", mais d'un contrôle runtime strict.
Pages liées
Pour mieux traiter ce problème, vois aussi :
- Pourquoi les agents IA échouent - carte générale des échecs en production.
- Tool spam - comment limiter les appels d'outil dupliqués.
- Budget explosion - comment un loop devient une dépense incontrôlée.
- Agent Runtime - où implémenter loop guards et stop reasons.
- Tool Execution Layer - où garder retries, timeout et validation des appels.