MÓDULO 6 · OBSERVABILIDAD · LECCIÓN 07 · 25 MIN

Auditar trazas con el SDK

El dashboard sirve cuando ya sospechas. El SDK sirve para no tener que sospechar: list_runs convierte tus trazas en una consulta, y una consulta programada es una alerta. La observabilidad deja de ser algo que miras y pasa a ser algo que te avisa.
01 — Tres niveles
Y un salto que cuesta poco
NivelCómoCuándo te enteras
ReactivoAbres el dashboard porque alguien se ha quejadoDespués del usuario
AuditoríaConsultas con el SDK cuando quieres revisar algoCuando te acuerdas
ProactivoUn script periódico consulta y te avisaAntes que el usuario

El salto que importa es del segundo al tercero, y es pequeño: el script es el mismo, solo cambia quién lo lanza.

02 — La consulta
El parámetro que más se olvida
runs = client.list_runs(
    project_name=project_name,
    execution_order=1,   # solo runs raíz
    error=True,          # solo los que fallaron
)
Qué pasa sin execution_order=1
La consulta devuelve también los pasos anidados: cada span, cada llamada al modelo. Si estás contando "cuántas ejecuciones hubo", el resultado sale inflado por un factor que depende de lo profundas que sean tus trazas — es decir, un factor que cambia cuando cambias el código.
03 — Lo que no viene calculado
La latencia se calcula, y a veces no se puede
if run.end_time and run.start_time:
    duracion = (run.end_time - run.start_time).total_seconds()

La guarda del if no es cosmética. Un run todavía en curso, o uno que murió sin cerrarse, tiene end_time a None, y sin la comprobación un solo registro raro revienta el bucle entero. Es lo que hay que asumir en cualquier consulta a datos de observabilidad: algunos registros están incompletos por diseño.

04 — Dónde filtras
Servidor primero, Python después
Filtras por…¿Lo hace la API?Coste si no
ProyectoSí—
Error sí/noSí—
Ventana de tiempoSí—
TagsSí—
Latencia > 3 sNo, se calculaTe descargas todo y filtras en Python
Longitud de la respuestaNoÍdem
// todo lo que filtres después ya te lo has descargado. Con cien trazas da igual; con cien mil, no.
05 — Tags y metadata
La cardinalidad decide
Tags · baja cardinalidad
Para filtrar en la interfaz: staging, variant-a, experimento-3. Pocos valores, muy repetidos.
Metadata · alta cardinalidad
Para segmentar después: user_id, session_id, versión del prompt. Muchos valores, casi únicos.

Al revés falla en las dos direcciones. Un user_id como tag te deja un desplegable de filtro con diez mil entradas inservibles; un staging enterrado en metadata deja de estar a un clic en la interfaz, que era su único propósito.

06 — De consulta a alerta
El código es fácil; el umbral no

El watchdog son cinco líneas: ventana reciente, error=True, y si hay resultados, avisar. Lo que hay que decidir bien es el umbral. Mandar todas las trazas a Slack produce fatiga de alertas y, a las dos semanas, un canal silenciado. Alerta solo por lo que exige acción: errores críticos, o una tasa que se sale de lo normal.

Y ojo con la ventana
Hay latencia entre que un run se registra y que es consultable. Un watchdog que mire "los últimos 30 segundos" puede no ver nada aunque acabe de fallar algo.
Auditoría

run.id después de create_run. La guía crea runs a mano y usa el objeto devuelto en tres lecciones distintas:

run = client.create_run(name="...", run_type="chain", inputs={...})
client.update_run(run.id, outputs={"status": "ok"})

¿Qué devuelve create_run en la versión del SDK que instales? Si devuelve None — cosa habitual en clientes que encolan la escritura en segundo plano — run.id lanzaría AttributeError y ninguno de esos tres ejemplos correría tal cual; el patrón correcto sería generar tú el uuid y pasárselo. Es la primera cosa que ejecutaría de esta parte del temario, antes de construir nada encima.

La ventana del watchdog. El filtro se construye con datetime.now() - timedelta(minutes=5), que produce una fecha sin zona horaria, comparada contra marcas de tiempo de un servidor. El troubleshooting de la guía lo atribuye a que "el reloj de tu máquina no esté sincronizado", pero la causa mucho más probable es mezclar fechas ingenuas con fechas con zona. Si tu watchdog no encuentra errores que sabes que existen, prueba datetime.now(timezone.utc) antes de tocar el reloj del sistema.

07 — Ahora tú
Mide la profundidad de tus trazas

Lanza la misma consulta list_runs sobre un proyecto tuyo dos veces: una con execution_order=1 y otra sin él. Cuenta los resultados de cada una.

El cociente entre los dos números es la profundidad media de tus trazas, un dato que no sabías que tenías. Más útil todavía: es el factor por el que estarías inflando cualquier métrica de "número de ejecuciones" si te olvidas del parámetro. Apúntalo antes de escribir tu primer informe.