learn.chetana.fr

Langfuse : la fiche technique

14 min readCore

À quoi ça sert (le pitch en 30 secondes)

Langfuse est l'APM des applications LLM (open-source, auto-hébergeable). Pour chaque interaction il capture la hiérarchie complÚte :

Trace  « extract.{request_id} »            ← une requĂȘte de bout en bout
 ├─ Span  « node.matcher_fast »  240 ms    ← une Ă©tape (node LangGraph)
 ├─ Generation « structurer.extract »      ← UN appel LLM :
 │     prompt EXACT envoyĂ©, rĂ©ponse, modĂšle, tokens in/out, coĂ»t, latence
 └─ Event « COPILOT_TURN_TRUNCATED »       ← un fait notable

Ce que ça débloque concrÚtement : relire le prompt exact qui a produit une mauvaise réponse (le « stack trace » du LLM), suivre coûts et tokens par feature/tenant/modÚle, voir la latence par node du pipeline, et rejouer une conversation entiÚre via les sessions. Sans ça, débugger un agent = archéologie dans les logs.

Les concepts, mappés sur le projet

  • Session = un thread de conversation copilot (session_id = thread_id, avec user_id) : toute la conversation regroupĂ©e dans l'UI ;
  • Generation = une itĂ©ration de la boucle ReAct, avec usage_details (tokens + lecture cache — on vĂ©rifie que le prompt caching du module 4 fonctionne vraiment !) ;
  • Span = un node LangGraph ou l'exĂ©cution d'un tool ;
  • l'API v3+ : client.start_observation(as_type="agent"|"span"|"generation"|"tool"|"event") — le projet passe par une classe LangfuseObserver maison plutĂŽt que les dĂ©corateurs @observe, pour garder le contrĂŽle.

Les trois patterns d'intégration à retenir

  1. Best-effort, toujours : Langfuse down → un observer Noop prend la place ; les flush partent via asyncio.to_thread. L'observabilitĂ© ne casse jamais la prod ni ne bloque l'event loop ;
  2. Redactor + Sampler : les inputs sont scrubĂ©s (PII, prix) avant envoi — les traces vivent hors de la base RLS, elles n'ont pas le droit d'ĂȘtre plus bavardes qu'elle ; et on n'Ă©chantillonne pas forcĂ©ment 100 % du trafic ;
  3. Le piùge async (ADR-0013) : la propagation de contexte OTel (start_as_current_span) se perd aux points de suspension de LangGraph (chaque await peut changer de contexte). Solution du projet : porter la trace courante dans des ContextVar explicites (_CURRENT_PIPELINE_TRACE, _CURRENT_NODE_SPAN) — la propagation manuelle, fiable par construction.

Les évolutions récentes (l'outil vit)

Deux progrÚs depuis le premier état des lieux :

  • les datasets d'Ă©val sont maintenant utilisĂ©s : le harness d'Ă©val (module 3, Ă©tude de cas) pousse chaque run comme dataset run comparable dans l'UI — les scores d'un fix se lisent cĂŽte Ă  cĂŽte avec la baseline ;
  • les coĂ»ts sont devenus rĂ©els : le suivi de coĂ»ts couvre dĂ©sormais le cache de prompt (lu ≠ facturĂ© plein tarif), l'OCR et le rerank — pas seulement les tokens du LLM principal. Une trace qui ignore 30 % de la facture ment ; le multi-provider (LLM + OCR + rerank) exige une comptabilitĂ© par fournisseur.

Restent inutilisĂ©s : le prompt management (versionner/itĂ©rer les prompts sans redĂ©ploiement — la porte d'entrĂ©e naturelle quand les consignes par tenant arriveront) et les scores automatiques.

đŸ§© Quiz1/4

La différence entre un Span et une Generation :

🃏 Flashcards1/5