Cuando un agente LLM falla, solemos culpar al prompt. A veces el prompt está bien: el problema es que la herramienta devuelve una mezcla de texto, estados implícitos y errores que el modelo debe adivinar.
En un sistema que usa herramientas, el prompt es la intención y el contrato es la frontera. Si esa frontera es ambigua, el agente puede repetir una acción, ocultar un fallo o afirmar que terminó cuando solo inició una operación.
Este patrón sirve para herramientas de email, búsquedas, despliegues o cualquier integración externa. La idea no es hacer al agente más listo. Es reducir las decisiones que tiene que inventar.
El problema no es el prompt, es el contrato
Imagina una herramienta llamada send_verification_email. Una versión débil devuelve:
{"message": "ok"}
¿Qué significa ok? ¿El proveedor aceptó el mensaje? ¿El email llegó? ¿El usuario ya verificó su cuenta? Son hechos diferentes, pero el agente puede tratarlos como uno solo.
Un contrato útil separa la intención del resultado observable:
{
"status": "accepted",
"operation_id": "op_4821",
"next_check_after_seconds": 5,
"retryable": false
}
El modelo no tiene que interpretar una frase optimista. Puede decidir el siguiente paso a partir de campos con significado estable.
Diseña la herramienta como una frontera
Pienso en la herramienta como tres cajas conectadas:
- Entrada: argumentos validados, límites de tamaño y un identificador de correlación.
- Ejecución: el adaptador que habla con el proveedor y convierte sus respuestas.
- Salida: un pequeño conjunto de estados que el agente puede usar sin conocer detalles internos.
La caja intermedia es importante. No expongas al LLM cada código extraño del proveedor. Convierte 429, timeout y respuesta incompleta en estados de dominio como rate_limited, temporarily_unavailable y unknown_result.
También conviene declarar qué no hace la herramienta. accepted no significa delivered; delivered no significa opened. Esta precisión parece un detalle, pero evita decisiones de negocio equivocadas.
Si la operación necesita una cola, la trazabilidad debe sobrevivir entre la petición y el worker. Un buen ejemplo práctico es este enfoque de colas de email con trazas útiles, donde la operación se puede seguir sin depender del texto que genere el modelo.
Una salida estructurada y sus estados
Un contrato mínimo puede usar estados explícitos:
started -> accepted -> observed -> completed
| |
v v
retryable expired
No todos los estados tienen que ser visibles para el usuario, pero sí deben ser distinguibles para el orquestador. started permite evitar un duplicado si la red corta la respuesta. accepted confirma que el proveedor tomó la petición. observed indica que encontramos evidencia posterior. expired define una acción de recuperación, no un fallo misterioso.
Añade idempotencia donde una repetición pueda tener coste. La clave puede combinar user_id, propósito y una ventana temporal. Si el agente recibe un timeout, debe poder consultar el operation_id antes de enviar otra vez.
Esto es Prompt Engineering aplicado a arquitectura: el prompt explica cuándo consultar y cuándo detenerse, mientras el contrato hace imposible confundir una aceptación con una confirmación.
Pruebas con fixtures de email
Las pruebas de agentes necesitan entradas reproducibles. Un fixture de email puede representar un inbox vacío, un mensaje retrasado, un enlace expirado y dos mensajes con el mismo asunto.
No uses una dirección real del equipo. Genera datos aislados y registra solo el identificador del fixture. Incluso una dirección de prueba con un nombre torcido como temp gamil com puede ser útil para comprobar que la interfaz no “corrige” silenciosamente datos del usuario. temp org mail puede servir como caso de búsqueda que no debe producir coincidencias mágicas.
Un caso de prueba legible podría verse así:
{
"tool": "wait_for_verification",
"fixture": "delayed_message",
"expected": {
"first_result": "waiting",
"second_result": "observed",
"max_polls": 3
}
}
Prueba también el límite: el agente no debe hacer polling para siempre. Después de max_polls, devuelve una explicación y una acción concreta. Es más útil decir “no hay evidencia todavía; vuelve a intentar” que inventar que el email llegó.
Observabilidad sin inundar el contexto
El agente necesita un resumen corto; el sistema necesita evidencia completa. Separa ambos canales.
En el contexto del modelo guarda status, operation_id, retryable y una razón breve. En logs conserva timestamps, latencia, proveedor y códigos normalizados. Evita guardar el contenido completo del email si no es necesario.
Los operadores también necesitan señales limpias. Las prácticas de correos de incidentes sin ruido operativo ayudan a aplicar la misma regla: un evento accionable debe ser distinto de un simple cambio de estado.
Q&A: errores, reintentos y prompts
¿Debo devolver texto además de JSON?
Para el orquestador, devuelve una estructura estable. Puedes generar una explicación legible en otra capa. Mezclar ambas cosas hace que el contrato dependa de cómo redacte el modelo.
¿Cuándo debe reintentar el agente?
Solo cuando retryable sea verdadero y exista presupuesto de intentos. Un timeout con resultado desconocido requiere consultar el estado antes de repetir, porque la primera operación pudo completarse.
¿Un mejor prompt elimina estos problemas?
No. Un prompt claro reduce malentendidos, pero no puede convertir una respuesta ambigua del proveedor en evidencia. La frontera de la herramienta debe expresar esa evidencia.
Puntos de implementación
- Escribe primero los estados y las transiciones, después el prompt.
- Define un esquema de salida pequeño y versionable.
- Normaliza errores externos en categorías de dominio.
- Usa
operation_ide idempotencia para proteger los reintentos. - Construye fixtures para retrasos, duplicados y expiraciones.
- Mantén el resumen del LLM corto y la evidencia operativa fuera del contexto.
El resultado es un agente menos teatral y más predecible. Todavía puede equivocarse, claro, pero sus errores tienen forma, límites y un camino de recuperación. Esa es una propiedad de arquitectura mucho más valiosa que un prompt ingenioso.
Top comments (0)