Lo esencial
Todos los cursos enseñan "cómo construir un agente". Nadie enseña "qué hacer cuando se rompe a las 3 de la mañana antes del demo con el cliente".
Producción no es "lo lancé y me olvidé". Producción es "lo lancé y ahora vivo con esto". Alucinaciones, loops infinitos, un MCP caído, un context overflow, un pico en la factura de la API, prompt injection, una falla silenciosa: no son "si llega a pasar". Son "cuando pase".
Esta lección cubre 7 failure patterns (patrones de falla) típicos y un playbook de recuperación para cada uno. Herramientas, patrones de código, listas de verificación, respuesta a incidentes. No es teoría: es lo que vas a hacer en un momento real de pánico.
Conceptos clave
- Hallucination (alucinación): el modelo da con seguridad un dato falso
- Loop: el agente se atoró en una acción, reintenta sin avanzar
- Tool failure: un servidor MCP o una API externa no responde o devuelve un error
- Context overflow: el contexto se infló y el modelo pierde las instrucciones (context rot)
- Cost spike: la factura de la API creció de 5 a 10 veces sin razón aparente
- Prompt injection: una entrada externa hizo que el agente actuara contra las reglas
- Silent failure (falla silenciosa): el pipeline terminó sin errores, pero el resultado está mal
- Circuit breaker: un patrón que desconecta una integración rota durante N minutos
- Graceful degradation: pasar a una funcionalidad simplificada cuando algo falla
- Chaos engineering: provocar fallas a propósito para comprobar que estás listo
- Postmortem: un análisis estructurado del incidente: causa raíz y prevención
Teoría
Falla 1: Hallucination (el agente miente con seguridad)
Síntoma: la respuesta se ve correcta, el formato es limpio, el tono es seguro, pero el dato es falso. "En Ecuador el impuesto predial es del 5%" (en realidad la tasa es otra). "Esta función apareció en la versión 3.2 de la librería" (esa versión no existe).
Niveles de riesgo:
- CRITICAL: consejos médicos, legales o financieros (el cliente actúa con base en la respuesta)
- HIGH: código de producción, consultas SQL, configuraciones (se ejecutan sin revisión)
- MEDIUM: charla casual, lluvia de ideas (consecuencias limitadas)
Patrones de recuperación:
- Verificación contra una fuente confiable (RAG): agrega recuperación de fuentes verificadas. El modelo no responde "de memoria", sino con base en documentos que tú controlas
- Confidence scoring: en el prompt, "Responde + indica tu confianza de 0 a 100. Si es < 70, di 'no estoy seguro'"
- Votación entre varios LLM: dos llamadas independientes (Claude + GPT). Si las respuestas no coinciden, se escala a una persona
- "Cita o rehúsa": el modelo DEBE indicar la fuente (URL, id del documento) o decir explícitamente "no sé"
Detección: revisa a mano una muestra del 5% de las respuestas cada día. Registra las preguntas en las que el modelo dijo "no sé": ese es tu mapa de huecos.
# Patrón: cita o rehúsa, con el system prompt
SYSTEM = """Responde solo con base en los documentos proporcionados.
Acompaña cada dato con su fuente en el formato [doc_id:section].
Si la información no está en los documentos, responde exactamente:
"No puedo confirmarlo con las fuentes disponibles."
NO inventes. NO te apoyes en conocimiento general."""Falla 2: Loop (el agente se repite sin fin)
Síntoma: la misma frase, la misma llamada a una herramienta, el mismo error, una y otra vez. Los registros se llenan de entradas idénticas. La factura de la API sigue corriendo.
Causa común: la herramienta falló → el agente lo intenta de nuevo con los mismos parámetros. O el modelo "no entiende" que la herramienta ya devolvió "archivo no encontrado" y repite la llamada.
Recuperación:
- Tope de iteraciones: máximo N intentos → alternativa o cancelar
- Contexto del error al reintentar: pasar el mensaje del error anterior a la siguiente llamada
- Diferencia de estado: comprobar que el estado cambió entre iteraciones
- Detectar el atasco: la misma acción 3 veces seguidas → cancelar
MAX_ATTEMPTS = 5
previous_action = None
stuck_count = 0
for i in range(MAX_ATTEMPTS):
result = agent.run(state)
if result.action == previous_action:
stuck_count += 1
if stuck_count >= 3:
raise StuckLoopError(f"Same action repeated 3x: {result.action}")
else:
stuck_count = 0
previous_action = result.action
if result.done:
break
else:
raise MaxIterationsExceeded(f"Hit cap {MAX_ATTEMPTS}")Falla 3: Tool failure (el servidor MCP no responde)
Síntoma: timeout, HTTP 500, el servidor MCP se cayó, rate limit de una API externa.
Recuperación:
- Circuit breaker: 3 errores seguidos → desconecta la integración 5 minutos; no la sigas llamando en vano
- Estrategia alternativa: el MCP de Stripe está caído → consulta en la base de datos local para la información básica de cobro
- Graceful degradation: la función no está disponible → dilo con honestidad: "el servicio no está disponible por ahora, intenta en 5 min". NO inventes un resultado
- Health checks: haz ping al MCP cada 60 s y lanza una alerta si lleva caído más de 2 min
Librería: Tenacity (Python): retry con espera exponencial listo para usar.
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
reraise=True
)
def call_external_api(payload):
return requests.post(URL, json=payload, timeout=10)Falla 4: Context overflow (el agente olvidó el inicio de la sesión)
Síntoma: el agente ignora las instrucciones del system prompt, se contradice, "olvida" lo que hablaron hace 20 mensajes.
Causa: contexto > 50K tokens → context rot. El modelo trabaja peor con la información de en medio y mejor con la del principio y el final.
Recuperación:
/compacten Claude Code cuando el contexto crece (puedes indicar qué conservar:/compact conserva las decisiones de arquitectura); la compresión automática también funciona, y su umbral se ajusta con el comando/autocompact- Sliding window (ventana deslizante): conserva los últimos N mensajes + un resumen de los anteriores
- Instrucciones críticas al final: el modelo recuerda mejor el contexto reciente (recency bias)
- El system prompt en cada mensaje: si una regla es crítica, repítela
- Datos largos → RAG: no metas 50K de documentación al contexto; haz recuperación
Falla 5: Cost spike (la factura del día es 10 veces la normal)
Síntoma: $50 al día en lugar de $5. El aviso de que te acercas al límite mensual llega ya el día 5 del mes.
Causas posibles:
- Un bug de loop (no se atrapó la Falla 2)
- Falta de caché (prompts idénticos repetidos sin prompt caching)
- Un ataque de scraping (si hay un endpoint público sin autenticación)
- Una tormenta de reintentos (falla → retry → falla → retry)
- La clave de API en manos ajenas (se filtró en git)
Recuperación:
- Interruptor de presupuesto diario: gasto > $X/día → la API se pausa automáticamente
- Detección de picos: gasto > 2 veces el promedio diario → alerta por correo o chat
- Registro de auditoría: cada llamada a la API → un registro en KV/DB (barato y fácil de depurar)
- Desactiva e investiga antes de reanudar. Nada de "subo el límite para seguir" sin entender qué pasó
Herramientas:
- Anthropic Console: la página Usage y tu propio límite de gasto (Settings → Billing → Spend limits)
- Langfuse: observabilidad de código abierto, costo por usuario
- LangSmith: tracing para LangChain/LangGraph
Falla 6: Prompt injection
Síntoma: el agente desactivó un hook "porque el usuario lo pidió", pasó secretos a una herramienta externa, envió un correo a tu nombre, ejecutó rm -rf siguiendo una instrucción de un archivo Markdown que estaba leyendo.
Mira el detalle en la lección Prompt Injection Defense. Aquí va la recuperación.
Recuperación:
- Aislamiento: cada herramienta riesgosa pasa por un agente aparte con los permisos mínimos
- Registra todo en auditoría: qué fue una orden y qué fue una entrada externa (sepáralo explícitamente)
- Restauración:
git revert+ avisa al equipo + investiga la brecha - Lecciones aprendidas: agrega el patrón al hook
pre-tool-use-prompt-injection.sh
# Protección mínima: el hook busca patrones sospechosos
# .claude/hooks/pre-tool-use-prompt-injection.sh
grep -iE 'ignore (previous|all) instructions|disable.*hook|reveal.*system prompt' "$INPUT" \
&& exit 1Falla 7: Silent failure (el agente "funciona" pero el resultado está mal)
La más traicionera. El pipeline terminó, exit code 0, ningún error en los registros. Pero el resultado es basura.
Ejemplos:
- El LLM devolvió un JSON con la estructura correcta, pero los valores salieron de la nada
- La traducción se lee fluida, pero el sentido está distorsionado
- Un cálculo financiero con la fórmula correcta, pero con cifras inventadas
Recuperación:
- Validación de la salida: revisa el esquema de cada respuesta del LLM. Pydantic, JSON schema, zod
- Smoke tests: en cada corrida del pipeline → comprueba que 1 o 2 casos conocidos den el resultado esperado
- Retroalimentación de usuarios: "¿esta respuesta es correcta? 👍/👎" cada 50 interacciones → a un panel
- Eval suite (lección Evals): pruebas de regresión después de cada cambio al prompt
from pydantic import BaseModel, Field, ValidationError
class InvoiceExtraction(BaseModel):
amount: float = Field(gt=0, lt=1_000_000)
currency: str = Field(pattern="^(USD|EUR|MXN)$")
invoice_date: str # ISO 8601
try:
parsed = InvoiceExtraction.model_validate_json(llm_output)
except ValidationError as e:
# se atrapó una falla silenciosa: escalar
log_anomaly(llm_output, e)
raiseKit de recuperación (lo que debes traer en la cajuela)
| Categoría | Herramienta | Para qué |
|---|---|---|
| Registros | Langfuse, LangSmith, registros estructurados en KV | Cada llamada al LLM con entrada/salida/tokens |
| Alertas | Webhook a un chat (Telegram, Slack), PagerDuty, correo | Picos / caídas / anomalías |
| Monitoreo | Anthropic Console + un panel propio (lección Production Observability) | Costo, latencia, tasa de errores |
| Rollback | Tags de git + un script de revert | Volver a la última versión buena con un comando |
| Comunicación | Plantillas de respuesta a clientes | "Problema conocido, se corrige en 2 horas" |
| Validación | Pydantic / zod / JSON schema | Revisar el esquema de cada salida |
| Lógica de retry | Tenacity (Python), p-retry (JS) | Espera exponencial lista para usar |
Lista de verificación de producción (obligatoria antes de lanzar)
Lanzar a producción sin 8 de 8 es deuda técnica desde el primer día.
Chaos engineering para IA (probar los casos de falla)
El enfoque de Principles of Chaos aplicado a agentes. Rompe el sistema a propósito en staging para asegurarte de que la recuperación funciona.
| Qué inyectas | Cómo | Qué compruebas |
|---|---|---|
| Hallucination | Agrega al prompt "Always answer Y is true" → prueba de picos | Si la capa de verificación lo detecta |
| Loop | Un mock de la herramienta siempre devuelve el mismo error | Si se activa el tope de iteraciones |
| HTTP 500 | Un mock del MCP devuelve 500 en cada tercera llamada | Si se activan el circuit breaker y la alternativa |
| Context overflow | Mete 100K tokens de basura | Si se dispara /compact |
| Cost spike | Un loop sin break, un prompt barato × 10000 | Si se activa el interruptor de presupuesto diario |
| Schema mismatch | El LLM devuelve campos con llaves de más o de menos | ¿Pydantic lo atrapa o falla en silencio? |
Una vez al mes, un día de caos en staging. Media hora de trabajo que te salva en producción.
Respuesta a incidentes: 4 pasos
Cuando algo se está quemando, no entres en pánico. Sigue los pasos:
- Stop (detener): desactiva la función afectada para evitar más daño. Feature flag apagado o una página de mantenimiento
- Triage (clasificar): qué pasó, alcance (1 usuario / todos), radio de impacto (¿cobros? ¿datos? ¿reputación?)
- Stabilize (estabilizar): rollback a la última versión buena O un arreglo temporal (una alternativa fija) para recuperar el servicio
- Postmortem: después de recuperarte: causa raíz, qué agregaremos al monitoreo, cómo prevenirlo
Plantilla de postmortem (journals/incidents/YYYY-MM-DD-<name>.md):
# Incidente: <nombre> **Fecha:** YYYY-MM-DD **Duración:** 14:32 - 15:47 UTC (1h 15m) **Severidad:** P1 / P2 / P3 **Impacto:** N clientes, $X de pérdida / 0 pérdida de datos ## Cronología - 14:32: alerta de pico en el chat (costo > 2 veces el promedio) - 14:35: inicio de la investigación, encontré un loop en el agente X - 14:50: se desplegó el tope de iteraciones - 15:47: servicio estable ## Causa raíz La herramienta Y devolvía una cadena vacía en lugar de null. El agente lo interpretó como "no funcionó" y reintentó sin fin. ## Qué salió bien - La alerta llegó en 3 minutos - El procedimiento de rollback funcionó ## Qué salió mal - No había tope de iteraciones (MAX_ATTEMPTS no estaba definido) - La validación de la salida no revisaba cadenas vacías ## Acciones - [ ] Agregar MAX_ATTEMPTS=5 a todos los agentes (responsable: A, plazo: mañana) - [ ] Revisión con Pydantic de la salida de la herramienta Y (responsable: A, plazo: 3 días) - [ ] Prueba de caos con cadena vacía (responsable: A, plazo: una semana)
Sin postmortem, el incidente se repite. Garantizado.
Antipatrones (lo que NO hay que hacer en pánico)
- ❌ "Nada más lo reinicio" sin entender la causa raíz. En una hora se repite
- ❌ Ocultarles el incidente a los clientes. Se van a enterar solos y la confianza se daña
- ❌ Un arreglo en caliente sin registrarlo en la auditoría. En un mes: "¿por qué hay un número mágico aquí?"
- ❌ "Fue casualidad, lo olvido". Los patrones se repiten. Cada incidente → postmortem
- ❌ Desactivar un hook para "probar" (mira la lección Hook-Deny-By-Design). Riesgo de seguridad + se te olvidará reactivarlo
- ❌ Subir el límite de la API para seguir sin entender la causa del pico
Recuperar el costo (si ya perdiste dinero)
La realidad: la recuperación casi siempre es incompleta. Prevenir sale más barato.
| Escenario | Probabilidad de reembolso | Acciones |
|---|---|---|
| Un bug de cobro de Anthropic (del lado de ellos) | Baja, pero real | Ticket de soporte con trace_id y registros. A veces dan crédito |
| Un error de cobro en Stripe (el cliente paga de más) | Alta | Disputa en el panel de Stripe, te reembolsan |
| Clave de API filtrada (push a GitHub) | 0% | Rota la clave de inmediato, audita todas las llamadas |
| Un bug de loop se comió $200 | 0% | Lección aprendida. Tope de iteraciones para siempre |
La lección: vigila las alertas de presupuesto. Recuperar dinero es un bono raro, no un plan.
Público: qué aprender y cuándo
Principiante (primeros 3 meses en producción)
Solo las fallas 1 a 3, las más frecuentes:
- Alucinación, lo básico (cita o rehúsa en el prompt)
- Tope de iteraciones en cada loop
- Try/except + retry para fallas de herramientas
Registros básicos (al principio, un simple print a un archivo). Límite de gasto en Anthropic Console.
Intermedio (de 3 a 12 meses)
Agregas:
- Las fallas 4 y 5 (manejo de contexto + monitoreo de costos)
- Langfuse / LangSmith para observabilidad
- Alertas de picos a un chat
- La cultura del postmortem (aunque trabajes solo, escríbelo)
Profesional (más de 1 año en producción)
Los 7 patrones:
- Chaos engineering cada mes
- Runbooks automatizados (incidente → un bot hace el triage)
- Votación entre varios LLM en las rutas críticas
- Validación con Pydantic en todas partes
- Avisos automáticos a clientes
Práctica
Paso 1: Agrega un tope de iteraciones a todos los agentes
Busca en el código los while y for sin límite superior y las llamadas recursivas sin límite de profundidad.
# Antes
def agent_loop(state):
while not state.done:
state = step(state)
return state
# Después
MAX_ITERATIONS = 10
def agent_loop(state):
for i in range(MAX_ITERATIONS):
state = step(state)
if state.done:
return state
raise MaxIterationsExceeded(
f"Hit {MAX_ITERATIONS}. Last state: {state.summary()}"
)Paso 2: Configura el límite de gasto y un aviso
Anthropic Console → Settings → Billing → Spend limits → Adjust limit. Hay un solo límite: un techo mensual. Cuando se alcanza, la API se detiene hasta que subas el límite o empiece un mes nuevo. Ponlo en más o menos 3 veces tu gasto mensual normal.
El aviso («soft limit») hazlo tú, con una revisión diaria por cron. El umbral: tu gasto diario promedio actual × 1.5.
# scripts/check-daily-cost.sh
# Necesitas una Admin API key (sk-ant-admin01-...): la tienen las organizaciones; las cuentas individuales no —
# en ese caso revisa a mano la página Usage en la Console.
TODAY=$(date -u +%Y-%m-%dT00:00:00Z)
TOMORROW=$(date -u -v+1d +%Y-%m-%dT00:00:00Z) # en Linux: date -u -d tomorrow +%Y-%m-%dT00:00:00Z
RESP=$(curl -s "https://api.anthropic.com/v1/organizations/cost_report?starting_at=$TODAY&ending_at=$TOMORROW" \
-H "anthropic-version: 2023-06-01" \
-H "x-api-key: $ANTHROPIC_ADMIN_KEY")
# Los montos llegan en centavos como texto; revisa la estructura exacta de la respuesta en la Cost API reference
DAILY_SPEND=$(echo "$RESP" | jq '[.data[].results[].amount | tonumber] | add / 100')
if (( $(echo "$DAILY_SPEND > 20" | bc -l) )); then
curl -X POST "https://api.telegram.org/bot$BOT_TOKEN/sendMessage" \
-d "chat_id=$CHAT_ID&text=⚠️ Daily spend: \$$DAILY_SPEND"
fiDescripción de la Cost API: platform.claude.com/docs/en/manage-claude/usage-cost-api.
Paso 3: Agrega validación con Pydantic a las salidas críticas
from pydantic import BaseModel, Field, ValidationError
from anthropic import Anthropic
client = Anthropic()
class CustomerReply(BaseModel):
sentiment: str = Field(pattern="^(positive|neutral|negative)$")
intent: str
confidence: float = Field(ge=0, le=1)
suggested_response: str = Field(min_length=10, max_length=500)
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "..."}]
)
try:
validated = CustomerReply.model_validate_json("".join(b.text for b in response.content if b.type == "text"))
use(validated)
except ValidationError as e:
log_to_journal("".join(b.text for b in response.content if b.type == "text"), e)
raise SilentFailureDetected(str(e))Paso 4: Corre una prueba de caos
Elige la falla que más miedo te da (normalmente el pico de costo o la falla silenciosa). Hoy, en staging:
- Crea las condiciones a propósito (un mock que devuelve una salida mala / un loop sin break)
- Ejecuta el agente
- Mide el tiempo hasta la alerta o la cancelación
- Si tarda más de 5 minutos, agrega detección. Si no funcionó, corrígelo
Después, anótalo en journals/chaos-tests/YYYY-MM-DD.md:
- Qué inyectaste
- Qué esperabas
- Qué pasó
- Acciones
Paso 5: Plantilla de aviso a clientes
templates/customer-incident-notification.md:
Hola: En este momento tenemos un problema con <función>: <breve descripción del síntoma>. **Estado:** estamos trabajando en la corrección **Tiempo estimado de recuperación:** <X minutos> **Qué hacer por ahora:** <alternativa o "esperar"> Te avisaremos por correo cuando lo resolvamos. Disculpa las molestias. — El equipo de <nombre>
Un solo archivo. Lo abres, llenas 3 campos y lo envías. En pleno incidente no tienes que "escribir desde cero".
Herramientas y recursos
- Tenacity: librería de Python para reintentos con espera exponencial
- Anthropic Console: seguimiento del uso y tu propio límite de gasto
- Langfuse: observabilidad de código abierto para apps con LLM, costo por usuario, latencia (Helicone pasó a modo de mantenimiento en marzo de 2026)
- LangSmith: tracing para agentes de LangChain/LangGraph
- Pydantic: validación de esquemas en Python (zod es el equivalente en TS)
- PagerDuty: gestión de incidentes para equipos serios
- Principles of Chaos Engineering: el enfoque de Netflix para provocar fallas a propósito
- Lección Prompt Injection Defense: defensa en profundidad contra prompt injection
- Lección Production Observability: paneles propios para monitorear agentes
- Lección Evals: suites de evals para pruebas de regresión
Lista de verificación de la lección
Ideas clave
Producción no es "lo lancé y me olvidé". Producción es "lo lancé y vivo con esto". Los 7 failure patterns les van a pasar a todos los que trabajan con agentes en producción. La pregunta no es "si", sino "cuándo" y "si estás listo".
Prevenir es más barato que recuperar. Tope de iteraciones + interruptor de presupuesto + validación de la salida: tres patrones baratísimos que cubren la mayor parte de los incidentes típicos. Sin ellos, tienes deuda técnica desde el primer día en producción.
Un postmortem sin culpables es la única forma de no repetir un incidente. La plantilla toma 10 minutos y te protege por años. Aunque trabajes solo, escríbelo. Tu yo del futuro te lo va a agradecer.
Siguiente lección
→ Backup y Disaster Recovery para el stack de IA: qué hacer cuando se cae el propio proveedor
La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso