Corruption de réponse : quand les sorties de l’agent se cassent

Une corruption de réponse apparaît quand la sortie d’un agent devient incomplète, mal formée ou incohérente. Causes et détection.
Sur cette page
  1. Le problème
  2. Pourquoi ça arrive
  3. Pannes les plus fréquentes
  4. 2xx réussi, mais payload cassé
  5. JSON partiel ou tronqué (Partial payload)
  6. Schema drift silencieux
  7. Corruption sémantique après parse (Semantic corruption)
  8. Comment détecter ces problèmes
  9. Comment distinguer la corruption de réponse d'un simple tool failure
  10. Comment arrêter ces pannes
  11. Où c'est implémenté dans l'architecture
  12. Auto-vérification
  13. FAQ
  14. Pages liées

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 :

  1. le tool renvoie un statut formellement réussi, mais un body corrompu ;
  2. le runtime vérifie seulement le status code et laisse passer le payload ;
  3. schema/invariant checks sont absents ou trop souples ;
  4. l'agent interprète des données "presque valides" comme des faits réels ;
  5. 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étriqueSignal de corruption de réponseAction
tool_output_invalid_ratela part de payload invalides augmenteintroduire strict parse + schema/invariant gate
tool_2xx_with_invalid_payload_ratebeaucoup de 2xx, mais payload invalidene pas se fier au status code seul, vérifier content-type et schema
schema_mismatch_rateincompatibilités de contrat fréquentesversionner les schemas et bloquer les formats inconnus
write_blocked_by_validation_ratewrite souvent bloquée après validationvérifier la dépendance, activer degraded mode
recovery_fallback_ratefallback 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/timeout et l'appel n'atteint pas les données ;
  • la stop reason ressemble à tool_timeout ou tool_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 :

  1. placer size et content-type gate avant tout parse ;
  2. faire un strict parse sans réparation JSON "best effort" ;
  3. vérifier schema et invariants métier avant usage des données ;
  4. en cas de violation, renvoyer stop reason et bloquer les write-actions.

Guard minimal pour vérifier tool output :

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

⏱️ 8 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.