Danila (Dayfing)
Volver a publicaciones
2247 palabras10 min

Observabilidad de un agente de IA: trazas, latencia, tokens, coste y errores

Por qué un agente necesita otro modelo de observabilidad

Una petición API normal tiene un inicio, un gestor y una respuesta. Un agente de IA añade un bucle. Elige un modelo, decide si llama a una herramienta, espera un sistema remoto, lee el resultado y puede volver al modelo. Una petición puede contener varias llamadas al modelo, búsquedas, ejecuciones de herramientas, reintentos y comprobaciones de políticas. Un registro que solo dice «la petición falló» no muestra qué rama consumió el tiempo o el presupuesto.

La observabilidad hace visible el recorrido mediante trazas, métricas y registros correlacionados. OpenTelemetry describe una traza como el camino de una petición y un span como una operación de ese camino. Usa un span raíz para la ejecución que ve el usuario y spans hijos para inferencia, recuperación, herramientas, guardrails y serialización. Mantén los nombres de modelos y herramientas con baja cardinalidad. Pon los identificadores de una petición en el contexto de la traza o en registros estructurados, no en etiquetas de métricas. Así el operador puede saber qué ocurrió en una ejecución, con qué frecuencia y qué flujos están afectados.

Este artículo usa un esquema independiente del proveedor. La documentación del proveedor define los campos exactos de uso y facturación. Las convenciones de spans GenAI de OpenTelemetry y sus convenciones de métricas GenAI ofrecen un vocabulario común.

Un esquema de traza que resiste el bucle del agente

Crea el span raíz cuando la aplicación acepta la petición, no al llegar al primer modelo. Usa invoke_agent y atributos para servicio, despliegue, entorno, versión del flujo y clase de inquilino no sensible. Registra el identificador de conversación solo si la retención lo permite. Nunca pongas mensaje, prompt completo ni parámetros de herramienta en una etiqueta.

Cada span hijo debe responder una pregunta operativa. Un mínimo útil es:

Span Qué registrar
agent.run nombre y versión del flujo, resultado, intentos, duración
gen_ai.inference proveedor, modelo solicitado y usado, operación, streaming, finalización, tokens
gen_ai.retrieval clase de índice o fuente, modo de consulta, resultados, caché
gen_ai.tool nombre y tipo de herramienta, autorización, timeout, resultado
guardrail.check versión de política, decisión, código de razón, duración

Asigna un estado al span y un valor error.type cuando una operación falla. Para un reintento, registra un evento con número, espera y código de razón. Un reintento no es una segunda ejecución raíz. Es otro intento de la misma operación lógica, con un span cliente separado cuando se envía una petición por red. Así el panel no duplica peticiones de usuarios, pero sí muestra intentos al proveedor.

El JSON siguiente representa una forma de registro exportado, no un formato obligatorio de un proveedor. Contiene contadores y códigos, no contenido.

{
  "trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
  "span_id": "00f067aa0ba902b7",
  "parent_span_id": "b7ad6b7169203331",
  "name": "chat model",
  "kind": "CLIENT",
  "status": "OK",
  "attributes": {
    "gen_ai.operation.name": "chat",
    "gen_ai.provider.name": "provider.example",
    "gen_ai.request.model": "model.example",
    "gen_ai.response.model": "model.example-2026-01",
    "gen_ai.usage.input_tokens": 820,
    "gen_ai.usage.output_tokens": 146,
    "gen_ai.response.finish_reasons": ["stop"],
    "app.agent.attempt": 1
  }
}

El nombre de un span debe describir una clase de operación, no un ID ni texto del usuario. Conserva la versión de la convención semántica en los metadatos de instrumentación. Si el proveedor separa tokens facturables de tokens procesados, guarda ambos en la contabilidad privada y usa los facturables para el coste. No mezcles la instrumentación cliente y servidor de una petición sin indicar la capa, porque contarías dos veces.

Correlation IDs y propagación de contexto

El trace ID conecta servicios. El span ID identifica una operación. Un request ID de la aplicación ayuda a soporte, pero no sustituye el contexto. Propaga la cabecera W3C traceparent por la pasarela API, el servicio del agente, el buscador y los adaptadores de herramientas. La guía de propagación de contexto de OpenTelemetry describe extraer el contexto remoto y crear un span hijo. El mismo contexto puede inyectarse en registros estructurados para saltar de un registro a una traza.

Crea un request ID aleatorio separado cuando soporte necesite un identificador corto. Guarda su relación con el trace ID en registros, no en una métrica de alta cardinalidad. En una cola, inyecta el contexto en los metadatos del mensaje y crea un span consumidor. Para trabajos paralelos sin un único padre, usa span links. En fronteras públicas trata las cabeceras entrantes como datos no confiables, valida su formato y no envíes baggage interno al proveedor o a terceros. Baggage puede contener credenciales o datos personales.

Los spans de herramientas muestran el comportamiento real

Separa la decisión del modelo y la ejecución cuando esa diferencia sea útil. El span del modelo muestra que pidió una herramienta. El span de la herramienta muestra qué ejecutó realmente la aplicación. Incluye un nombre estable tool.name, un tipo como function, extension o datastore, una decisión de política y la operación externa. Añade método o clase de consulta solo si no revela secretos. No guardes tokens de acceso, parámetros SQL, documentos ni URL completas con query.

Para cada herramienta registra tiempos, timeout, clase de resultado, reintentos y tamaño acotado del resultado. Un timeout, un rechazo de negocio, una denegación de política y un error upstream 5xx necesitan códigos distintos. Propaga el contexto cuando la herramienta llama a otro servicio. Si ejecuta un comando local, registra la familia del comando y la clase de salida, nunca la entrada del usuario.

Muestra también esperas que no son llamadas de herramientas. Añade spans para espera en cola, pausa por límite, circuito abierto y streaming. De lo contrario el tiempo del modelo puede ocultar la espera de un cupo de concurrencia. En un flujo multiagente, asigna nombre a cada flujo delegado y relaciónalo con la traza padre. No crees una traza nueva por cada estado interno.

Contabilidad de tokens y coste

Cuenta el uso en la respuesta del proveedor cuando esté disponible. Los tokens de entrada, salida, caché y razonamiento pueden tener tarifas distintas, igual que imágenes y unidades de herramientas. La guía de tokens de OpenAI explica que la tokenización cambia por modelo e idioma y que el objeto usage de la respuesta es la base para una petición terminada. Un tokenizador local sirve para un presupuesto previo, pero no sustituye el uso del proveedor al conciliar la factura.

Guarda por intento el proveedor, los modelos solicitado y usado, categoría, cantidad, moneda, versión de precios y centro de coste. Calcula después de recibir la respuesta:

cost = input_billable_tokens * input_price
     + cached_input_tokens * cached_input_price
     + output_billable_tokens * output_price
     + provider_units * unit_price

Los precios deben ser configuración fechada, no una constante del exportador. Conserva cantidad original y suma calculada para auditar correcciones. Suma los intentos porque una llamada fallida puede consumir tokens. Marca cargos estimados o retrasados como provisionales y compáralos con el informe del proveedor.

Expón dimensiones que un propietario pueda cambiar: flujo, familia de modelo, entorno, clase de inquilino y resultado. No uses ID de usuario, prompt ni argumentos de herramienta como dimensiones. El panel de coste debe mostrar total, coste por ejecución, tokens por ejecución, proporción de reintentos y proporción de modelos caros. Más tokens de salida con latencia normal puede indicar respuesta sin límite, bucle o cambio de prompt.

Distribuciones de latencia: p50, p95 y p99

La media oculta la cola que percibe el usuario durante una espera de cola o herramienta lenta. Registra la duración del run raíz y de spans importantes como histogramas en segundos. Mide time to first token, intervalo entre partes de streaming cuando afecta a la pantalla y tiempo total. Separa servidor, cola y red cuando el proveedor los exponga.

p50 representa el caso típico, p95 a los usuarios lentos y p99 los casos raros graves. Son cuantiles de una distribución, no tres medias. Elige buckets alrededor del objetivo real, desde fracciones de segundo hasta minutos, y mantén las unidades. La función de Prometheus histogram_quantile estima un cuantil desde buckets. Para histogramas clásicos agrega por le antes de calcular.

histogram_quantile(
  0.95,
  sum by (le, workflow) (
    rate(agent_run_duration_seconds_bucket[10m])
  )
)

No pongas trace IDs en etiquetas. Divide por flujo, familia de modelo, región y resultado. Una traza puede explicar el movimiento de p95. No sumes duraciones de spans concurrentes. Usa el camino crítico.

Errores, reintentos y límites de velocidad

Define una taxonomía antes de crear alertas. Distingue validación, denegación de política, autenticación, rate limit, timeout, error upstream, respuesta mal formada, fallo de herramienta y cancelación. Mapea códigos del proveedor a esas clases y conserva un código original acotado. Marca la cancelación esperada por el usuario aparte del fallo del servidor.

Cada reintento necesita razón, número, backoff y resultado final. Usa backoff exponencial acotado con jitter solo cuando el contrato lo permita. No repitas validación, autorización, políticas ni errores deterministas de esquema. Establece una deadline para todo el run y cada intento. El span raíz comunica retry_count, attempt_count y resultado; cada intento conserva su estado. Muchos reintentos pueden elevar el coste aunque el éxito parezca normal.

Registra la selección de un modelo de reserva como evento o span. El panel debe separar rate limit, latencia, política de seguridad y comprobación de capacidades. Supervisa por separado argumentos de herramienta mal formados y bucles de reparación de esquema. Si permites reparación, limita iteraciones y emite loop_limit al alcanzar el límite.

Privacidad, redacción y muestreo

Los prompts y datos de herramientas pueden contener información personal, confidencial o sensible. El valor predeterminado seguro es recoger metadatos, contadores, huellas y códigos de razón sin contenido. Si la depuración necesita ejemplos, usa un almacén separado con consentimiento, retención breve, cifrado, registro de accesos y redacción por campo. Redacta antes de exportar.

Usa una allowlist de atributos. Elimina cabeceras de autorización, cookies, claves API, datos de contacto, números de cuenta, URL con query y texto documental. El hash no vuelve anónimos los datos automáticamente. Separa la huella del prompt del prompt y documenta quién puede unirlos. Prueba la redacción con secretos realistas y datos personales multilingües.

El muestreo reduce volumen, pero no debe ocultar incidentes. Usa muestreo parent-based para mantener trazas completas. En el Collector aplica tail sampling para conservar errores, timeouts, ejecuciones caras y trazas lentas, mientras muestreas éxitos normales. La especificación de muestreo de OpenTelemetry distingue registro y exportación. Mantén métricas sin muestreo y usa trazas para exemplars.

OpenTelemetry, Grafana y Sentry juntos

Instrumenta el agente con APIs y convenciones semánticas de OpenTelemetry, envía OTLP a un Collector y deja que gestione lotes, memoria, redacción, muestreo y enrutamiento. Exporta trazas, métricas y registros a sus backends con los mismos atributos de servicio, versión, entorno, región y despliegue. Valida una traza completa en staging antes del muestreo de producción.

Grafana ofrece una vista operativa compartida. Crea paneles de volumen, éxito, p50/p95/p99, primer token, tokens, coste, reintentos, duración de herramientas y estado del proveedor. Enlaza paneles con búsqueda de trazas y runbooks. La documentación de alertas de Grafana cubre consultas, condiciones, evaluación y notificaciones. Sentry añade agrupación de problemas, contexto de errores e inspección de trazas. Su Trace API expone los spans y errores de una traza. En cualquier backend de errores envía solo datos redactados y configura el muestreo explícitamente.

SLO y alertas útiles para operadores

Un SLO debe expresar una promesa al usuario, no la salud del Collector. Define disponibilidad como ejecuciones completadas sin error clasificado de servidor, proveedor o herramienta. Define latencia como ejecuciones raíz por debajo de un umbral. Si el coste es una restricción, sigue un SLI de presupuesto separado.

Elige objetivos según una línea base medida y el requisito del producto. Separa flujos con expectativas diferentes. Muestra el error budget en una ventana larga y una vista corta para despliegues. La documentación de SLO de Grafana describe SLI, consumo de presupuesto y alertas fast-burn y slow-burn. Una alerta de página solo debe existir cuando haya una acción posible. Un cambio lento puede crear un ticket.

Alertas prácticas son el aumento sostenido de errores raíz, consumo rápido de presupuesto, p95 sobre el contrato, pico de rate limit, más reintentos, registros de uso ausentes, coste inesperado y cola detenida. Incluye flujo, región, despliegue, valor, umbral, enlace de traza, propietario y runbook. Un pending period evita alertar por un punto aislado. Agrupa por servicio y severidad. Si no hay acción inmediata, usa un panel.

Del síntoma a la causa

Empieza con el SLO o el informe del usuario y elige una traza representativa. Comprueba los hijos del span raíz y la propagación en pasarela, cola y frontera de herramienta. Si faltan spans, revisa exportador, muestreo e inyección de contexto antes del código de negocio.

Para una ejecución lenta compara espera de cola, primer token, salida, recuperación y caminos críticos de herramientas. Un p99 alto con p50 normal suele indicar dependencia de cola, límite de concurrencia o reintento. Un cambio de p50 apunta a despliegue, modelo, prompt o región. Para un pico de coste agrupa por modelo real, categoría de token, versión del flujo e intentos, y concilia con el informe del proveedor.

Para un error parte del primer span fallido, no de la excepción envolvente. Comprueba status, error.type, código del proveedor, presupuesto de timeout y eventos de reintento. Distingue rechazo de herramienta, caída del proveedor, respuesta mal formada y error del parser. Comprueba que la redacción conserve un código seguro. Cuenta un bloqueo de política esperado como resultado del producto y alerta solo si cambia de forma inesperada.

Mantén peticiones sintéticas con fixtures deterministas y no sensibles. Tras cambiar instrumentación, modelo, prompt o enrutamiento, comprueba que trazas, métricas, registros de tokens y errores compartan el mismo correlation ID. Para el contexto de arquitectura, consulta production AI agent architecture, AI agent evaluations, prompt injection and MCP security y hybrid RAG with pgvector. Esos temas determinan los spans y el SLO aceptable, mientras la observabilidad sigue siendo la capa de evidencia.

Más publicaciones