Boucle infinie d'agent : quand un agent IA ne s'arrête pas

Une infinite loop apparaît quand l'agent continue à générer de nouvelles étapes sans progrès réel. Pourquoi cela arrive et comment on l'arrête en production.
Sur cette page
  1. Le problème
  2. Pourquoi cela arrive
  3. Quels échecs arrivent le plus souvent
  4. Boucle dure (Hard loop)
  5. Boucle douce (Soft loop)
  6. Tempête de retries (Retry storm)
  7. Boucle sémantique (Semantic loop)
  8. Comment détecter ces problèmes
  9. Comment distinguer un échec d'une tâche vraiment complexe
  10. Comment arrêter ces échecs
  11. Où cela s'implémente dans l'architecture
  12. Auto-vérification
  13. FAQ
  14. Pages liées

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 :

  1. le LLM propose l'action suivante ;
  2. l'agent appelle un tool ;
  3. il reçoit une observation, mais sans nouveau signal ;
  4. 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étriqueSignal de boucleQue faire
steps_per_taskhausse brutale des étapes sans finajouter un max_steps dur et un stop reason
repeated_tool_signature_raterépétitions tool+args dans un même runactiver dedupe et limiter les répétitions
no_progress_stepsplusieurs étapes sans faits/artifacts nouveauxarrêter le run via règle no-progress window
stop_reason_distributionbeaucoup de timeout et max_steps_reachedvérifier retry policy et runtime gates
tokens_per_taskcoût en hausse, qualité stablelimiter 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 tool changent 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 tool se 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 :

  1. définir des limites runtime dures : max_steps, timeout, max_tool_calls, max_tokens ;
  2. ajouter dedupe sur tool+args et limite de répétitions ;
  3. arrêter le run s'il n'y a pas de progrès pendant N étapes ;
  4. renvoyer un stop reason contrôlé et un résultat partiel, pas une erreur "silencieuse".

Loop-guard minimal en runtime :

PYTHON
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 :

⏱️ 7 min de lectureMis à jour 12 mars 2026Difficulté: ★★☆
Implémenter dans OnceOnly
Guardrails for loops, retries, and spend escalation.
Utiliser dans OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
controls:
  loop_detection:
    enabled: true
    dedupe_by: [tool, args_hash]
  retries:
    max: 2
    backoff_ms: [200, 800]
stop_reasons:
  enabled: true
logging:
  tool_calls: { enabled: true, store_args: false, store_args_hash: true }
Intégré : contrôle en productionOnceOnly
Ajoutez des garde-fous aux agents tool-calling
Livrez ce pattern avec de la gouvernance :
  • Budgets (steps / plafonds de coût)
  • Kill switch & arrêt incident
  • Audit logs & traçabilité
  • Idempotence & déduplication
  • Permissions outils (allowlist / blocklist)
Mention intégrée : OnceOnly est une couche de contrôle pour des systèmes d’agents en prod.
Exemple de policy (concept)
# Example (Python — conceptual)
policy = {
  "budgets": {"steps": 20, "seconds": 60, "usd": 1.0},
  "controls": {"kill_switch": True, "audit": True},
}

Auteur

Nick — ingénieur qui construit une infrastructure pour des agents IA en production.

Focus : patterns d’agents, modes de défaillance, contrôle du runtime et fiabilité des systèmes.

🔗 GitHub: https://github.com/mykolademyanov


Note éditoriale

Cette documentation est assistée par l’IA, avec une responsabilité éditoriale humaine pour l’exactitude, la clarté et la pertinence en production.

Le contenu s’appuie sur des défaillances réelles, des post-mortems et des incidents opérationnels dans des systèmes d’agents IA déployés.