Idea en 30 segundos
Las metricas de agentes muestran el estado del sistema en muchos runs, no en un solo caso.
Responden: si el sistema es estable, si suben los costos, y donde empieza la degradacion.
Sin metricas, los problemas suelen verse tarde: despues de quejas de usuarios o sobrecostos de presupuesto.
Problema principal
Los logs y el tracing explican bien un incidente concreto.
Pero en production importa ver tendencias: que pasa con latency, token usage, error rate y tool calls entre releases. Sin metricas, el sistema puede degradarse de forma gradual y pasar desapercibido por mucho tiempo.
En production normalmente se ve asi:
- el tiempo de respuesta promedio parece normal, pero p95 ya esta subiendo;
- los costos de tokens suben en olas despues de una release;
- crece la cantidad de tool calls por run;
- el equipo detecta el problema solo despues del incidente.
Por eso las metricas son una senal separada de observability: ayudan a detectar anomalias a tiempo y reaccionar antes de fallos grandes.
Como funciona
Las metricas son senales numericas agregadas que muestran el comportamiento del sistema en el tiempo.
Normalmente hay tres niveles de metricas:
- nivel run (
run_count,success_rate,stop_reason); - pasos y herramientas (
tool_calls_per_run,tool_error_rate,step_count); - costo y velocidad (
token_usage,cost_per_run(se calcula desdetoken_usageen dashboards o consultas de metricas),latency_p50/p95).
Las metricas dan una senal temprana cuando el sistema empieza a degradarse. Los logs responden "que paso", y el tracing responde "como exactamente paso en un run concreto".
Metricas tipicas de production para agentes
| Metrica | Que muestra | Para que sirve |
|---|---|---|
| run_count | cantidad de runs por periodo | control de carga y volumen de trafico |
| success_rate | proporcion de runs exitosos | revision rapida de estabilidad |
| latency_p50 / latency_p95 | latencia tipica y de cola | deteccion de degradacion de rendimiento |
| token_usage_per_run | cuantos tokens consume cada run | control de costo LLM |
| cost_per_run | costo estimado de un run | control de presupuesto y proyeccion de costos |
| tool_calls_per_run | cuantas veces un run llama herramientas | deteccion de llamadas excesivas o ciclicas |
| tool_error_rate | frecuencia de errores de herramientas | deteccion temprana de dependencias inestables |
| stop_reason_distribution | distribucion de motivos de fin de run | control de limites y fallos tipicos |
Para que las metricas sean utiles, normalmente se segmentan por release, model o tool.
Importante: no agregues campos de alta cardinalidad (run_id, request_id, user_id) en labels, o el almacenamiento de metricas se sobrecarga rapido.
Cuando usar
Un conjunto amplio de metricas no siempre es necesario.
Para un prototipo temprano, a veces bastan contadores basicos de runs y errores.
Pero las metricas se vuelven criticas cuando:
- el sistema de agentes ya esta en production;
- hay SLO de latency o calidad;
- se deben controlar costos de tokens y tool calls;
- hay releases frecuentes y se deben ver regresiones antes de incidentes.
Ejemplo de implementacion
Abajo tienes un ejemplo simplificado de instrumentacion de runtime al estilo Prometheus. En sistemas reales, los mismos principios funcionan para Datadog, Grafana Cloud, CloudWatch y otras plataformas.
import time
from prometheus_client import Counter, Histogram
RUN_TOTAL = Counter(
"agent_run_total",
"Total number of agent runs",
["status", "stop_reason", "release"],
)
# success_rate = RUN_TOTAL{status="ok"} / RUN_TOTAL
RUN_LATENCY_MS = Histogram(
"agent_run_latency_ms",
"Run latency in milliseconds",
["release"],
buckets=(100, 250, 500, 1000, 2000, 5000, 10000),
)
STEP_COUNT = Histogram(
"agent_steps_per_run",
"Number of steps per run",
["release"],
buckets=(1, 2, 4, 8, 12, 16, 24, 32),
)
TOOL_CALL_TOTAL = Counter(
"agent_tool_call_total",
"Total tool calls",
["tool", "status", "release"],
)
TOOL_ERROR_TOTAL = Counter(
"agent_tool_error_total",
"Total tool errors by class",
["tool", "error_class", "release"],
)
LLM_ERROR_TOTAL = Counter(
"agent_llm_error_total",
"Total LLM step errors by model and class",
["model", "error_class", "release"],
)
TOOL_LATENCY_MS = Histogram(
"agent_tool_latency_ms",
"Tool call latency in milliseconds",
["tool", "release"],
buckets=(20, 50, 100, 250, 500, 1000, 2000, 5000),
)
TOKEN_USAGE_TOTAL = Counter(
"agent_token_usage_total",
"Total LLM tokens",
["model", "token_type", "release"],
)
def observe_llm_usage(model, token_usage, release):
# la mayoria de proveedores LLM devuelve token usage en la respuesta
if not token_usage:
return
TOKEN_USAGE_TOTAL.labels(model=model, token_type="prompt", release=release).inc(
token_usage.get("prompt_tokens", 0)
)
TOKEN_USAGE_TOTAL.labels(model=model, token_type="completion", release=release).inc(
token_usage.get("completion_tokens", 0)
)
def run_agent(agent, task, release="2026-03-21"):
started_at = time.time()
steps = 0
stop_reason = "max_steps"
run_status = "ok"
try:
for step in agent.iter(task):
steps += 1
step_type = step.type
result = None # puede quedar en None para tipos de paso desconocidos (protegido por la verificacion de abajo)
if step_type == "tool_call":
tool_name = getattr(step, "tool_name", "unknown")
tool_started_at = time.time()
try:
result = step.execute()
TOOL_CALL_TOTAL.labels(tool=tool_name, status="ok", release=release).inc()
TOOL_LATENCY_MS.labels(tool=tool_name, release=release).observe(
(time.time() - tool_started_at) * 1000
)
except Exception as error:
TOOL_CALL_TOTAL.labels(tool=tool_name, status="error", release=release).inc()
TOOL_ERROR_TOTAL.labels(
tool=tool_name,
error_class=type(error).__name__,
release=release,
).inc()
TOOL_LATENCY_MS.labels(tool=tool_name, release=release).observe(
(time.time() - tool_started_at) * 1000
)
run_status = "error"
stop_reason = "tool_error"
raise
else:
try:
result = step.execute()
observe_llm_usage(
model=getattr(step, "model", "unknown"),
token_usage=getattr(result, "token_usage", None),
release=release,
)
except Exception as error:
LLM_ERROR_TOTAL.labels(
model=getattr(step, "model", "unknown"),
error_class=type(error).__name__,
release=release,
).inc()
run_status = "error"
stop_reason = "step_error"
raise
if result and result.is_final:
stop_reason = "completed"
break
finally:
RUN_TOTAL.labels(status=run_status, stop_reason=stop_reason, release=release).inc()
RUN_LATENCY_MS.labels(release=release).observe((time.time() - started_at) * 1000)
STEP_COUNT.labels(release=release).observe(steps)
En production, estas metricas normalmente alimentan dashboards y alertas.
Asi se pueden ver estas metricas juntas en un dashboard real:
| Metrica | Valor actual | Tendencia | Estado |
|---|---|---|---|
| latency_p95 | 2.4s | +38% en 30 min | warning: por encima del SLO |
| tool_error_rate | 7.2% | +4.1pp en 15 min | critical: alert |
| token_usage_per_run | 8.9k | +22% despues de release | warning: anomalia |
| success_rate | 91.4% | -5.3pp en 1 hora | warning: caida |
Para error_class, es mejor usar un diccionario normalizado de valores para evitar cardinalidad innecesaria.
Por ejemplo, una linea de metrica puede verse asi:
agent_tool_call_total{tool="search_docs",status="error",release="2026-03-21"} 47
Errores tipicos
Incluso cuando ya hay metricas, muchas veces no ayudan por errores tipicos como estos.
Solo promedios, sin p95/p99
El promedio oculta la cola larga de runs lentos.
Para production, el minimo es p50 y p95.
Labels de alta cardinalidad
Labels como run_id o user_id aumentan fuertemente la carga del backend de metricas.
Mejor segmentar por release, model, tool.
Sin metricas de stop_reason
Sin distribucion de stop_reason, es dificil entender por que los runs terminan por max_steps o tool_error.
Esto suele ocultar fallo de herramienta y senales tempranas de explosion de presupuesto.
Sin alertas para anomalias clave
Metricas sin alertas se convierten en graficos pasivos. Sin alertas, es facil perder spam de herramientas o una caida brusca de success rate despues de una release.
Autoevaluacion
Abajo tienes un checklist corto de metricas base de agentes antes del 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
P: En que se diferencian las metricas de logs y tracing?
R: Las metricas muestran tendencias y estado del sistema en el tiempo. Los logs explican eventos, y el tracing muestra el camino de un run concreto.
P: Cual es el minimo de metricas para una primera release de production?
R: Empieza con run_count, success_rate, latency_p95, tool_error_rate, token_usage_per_run y stop_reason_distribution.
P: Por que no alcanza con la latency promedio?
R: El promedio oculta runs largos y lentos. p95 muestra mucho antes la degradacion real para usuarios.
P: Que labels suelen romper el almacenamiento de metricas?
R: Todo lo de alta cardinalidad: run_id, request_id, user_id, prompts completos o raw args.
Paginas relacionadas
Siguiente sobre el tema:
- Observabilidad para agentes de IA — modelo general de tracing, logging y metricas.
- Logging de agentes — que eventos capturar en runtime.
- Tracing de agente — como ver la ruta de un run paso a paso.
- Logging semantico para agentes — como unificar eventos para analitica.
- Monitoreo de costos para agentes de IA — como controlar cost en production.