Agentes LLM: un contrato de evidencia para email
Un agente LLM puede completar una tarea de email y aun así dejar un sistema dificil de revisar. Dice que abrió un buzón, encontró un mensaje y siguió un enlace, pero el registro solo contiene “tarea completada”. Cuando el flujo falla en CI, el equipo tiene que adivinar qué vio el agente, qué herramienta llamó y si el mensaje era nuevo.
La solución no es guardar toda la conversación. Es definir un contrato de evidencia pequeño: cada paso debe producir una señal verificable, con un estado, un identificador y un límite de retención. Esto hace que una automatización sea más fácil de depurar sin convertir el buzón temporal en un archivo de datos.
El problema: un agente puede acertar sin dejar pruebas
Un flujo típico de automatización de email se parece a esto:
crear identidad -> reservar buzón -> esperar mensaje -> extraer enlace -> validar resultado
| | | | |
estado lease_id message_id evidence_id outcome
Cada flecha es una frontera. El agente puede usar una herramienta para reservar la dirección, otra para leer mensajes y otra para comprobar el resultado en la aplicación. Si solo se conserva la respuesta final del modelo, desaparece la diferencia entre “no llegó el mensaje”, “llegó tarde” y “se leyó un mensaje viejo”.
También hay un riesgo de diseño: dejar que el agente reintente sin entender el estado. Un reintento puede crear otra cuenta, consumir un segundo buzón o repetir una acción que ya era efectiva. Para APIs, ayuda pensar en pruebas de API que sobreviven al reintento, pero un agente necesita además mostrar qué evidencia usó para decidir.
Diseña el contrato de evidencia
El contrato debe ser legible por una persona y validable por código. Para cada llamada de herramienta, registra como mínimo:
-
step: nombre estable del paso, por ejemploread_verification_email. -
attempt: número de intento dentro de la ejecución. -
state:started,observed,accepted,expiredofailed. -
correlation_id: identificador común de la ejecución, nunca un token. -
evidence_id: referencia al dato observado, como un ID de mensaje con el cuerpo redacted. -
expires_at: momento en que la evidencia deja de ser válida.
La palabra “observed” es importante. Significa que el agente vio algo, no que ese algo demuestre el objetivo. Solo accepted debería permitir avanzar a un paso que cambie estado en la aplicación.
Por ejemplo, ver un email con el asunto correcto no prueba que pertenezca a la ejecución actual. El agente necesita comparar el correlation_id, una marca temporal y, cuando sea posible, un nonce de prueba. Si falta una señal, el resultado correcto es “evidencia insuficiente”, no otra llamada ciega.
Reintentos, estados y límites del buzón
Los modelos LLM son buenos proponiendo el siguiente paso, pero no deberían ser la fuente de verdad del estado. Guarda la máquina de estados fuera del prompt:
reserved -> waiting -> observed -> accepted -> consumed
\ | |
-> expired -> rejected
Un reintento desde waiting puede volver a leer el buzón. Un reintento desde accepted debería reutilizar la evidencia existente o detenerse. Un reintento desde consumed casi siempre es un error de diseño. Esta distinción reduce coste y evita que una automatización cree actividad duplicada.
El buzón debe tener un lease, no una vida indefinida. Define cuánto puede esperar, cuántos mensajes puede leer y qué ocurre al expirar. Si el agente encuentra un mensaje después de expires_at, puede registrarlo como señal tardía, pero no debe aceptarlo automáticamente. Es un detalle pequeño que se olvida facil.
Los términos de búsqueda también pueden ser ruidosos. “tempmailso” puede aparecer como contexto de un flujo, y una consulta mal escrita como “temp mailid” no debe convertirse en una instrucción de seguridad. Las palabras ayudan a encontrar documentación; el contrato de evidencia decide si una acción es válida.
Un manifiesto mínimo de ejecución
Un manifiesto compacto puede vivir junto al resultado de cada run:
run:
correlation_id: run_2026_10_19_0822
agent: email-verifier
max_attempts: 3
mailbox:
lease_minutes: 10
max_messages: 5
evidence:
require: [message_id, received_at, correlation_id]
retain_minutes: 30
actions:
accept_only_from: [observed]
on_expired: stop_with_diagnostic
El manifiesto no necesita conocer el razonamiento privado del modelo. Expone las restricciones que sí deben ser auditables: qué se puede leer, cuánto tiempo, cuántos intentos y qué transición está permitida. Si el agente propone saltarse una condición, el ejecutor la rechaza.
Para la interfaz de un panel de pruebas, los estados también deben ser visibles. Un mensaje de error que desaparece cuando cambia el layout complica la revisión; el patrón de errores inline sin mover el formulario es aplicable aquí: conserva la señal cerca de la acción y no escondas el motivo del fallo.
Puntos de control antes de publicar
Antes de poner el flujo en CI, comprueba:
- Cada herramienta devuelve un estado estructurado, no solo texto libre.
- Un mensaje antiguo no puede satisfacer una espera nueva.
- Los reintentos son seguros para cada estado, o se detienen.
- Los tokens, enlaces completos y cuerpos de email no aparecen en logs.
- La evidencia tiene una expiración y un ID correlacionable.
- El resultado final explica por qué fue aceptado o rechazado.
- El cleanup se ejecuta también después de un fallo parcial.
El objetivo no es hacer que el agente sea menos flexible. Es separar la flexibilidad del modelo de las garantías del sistema. Los modelos LLM pueden elegir herramientas y proponer recuperaciones; la máquina de estados, el lease del buzón y el contrato de evidencia ponen los límites.
Preguntas frecuentes
¿Debo guardar la respuesta completa del agente?
No como requisito del flujo. Guarda eventos estructurados y referencias redacted. La conversación puede conservarse con una política aparte para depuración, pero no debe ser necesaria para demostrar que un email fue validado.
¿Qué hago cuando no hay evidencia suficiente?
Marca el paso como rejected o expired, incluye el correlation_id y termina con un diagnóstico accionable. Reintentar es razonable solo si la causa y el límite de intentos están definidos.
¿Por qué no dejar que el agente decida todo?
Porque el agente optimiza una respuesta probable, mientras que el sistema necesita invariantes. Un contrato corto permite aprovechar su capacidad de adaptación sin confiarle la autoridad sobre credenciales, retención o cambios irreversibles.
Top comments (0)