7 Patrones Que Evitan Que Tu Agente AI Se Descontrole en Producción
Tu agente AI funciona perfecto en desarrollo. Pasa todos los tests, maneja los escenarios de demo sin problemas y deja boquiabiertos a los stakeholders en la sprint review. Después lo desplegás. En menos de 48 horas, quema $400 en costos de API procesando un loop recursivo, le manda a un cliente los datos personales de su vecino, y genera con toda confianza un query SQL que tira un índice de la base de producción.
Esto no es hipotético. Es un patrón que se repite en toda la industria en 2026. La brecha entre "listo para demo" y "listo para producción" en agentes AI es más amplia de lo que la mayoría de los equipos creen, y los modos de fallo son fundamentalmente distintos al software tradicional. Tu API REST no decide responder una pregunta distinta a la que le hicieron. Tu driver de base de datos no alucina un nombre de tabla. Pero tu agente AI hace las dos cosas, y las hace con absoluta confianza.
Esta guía cubre siete patrones probados en batalla para mantener agentes AI confiables en producción. No son frameworks teóricos — están extraídos de post-mortems reales, caídas de producción, y lecciones ganadas a la fuerza por equipos que operan agentes a escala.
Patrón 1: El Circuit Breaker
El software tradicional usa circuit breakers para prevenir fallos en cascada cuando los servicios downstream se caen. Los agentes AI también los necesitan, pero con una vuelta: no sólo estás protegiendo contra HTTP 500s. Estás protegiendo contra un modelo que empieza a devolver basura.
Por Qué los Agentes Necesitan Circuit Breakers
Un agente AI que llama a una herramienta que falla no crashea. Reintenta. Y reintenta. Y como es "inteligente", puede probar approaches ligeramente distintos cada vez — todos fallan, todos cuestan tokens. Sin un circuit breaker, una sola herramienta rota puede quemar todo tu presupuesto diario de API en minutos.
Implementación
class AgentCircuitBreaker { private failures: Map<string, { count: number; lastFailure: number }> = new Map(); private readonly threshold = 5; private readonly resetTimeout = 60000; async callTool(toolName: string, fn: () => Promise<any>): Promise<any> { const state = this.failures.get(toolName) || { count: 0, lastFailure: 0 }; if (state.count >= this.threshold) { const elapsed = Date.now() - state.lastFailure; if (elapsed < this.resetTimeout) { throw new CircuitOpenError( `Herramienta "${toolName}" deshabilitada temporalmente. ` + `${Math.ceil((this.resetTimeout - elapsed) / 1000)}s para reintentar.` ); } state.count = this.threshold - 1; } try { const result = await fn(); this.failures.set(toolName, { count: 0, lastFailure: 0 }); return result; } catch (error) { state.count++; state.lastFailure = Date.now(); this.failures.set(toolName, state); throw error; } } }
La Clave
Cuando el circuito se abre, devolvé el error al agente como contexto. No tires una excepción y listo — decile al modelo que la herramienta no está disponible y sugerile alternativas:
if (error instanceof CircuitOpenError) { return { role: 'tool', content: `El servicio ${toolName} no está disponible temporalmente (circuit breaker abierto). ` + `Informale al usuario que esta función está temporalmente caída, ` + `o intentá un approach alternativo que no requiera esta herramienta.` }; }
Esto convierte un fallo duro en una degradación elegante.
Patrón 2: Retry-Classify (No Reintentar a Ciegas)
El patrón naive de retry — "si falla, intentá lo mismo de nuevo" — es activamente dañino con agentes AI. Si el modelo generó una llamada API malformada, reintentar el mismo prompt probablemente genere la misma llamada malformada. Estás pagando doble por el mismo fallo.
El Patrón Retry-Classify
En vez de reintentos ciegos, clasificá el error primero y rutéalo a la estrategia de recuperación apropiada:
class RetryClassifier: def classify(self, error: Exception, tool_name: str) -> RetryStrategy: if isinstance(error, RateLimitError): return RetryStrategy.BACKOFF if isinstance(error, ValidationError): return RetryStrategy.REPAIR if isinstance(error, AuthenticationError): return RetryStrategy.FAIL_FAST if isinstance(error, TimeoutError): return RetryStrategy.BACKOFF if isinstance(error, ToolNotFoundError): return RetryStrategy.FALLBACK return RetryStrategy.FAIL_FAST async def execute_with_retry(agent, action, max_retries=3): classifier = RetryClassifier() for attempt in range(max_retries): try: return await agent.execute(action) except Exception as e: strategy = classifier.classify(e, action.tool_name) if strategy == RetryStrategy.FAIL_FAST: raise if strategy == RetryStrategy.BACKOFF: wait = (2 ** attempt) + random.uniform(0, 1) await asyncio.sleep(wait) continue if strategy == RetryStrategy.REPAIR: action = await agent.repair_action(action, error=str(e)) continue if strategy == RetryStrategy.FALLBACK: action = agent.get_fallback_action(action) continue raise MaxRetriesExceeded(f"Falló después de {max_retries} intentos")
La Estrategia de Reparación en Detalle
La estrategia REPAIR es donde la cosa se pone interesante. En vez de reintentar el mismo prompt, le pasás el mensaje de error al modelo como contexto adicional:
async def repair_action(self, failed_action, error: str): repair_prompt = f"""Tu llamada anterior a la herramienta falló con este error: Herramienta: {failed_action.tool_name} Input: {json.dumps(failed_action.input)} Error: {error} Analizá el error y generá una llamada corregida. NO repitas el mismo input que causó la falla.""" corrected = await self.llm.generate(repair_prompt) return corrected
Este patrón resuelve una parte significativa de los errores de validación en el primer intento de reparación. Formatos de fecha incorrectos, campos requeridos faltantes, valores fuera de rango — son exactamente el tipo de errores estructurados que los modelos pueden auto-corregir cuando se les muestra el mensaje de error específico. En la práctica, los equipos reportan tasas de reparación muy por encima del 50% para fallos a nivel de schema.
Patrón 3: Gobernadores de Presupuesto
La falla más aterradora de un agente AI no es un crash — es una espiral de costos descontrolada. Un agente atrapado en un loop de razonamiento puede quemar cientos de dólares en costos de API antes de que alguien se dé cuenta. Los gobernadores de presupuesto son límites duros que previenen esto.
Tres Capas de Control de Presupuesto
interface BudgetConfig { maxTokensPerRequest: number; maxTokensPerSession: number; maxToolCallsPerSession: number; maxCostPerSession: number; maxDurationSeconds: number; } class BudgetGovernor { private usage = { tokens: 0, toolCalls: 0, cost: 0, startTime: Date.now() }; check(config: BudgetConfig): void { if (this.usage.tokens > config.maxTokensPerSession) throw new BudgetExceededError('Presupuesto de tokens excedido'); if (this.usage.toolCalls > config.maxToolCallsPerSession) throw new BudgetExceededError('Límite de llamadas a herramientas — posible loop infinito'); if (this.usage.cost > config.maxCostPerSession) throw new BudgetExceededError(`Techo de costo alcanzado: $${this.usage.cost.toFixed(2)}`); const elapsed = (Date.now() - this.usage.startTime) / 1000; if (elapsed > config.maxDurationSeconds) throw new BudgetExceededError(`Timeout de sesión: ${elapsed.toFixed(0)}s`); } recordUsage(tokens: number, cost: number, isToolCall: boolean): void { this.usage.tokens += tokens; this.usage.cost += cost; if (isToolCall) this.usage.toolCalls++; } }
Calibrando los Límites
| Tipo de Presupuesto | Desarrollo | Staging | Producción |
|---|---|---|---|
| Tokens por sesión | 50,000 | 30,000 | 20,000 |
| Llamadas a herramientas por sesión | 50 | 25 | 15 |
| Costo por sesión | $5.00 | $2.00 | $0.50 |
| Timeout | 5 min | 3 min | 2 min |
Arrancá restrictivo en producción y aflojá basándote en datos de uso real. Es mucho más fácil subir límites que explicar una factura sorpresa de $2,000.
El Patrón "Detección de Agente Atascado"
def detect_stuck_agent(tool_call_history: list[str], window: int = 5) -> bool: if len(tool_call_history) < window: return False recent = tool_call_history[-window:] most_common = max(set(recent), key=recent.count) return recent.count(most_common) / len(recent) >= 0.8
Patrón 4: Guardrails de Salida
El modelo eventualmente va a generar algo que no debería. PII en una respuesta al cliente. Un statement SQL en un payload de webhook. Una URL alucinada que lleva a un sitio de phishing. Los guardrails de salida son tu última línea de defensa.
El Pipeline de Guardrails
class GuardrailPipeline { private guardrails: Guardrail[] = []; async validate(output: string, context: AgentContext): Promise<string> { for (const guardrail of this.guardrails) { const result = guardrail.check(output, context); if (result.action === 'BLOCK') throw new GuardrailViolation(guardrail.name, result.reason); if (result.action === 'REDACT') output = result.redactedOutput; if (result.action === 'FLAG') await this.alertOncall(guardrail.name, output, result.reason); } return output; } }
Guardrails Esenciales
1. Detección de PII — Regex patterns para SSN, emails, teléfonos, tarjetas de crédito con acción REDACT.
2. Prevención de Inyección de Código — Bloquear DROP TABLE, <script>, eval(), rm -rf en respuestas user-facing.
3. Ancla de Groundedness — Verificar que las URLs referenciadas en la salida existan en el contexto fuente.
Patrón 5: El Kill Switch
Todo agente AI en producción necesita un mecanismo de parada de emergencia. No "degradar gradualmente en los próximos minutos" — una parada dura e inmediata que frena toda actividad del agente en todas las instancias.
Por Qué Lo Necesitás
Los kill switches no son para el manejo normal de errores. Son para escenarios como: el agente empieza a mandar contenido inapropiado a clientes, un ataque de prompt injection está siendo explotado activamente, el agente está haciendo cambios no autorizados en datos de producción, o los costos se espiralan y los gobernadores de presupuesto no lo atrapan.
Implementación: Feature Flag + Remote Config
class AgentKillSwitch { async checkBeforeAction(agentId: string): Promise<void> { const config = await this.getRemoteConfig(); if (config.globalKillSwitch) throw new AgentHaltedError('Todos los agentes detenidos por kill switch global'); if (config.disabledAgents.includes(agentId)) throw new AgentHaltedError(`Agente ${agentId} detenido por kill switch dirigido`); if (await this.abuseDetector.isCompromised(agentId)) { await this.activateKillSwitch(agentId, 'Automático: abuso detectado'); throw new AgentHaltedError('Agente detenido: patrón de abuso detectado'); } } }
La Regla Crítica
El check del kill switch debe ocurrir antes de cada llamada LLM y cada ejecución de herramienta — no solo al inicio de la sesión.
while (hasMoreSteps) { await killSwitch.checkBeforeAction(this.agentId); // ← Cada iteración const response = await llm.chat(messages); await killSwitch.checkBeforeAction(this.agentId); // ← Después del LLM, antes de la herramienta if (response.toolCalls) { for (const call of response.toolCalls) { await killSwitch.checkBeforeAction(this.agentId); // ← Antes de cada herramienta await executeTool(call); } } }
Patrón 6: Observabilidad y Tracing
No podés arreglar lo que no podés ver. Y los agentes AI son notoriamente opacos — el mismo input puede producir diferentes cadenas de razonamiento, diferentes secuencias de tool calls, y diferentes outputs. El monitoreo tradicional (tiempos de respuesta, tasas de error) te dice casi nada sobre por qué un agente falló.
Qué Tracear
Cada ejecución de agente debe producir un trace estructurado con: traceId, sessionId, la cadena completa de pasos (cada LLM call, tool call y guardrail check), métricas agregadas (tokens, costo, duración, cantidad de tool calls, retries, guardrails triggered), y el outcome final.
Los Tres Dashboards Que Necesitás
1. Dashboard de Operaciones en Tiempo Real
| Métrica | Qué Te Dice |
|---|---|
| Sesiones activas | Cuántos agentes están corriendo ahora |
| Tasa de error (ventana 5 min) | Si algo se acaba de romper |
| Latencia P95 | Degradación de experiencia de usuario |
| Costo por minuto | Velocidad de consumo de presupuesto |
| Estado de circuit breakers | Qué herramientas están fallando |
2. Dashboard de Calidad (Diario)
| Métrica | Qué Te Dice |
|---|---|
| Tasa de completitud de tareas | Si los agentes realmente resuelven problemas |
| Tasa de triggers de guardrails | Qué tan seguido se porta mal el modelo |
| Tasa de retry por herramienta | Qué integraciones son inestables |
| Pasos promedio por tarea | Si los prompts necesitan optimización |
| Satisfacción del usuario (si está disponible) | La única métrica que importa al final |
3. Vista de Investigación de Incidentes
Cuando algo sale mal, necesitás poder reproducir la secuencia exacta: cada mensaje, cada respuesta del LLM, cada input/output de tool call, cada verificación de guardrail. Almacená los traces por al menos 30 días. Cuando ocurre un incidente, este trace es tu evidencia forense.
Tip Práctico: Logueá el Prompt, No Solo la Respuesta
La mayoría de los equipos loguean las respuestas del LLM pero no el prompt completo que se envió. Esto hace el debugging imposible. Logueá el prompt completo (mensaje de sistema + historial + definiciones de herramientas) para cada llamada. Sí, es verboso. Sí, cuesta storage. Te va a ahorrar horas de debugging cuando las cosas salgan mal.
Patrón 7: Puertas de Aprobación Human-in-the-Loop
La autonomía total es un objetivo, no un punto de partida. Los agentes más confiables en producción usan autorización por niveles — el agente puede hacer cosas de bajo riesgo de forma autónoma, pero las acciones de alto riesgo requieren aprobación humana.
Definiendo Niveles de Riesgo
enum RiskTier { LOW = 'low', // Autónomo: leer datos, buscar, generar texto MEDIUM = 'medium', // Notificar: enviar emails, actualizar registros HIGH = 'high', // Aprobar: borrar datos, transacciones financieras CRITICAL = 'critical', // Multi-aprobar: cambios de schema, control de acceso }
El Flujo de Aprobación
async function executeWithApproval( agent: Agent, toolCall: ToolCall, context: AgentContext ): Promise<ToolResult> { const risk = toolRiskMap[toolCall.name] || RiskTier.HIGH; switch (risk) { case RiskTier.LOW: return await executeTool(toolCall); case RiskTier.MEDIUM: const result = await executeTool(toolCall); await notifyTeam(toolCall, result, context); return result; case RiskTier.HIGH: const approval = await requestApproval({ toolCall, context, timeout: 300_000 }); if (approval.approved) return await executeTool(toolCall); else return { role: 'tool', content: `Acción denegada: ${approval.reason}` }; case RiskTier.CRITICAL: const approvals = await requestMultiApproval({ toolCall, context, requiredApprovals: 2, timeout: 600_000 }); if (approvals.every(a => a.approved)) return await executeTool(toolCall); else return { role: 'tool', content: 'Se requiere aprobación adicional.' }; } }
La Realidad Práctica
Human-in-the-loop crea latencia. Mitigalo con: pre-aprobación de patrones frecuentes, batching de aprobaciones, workflows asíncronos para tareas no urgentes, y confianza progresiva (empezá con HITL para todo y bajá el nivel de riesgo a medida que ganás confianza).
Armando Todo: El Stack de Confiabilidad
┌─────────────────────────────────────────┐
│ Human-in-the-Loop │ ← Acciones de alto riesgo gateadas
├─────────────────────────────────────────┤
│ Guardrails de Salida │ ← PII, inyección, alucinación
├─────────────────────────────────────────┤
│ Gobernadores de Presupuesto │ ← Costo, tokens, tiempo
├─────────────────────────────────────────┤
│ Kill Switch │ ← Parada de emergencia
├─────────────────────────────────────────┤
│ Circuit Breakers │ ← Aislamiento de fallos
├─────────────────────────────────────────┤
│ Retry-Classify │ ← Recuperación inteligente
├─────────────────────────────────────────┤
│ Observabilidad │ ← Trace completo de cada decisión
└─────────────────────────────────────────┘
Orden de Implementación
- Gobernadores de Presupuesto (Día 1) — Previene daño financiero inmediatamente
- Kill Switch (Día 1) — Tu freno de emergencia
- Observabilidad (Semana 1) — No podés mejorar lo que no podés medir
- Guardrails de Salida (Semana 1-2) — Frenar contenido malo antes de que llegue al usuario
- Circuit Breakers (Semana 2) — Aislar fallos de herramientas
- Retry-Classify (Semana 2-3) — Mejorar tasas de éxito
- Human-in-the-Loop (Semana 3-4) — Agregar confianza para acciones críticas
La Realidad 2026
El ecosistema de agentes AI está madurando rápido. Frameworks como LangGraph, CrewAI, y los SDKs de agentes de OpenAI y Google están agregando más primitivas de confiabilidad built-in. Pero no alcanzan por sí solos. Los defaults de los frameworks son permisivos — están diseñados para hacer demos fáciles, no para mantener sistemas de producción seguros.
Tu agente eventualmente va a hacer algo inesperado. La pregunta no es "si" sino "cuándo", y si tu stack de confiabilidad lo atrapa antes de que llegue a un usuario, una base de datos o un sistema de facturación.
Los mejores agentes AI no son los más inteligentes. Son los que fallan con gracia.
Explora herramientas relacionadas
Prueba estas herramientas gratuitas de Pockit