# 1. Wrapper del cliente — cambia el import, nada más
from langfuse.openai import openai
client = openai.OpenAI()
# 2. Decorador — convierte TU función en un nodo de la traza
@observe() # LangFuse
def contar_historia(tema): ...
@traceable(run_type="chain") # LangSmith
def mi_pipeline(texto): ...
# 3. Callback — objeto pasivo que escucha a LangChain
chain.invoke(entrada, config={"callbacks": [handler]})
# 4. Entorno — LangChain ya viene instrumentado; solo enciendes el envío
# (variables LANGSMITH_* en .env, cero código)
El wrapper registra cada llamada al modelo con tokens y coste sin tocar una línea más, pero no sabe dónde empiezan y acaban tus funciones: te da hojas sueltas, no un árbol. El decorador es justo lo contrario: es la ruta que da estructura, porque la función decorada pasa a ser la raíz y todo lo que ocurra dentro cuelga de ella.
| Wrapper | Decorador | Callback | Entorno | |
|---|---|---|---|---|
| Llamadas al modelo, tokens y coste | sí | solo si dentro hay un cliente instrumentado | sí | sí |
| Fronteras de tus funciones | no | sí | no | no |
| Pasos internos del framework | no | no | sí | sí |
| Cambios en tu código | un import | un decorador por función | un argumento por invocación | ninguno |
| Sirve sin LangChain | sí | sí | no | no |
Wrapper y decorador se complementan: el decorador da la raíz y la forma del árbol, el wrapper rellena las hojas con el detalle caro — prompt final, tokens, coste. Es la combinación por defecto fuera de LangChain.
| Ruta | Cómo se rompe, sin dar error |
|---|---|
| Wrapper | Alguien "arregla" el import a import openai y la traza pierde todas las generations |
| Decorador | Solo trazas lo que decoraste; lo que llames desde fuera queda huérfano |
| Callback | Hay que pasarlo en cada invocación; olvidarlo en una ruta la deja invisible |
| Entorno | Solo cubre lo que el framework instrumenta; tu Python puro no aparece |
El del callback es el primer caso del troubleshooting de la guía, y tiene sentido: es el único que depende de acordarse en cada punto de llamada en vez de una sola vez al arrancar.
Todo el bloque de LangFuse de la guía importa por rutas de la versión
2 del SDK: from langfuse.decorators import observe,
from langfuse.callback import CallbackHandler,
langfuse.trace(...), langfuse.score(...). Y la
instrucción de instalación es pip install langfuse, sin fijar
versión.
¿Qué versión mayor instala hoy ese comando, y siguen existiendo
esas rutas en ella? Si el paquete ha pasado a una v3,
from langfuse.decorators import observe fallaría con
ImportError en la primera línea de casi todos los ejemplos del
módulo. El arreglo no sería tuyo: sería fijar la versión o
traducir los imports. Merece la pena resolverlo antes de empezar los
ejercicios, no en mitad del tercero.
En la misma línea, la guía usa handler.auth_check() como test
de humo de la conexión. Comprueba que ese método existe en la versión
que tengas instalada antes de fiarte de lo que imprima.
Coge una función tuya que llame al modelo y trázala dos veces: primero solo con el wrapper del cliente, después añadiendo el decorador encima de la función.
Abre las dos trazas una al lado de la otra. La pregunta que tienes que poder contestar mirándolas: ¿cuántos niveles tiene cada árbol, y qué nombre lleva la raíz en cada caso? Ahí está, literalmente dibujada, la diferencia entre las dos rutas.