Error Recovery en AI Agents: Por Qué el Try-Catch No Es Suficiente y Cómo Implementar un Sistema de Clasificación que No Falle
El 95% de los AI agents se rompen en su primer error real. Aprende a implementar Error Recovery con clasificación en 4 buckets, decoradores y checkpoints humanos. Código real en español.
El 95% de los AI Agents que Construyes Hoy se Van a Romper en Producción
No es culpa del modelo. Es culpa de tu try-catch.
La mayoría de los desarrolladores tratan los AI Agents como software determinista. Escribís código que asume que el LLM va a devolver JSON válido. Que la tool call va a funcionar. Que el contexto va a caber en la ventana.
Y cuando falla, ponéis un try-catch.
*El problema no es que los agents fallen. Es que los tratáis como funciones puras cuando son sistemas probabilísticos con estado.*
El error recovery no es un tema de manejo de excepciones. Es un tema de arquitectura de clasificación y checkpoints humanos.
Y si no lo resolvéis ahora, vais a acumular un 40% de deuda técnica que se paga después en debugging nocturno.
Voy a contaros exactamente cómo lo hago yo en producción. Sin teoría. Con código.
Por Qué los Try-Catch Fallan con LLMs
Un try-catch asume que el error es una excepción. Algo excepcional.
Pero en sistemas con LLMs, los errores semánticos — output malformado, alucinaciones, tool calls inventadas — no son excepciones. Son el comportamiento esperado en un porcentaje de casos.
❌ Try-catch clásico:
```python
try:
result = llm.call(prompt)
return result.choices[0].message.content
except Exception as e:
log.error(f"Error: {e}")
return None
```
Esto funciona en tu entorno de desarrollo con 3 ejecuciones. En producción, con escala y variabilidad de inputs, el rate de errores semánticos crece.
Y cuando falla, falla sin dejar rastro de por qué.
✅ Error Classifier:
```python
from enum import Enum
class ErrorCategory(Enum):
TRANSITORY = "transitory" # Timeouts, rate limits → backoff
SEMANTIC = "semantic" # LLM hallucinating → re-prompt
STATE = "state" # Context corrupted → partial rollback
FATAL = "fatal" # Schema broken → abort with state preserved
```
Un error que ocurre el 5-10% de las veces no es una excepción. Es un patrón. Y los patrones necesitan su propia lógica de manejo.
La diferencia entre re-intentar y re-prompting es el 90% de los fallos en producción.
El Sistema de Clasificación que Cambia Todo
El error más común en implementaciones de agents: reintentar la misma tool call cuando falla.
Si el error es transitorio — rate limit, timeout — el backoff exponencial funciona.
Pero si el error es semántico — el LLM entendió mal la instrucción — reintentar lo mismo produce el mismo fallo.
La clave está en clasificar:
Errores transitorios → backoff exponencial + reintento
Errores semánticos → re-prompt con contexto adicional del fallo anterior
Errores de estado → rollback parcial a un checkpoint válido
Errores fatales → abort con estado preservado para revisión humana
Esto no es teoría. Lo implementé en conversoriaecnae.es, un agent que procesa consultas de asesorías. El rate de fallos en producción pasó de ~15% a ~2% simplemente clasificando antes de actuar.
Paso 1: Implementa el Error Classifier
Aquí tienes el clasificador que uso en producción. No es complicado. Son 40 líneas que salvan semanas de debugging.
```python
import time
from dataclasses import dataclass, field
from typing import Any, Optional
@dataclass
class ErrorRecord:
category: ErrorCategory
step_name: str
tool_name: str
error_message: str
context_snapshot: dict
timestamp: float = field(default_factory=time.time)
retry_count: int = 0
class ErrorClassifier:
def classify(self, error: Exception, context: dict) -> ErrorCategory:
if self._is_transitory(error):
return ErrorCategory.TRANSITORY
if self._is_semantic(error, context):
return ErrorCategory.SEMANTIC
if self._is_state_corruption(error, context):
return ErrorCategory.STATE
return ErrorCategory.FATAL
def _is_transitory(self, error: Exception) -> bool:
transitory = ["timeout", "rate limit", "429", "503", "connection"]
return any(t in str(error).lower() for t in transitory)
def _is_semantic(self, error: Exception, context: dict) -> bool:
semantic = ["invalid tool", "hallucination", "malformed",
"unexpected format", "wrong parameters"]
return any(s in str(error).lower() for s in semantic)
def _is_state_corruption(self, error: Exception, context: dict) -> bool:
state = ["memory inconsistent", "context overflow",
"state mismatch", "corrupted"]
return any(s in str(error).lower() for s in state)
```
Cada tool call y cada step del agent se envuelve con un decorator que registra: el tipo de error, el timestamp, el contexto parcial, y el número de reintentos — antes de decidir la acción.
Paso 2: El Decorator que Valida Todo
No confiéis en que el LLM devolvió lo prometido. Verificad contra un esquema definido antes de avanzar.
```python
from functools import wraps
import json
def validate_step(schema: dict):
def decorator(func):
@wraps(func)
def wrapper(self, args, *kwargs):
result = func(self, args, *kwargs)
Post-action validation
validation = self._validate_against_schema(result, schema)
if not validation["valid"]:
error = ValueError(f"Schema mismatch: {validation['errors']}")
category = self.classifier.classify(error, self.state.snapshot())
if category == ErrorCategory.TRANSITORY:
return self._handle_transitory(func, args, *kwargs)
elif category == ErrorCategory.SEMANTIC:
return self._re_prompt_with_context(func, args, *kwargs)
elif category == ErrorCategory.STATE:
return self._rollback_to_checkpoint()
else:
return self._abort_with_state_preserved()
Only advance on validated output
self.state.advance(step=func.__name__, result=result)
return result
return wrapper
return decorator
```
Este decorator intercepta cada paso del agent. Clasifica el error. Decide si reintentar (backoff exponencial), escalar a humano, o reescribir el prompt parcialmente — vs un simple try-except que solo relanza.
La diferencia: el decorator deja rastro. El try-catch no.
Paso 3: Define Checkpoints Humanos Obligatorios — No Opcionales
Aquí viene la parte que más duele.
Culturalmente, escalar a un humano se ve como una derrota técnica. No lo es. Es la feature más infravalorada de los agents en producción.
Un checkpoint humano no es una pausa del sistema. Es un punto de validación que permite al agent operar con autonomía el 90% del tiempo pero asegurar corrección en el edge case crítico.
La regla es simple:
Cuando un error semántico ocurre más de N veces seguidas (ej: 3)
O cuando un error fatal interrumpe el flujo
El agent pausa, serializa su estado interno, y emite un evento para que un humano revise y decida: continuar, modificar, o abortar
No es una interrupción constante. Es una red de seguridad para el edge case.
Los agents más exitosos en producción — implementaciones de LangGraph, CrewAI — usan human-in-the-loop precisamente porque permite más autonomía, no menos.
Paso 4: Implementa un Agent State Tracker
Cada fallo es un dato. No lo tiréis.
```python
@dataclass
class AgentState:
step_history: list = field(default_factory=list)
error_log: list[ErrorRecord] = field(default_factory=list)
current_checkpoint: Optional[str] = None
def record_error(self, record: ErrorRecord):
self.error_log.append(record)
self._adjust_strategy(record)
def _adjust_strategy(self, record: ErrorRecord):
Detect patterns: 2 rate limits in a row → double sleep
recent = [e for e in self.error_log[-5:]
if e.category == record.category]
if len(recent) >= 2:
self._increase_backoff(record.step_name)
3 semantic errors on same step → flag for human review
semantic_errors = [e for e in self.error_log
if e.category == ErrorCategory.SEMANTIC
and e.step_name == record.step_name]
if len(semantic_errors) >= 3:
self._trigger_human_checkpoint(record)
```
Este tracker acumula el histórico de fallos por step y ajusta dinámicamente la estrategia: si detecta 2 rate limits seguidos, duplica el sleep entre tool calls automáticamente. Si detecta 3 errores semánticos en el mismo paso, dispara el checkpoint humano.
No es sobreingeniería. Es pagar la deuda técnica antes de que se acumule.
Paso 5: Validación Posterior a Cada Acción
No confiéis en que el LLM devolvió lo que prometió. Verificad contra un esquema definido antes de avanzar.
```python
def _validate_against_schema(self, output: Any, schema: dict) -> dict:
if not isinstance(output, dict):
return {"valid": False, "errors": ["Output must be a dict"]}
errors = []
for key, expected_type in schema.items():
if key not in output:
errors.append(f"Missing key: {key}")
elif not isinstance(output[key], expected_type):
errors.append(f"Type mismatch: {key}")
return {"valid": len(errors) == 0, "errors": errors}
```
Cada acción del agent se valida contra un esquema antes de registrar el paso como completado. Si falla la validación, el error se clasifica y se maneja según su categoría.
Esto no añade latencia significativa. Lo que añade es visibilidad: sabes exactamente dónde y por qué falló cada ejecución.
Lo Que la Gente se Equivoca
"Mi agent funciona con un try-catch simple — esto es sobreingeniería."
Funciona en tu ordenador con 3 ejecuciones. En producción, con 10.000 ejecuciones y inputs variados, el rate de errores semánticos escala. El try-catch funciona hasta que no funciona, y cuando falla, falla sin dejar rastro.
"Los checkpoints humanos rompen la autonomía del agent."
La autonomía no es binaria. Un agent con checkpoints humanos bien definidos opera autónomamente el 90%+ del tiempo. El checkpoint no es una interrupción constante: es una red de seguridad para el edge case. Los agents más exitosos en producción usan human-in-the-loop precisamente porque permite más autonomía, no menos.
"Clasificar errores añade latencia y complejidad."
Sí, añade complejidad inicial. Pero es complejidad explícita que reemplaza la complejidad implícita del debugging. El coste de implementar un Error Classifier es una fracción del coste de diagnosticar por qué un agent produjo silenciosamente resultados incorrectos durante 3 semanas.
El Framework: Error Recovery en 3 Capas
Llamadlo El Patrón de Recuperación por Clasificación. Tiene 3 capas:
Capa 1 — Clasificación: El ErrorClassifier distingue entre errores transitorios, semánticos, de estado y fatales. Cada categoría recibe su propia estrategia de manejo.
Capa 2 — Decoración: El decorator `@validate_step` envuelve cada tool call y cada paso del agent. Registra el error antes de actuar. Nunca asume que la acción funcionó — verifica contra un esquema.
Capa 3 — Checkpoint Humano: Cuando un error semántico supera el umbral de reintentos, el agent serializa su estado, pausa el flujo, y emite un evento. El humano revisa y decide. No es una interrupción — es un seguro.
Si implementáis estas 3 capas, vuestros agents no se rompen en el primer error real. Se recuperan, aprenden, y siguen adelante.
Y el 40% de deuda técnica acumulada que pagábamos en debugging nocturno — desaparece.
Para Recordar
Los try-catch asumen errores excepcionales. Los LLMs producen errores probabilísticos. Son categorías diferentes.
Clasificar antes de actuar reduce el rate de fallos de ~15% a ~2% en producción.
Los checkpoints humanos no rompen la autonomía — la hacen posible en el edge case.
Cada fallo es un dato. Registradlo, analizadlo, ajustad la estrategia.
Un Error Classifier de 40 líneas y un decorator de 20 líneas salvan semanas de debugging.
El error recovery no es un tema de manejo de excepciones. Es un tema de arquitectura. Y la arquitectura se construye antes de que el agent llegue a producción.
Construid agents que aprenden de sus fallos. No agents que fallan en silencio.
Lee el artículo completo en brianmenagomez.com
Más sobre mis servicios en brianmenagomez.com
Herramientas: Conversor IAE CNAE · Gestorias cerca de ti · Calculadora IRPF

