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

Anatomía de una traza: trace, span y generation

Un log plano te dice qué pasó. Una traza te dice quién llamó a quién, con qué entrada exacta, cuánto tardó cada paso y qué costó. La unidad de observabilidad de un sistema LLM no es la línea de texto: es el árbol.
01 — El punto de partida
Las cuatro preguntas que un log no contesta
Con tu app en producción y solo logging
  1. ¿Cuánto tardó exactamente la llamada al modelo?
  2. ¿Qué prompt exacto recibió, ya con las variables sustituidas?
  3. ¿Cuánto costó esa ejecución concreta?
  4. ¿Por qué falló la petición de ese usuario ayer a las 3 AM?

Las cuatro tienen la misma forma: preguntan por una ejecución concreta, no por una media. Un log agregado no te devuelve una ejecución; te devuelve líneas sueltas que tú tienes que volver a coser a mano.

02 — La estructura
Traza, span y generation
Una petición RAG, vista como árbol
trace · responder_usuario← la ejecución raíz   └─ span · recuperar_documentos← un paso       └─ generation · embed_consulta   └─ span · construir_prompt   └─ generation · respuesta_final← tokens + coste aquí

El detalle que importa: un span contiene a otros. La anidación es el dato. Sin ella tienes una lista de eventos y has vuelto al problema original.

03 — El mismo árbol, dos etiquetados
Vocabulario de LangFuse y de LangSmith
ConceptoLangFuseLangSmith
Contenedor de trabajoProyectoProyecto
Ejecución raíztracetrace (el run raíz)
Paso intermediospan / observationRun con run_type: chain, tool, retriever
Llamada al modelogenerationRun con run_type="llm"
Nota de calidadscorefeedback

LangSmith no usa clases distintas: todo es un Run y el run_type dice qué clase de paso es. LangFuse sí separa span de generation. Es la misma información con dos etiquetados; en cuanto lo ves así, cambiar de herramienta deja de ser aprender de cero.

04 — Lo que solo existe en el árbol
Latencia atribuida, coste desglosado, prompt final
La forma que buscas: qué rama se come el reloj
trace (total)
 └─ recuperar
 └─ construir_prompt
 └─ generation
// diagrama de forma, no de medición: las proporciones reales las pone tu dashboard, no esta página.

Con el árbol montado aparecen tres cosas que el log plano no puede darte. Latencia atribuida: no "la petición tardó 4 s", sino qué rama se llevó el tiempo. Coste desglosado: el coste de la traza es la suma de sus generations, así que una traza anómala te dice en qué rama se disparó. Y el prompt final: no la plantilla, el texto exacto que salió hacia el modelo con las variables ya sustituidas.

Por qué esto arregla un fallo de RAG
Si el bot responde "no sé", el árbol te dice si el retriever no trajo el documento (problema de búsqueda) o si lo trajo y el modelo no lo usó (problema de modelo). Son dos arreglos distintos y sin traza los confundes.
05 — La trampa operativa
El envío es asíncrono

Los dos SDK mandan los datos en segundo plano, en lotes, para no meter latencia en tu petición. Consecuencia directa: si tu script termina antes de que el lote salga, la traza no llega nunca. Por eso los dos ofrecen un flush() y por eso la guía lo repite en tres troubleshootings distintos.

Regístralo así
"No aparece la traza" no significa "no se trazó". Casi siempre significa una de tres: credenciales mal, el proceso murió antes del envío, o estás mirando otro proyecto.
Auditoría

La guía explica este mismo árbol dos veces — una en la primera lección de LangFuse y otra en la primera de LangSmith — con casi las mismas palabras, cambiando el vocabulario y sin decir en ningún momento que es la misma estructura. Deja la impresión de que son dos modelos mentales, y no lo son.

Y una que sí te va a costar tiempo: los bloques .env de LangSmith de la guía y de los dos ejercicios usan LANGSMITH_TRACING_V2=true. Las variables documentadas son LANGCHAIN_TRACING_V2 (nombre heredado) y LANGSMITH_TRACING (nombre actual). LANGSMITH_TRACING_V2 mezcla las dos convenciones. ¿Existe esa variable? Si no, no activa nada — y el síntoma sería exactamente el primer caso de la tabla de troubleshooting de la propia guía. Comprueba el nombre en la documentación oficial antes de dar tu configuración por buena.

06 — Ahora tú
Rompe el envío a propósito

Instrumenta la función más tonta que se te ocurra — una que llame al modelo una sola vez — y ejecútala sin llamar a flush(). Mira el dashboard. Después añade el flush() y vuelve a ejecutar.

Anota cuánto tarda la traza en aparecer en el segundo caso. Ese número es tu latencia de indexación, y es la razón por la que un script que crea runs y los consulta acto seguido puede devolverte una lista vacía sin que nada esté roto.