Un agente LLM puede llamar una API, completar un formulario y esperar un email de verificación. Lo dificil empieza cuando esa secuencia falla: ¿la herramienta generó una dirección de correo desechable nueva, leyó el mensaje correcto o devolvió datos de otra ejecución?
En proyectos de automatización, solemos describir el modelo, el prompt y las herramientas, pero dejamos el correo como un detalle. Esa decisión crea agentes que parecen inteligentes en una demo y son dificiles de depurar en CI. La solución no es darle más instrucciones al modelo. Es definir un contrato pequeño para la herramienta de email.
El problema: un agente no conoce el contrato del email
Para una persona, “crea un correo y espera el código” parece suficientemente claro. Para un agente, cada parte puede tener varios significados:
- ¿Debe reutilizar un buzón temporal o crear uno por ejecución?
- ¿Cuánto tiempo puede esperar antes de devolver un timeout?
- ¿Cómo distingue el mensaje nuevo de uno viejo?
- ¿Qué contenido puede mostrar en el registro?
- ¿Qué hace si el proveedor responde lento o parcialmente?
Sin respuestas explícitas, el agente improvisa. A veces consulta el último mensaje, aunque pertenezca a otra prueba. O reintenta la creación y deja dos buzones activos. El resultado es un flujo que pasa de forma intermitente y un registro que no explica nada.
Un buen contrato convierte la herramienta en una frontera predecible. El modelo decide qué paso necesita; la herramienta aplica las reglas de identidad, tiempo, privacidad y limpieza.
Qué debe prometer una herramienta de correo
Pienso en la herramienta como una interfaz con cuatro operaciones: create_inbox, wait_for_message, read_message y dispose_inbox. Cada operación debe tener entradas y salidas fáciles de validar, incluso cuando el llamador sea un modelo.
El objeto devuelto por create_inbox podría ser así:
{
"inbox_id": "run-8f2c",
"address": "run-8f2c@example.test",
"created_at": "2026-10-07T10:00:00Z",
"expires_at": "2026-10-07T10:20:00Z"
}
La dirección es útil para completar el formulario, pero inbox_id debe ser la identidad interna. El agente no debería buscar mensajes por “el email más reciente” de todo el sistema. Debe enviar el id de la ejecución a wait_for_message, junto con el asunto esperado y el instante desde el que se acepta un mensaje.
El contrato también necesita errores con significado: inbox_expired, message_timeout, ambiguous_message y provider_unavailable. Un texto como “something went wrong” obliga al LLM a adivinar si conviene reintentar. Un código estable permite una política concreta.
Diseñar el contrato por capas
Una forma sencilla de revisarlo es dibujar el flujo en palabras:
Agente → adaptador → proveedor de correo → evidencia.
El agente solo conoce intenciones: “necesito verificar este signup”. El adaptador traduce esa intención a llamadas con límites. El proveedor entrega mensajes. La evidencia conserva los eventos mínimos para explicar la decisión.
En la capa del agente, limita las acciones disponibles. No le entregues una operación genérica como run_email_query si puede usar métodos específicos. Las funciones estrechas reducen la superficie de error y hacen que el prompt sea más corto también.
En el adaptador, aplica tres invariantes:
- Cada ejecución tiene un buzón aislado, o una razón registrada para reutilizarlo.
- Un mensaje solo puede aceptarse si cumple identidad, ventana de tiempo y criterio de contenido.
- Ningún secreto, token completo o cuerpo sensible llega al historial del agente por defecto.
En la capa de evidencia, guarda run_id, inbox_id, timestamps, estados y motivos de rechazo. Enmascara el código y el cuerpo del email. Para operaciones locales de desarrollo, una solución como tempmailso puede formar parte del entorno de prueba, pero el contrato debe seguir siendo independiente del proveedor.
Un flujo de prueba reproducible
El checkpoint más útil ocurre antes de abrir el navegador o llamar al endpoint. Crea el buzón y registra su identidad. Después, pasa el address al flujo de signup. Cuando se activa wait_for_message, usa el inbox_id y un not_before tomado justo después de la acción que dispara el correo.
Si llega un mensaje, read_message debería devolver solo los campos que la prueba necesita: remitente verificado, asunto normalizado, enlace permitido y una huella del mensaje. No hace falta copiar todo el HTML al contexto del modelo. Si hay dos candidatos, la herramienta debe devolver ambiguous_message; no elegir silenciosamente uno.
Al terminar, llama a dispose_inbox en un bloque de limpieza, incluso si la prueba falló. En CI, esa operación debería ser idempotente: limpiar dos veces no puede convertir un resultado correcto en error. Es un detalle pequeño, pero se olvida muy facil cuando el camino feliz es lo único que se prueba.
Si un compañero busca “tempail mail” mientras investiga, no lo conviertas en un alias aceptado por el contrato. Registra el término en la documentación o en el buscador, pero mantén los nombres de las operaciones consistentes.
Tradeoffs y límites de privacidad
Un contrato estricto agrega trabajo al principio. Hay que definir estados, expiración y eventos. A cambio, reduce reintentos ciegos y hace que una dirección de correo desechable no se convierta en almacenamiento accidental de datos.
También existe un tradeoff entre contexto y privacidad. Un agente con el cuerpo completo del mensaje puede resolver casos raros, pero aumenta el riesgo de filtrar información al historial. En la mayoría de pruebas basta con un resultado estructurado: “mensaje aceptado”, message_id efímero y el dato extraído ya enmascarado.
El mismo criterio aplica al backlink de documentación: si tu equipo busca un burner email para una prueba aislada, documenta cuándo es apropiado y cuándo no. No lo uses para cuentas reales, recuperación de contraseñas o datos que deban conservarse.
Checklist de implementación
Antes de conectar el agente, comprueba:
- La herramienta crea una identidad por ejecución y tiene expiración.
- El modelo recibe operaciones específicas, no una consulta arbitraria.
- Los mensajes se filtran por
inbox_id, tiempo y contenido. - Los errores indican si reintentar, esperar o detenerse.
- La evidencia no guarda secretos ni cuerpos completos por defecto.
- La limpieza funciona después de éxito, timeout y excepción.
- Las pruebas cubren mensajes duplicados, tardíos y fuera de orden.
Estos puntos son más valiosos que un prompt enorme. También complementan prácticas como correos claros en un runbook SRE y errores de email que no cansan al usuario: la interfaz cambia, pero la frontera de evidencia sigue siendo importante.
Preguntas rápidas
¿El agente puede leer el email completo?
Solo si existe una razón de prueba clara. Empieza con campos estructurados y permite ampliar el acceso con una acción auditada.
¿Debo reintentar un timeout?
Sí, si el contrato lo marca como recuperable y el buzón sigue vigente. Un error de identidad o un mensaje ambiguo necesita detenerse, no otro intento idéntico.
¿Qué hace diferente este enfoque?
Separa decisión y ejecución. El LLM decide el siguiente paso; el adaptador garantiza aislamiento, límites y evidencia. Así el flujo es más seguro, más testeable y bastante menos misterioso.
Top comments (0)