El problema
La solicitud parece simple: actualizar un campo de CRM después de validar el perfil del usuario.
En trazas se ve otra cosa: en 11 minutos un run hizo 18 pasos,
recibió 6 respuestas 200 OK, pero 4 de ellas tenían payload dañado
(HTML en lugar de JSON, body truncado o campos inválidos).
El servicio está formalmente "vivo": no hay timeout, los estados parecen exitosos. Pero el agente toma decisiones sobre datos rotos y ejecuta acciones equivocadas.
El sistema no se cae.
Simplemente degrada el resultado en silencio bajo apariencia de respuesta "exitosa".
Analogía: imagina una cajera escaneando un código de barras dañado. La caja no se apaga, pero en el ticket aparece el producto incorrecto. Response corruption en sistemas de agentes se ve igual: el proceso sigue, pero los datos ya están rotos.
Por qué pasa
Response corruption suele aparecer no por una sola caída de API, sino por un control débil de calidad de tool output en runtime.
LLM tiene un sesgo fuerte hacia una respuesta "completa". Por eso, sin schema gates, el agente tiende más a continuar el run con datos "casi válidos" en vez de detenerse con error.
En producción, normalmente pasa así:
- el tool devuelve un estado formalmente exitoso, pero body dañado;
- runtime revisa solo el status code y deja pasar el payload;
- schema/invariant checks faltan o son demasiado suaves;
- el agente interpreta datos "casi válidos" como hechos reales;
- sin fail-closed, los datos rotos llegan a acciones write.
En traza se ve como subida de tool_output_invalid_rate
con tool_2xx_rate simultáneamente alto.
El problema no es un único JSON roto.
Runtime no corta el tool output dañado antes de que se convierta en decisión o write-action.
Fallos más frecuentes
En producción se repiten cuatro patrones de response corruption.
2xx exitoso, pero payload roto
El tool devuelve 200, pero el body no cumple el contrato.
Causa típica: el control está basado solo en el estado HTTP.
JSON parcial o truncado (Partial payload)
La respuesta llega incompleta: faltan campos o el JSON se corta a mitad.
Causa típica: no hay strict parse ni size/content-type gate.
Schema drift silencioso
El proveedor del tool cambia nombres o tipos de campos, y el agente sigue funcionando con contrato viejo.
Causa típica: no hay schema versioning ni control de campos obligatorios.
Corrupción semántica tras parse (Semantic corruption)
El JSON es formalmente válido, pero los valores violan invariantes
(currency="USD" + amount=-15, status="active" + deleted_at lleno,
status="paid" + paid_at=null).
Causa típica: se valida sintaxis, pero no invariantes de negocio.
Cómo detectar estos problemas
Response corruption se ve bien con combinación de métricas data-quality y runtime.
| Métrica | Señal de response corruption | Qué hacer |
|---|---|---|
tool_output_invalid_rate | sube la proporción de payload inválido | introducir strict parse + schema/invariant gate |
tool_2xx_with_invalid_payload_rate | muchos 2xx, pero payload no pasa validación | no confiar solo en status code, validar content-type y schema |
schema_mismatch_rate | mismatches de contrato frecuentes | versionar schema y bloquear formatos desconocidos |
write_blocked_by_validation_rate | write se bloquea con frecuencia tras validación | revisar dependencia y activar degraded mode |
recovery_fallback_rate | fallback frecuente por baja calidad de datos | actualizar contrato del tool y runbook de recovery |
Cómo distinguir response corruption de un simple tool failure
No toda caída de herramienta implica corrupción de datos. La pregunta clave: si el problema es disponibilidad del tool o calidad del payload.
Normal para tool failure si:
- el tool devuelve
5xx/timeouty la llamada ni siquiera llega a datos; - la stop reason parece
tool_timeoutotool_unavailable; - tras retry, la misma solicitud devuelve payload válido.
Peligroso para response corruption si:
- hay muchos
2xx, pero payload falla parse/schema/invariant checks; - el agente continúa el run con datos "casi válidos";
- el incidente aparece como acción de negocio errónea, no como error explícito de API.
Cómo frenar estos fallos
En práctica, se ve así:
- pones size y content-type gate antes de cualquier parse;
- haces strict parse sin reparación JSON "best effort";
- validas schema e invariantes de negocio antes de usar los datos;
- ante violación, devuelves stop reason y bloqueas acciones write.
Guard mínimo para validar 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
Este es un guard básico.
En producción suele ampliarse con schema versioning,
validators por tool y flujo quarantine para payload sospechoso.
verify_tool_output(...) se llama antes de cualquier write-action,
para que datos corruptos no lleguen a sistemas externos.
Dónde se implementa en la arquitectura
En producción, el control de response corruption casi siempre se reparte entre tres capas del sistema.
Tool Execution Layer lleva la primera línea: content-type, size limits, strict parse, schema checks y versionado de contratos. Aquí se define el límite de calidad entre "datos válidos" y "datos corruptos".
Agent Runtime decide qué hacer después: stop reasons, fail-closed, fallback y bloqueo de write-actions. Si runtime no es disciplinado, un payload corrupto se convierte rápido en incidente de negocio.
Policy Boundaries define cuándo el sistema debe terminar run en fail-closed y qué acciones se prohíben con datos inválidos. Esto es crítico para cualquier herramienta de write.
Autoevaluación
Verificación rápida antes del release. Marca los puntos y mira el estado abajo.
Este es un sanity-check corto, no una auditoría formal.
Progreso: 0/8
⚠ Hay señales de riesgo
Faltan controles básicos. Cierra los puntos clave del checklist antes del release.
FAQ
Q: Si el tool es interno, ¿podemos relajar validaciones?
A: No. Los tools internos también derivan y devuelven payload dañado. La validación es igual de necesaria que en API externas.
Q: ¿Por qué no dejar al modelo "arreglar" JSON inválido?
A: Porque el modelo no recupera la verdad: genera una versión plausible de datos dañados. En escenarios write es más seguro detener el run y devolver stop reason explícita.
Q: ¿Es obligatoria una librería completa de JSON schema desde el día 1?
A: No. Empieza con strict parse e invariantes críticos, y amplía cobertura schema-level en zonas con alto blast radius.
Q: ¿Qué mostrar al usuario cuando el payload es inválido?
A: Motivo de parada, qué ya se verificó y siguiente paso seguro: respuesta partial o rerun tras recovery de la dependencia.
Response corruption casi nunca parece un accidente ruidoso. Es una degradación silenciosa de calidad de datos que rompe decisiones del agente sin avisar. Por eso los agentes de producción necesitan no solo tools disponibles, sino validación estricta de sus respuestas.
Páginas relacionadas
Si este problema aparece en producción, también conviene revisar:
- Por qué fallan los agentes de IA - mapa general de fallos en producción.
- Tool failure - cómo distinguir fallos de herramientas de corrupción de payload.
- Hallucinated sources - cómo datos inválidos se convierten en fuentes no confiables.
- Prompt injection - por qué untrusted tool output no debe tratarse como instrucción.
- Agent Runtime - dónde aplicar stop reasons, fail-closed y fallback.
- Tool Execution Layer - dónde hacer parse/schema/invariant validation.