Un agente con LLM puede completar una tarea y aun así dejar un sistema difícil de operar. El resultado final dice “éxito”, pero no explica qué herramientas llamó, qué datos recibió ni por qué eligió una ruta. Cuando falla, el equipo suele repetir el workflow y esperar que esta vez salga bien. Eso no es observabilidad; es una apuesta con mejor interfaz.
En mis automatizaciones prefiero tratar cada ejecución como una pequeña transacción con recibo. El recibo no necesita guardar todo el prompt ni información sensible. Tiene que conservar las decisiones y los límites necesarios para reconstruir el caso. Esta idea también encaja con registrar intentos sin perder contexto y con probar flujos de email por entorno: una prueba o una tarea automática vale más cuando deja señales interpretables.
El problema no es que el agente falle
Los fallos de un agente suelen mezclarse en una sola categoría: “la IA se equivocó”. En realidad hay varias causas distintas:
- la herramienta devolvió un error temporal;
- el modelo eligió una herramienta válida con argumentos incompletos;
- el contexto estaba viejo o era demasiado grande;
- el proceso terminó dos veces por un reintento;
- una salida con apariencia correcta violó una regla del negocio.
Si sólo guardamos la respuesta final, todas estas causas parecen iguales. En un flujo de automatización eso sale caro, porque el operador no sabe si debe corregir el prompt, el adaptador de la herramienta o la política de reintentos. A veces el log tiene el texto completo y todavía falta lo importante: qué contrato se estaba intentando cumplir.
Qué debe contener un recibo de ejecución
Un recibo útil responde cinco preguntas en menos de un minuto:
- ¿Qué ejecución es y qué versión de instrucciones usó?
- ¿Qué objetivo y restricciones recibió el agente?
- ¿Qué herramientas llamó, en qué orden y con qué resultado?
- ¿Dónde se detuvo, reintentó o pidió intervención humana?
- ¿Qué evidencia permite aceptar el resultado?
Una forma compacta de modelarlo es:
type RunReceipt = {
runId: string;
workflow: string;
instructionVersion: string;
startedAt: string;
steps: Array<{
name: string;
inputHash: string;
status: "ok" | "retry" | "failed";
durationMs: number;
outputRef?: string;
errorCode?: string;
}>;
finalStatus: "completed" | "blocked" | "failed";
};
El inputHash ayuda a comparar ejecuciones sin copiar secretos al log. outputRef puede apuntar a un almacenamiento con controles de acceso. El recibo describe la ejecución, no se convierte en una segunda base de datos con todos los contenidos.
Un contrato pequeño para cada tool call
Cada herramienta debería declarar antes de ejecutarse tres cosas: precondiciones, forma de éxito y errores reintentables. Por ejemplo, una herramienta que crea una tarea puede exigir un identificador de cliente, devolver el identificador creado y marcar un timeout como reintentable. Un error de validación no debe entrar en el mismo circuito.
En el diagrama mental hay cuatro capas: el agente decide, el orquestador valida, el adaptador ejecuta y el recibo registra. Si el adaptador escribe directamente en un log genérico, se pierde la frontera entre decisión y efecto. Si el orquestador valida después de escribir, un reintento puede duplicar datos.
Por eso uso una clave de idempotencia derivada de runId y del nombre de la operación. El adaptador la envía al servicio y guarda el resultado asociado. Esto hace que un retry sea una consulta o una continuación controlada, no una nueva orden ciega.
Reintentos, idempotencia y límites
Un reintento sólo es razonable si conocemos el tipo de fallo. Un timeout de red puede repetirse con backoff. Un rechazo de permisos necesita intervención. Una respuesta ambigua puede necesitar verificación, no otra llamada igual.
También conviene limitar el presupuesto: máximo de intentos, tiempo total, llamadas por herramienta y coste estimado. Cuando el presupuesto se agota, el estado debe ser blocked o failed, con una razón concreta. “No se pudo completar” ayuda poco; “dos timeouts en send_email y presupuesto agotado” permite actuar.
En pruebas de desarrollo, un buzón temporal puede servir para revisar el camino completo sin contaminar cuentas reales. Si el equipo busca un temp mail so para un fixture, debe documentar que esa dirección no prueba identidad ni entrega en producción. La separación entre entorno, datos y permisos es parte del diseño, no un detalle para después.
Checklist de implementación
- Asigna un
runIdestable desde el primer evento. - Versiona las instrucciones y los esquemas de herramientas.
- Registra entradas mediante hashes cuando contengan datos privados.
- Distingue errores reintentables de errores definitivos.
- Usa idempotencia en toda herramienta con efectos externos.
- Guarda duración, estado y referencia de salida por paso.
- Define un límite explícito de tiempo, llamadas y coste.
- Añade una prueba que fuerce un timeout y compruebe el recibo.
Un detalle que se suele olvidar: el recibo también necesita una política de retención. Guardarlo para siempre aumenta riesgo y coste. En cambio, eliminarlo demasiado pronto impide investigar una incidencia. Elige una ventana según el impacto del workflow y elimina el contenido sensible aunque conserves métricas agregadas.
Preguntas frecuentes
¿Hay que guardar el prompt completo?
No siempre. Para depuración puede bastar con una versión, un hash y los campos estructurados relevantes. Guarda el texto completo sólo cuando exista una justificación de seguridad y retención.
¿Un recibo reemplaza los logs?
No. Los logs sirven para detalle operativo; el recibo es un resumen estable de la ejecución. Ambos deben compartir el runId para navegar entre niveles.
¿Qué hago si el modelo devuelve JSON inválido?
Regístralo como fallo de contrato, conserva una referencia segura a la salida y evita ejecutar efectos secundarios. Reparar el formato automáticamente puede ser útil, pero debe quedar visible en el recibo.
La meta no es que el agente nunca falle. Es que cada fallo reduzca la incertidumbre. Con recibos pequeños, contratos claros y reintentos con límites, la automatización deja de ser una caja negra y se convierte en un sistema que se puede mantener.
Top comments (0)