Le problème
La demande paraît simple : mettre à jour un champ CRM après vérification du profil utilisateur.
Dans les traces, c'est autre chose : en 11 minutes un run a fait 18 étapes,
a obtenu 6 réponses 200 OK, mais 4 d'entre elles avaient un payload corrompu
(HTML au lieu de JSON, body tronqué ou champs invalides).
Le service est formellement "vivant" : pas de timeout, statuts apparemment réussis. Mais l'agent prend des décisions sur des données cassées et exécute de mauvaises actions.
Le système ne tombe pas.
Il dégrade silencieusement le résultat sous l'apparence d'une réponse "réussie".
Analogie : imagine une caissière qui scanne un code-barres abîmé. La caisse ne s'éteint pas, mais le mauvais article arrive sur le ticket. La corruption de réponse dans les systèmes d'agents fonctionne pareil : le processus continue, mais les données sont déjà cassées.
Pourquoi ça arrive
La corruption de réponse apparaît généralement non pas à cause d'une seule panne API, mais à cause d'un contrôle faible de la qualité de tool output dans le runtime.
LLM a un fort biais vers une réponse "complète". Sans schema gates, l'agent poursuit plus souvent le run avec des données "presque valides" au lieu de s'arrêter avec une erreur.
En production, c'est généralement ce scénario :
- le tool renvoie un statut formellement réussi, mais un body corrompu ;
- le runtime vérifie seulement le status code et laisse passer le payload ;
- schema/invariant checks sont absents ou trop souples ;
- l'agent interprète des données "presque valides" comme des faits réels ;
- sans fail-closed, les données cassées arrivent jusqu'aux write-actions.
Dans la trace, on voit une hausse de tool_output_invalid_rate
avec un tool_2xx_rate simultanément élevé.
Le problème n'est pas un seul JSON cassé.
Le runtime n'écarte pas le tool output corrompu avant qu'il ne devienne une décision ou une write-action.
Pannes les plus fréquentes
En production, quatre patterns de corruption de réponse reviennent le plus souvent.
2xx réussi, mais payload cassé
Le tool renvoie 200, mais le body ne respecte pas le contrat.
Cause typique : le contrôle repose uniquement sur le statut HTTP.
JSON partiel ou tronqué (Partial payload)
La réponse arrive incomplète : des champs manquent ou le JSON est coupé au milieu.
Cause typique : absence de strict parse et de size/content-type gate.
Schema drift silencieux
Le fournisseur de tool modifie les noms ou types de champs, et l'agent continue d'utiliser l'ancien contrat.
Cause typique : pas de versioning de schema et pas de contrôle des champs obligatoires.
Corruption sémantique après parse (Semantic corruption)
Le JSON est formellement valide, mais les valeurs violent des invariants
(currency="USD" + amount=-15, status="active" + deleted_at rempli,
status="paid" + paid_at=null).
Cause typique : on vérifie la syntaxe, pas les invariants métier.
Comment détecter ces problèmes
La corruption de réponse se voit bien via la combinaison de métriques data-quality et runtime.
| Métrique | Signal de corruption de réponse | Action |
|---|---|---|
tool_output_invalid_rate | la part de payload invalides augmente | introduire strict parse + schema/invariant gate |
tool_2xx_with_invalid_payload_rate | beaucoup de 2xx, mais payload invalide | ne pas se fier au status code seul, vérifier content-type et schema |
schema_mismatch_rate | incompatibilités de contrat fréquentes | versionner les schemas et bloquer les formats inconnus |
write_blocked_by_validation_rate | write souvent bloquée après validation | vérifier la dépendance, activer degraded mode |
recovery_fallback_rate | fallback fréquent à cause de données de mauvaise qualité | mettre à jour le contrat du tool et le runbook de recovery |
Comment distinguer la corruption de réponse d'un simple tool failure
Tout incident de tool ne signifie pas corruption des données. La question clé : problème de disponibilité du tool ou problème de qualité du payload.
Normal pour tool failure si :
- le tool renvoie
5xx/timeoutet l'appel n'atteint pas les données ; - la stop reason ressemble à
tool_timeoutoutool_unavailable; - après retry, la même requête renvoie un payload valide.
Dangereux pour response corruption si :
- beaucoup de
2xx, mais payload échoue aux checks parse/schema/invariant ; - l'agent continue le run sur des données "presque valides" ;
- l'incident apparaît comme une action métier erronée, pas comme une erreur API explicite.
Comment arrêter ces pannes
En pratique, cela ressemble à ceci :
- placer size et content-type gate avant tout parse ;
- faire un strict parse sans réparation JSON "best effort" ;
- vérifier schema et invariants métier avant usage des données ;
- en cas de violation, renvoyer stop reason et bloquer les write-actions.
Guard minimal pour vérifier tool output :
import json
from dataclasses import dataclass
from typing import Any
@dataclass(frozen=True)
class OutputLimits:
max_chars: int = 200_000
required_content_type: str = "application/json"
def parse_json_strict(raw: str, max_chars: int) -> Any:
if len(raw) > max_chars:
raise ValueError("output_too_large")
return json.loads(raw)
def validate_profile(obj: Any) -> None:
if not isinstance(obj, dict):
raise ValueError("schema:expected_object")
if not isinstance(obj.get("user_id"), str):
raise ValueError("schema:user_id_missing")
if obj.get("plan") not in {"free", "pro", "enterprise"}:
raise ValueError("schema:plan_invalid")
if obj.get("quota", 0) < 0:
raise ValueError("invariant:quota_negative")
def verify_tool_output(raw: str, content_type: str, limits: OutputLimits = OutputLimits()) -> str | None:
if content_type != limits.required_content_type:
return "response_corruption:content_type_mismatch"
try:
obj = parse_json_strict(raw, limits.max_chars)
validate_profile(obj)
except json.JSONDecodeError:
return "response_corruption:invalid_json"
except ValueError as e:
return f"response_corruption:{e}"
return None
C'est un guard de base.
En production, il est généralement complété par schema versioning,
validators par tool et un flux quarantine pour payloads suspects.
verify_tool_output(...) est appelé avant toute write-action,
pour empêcher les données corrompues d'atteindre les systèmes externes.
Où c'est implémenté dans l'architecture
En production, le contrôle de response corruption est presque toujours réparti sur trois couches du système.
Tool Execution Layer porte la première ligne : content-type, size limits, strict parse, schema checks et versioning des contrats. C'est ici qu'on fixe la frontière qualité entre "données valides" et "données corrompues".
Agent Runtime décide quoi faire ensuite : stop reasons, fail-closed, fallback et blocage des write-actions. Si le runtime manque de discipline, un payload corrompu devient vite un incident métier.
Policy Boundaries définit quand le système doit terminer un run en fail-closed et quelles actions sont interdites avec données invalides. C'est critique pour tous les tools de write.
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 : Si le tool est interne, peut-on alléger les vérifications ?
R : Non. Les tools internes dérivent aussi et renvoient des payloads corrompus. La validation est tout aussi nécessaire que pour des API externes.
Q : Pourquoi ne pas laisser le modèle "corriger" un JSON invalide ?
R : Parce que le modèle ne restaure pas la vérité, il génère une version plausible de données corrompues. Pour des scénarios write, il est plus sûr d'arrêter le run avec une stop reason explicite.
Q : Une bibliothèque complète JSON schema est-elle obligatoire dès le premier jour ?
R : Non. Commence avec strict parse et invariants critiques, puis étends la couverture schema-level sur les zones à fort blast radius.
Q : Que montrer à l'utilisateur quand le payload est invalide ?
R : La raison d'arrêt, ce qui a déjà été vérifié, et l'étape suivante sûre : réponse partial ou rerun après recovery de la dépendance.
Response corruption ressemble rarement à un crash bruyant. C'est une dégradation silencieuse de la qualité des données qui casse discrètement les décisions de l'agent. Les agents de production ont donc besoin non seulement de tools disponibles, mais aussi d'une validation stricte de leurs réponses.
Pages liées
Si ce problème apparaît en production, ces pages sont aussi utiles :
- Pourquoi les agents IA échouent - carte générale des pannes en production.
- Tool failure - comment distinguer pannes de tools et corruption de payload.
- Hallucinated sources - comment des données invalides deviennent des sources non fiables.
- Prompt injection - pourquoi untrusted tool output ne doit pas être traité comme instruction.
- Agent Runtime - où placer stop reasons, fail-closed et fallback.
- Tool Execution Layer - où faire parse/schema/invariant validation.