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.
| Nivel | Cómo | Cuándo te enteras |
|---|---|---|
| Reactivo | Abres el dashboard porque alguien se ha quejado | Después del usuario |
| Auditoría | Consultas con el SDK cuando quieres revisar algo | Cuando te acuerdas |
| Proactivo | Un script periódico consulta y te avisa | Antes 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.
runs = client.list_runs(
project_name=project_name,
execution_order=1, # solo runs raíz
error=True, # solo los que fallaron
)
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.
| Filtras por… | ¿Lo hace la API? | Coste si no |
|---|---|---|
| Proyecto | Sí | — |
| Error sí/no | Sí | — |
| Ventana de tiempo | Sí | — |
| Tags | Sí | — |
| Latencia > 3 s | No, se calcula | Te descargas todo y filtras en Python |
| Longitud de la respuesta | No | Ídem |
staging, variant-a,
experimento-3. Pocos valores, muy repetidos.
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.
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.
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.
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.