Problème
La demande semble standard : vérifier un paiement et confirmer le statut d'une commande.
Dans les traces, c'est autre chose : en 9 minutes, un run a fait 29 appels d'outils
(billing.get_invoice - 18, payments.verify - 11), et la plupart ont fini en timeout ou 5xx.
Pour ce type de tâche, cela peut coûter ~$2.50 au lieu des ~$0.12 habituels.
Le service n'est pas formellement "mort" : une partie des appels renvoie 200.
Mais l'utilisateur n'obtient pas de réponse finale, et le backlog des runs ainsi que la latence augmentent.
Le système ne crash pas.
Il reste bloqué entre erreurs d'outils et retries, en accumulant progressivement latence et backlog des runs.
Analogie : imagine un coursier qui arrive devant un dépôt fermé, rappelle, attend, rappelle encore et revient toujours à la même porte. Il est en permanence "en cours", mais la commande n'avance pas. Un tool failure chez les agents ressemble à cela : des actions, pas de résultat.
Pourquoi cela arrive
Le tool failure ne vient pas uniquement d'une API instable.
Le plus souvent, le problème principal est l'absence de stratégie claire dans la runtime pour classifier et traiter les erreurs d'outils.
En production, cela ressemble généralement à ceci :
- un service externe renvoie
timeout,5xxou un payload instable ; - la runtime ou le tool gateway retry sans classification claire de l'erreur ;
- des erreurs non-retryable entrent aussi dans des boucles de retries ;
- sans circuit breaker ni fallback, le run se bloque ou brûle du budget.
Le problème n'est pas une erreur API isolée. Le problème est que le système n'arrête pas la vague d'échecs avant qu'elle ne devienne un incident.
Ce type d'incident est généralement appelé agent tool failure -
quand un système d'agents ne peut pas fonctionner de manière stable à cause
de l'instabilité ou d'erreurs d'outils externes.
Pannes les plus fréquentes
Pour rester pratique, en production on voit surtout quatre patterns de tool failure.
Transient failures
L'outil renvoie parfois 408/429/5xx.
Si le contrôle des retries est faible, une panne courte devient un retry storm.
Cause typique : absence de backoff+jitter et de retry budget.
Wrong retry classification
401, 403, 404, 409, schema validation errors ou policy denials partent en retries,
alors qu'il faudrait arrêter immédiatement.
Cause typique : retryable et non-retryable ne sont pas séparés au même endroit.
Tool contract drift
L'outil change le format de réponse ou la structure d'erreur. L'agent n'interprète plus le résultat de façon stable et recommence à "requestionner" le même service.
Cause typique : pas de versioning du contrat et pas de validation de payload dans le gateway.
Cascading failure
Un outil problématique augmente la latence de tout le système : les workers attendent, la file grossit, les autres runs ralentissent aussi.
Cause typique : absence de circuit breaker et de fallback pour des dependencies dégradées.
Comment détecter ces problèmes
Le tool failure est bien visible avec la combinaison des métriques runtime et gateway.
| Métrique | Signal de tool failure | Action |
|---|---|---|
tool_error_rate | hausse brutale des 4xx/5xx/timeout | activer degraded mode et vérifier la dépendance |
retry_attempts_per_call | trop de retries pour un appel | limiter le retry budget, ajouter backoff+jitter |
non_retryable_retry_rate | retries de 401/403/404/409/422 | arrêter le run immédiatement avec stop reason explicite |
circuit_open_rate | circuit breaker s'ouvre fréquemment | vérifier SLA de l'outil et scénario de fallback |
queue_backlog | la file grossit avec un trafic normal | vider les runs bloqués et réduire le fan-out |
Comment distinguer tool failure d'une erreur de logique d'agent
Chaque run en échec ne veut pas dire que l'agent "pense mal". Le critère clé : où exactement la boucle se casse.
Normal si :
- l'erreur est localisée à un outil externe ;
- le stop reason pointe directement la dépendance (
tool_timeout,tool_5xx,circuit_open) ; - après fallback, l'utilisateur reçoit un résultat partiel mais correct.
Dangereux si :
- l'agent retry des erreurs non-retryable comme retryable ;
- il n'y a pas de stop reasons claires au niveau tool gateway ;
- l'échec d'un outil entraîne tout le workflow.
Comment arrêter ces pannes
En pratique, cela ressemble à ceci :
- classifier les erreurs d'outils en retryable et non-retryable ;
- garder retry policy dans un seul tool gateway (backoff+jitter + budget) ;
- poser un circuit breaker contre les vagues d'échecs ;
- en cas d'outil indisponible, retourner fallback/partial et stop reason.
Garde minimale pour les erreurs d'outils :
from dataclasses import dataclass
import time
RETRYABLE = {408, 429, 500, 502, 503, 504}
NON_RETRYABLE = {400, 401, 403, 404, 409, 422}
@dataclass(frozen=True)
class ToolFailureLimits:
max_retry: int = 2
open_circuit_after: int = 3
circuit_cooldown_s: int = 20
class ToolFailureGuard:
def __init__(self, limits: ToolFailureLimits = ToolFailureLimits()):
self.limits = limits
self.fail_streak = 0
self.circuit_open_until = 0.0
def before_call(self) -> str | None:
if time.time() < self.circuit_open_until:
return "tool_unavailable:circuit_open"
return None
def on_result(self, status_code: int, attempt: int) -> str | None:
if status_code in NON_RETRYABLE:
self.fail_streak = 0
return "tool_failure:non_retryable"
if status_code in RETRYABLE:
self.fail_streak += 1
if self.fail_streak >= self.limits.open_circuit_after:
self.circuit_open_until = time.time() + self.limits.circuit_cooldown_s
return "tool_unavailable:circuit_open"
if attempt >= self.limits.max_retry:
return "tool_failure:retry_exhausted"
return "tool_retry:allowed"
self.fail_streak = 0
return None
C'est une garde de base.
En production, on l'étend généralement avec des limites par outil et un backoff exponentiel avec jitter.
attempt est généralement 1-based (1, 2, 3...), et l'état du guard est souvent conservé par tool ou par run.
Où c'est implémenté dans l'architecture
En production, le contrôle des tool failures est presque toujours réparti sur trois couches du système.
Tool Execution Layer est le point de contrôle principal : validation des args et payload, retry policy, classification des erreurs, circuit breaker. Si cette couche est faible, un problème API simple devient vite une cascade.
Agent Runtime gère le cycle de vie du run : stop reasons, timeout, fin contrôlée et réponse fallback. Ici, il est essentiel de ne pas poursuivre un run à tout prix.
Policy Boundaries définit quels outils sont autorisés et quand un run doit se terminer en fail-closed. C'est particulièrement important pour les write-tools et les erreurs de permission.
Checklist
Avant de shipper un agent en production :
- [ ] les erreurs retryable/non-retryable sont séparées explicitement ;
- [ ] les retries sont implémentés dans un seul gateway, pas dans plusieurs couches ;
- [ ]
max_retry, backoff+jitter et retry budget sont définis ; - [ ] circuit breaker et cooldown sont réglés pour chaque outil critique ;
- [ ] les stop reasons couvrent
timeout,5xx,non_retryable,circuit_open; - [ ] la réponse fallback/partielle est définie avant incident ;
- [ ] alertes sur
tool_error_rate,retry_attempts_per_call,queue_backlog; - [ ] un runbook existe pour le degraded mode et le rollback des dépendances.
FAQ
Q : Est-ce suffisant d'augmenter le timeout de l'outil problématique ?
R : Non. Cela masque souvent le problème et augmente la latence. Il faut error classification, retry budget et circuit breaker.
Q : Où doivent vivre les retries ?
R : Dans un choke point unique, généralement le tool gateway. Des retries dans plusieurs couches créent presque toujours de l'amplification.
Q : Quelles erreurs sont généralement non-retryable ?
R : 401, 403, 404, 409, 422, schema validation errors et policy denials. Ces runs doivent en général être stoppés immédiatement avec stop reason explicite.
Q : Que montrer à l'utilisateur quand un outil est indisponible ?
R : La raison de l'arrêt, ce qui a déjà été vérifié, et la prochaine étape sûre : fallback, résultat partiel ou escalade manuelle.
Un tool failure ressemble rarement à une grosse panne unique. Le plus souvent, c'est une série de petits échecs qui s'accumulent en boucles de retries et en file d'attente. C'est pourquoi les agents de production ont besoin non seulement d'outils, mais aussi d'une discipline stricte d'exécution.
Pages liées
Si ce problème apparaît en production, il est aussi utile de voir :
- Pourquoi les agents IA échouent - carte générale des pannes en production.
- Tool spam - comment des appels répétés transforment une erreur d'outil en incident.
- Partial outage - comment une dégradation partielle des dépendances casse le workflow.
- Budget explosion - comment un retry storm gonfle silencieusement les coûts.
- Agent Runtime - où contrôler stop reasons et cycle de vie du run.
- Tool Execution Layer - où garder retries, validation et circuit breaker.