DEV Community

Silviu Technology
Silviu Technology

Posted on

LLMs: recibos para depurar tool calls

Cuando un LLM llama una herramienta, el resultado visible suele ser solo “éxito” o “fallo”. Eso alcanza para una demo, pero se queda corto en una automatización real. Si una llamada se repite, tarda demasiado o devuelve un dato inesperado, el equipo necesita reconstruir qué ocurrió sin leer todo el historial del agente.

Mi solución preferida es tratar cada llamada como una pequeña operación con un recibo de ejecución. No es una transcripción completa ni un almacén de prompts: es una frontera técnica con los datos mínimos para explicar una decisión.

El problema: una tool call sin recibo

Imagina un agente que clasifica un ticket y consulta tres herramientas: busca el cliente, revisa el estado del pago y crea una tarea. Una petición tarda 12 segundos y luego se reintenta porque el modelo no recibió la respuesta a tiempo. En los logs aparecen dos request_id, pero nadie sabe si la tarea fue creada una o dos veces.

El diagrama mental es sencillo:

modelo → adaptador de herramienta → servicio externo → respuesta → modelo

El recibo debe vivir en el adaptador, justo donde se conocen el nombre de la herramienta, el intento y el resultado. Si se registra solamente en el servicio externo, se pierde el contexto del agente; si se registra todo en el prompt, el coste y el ruido suben rápido.

Qué debe contener un recibo

Un registro útil puede ser pequeño:

{
  "run_id": "run_82f",
  "tool_call_id": "call_17",
  "tool": "lookup_customer",
  "attempt": 2,
  "status": "ok",
  "started_at": "2026-09-13T02:00:00Z",
  "duration_ms": 842,
  "input_hash": "sha256:...",
  "output_summary": {"fields": ["customer_id", "plan"]},
  "error_class": null
}
Enter fullscreen mode Exit fullscreen mode

Hay dos decisiones importantes. Primero, guardar un hash del input en lugar del input completo cuando no hace falta inspeccionar el contenido. Segundo, registrar un resumen del output, no una copia indiscriminada. Así la trazabilidad mejora sin convertir los logs en una segunda base de datos de información sensible.

Para auditar correos automáticos con LLMs puede ser útil enlazar el recibo con el identificador del mensaje, manteniendo separado el contenido. También conviene validar correos operativos tras cambios delicados como rotaciones de secretos, pero esa validación debe quedar como otra operación observable, no escondida dentro de la tool call.

Un contrato mínimo

El adaptador puede imponer un contrato uniforme a todas las herramientas:

  1. Generar tool_call_id antes de ejecutar.
  2. Marcar started y medir duración con un reloj monotónico.
  3. Clasificar el resultado como ok, timeout, retryable_error o terminal_error.
  4. Emitir el recibo en una salida append-only.
  5. Devolver al modelo únicamente el resultado permitido por el contrato de la herramienta.

Esto crea una separación sana: el LLM decide qué pedir, pero el adaptador decide qué registrar, qué campos enmascarar y qué errores pueden reintentarse. Es una barrera pequeña, y justamente por eso es más fácil de revisar.

Reintentos y deduplicación

Un timeout no significa que el servidor no haya ejecutado la operación. Por eso attempt no basta. Las herramientas con efectos secundarios necesitan una clave de idempotencia derivada de run_id y tool_call_id, o una clave explícita aceptada por el servicio.

El flujo recomendado es:

iniciado → timeout → consulta de estado → reintento seguro

Si no existe una consulta de estado, el recibo debe dejar la operación en unknown, en vez de afirmar que falló. Esa palabra parece poco elegante, pero evita duplicar tareas, mensajes o pagos. A veces el sistema sabe menos de lo que nos gustaría y hay que decirlo claro.

Un detalle que suele olvidarse: conserva los recibos de todos los intentos, agrupados por tool_call_id. Sobrescribir el primer intento borra precisamente la pista que explica el incidente.

Checklist de implementación

  • ¿Cada llamada tiene un identificador estable y un run_id?
  • ¿Se diferencian errores reintentables de errores terminales?
  • ¿Las operaciones con efectos secundarios usan idempotencia?
  • ¿El recibo contiene duración, intento y clase de error?
  • ¿Los inputs y outputs sensibles están resumidos o enmascarados?
  • ¿Puedes reconstruir la secuencia sin guardar el prompt entero?
  • ¿Existe una retención definida y una forma de buscar por herramienta?

Prueba primero con una sola herramienta de lectura. Después añade una herramienta con efecto secundario y simula timeout, respuesta tardía y reintento. Si el recibo explica esos tres casos, el diseño ya tiene una base bastante buena.

Conclusión

Los LLMs no necesitan más texto de logs; necesitan límites observables. Un recibo pequeño convierte una tool call opaca en una operación que se puede medir, comparar y repetir con cuidado. Para una automatización, esa evidencia suele valer más que una explicación larga generada después del incidente.

La arquitectura final sigue siendo simple: adaptador con contrato, recibos append-only, datos sensibles resumidos e idempotencia donde hay efectos secundarios. Con eso, depurar deja de depender de la memoria del agente o de la intuición del operador.

Top comments (0)