Un prompt, c’est un appel : entrée, sortie, facile à lire. Un agent, c’est un graphe — comme je le raconte dans d’un prompt à l’agentique : il va chercher du contexte, décide, appelle un ou plusieurs modèles, se relit, boucle. Quand la sortie est mauvaise, la vraie question est : quelle étape a dérapé ?
1 appel.
N appels imbriqués.
Langfuse est fait pour ça : il enregistre l’arbre complet d’un run — chaque étape, chaque appel LLM, avec tokens, coût et latence — et te laisse y accrocher des notes de qualité.
Avant d’instrumenter, quatre notions suffisent à tout comprendre.
On installe le SDK Langfuse et son intégration LangChain, puis on pose les clés — celles de ton instance.
L’instrumentation tient en un objet : le CallbackHandler de langfuse-langchain passé à graph.invoke relie chaque nœud du graphe — et chaque appel de modèle — à une seule trace. Pas de décorateur à poser partout.
Chaque nœud qui appelle un ChatOpenAI apparaît alors comme une génération — prompt, réponse, tokens, coût — sans rien ajouter.
Résultat : dans Langfuse, tu vois l’arbre gather → clarify → draft → review → apply, avec la latence et le coût de chaque nœud. La boucle review ↺ draft apparaît telle quelle.
Une trace te dit ce qui s’est passé. Un score te dit si c’était bon. Trois sources, du moins au plus automatique.
Règle métier et feedback utilisateur d’abord — simples, fiables, gratuits.
Pour la qualité subjective (fidélité, ton, pertinence), un LLM-as-a-judge note la sortie d’un autre. On le trace aussi — un juge qui déraille, ça se débogue comme le reste.
Un juge LLM n’est pas la vérité : calibre-le sur quelques exemples notés à la main avant de lui faire confiance. Et garde-le petit (gpt-4o-mini suffit) — sinon scorer coûte plus cher que produire.
Le vrai gain arrive quand tu rejoues un jeu de cas figé à chaque changement, via les datasets & experiments de Langfuse. Tu figes une trentaine de briefs représentatifs, et avant chaque déploiement tu relances l’agent dessus, noté par le juge. Une régression se voit avant la prod, pas après.
Garder les traces chez toi règle le point PII d’un coup. C’est exactement ce que couvre mon guide déployer Langfuse sur AWS EC2 — self-hosted, tes données restent tes données.
Une trace isolée répond à « ce run a-t-il marché ? ». Mais en prod, la vraie question est « ce run a-t-il marché *pour ce user*, dans *cette conversation*, sur *cette version* de l’agent ? ». Trois attributs suffisent à passer d’un tas de runs anonymes à quelque chose que tu peux filtrer et agréger.
Avec le CallbackHandler, rien de neuf à installer : tu passes ces attributs dans le `metadata` de l’invocation, et Langfuse les remonte au niveau de la trace. Les sessions acceptent n’importe quelle chaîne US-ASCII de moins de 200 caractères comme identifiant.
Le user tracking accepte email, username ou tout identifiant stable. Attention : si tu passes un email en clair, il finit dans Langfuse — même précaution PII que pour les prompts (section 06).
Tant que ton prompt vit dans le code, changer une consigne = redéployer. Le prompt management de Langfuse le sort du code : chaque édition crée une version immuable (1, 2, 3…), et un label mobile (`production`, `staging`) pointe vers la version active. Tu peux rollback en réassignant le label `production` à l’ancienne version, sans toucher au code. Le SDK met le prompt en cache côté client, donc zéro latence ajoutée.
Le gain se combine avec le scoring : quand une génération est liée à sa version de prompt, tu compares les scores du juge *par version*. « La v4 du prompt de review a fait chuter la fidélité de 8 points » devient une phrase que la donnée te dit, pas une intuition.
Trois problèmes apparaissent quand le trafic monte : s’intégrer à une stack d’observabilité existante, ne pas exploser le volume, et être prévenu quand ça part en vrille sans regarder le dashboard toute la journée.
Si tu as déjà un collecteur OpenTelemetry, tu n’as pas à choisir. Langfuse est OTel-natif : il expose un endpoint OTLP sur `/api/public/otel/v1/traces` (HTTP, JSON ou protobuf — pas de gRPC pour l’instant). Tu ajoutes Langfuse comme *exporter* de ton collecteur, et les spans émis par n’importe quelle librairie instrumentée OTel se nichent automatiquement dans la bonne trace.
Piège classique : les attributs de trace (userId, sessionId, tags, version) doivent être propagés sur *chaque* span pour que le filtrage et l’agrégation marchent côté Langfuse. Un span orphelin sans userId, et il disparaît de tes segmentations.
À gros volume, tout tracer coûte du stockage et noie le signal. L’échantillonnage est géré côté client : tu poses `LANGFUSE_SAMPLE_RATE` (ou `sampleRate` au constructeur) entre 0 et 1. La décision se prend *au niveau de la trace* — si une trace est retenue, toutes ses observations et scores le sont ; sinon rien ne part. Pas de trace à moitié envoyée.
Ma règle : échantillonne le trafic à faible valeur et gros volume, mais garde 100 % des runs qui portent un feedback négatif ou un score de juge sous le seuil — ce sont précisément ceux que tu veux déboguer. Un rate global à 0.2 sur le tout-venant, un handler dédié à 1.0 sur les cas signalés.
Un dashboard, personne ne le regarde à 3 h du matin. Les monitors & alertes déclenchent sur un seuil — coût moyen, p95 de latence, ou un agrégat de qualité — et notifient via Slack, webhook ou un déclencheur GitHub Actions. « p95 latence > 8 s » ou « coût moyen par trace > 0,05 $ » te pinguent avant que l’utilisateur ne râle.
Un bémol à connaître : les monitors sont pour l’instant réservés à Langfuse Cloud. En self-hosted, tu récupères les mêmes chiffres via la Metrics API v2 (count, latency, totalCost, totalTokens) et tu branches ton propre système d’alerte dessus.
Mon premier agent LangGraph, et pourquoi un bon prompt ne suffit plus quand la tâche devient réelle.
La stack v3 complète en Docker Compose — Postgres, ClickHouse, Redis, MinIO — derrière un HTTPS. Blocs copiables.