Idea en 30 segundos
El debugging de runs de agentes ayuda a pasar del síntoma a la causa: qué se rompió, en qué paso y por qué.
Para eso, hay que correlacionar tracing, logs y métricas de un run problemático.
Sin esa correlación, el equipo suele ver solo el error final y no el camino completo que llevó hasta él.
Problema principal
En sistemas de agentes, un incidente rara vez tiene una sola causa obvia.
El error final puede ser solo una consecuencia: el problema real pudo empezar antes, por ejemplo con un tool call lento, un retry fallido o una regresión después del release. Sin debugging sistemático, esto es difícil de localizar rápido.
Ahora veamos cómo leer estas señales y encontrar root cause de forma estable.
En producción, esto suele verse así:
- en logs hay muchos eventos, pero sin secuencia clara;
- la causa se mezcla con errores secundarios;
- el incidente fue "arreglado", pero vuelve después del release;
- el MTTR sube porque el equipo reconstruye el contexto del incidente desde cero cada vez.
Por eso, el debugging de un run debe ser un proceso operativo separado, no una búsqueda manual del "primer error".
Cómo funciona
Un debugging run práctico normalmente tiene tres niveles:
- contexto del run (
run_id,trace_id,release,workflow); - evidence -> análisis (
spans,logs,metrics,stop_reason); - decisión (hipótesis -> fix -> verificación con replay y tests).
Estos niveles responden: dónde está el problema, por qué apareció y si el fix realmente lo elimina. Tracing muestra el camino, logs muestran eventos y métricas muestran escala y tendencia.
Muchos logs != debugging rápido. La velocidad no aparece por la cantidad de datos, sino por su correlación alrededor de un run.
Señales típicas de producción para debugging de runs
| Señal | Dónde mirar | Para qué sirve |
|---|---|---|
| first_error_span | tracing | encontrar el punto donde apareció primero el error |
| slowest_span | tracing + métricas | candidato a bottleneck (requiere verificación) |
| stop_reason | log run_finished | entender cómo terminó el run |
| error_class | logs tool_result / llm_result | separar timeout de error lógico |
| repeated_tool_calls | logs tool_call + tool metrics | detectar llamadas repetidas (loops, retries, tool spam) |
| run_latency_p95 | métricas | validar si el incidente ya es sistémico |
| release_diff | dashboard de comparación de releases | detectar regresión tras cambios |
| synthetic_run_status | health checks | verificar impacto en workflow crítico |
Para mantener estable el debugging, estas señales normalmente se segmentan por release, workflow, model y tool.
Importante: no agregues labels de alta cardinalidad (run_id, request_id, user_id) en métricas. Para eso, usa logs y tracing.
Cómo leer el debugging-layer
Qué run falló -> en qué paso -> por qué exactamente pasó. Estos tres niveles siempre deben leerse juntos.
Es clave mirar tendencias y diferencias entre releases, no solo un evento aislado.
Combinaciones típicas de señales:
first_error_span=tool_call+tool_error_ratesube -> problema en un tool-layer específico;run_latency_p95sube +tool_latency_p95estable -> probable problema en LLM o lógica runtime;repeated_tool_callssube +stop_reason=max_steps-> el agente quedó en loop;error_ratesube tras release +release_diffpositivo -> regresión por cambio, no incidente puntual;synthetic_run_status=fail+health_scorebaja -> el problema ya impacta el workflow crítico.
Cuándo usarlo
Un debugging-flow formal no siempre es necesario.
Para un escenario simple single-shot sin tools, puede bastar logging básico y revisión manual del error.
Pero un enfoque sistemático de debugging se vuelve crítico cuando:
- el run tiene varios pasos de reasoning y tool calls;
- los incidentes impactan latencia, costo o SLO;
- hay releases frecuentes y es importante detectar regresiones rápido;
- el equipo tiene proceso on-call y necesita MTTR predecible.
Ejemplo de implementación
Abajo hay una función simplificada que recoge evidence de un run y forma una hipótesis base. No reemplaza el incident tooling completo, pero muestra un proceso práctico de debugging.
from collections import Counter
def debug_run(run_id, trace_events, log_events, debug_metrics_snapshot):
run_spans = sorted(
[s for s in trace_events if s.get("run_id") == run_id],
key=lambda s: s.get("started_at_ms", 0),
)
run_logs = [e for e in log_events if e.get("run_id") == run_id]
first_error_span = next((s for s in run_spans if s.get("status") == "error"), None)
# slowest_span puede ser None si el run no contiene spans
slowest_span = max(run_spans, key=lambda s: s.get("latency_ms", 0), default=None)
stop_reason = "unknown"
for event in reversed(run_logs):
if event.get("event") == "run_finished":
stop_reason = event.get("stop_reason", "unknown")
break
seen_signatures = set()
repeated_tools = Counter()
for event in run_logs:
if event.get("event") != "tool_call":
continue
signature = (event.get("tool"), event.get("args_hash"))
if signature in seen_signatures:
repeated_tools[event.get("tool")] += 1
else:
seen_signatures.add(signature)
hypotheses = []
if first_error_span and first_error_span.get("step_type") == "tool_call":
hypotheses.append("Fallo probable en tool-layer: revisar disponibilidad de herramienta y timeout policy.")
if repeated_tools:
hypotheses.append("Hay tool calls repetidos: revisar dedupe/cache y stop conditions.")
if slowest_span and debug_metrics_snapshot.get("run_latency_p95_ms", 0) > debug_metrics_snapshot.get("slo_latency_ms", 2500):
hypotheses.append("La p95 latency está por encima del SLO: localizar bottleneck con slowest_span.")
if debug_metrics_snapshot.get("release_error_rate_delta", 0) > 0:
hypotheses.append("error_rate subió después del release: revisar cambios en prompt/runtime/tool routing.")
return {
"run_id": run_id,
"first_error_span": first_error_span,
"slowest_span": slowest_span,
"stop_reason": stop_reason,
"repeated_tools": dict(repeated_tools),
"hypotheses": hypotheses,
}
El debugging no se considera terminado hasta que el problema sea reproducible (replay) y se confirme que el fix lo elimina de forma estable. Si el problema no se puede reproducir, el debugging pasa a modo hipótesis, no a modo evidencia.
Replay != optional.
Sin replay, es una suposición.
Con replay, es evidencia.
Así puede verse un debugging snapshot corto:
| Run | first_error_span | slowest_span | stop_reason | Conclusión |
|---|---|---|---|---|
| run_9fd2 | tool_call: search_docs | tool_call: search_docs (1.8s) | tool_error | tool degradado + retries |
| run_a113 | llm_generate | llm_generate (2.4s) | step_error | fallo de modelo tras release |
| run_d77c | — | reasoning (3.1s) | max_steps | loop sin error explícito |
Investigación
Cuando se dispara una señal de incidente:
- fijar
run_id,trace_id,releasey workflow afectado; - encontrar
first_error_spanyslowest_spanen tracing; - revisar
stop_reason,error_class,repeated_tool_callsen logs; - confirmar escala del problema en métricas (spike o tendencia) y comparar diferencias entre releases.
Errores típicos
Incluso con observability configurada, el debugging suele fallar por errores comunes.
Empezar análisis con "cualquier" error del log
Sin vincular el análisis a un run_id específico, el equipo mezcla síntomas de incidentes distintos.
En ese modo, es difícil separar un problema local de un fallo en cascada.
Sin correlación trace + logs + metrics
Si tracing, logs y métricas se revisan por separado, las hipótesis suelen contradecirse. Por eso, MTTR sube incluso en fallos de herramienta simples.
Ignorar repeated calls y stop_reason
Sin esas señales, es fácil perder loops y retry storms. Eso suele ocultar la fase temprana de spam de herramientas.
No comparar con el release anterior
Sin release_diff, el equipo no ve si el problema apareció tras cambios.
Como resultado, la regresión permanece más tiempo en producción.
Cerrar incidente sin replay ni verificación
Un fix puede quitar el síntoma, no la causa. Esto eleva el riesgo de caída parcial repetida.
Autoevaluación
Checklist corta de debugging-flow baseline antes de release.
Progreso: 0/9
⚠ Falta observability base
Será difícil depurar el sistema en production. Empieza con run_id, structured logs y tracing de tool calls.
FAQ
Q: ¿Por dónde empezar el debugging de un run problemático?
A: Empieza con run_id y trace_id: encuentra first_error_span, revisa stop_reason y luego confirma el alcance en métricas. first_error_span es la forma más rápida de hallar el punto de fallo.
Q: ¿Qué es más importante para debugging: tracing o logs?
A: Ambos juntos: tracing muestra el camino de pasos, logs aportan detalles de eventos (error_class, args_hash, policy decision).
Q: ¿Cómo saber si es regresión de release y no fallo puntual?
A: Compara error_rate, latency_p95, repeated_tool_calls entre releases. Si la señal es consistentemente peor después del release, es regresión.
Q: ¿Cuál es el mínimo de datos para depurar en 10-15 minutos?
A: Mínimo: run_id, trace_id, first_error_span, stop_reason, error_class, latency_p95 y contexto de release.
Páginas relacionadas
Siguiente en este tema:
- Tracing de agentes — cómo ver el camino de un run paso a paso.
- Logging de agentes — qué eventos se necesitan para analizar incidentes.
- Métricas de agentes — cómo separar fallo puntual y tendencia.
- Health checks de agentes — señales tempranas de degradación antes del incidente.
- Alerting de fallos de agentes — cómo activar investigación a tiempo.