Normal path: execute → tool → observe.
Le problème (côté prod)
Ce n’est pas une outage totale. C’est intermittent.
Un tool :
- parfois 200
- parfois timeout
- parfois 502
L’agent insiste pour “terminer”. Le user refresh parce qu’il voit des timeouts. Chaque refresh = un nouveau run = du budget brûlé.
Pourquoi ça casse en prod
1) Intermittent = tentation de loop
“Ça marche parfois” pousse l’agent à réessayer.
2) Pas de signal de santé tool
Sans health/breaker, l’agent ne sait pas qu’il doit arrêter.
3) Pas de safe-mode
Safe-mode = comportement dégradé : cache/partiel/read-only.
4) API “all or nothing”
Si ton contrat exige “réponse complète”, tu forces la boucle.
Exemple d’implémentation (code réel)
Snapshot health au début du run :
- tool critique dégradé → désactivé pour ce run
- safe-mode + partial
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class Health:
degraded_tools: set[str]
def snapshot_health() -> Health:
return Health(degraded_tools=set(get_degraded_tools())) # (pseudo)
def safe_tools_for_run(health: Health) -> set[str]:
allow = {"search.read", "kb.read", "http.get"}
for t in health.degraded_tools:
allow.discard(t)
return allow
def run(task: str) -> dict[str, Any]:
health = snapshot_health()
allow = safe_tools_for_run(health)
if "kb.read" not in allow:
return {
"status": "degraded",
"reason": "kb.read degraded",
"partial": "KB pas fiable en ce moment. Voilà ce que je peux faire sans…",
}
return agent_loop(task, allow=allow) # (pseudo)export function snapshotHealth() {
return { degradedTools: new Set(getDegradedTools()) }; // (pseudo)
}
export function safeToolsForRun(health) {
const allow = new Set(["search.read", "kb.read", "http.get"]);
for (const t of health.degradedTools) allow.delete(t);
return allow;
}
export function run(task) {
const health = snapshotHealth();
const allow = safeToolsForRun(health);
if (!allow.has("kb.read")) {
return {
status: "degraded",
reason: "kb.read degraded",
partial: "KB pas fiable en ce moment. Voilà ce que je peux faire sans…",
};
}
return agentLoop(task, { allow }); // (pseudo)
}En outage partielle, l’objectif n’est pas “réussir à tout prix”. C’est “ne pas transformer une petite panne en grosse panne”.
Incident réel (avec chiffres)
Agent support basé sur kb.read.
Service KB dégradé (p95 ~300ms → 9s, timeouts). L’agent insistait “parce que parfois ça marche”.
Impact :
- durée moyenne d’un run : 8s → 52s
- retries côté client → trafic doublé
- pager sur “agent timeouts” au lieu de la vraie cause
- spend ~+$180/jour
Fix :
- health snapshot + degrade mode
- fail-fast quand breaker open
- partial + stop reason
- message “retry later” au lieu d’un timeout silencieux
Compromis
- Réponses dégradées moins complètes.
- Fail-fast baisse le taux de succès à court terme.
- Health peut faire des faux positifs.
Quand NE PAS l’utiliser
- Besoin de complétude stricte : async + progress.
- Pas de sémantique “partial” : tu finis en timeouts.
- Pas de health signals : commence par budgets + breaker.
Checklist (copier-coller)
- [ ] Health snapshot au début du run
- [ ] Degrade mode policy
- [ ] Fail-fast sur breaker open
- [ ] Partial + stop reason
- [ ] Budgets toujours actifs
- [ ] Alerting: runs dégradés vs normaux
Config par défaut sûre (JSON/YAML)
degrade_mode:
enabled: true
disable_tools_when_degraded: true
allow_partial_results: true
health:
breaker_open_means_degraded: true
budgets:
max_seconds: 60
max_tool_calls: 12
FAQ (3–5)
Utilisé par les patterns
Pannes associées
Gouvernance requise
Q : Pourquoi ne pas retry jusqu’à ce que ça marche ?
R : Parce que intermittent + retries amplifie l’outage. Ton agent devient un générateur de charge.
Q : Que renvoyer en degrade mode ?
R : Du partiel, du cache, ou un “pas possible maintenant” avec stop reason.
Q : Il faut une health par tool ?
R : Oui pour les dépendances externes. Commence par breaker + error rate.
Q : Les users gèrent le partiel ?
R : Mieux que les timeouts. Donne une stop reason et une option de retry.
Pages liées (3–6 liens)
- Foundations: Un agent prêt pour la prod · Pourquoi les agents échouent
- Failure: Pannes en cascade · Tool spam loops
- Governance: Tool permissions
- Production stack: Production stack