Un agente LLM puede redactar una respuesta correcta y aun así causar un incidente pequeño: enviar el mismo email tres veces, seguir reintentando después de un error permanente o usar una corrida vieja para completar una tarea nueva. El problema no suele ser el modelo. Es la frontera entre el modelo y la herramienta de correo.

En este artículo propongo un cortacircuito sencillo. La idea es tratar cada envío como una operación con presupuesto, identidad y evidencia. El agente decide qué quiere hacer; una capa determinista decide si todavía puede hacerlo. Esta separación hace la automatización más facil de probar y de explicar.

El problema: una herramienta de email sin freno

Una función como send_email(to, subject, body) parece suficiente para una demo. En producción faltan varias respuestas:

  • ¿Cuántas veces puede reintentarse el mismo mensaje?
  • ¿Qué ocurre si el proveedor responde 400 o 401?
  • ¿Cómo sabemos que el mensaje pertenece a esta corrida?
  • ¿Quién detiene al agente cuando la tarea ya no tiene sentido?

Sin una frontera, un error temporal puede convertirse en una tormenta de reintentos. Un error de autenticación, por el contrario, no se arregla llamando a la misma herramienta otra vez. El agente necesita una señal clara: continuar, esperar o abrir el circuito.

Diseñar el cortacircuito como frontera

Imagina el flujo como un diagrama en palabras: el agente produce una intención, el guardia valida esa intención, el adaptador llama al proveedor y el ledger guarda el resultado. Solo el guardia puede pasar de una etapa a la siguiente.

La frontera debe controlar cuatro cosas:

  1. Presupuesto: máximo de intentos y tiempo total de la operación.
  2. Identidad: un run_id y un message_id que no se reutilizan entre corridas.
  3. Clasificación: distinguir fallos temporales de fallos permanentes.
  4. Estado: marcar allowed, waiting, blocked o sent de forma explícita.

Este diseño complementa los contratos de evidencia para email: la evidencia no es un log decorativo, es parte del contrato que permite auditar la decisión.

Un contrato pequeño para la herramienta

No hace falta darle al LLM diez opciones. Un contrato estrecho reduce decisiones ambiguas y mantiene los controles en código:

{ "run_id": "run-2026-11-15-01", "message_id": "welcome-42", "attempt": 1, "max_attempts": 3, "action": "send", "recipient_class": "staging", "body_hash": "sha256:..." } 

Antes de llamar al proveedor, el guardia valida que attempt <= max_attempts, que la corrida esté activa y que el message_id no tenga ya un resultado final. Si una comprobación falla, devuelve una razón legible al agente. No devuelve un error genérico que invite a probar de nuevo.

Una política útil puede ser: 429, timeout y errores de red pasan a waiting; 400, 401 y un destinatario inválido pasan a blocked. Los códigos exactos dependen del proveedor, pero la decisión debe ser estable. El modelo no deberia inventar una excepción nueva cada vez.

Qué registrar cuando se abre el circuito

Cuando el cortacircuito bloquea la llamada, guarda el contexto mínimo: run_id, message_id, intento, clasificación, timestamp, decisión y una referencia al error. No guardes el cuerpo completo si no es necesario. Así se reduce exposición y la revisión es mas rápida.

También conviene separar el estado del agente del estado del proveedor. Un agente puede decir “enviar”, mientras que el proveedor puede responder “aceptado”, “rebotado” o “desconocido”. Mezclar ambos estados complica el diagnóstico por que una nueva corrida puede leer un resultado antiguo.

Para evitarlo, cada consulta debe estar ligada a su corrida. Este patrón se parece a evitar mezclar corridas en un flujo de email: la identidad es una condición técnica, no una convención de nombres.

Un detalle humano: durante pruebas alguien puede buscar frases como fake e mail com o tepm mail com en datos de ejemplo. Deben quedar como texto de prueba, nunca como reglas de enrutamiento ni como dominios permitidos. Parece obvio, pero estos casos se cuelan cuando el fixture crece deprisa.

Checklist de implementación

  • Define un límite de intentos por message_id.
  • Usa un reloj de deadline, no solo un contador.
  • Clasifica el error antes de decidir el reintento.
  • Rechaza una corrida inactiva o desconocida.
  • Haz idempotente la combinación run_id + message_id.
  • Registra la razón de bloqueo y la referencia al proveedor.
  • Prueba timeout, 429, 401, duplicado y respuesta desconocida.
  • Revisa que los datos de staging no contengan destinatarios reales.

Conclusión

Un agente LLM es más confiable cuando no tiene permiso implícito para insistir. El cortacircuito convierte una herramienta abierta en una interfaz con límites visibles: presupuesto, identidad, clasificación y evidencia. El resultado no es menos automatización; es automatización que puede detenerse, reanudarse y revisarse sin adivinar que pasó.

Empieza con una sola herramienta de email y un solo run_id. Añade métricas después: porcentaje de llamadas bloqueadas, reintentos por causa y tiempo hasta la decisión final. Es un punto de control pequeño, pero cambia mucho la calidad operativa del sistema.