Langfuse : observer et évaluer ses pipelines LLM open source en production
Un pipeline RAG en place fonctionne, visiblement. Les réponses s’affichent, les logs ne signalent rien. Mais le LLM hallucine-t-il ? Le retrieval ramène-t-il des chunks vraiment pertinents ? Le modèle est-il plus lent depuis la mise à jour des embeddings ? Sans instrumenter son pipeline, ces questions restent sans réponse. Langfuse est un outil d’observabilité et d’évaluation open source conçu pour les applications LLM : il trace chaque appel, enregistre latences et coûts, permet d’annoter manuellement les sorties et d’automatiser des évaluations de qualité. Déployable en auto-hébergé, il s’intègre nativement avec Ollama, LangChain, OpenAI et tout SDK compatible OpenTelemetry.
Pourquoi l’observabilité LLM est différente de l’observabilité classique
Prometheus surveille une métrique numérique : temps de réponse, utilisation mémoire, nombre d’erreurs HTTP. Ces métriques sont objectives : on sait si un seuil est franchi. Les LLM introduisent un nouveau type de signal : la qualité sémantique d’une sortie. Une réponse peut être grammaticalement correcte, retournée en 800 ms, sans erreur technique et pourtant fausse, ou hors sujet. Cette dimension ne se mesure pas avec un simple compteur.
Les pipelines LLM sont également structurellement différents. Une requête n’est pas un appel unique, c’est une cascade : reformulation de la requête, appel au retriever, construction du contexte, appel au LLM, post-traitement. Chaque étape peut dégrader la qualité finale. Langfuse modélise cette hiérarchie avec un système de traces et spans : une trace représente l’interaction complète, les spans représentent chaque étape interne, avec leur durée, leur entrée, leur sortie et leurs métriques associées.
Sans cet outil, le débogage d’un pipeline RAG dégradé revient à inspecter des logs textuels ligne par ligne. Avec Langfuse, on dispose d’une interface structurée, filtrable, qui permet de retrouver en quelques secondes quelle trace a produit une mauvaise réponse, quelle étape était la plus lente, et quel chunk a été injecté en contexte.
Architecture et déploiement auto-hébergé
Langfuse est composé d’un serveur Node.js, d’une base PostgreSQL pour le stockage des traces, et d’une interface web. La stack complète se déploie avec Docker Compose en moins de cinq minutes :
services:
langfuse-server:
image: langfuse/langfuse:latest
environment:
DATABASE_URL: postgresql://langfuse:secret@db:5432/langfuse
NEXTAUTH_SECRET: my-secret-key
NEXTAUTH_URL: https://langfuse.mondomaine.fr
SALT: my-salt
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: langfuse
POSTGRES_USER: langfuse
POSTGRES_PASSWORD: secret
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
En production, on place un reverse proxy (Nginx ou Traefik) devant le port 3000 pour la terminaison TLS. Le volume PostgreSQL doit être sauvegardé régulièrement, avec BorgBackup par exemple, car il contient l’intégralité des traces.
Instrumenter son code Python en cinq lignes
Langfuse expose un SDK Python léger. L’instrumentation d’un pipeline RAG minimal ressemble à ceci :
from langfuse import Langfuse
lf = Langfuse(
public_key="pk-...",
secret_key="sk-...",
host="https://langfuse.mondomaine.fr"
)
# Créer une trace pour une interaction utilisateur
trace = lf.trace(name="rag-query", user_id="user-42")
# Span de retrieval
span = trace.span(name="retrieve-chunks", input={"query": user_query})
chunks = retriever.query(user_query)
span.end(output={"chunks": chunks, "count": len(chunks)})
# Génération LLM
generation = trace.generation(
name="llm-response",
model="mistral:7b",
input={"prompt": build_prompt(user_query, chunks)},
)
response = llm.generate(build_prompt(user_query, chunks))
generation.end(output={"text": response}, usage={"total_tokens": 1024})
trace.update(output={"final_answer": response})
Si vous utilisez LangChain, un handler dédié automatise entièrement cette instrumentation sans modifier la logique métier :
from langfuse.callback import CallbackHandler
handler = CallbackHandler(public_key="pk-...", secret_key="sk-...",
host="https://langfuse.mondomaine.fr")
chain.invoke({"question": user_query}, config={"callbacks": [handler]})
Ce que l’interface permet d’analyser
Une fois les traces collectées, l’interface Langfuse offre plusieurs vues complémentaires. La vue Traces liste toutes les interactions avec filtres sur l’utilisateur, la session, la date, un score de qualité ou un tag. On peut y retrouver les traces les plus lentes, les plus longues en tokens, ou celles annotées manuellement comme incorrectes.
La vue Generations agrège tous les appels LLM individuels : modèle utilisé, nombre de tokens en entrée et sortie, coût estimé (si un tarif est configuré), latence. On peut y repérer une dérive : si le p95 de latence passe de 1,2 s à 3,8 s après un changement de modèle, cela apparaît immédiatement dans les graphiques de tendance.
Les Datasets permettent de constituer un jeu de tests de régression. On extrait des traces passées, réelles, produites par des utilisateurs, pour construire un benchmark : question posée, contexte « retrievé », réponse attendue. On peut ensuite rejouer ce dataset après chaque changement de configuration (nouveau modèle, nouveau prompt, nouveau chunking) pour mesurer si la qualité progresse ou régresse.
Évaluation automatique et scoring LLM-as-a-judge
Langfuse intègre le pattern LLM-as-a-judge : un second LLM évalue automatiquement les sorties du premier selon des critères définis : pertinence de la réponse par rapport à la question, absence d’hallucination, fidélité au contexte. Ces scores sont associés à chaque trace et filtrable dans l’interface.
On peut aussi configurer des scores humains : l’interface expose un mode annotation qui permet à une personne non-technique de noter des sorties directement dans le navigateur : pouce levé, pouce baissé, score de 1 à 5, sans accéder au code. Ces annotations alimentent la base de données d’évaluation et peuvent guider le choix des exemples de fine-tuning.
Intégration dans une stack open source existante
Dans une stack ouverte, par exemple Ollama pour les modèles, Qdrant pour les vecteurs, n8n pour l’orchestration des workflows, CKAN pour les données sources, Langfuse s’insère comme couche transversale d’observabilité. Chaque workflow n8n qui appelle un LLM peut envoyer ses traces à Langfuse via le SDK Python ou une requête HTTP directe vers l’API REST.
Pour les modèles Ollama, Langfuse compatible OpenAI API peut être utilisé comme proxy transparent en configurant le endpoint OpenAI pour pointer vers Langfuse, qui redirige vers Ollama et intercepte les métriques au passage. Aucune modification du code applicatif n’est nécessaire dans ce cas.
Le résultat est une plateforme d’observation complète : on sait quels utilisateurs posent quelles questions, quels chunks sont fréquemment retrievés, quels prompts produisent les meilleures réponses, et où se situent les goulots d’étranglement de latence, le tout sans envoyer une seule donnée à un service externe.
Pour aller plus loin
- Documentation officielle : langfuse.com/docs
- Dépôt GitHub : github.com/langfuse/langfuse (licence MIT)
- Guide auto-hébergement : langfuse.com/docs/deployment/self-host
- Compléments naturels sur ce site : pipelines RAG, Qdrant, Ollama, n8n
