Langfuse : Plateforme open source d'observabilité et monitoring pour applications LLM

tl;dr: Langfuse est une plateforme open source d'observabilité pour applications LLM. Contrairement à LangSmith, Langfuse est entièrement open source (MIT), self-hostable gratuitement, et framework-agnostic (LangChain, LlamaIndex, OpenAI SDK). Idéal pour les équipes nécessitant contrôle total, conformité, et flexibilité.

Monitorer et optimiser vos applications LLM en production est crucial. Langfuse offre une alternative open source et flexible à LangSmith, avec des fonctionnalités avancées d’observabilité, d’évaluation et de gestion de prompts. Dans cet article, découvrez pourquoi Langfuse est devenu l’outil de prédilection des équipes enterprises.

Qu’est-ce que Langfuse ?

Langfuse est une plateforme open source d’observabilité pour applications LLM qui permet de :

  • 🔍 Tracer chaque appel LLM et opération (RAG, agents, chains)
  • 📊 Monitorer performances, coûts et qualité en temps réel
  • 🧪 Évaluer automatiquement avec LLM-as-a-judge et métriques custom
  • 📝 Gérer versions de prompts avec déploiement centralisé
  • 🎯 Analyser comportements utilisateurs et patterns d’usage
  • 🏢 Self-host sur votre infrastructure pour conformité totale

Diagramme d’architecture LangChain illustrant Langfuse pour l’observabilité LLM pour le développement d’applications IA

Pourquoi Langfuse ?

Open Source & Self-Hostable

  • Code source MIT entièrement accessible
  • Self-hosting gratuit (pas de vendor lock-in)
  • Contrôle total sur vos données sensibles
  • Conforme RGPD, HIPAA, SOC 2

Framework-Agnostic

  • LangChain & LangGraph
  • OpenAI SDK, Anthropic SDK
  • LlamaIndex
  • LiteLLM
  • Applications custom avec SDK Python/TypeScript

Enterprise-Ready

  • Prompt management avec versioning Git-like
  • Évaluations LLM-as-a-judge natives
  • Annotation queues pour labeling collaboratif
  • Datasets pour testing et benchmarking
  • API complète pour CI/CD et automatisation

Langfuse vs LangSmith

CritèreLangfuseLangSmith
LicenseOpen source (MIT)Propriétaire
Self-hostingGratuitPayant (Enterprise)
FrameworksLangChain, OpenAI, LlamaIndex, customLangChain principalement
Prompt ManagementGit-like versioning, deploymentBasique
ÉvaluationsLLM-as-a-judge, custom, annotationLLM-as-a-judge
Pricing CloudGratuit jusqu’à 1M events/moisGratuit jusqu’à 5K traces/mois
Best forEnterprises, conformité, multi-frameworkStartups, pure LangChain

Pour une vue d’ensemble sur les tests et l’évaluation des applications LangChain (au-delà du monitoring), consultez notre guide Testing et évaluation LangChain.

Installation et configuration

Cloud (Hébergé par Langfuse)

  1. Créer un compte

    • Allez sur cloud.langfuse.com
    • Créez un compte (gratuit jusqu’à 1M events/mois)
    • Créez un projet
    • Générez une paire de clés API (public/secret)
  2. Installer le SDK

pip install langfuse
  1. Configuration
import os
from dotenv import load_dotenv

load_dotenv()

# Configuration Langfuse Cloud
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..."  # Clé publique
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..."  # Clé secrète
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com"  # Optionnel (cloud par défaut)

Self-Hosting (Docker)

Avantage : Données 100% sous votre contrôle, conformité totale.

# docker-compose.yml
version: '3.8'

services:
  langfuse-server:
    image: langfuse/langfuse:latest
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/langfuse
      - NEXTAUTH_URL=http://localhost:3000
      - NEXTAUTH_SECRET=your-secret-key
      - SALT=your-salt-key
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=langfuse
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Lancez avec :

docker-compose up -d

Interface disponible sur http://localhost:3000

Intégration avec LangChain

Configuration automatique

Langfuse s’intègre de manière transparente avec LangChain via un callback handler :

from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langfuse.callback import CallbackHandler

# Initialiser le callback Langfuse
langfuse_handler = CallbackHandler()

# Créer votre chain
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
prompt = ChatPromptTemplate.from_template("Explique {concept} de manière simple")
chain = prompt | llm | StrOutputParser()

# Invoquer avec tracing Langfuse
result = chain.invoke(
    {"concept": "la blockchain"},
    config={"callbacks": [langfuse_handler]}
)

print(result)

Automatiquement tracé :

  • Prompt template avec variables
  • Appel LLM (modèle, tokens, latence, coût)
  • Réponse du LLM
  • Output parser

Tracing d’agents LangChain

from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.tools import Tool
from langfuse.callback import CallbackHandler

# Outils
search = DuckDuckGoSearchRun()
tools = [
    Tool(
        name="Search",
        func=search.run,
        description="Recherche sur le web"
    )
]

# Prompt
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
    ("system", "Tu es un assistant de recherche expert."),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad")
])

# Agent
llm = ChatOpenAI(model="gpt-4", temperature=0)
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Tracing avec Langfuse
langfuse_handler = CallbackHandler()

result = agent_executor.invoke(
    {"input": "Quels sont les derniers développements en IA générative ?"},
    config={"callbacks": [langfuse_handler]}
)

Dans l’interface Langfuse, vous verrez :

  • 🔍 Chaque appel de recherche
  • 🤖 Chaque itération du raisonnement
  • 💭 Les “thoughts” de l’agent
  • ⚙️ Les outils utilisés
  • 📊 Coûts et latences détaillés

Utilisation globale (toutes les chains)

Configurez Langfuse globalement pour tracer automatiquement toutes vos exécutions :

from langfuse.callback import CallbackHandler

# Créer un handler global
langfuse_handler = CallbackHandler()

# Configurer au niveau session
from langchain.globals import set_llm_cache
from langchain.callbacks import get_callback_manager

# Toutes les exécutions suivantes seront tracées
callback_manager = get_callback_manager()
callback_manager.add_handler(langfuse_handler)

Fonctionnalités avancées

Prompt Management

Langfuse offre un système de gestion de prompts centralisé avec versioning :

1. Créer un prompt dans l’interface

  • Allez dans “Prompts” → “New Prompt”
  • Nom : support-email-response
  • Version : v1
  • Template :
Tu es un agent de support client pour {{company}}.

Question client : {{question}}

Réponds de manière professionnelle et empathique.

2. Utiliser dans votre code

from langfuse import Langfuse

langfuse = Langfuse()

# Récupérer le prompt (avec cache)
prompt = langfuse.get_prompt("support-email-response")

# Compiler avec variables
compiled_prompt = prompt.compile(
    company="TechCorp",
    question="Comment annuler mon abonnement ?"
)

# Utiliser avec LLM
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke(compiled_prompt)

Avantages :

  • Modification prompts sans redéployer code
  • A/B testing de versions de prompts
  • Rollback instantané en cas de régression
  • Versioning Git-like avec historique complet
  • Cache client-side pour latence zéro

Évaluations automatiques

Langfuse supporte plusieurs méthodes d’évaluation :

LLM-as-a-Judge

from langfuse import Langfuse

langfuse = Langfuse()

# Récupérer une trace
trace = langfuse.get_trace("trace-id")

# Évaluer la qualité avec GPT-4
from langfuse.model import CreateScore

langfuse.score(
    trace_id=trace.id,
    name="helpfulness",
    value=0.9,  # Score automatique
    comment="Réponse claire et complète",
    data_type="NUMERIC"
)

Évaluations batch

# Évaluer toutes les traces d'un dataset
dataset = langfuse.get_dataset("customer-support-qa")

for item in dataset.items:
    # Exécuter votre chain
    result = chain.invoke(item.input)

    # Comparer avec expected output
    is_correct = evaluate_similarity(result, item.expected_output)

    # Logger l'évaluation
    langfuse.score(
        trace_id=item.trace_id,
        name="correctness",
        value=1.0 if is_correct else 0.0
    )

Annotation Queues

Pour labeling collaboratif par des humains :

# Créer une annotation queue dans l'interface
# Nom : "quality-review"
# Filter : traces avec score < 0.7

# Les annotateurs peuvent :
# - Réviser les traces
# - Ajouter des labels
# - Corriger les réponses
# - Créer des datasets pour fine-tuning

Datasets pour testing

from langfuse import Langfuse

langfuse = Langfuse()

# Créer un dataset
dataset = langfuse.create_dataset(
    name="rag-qa-eval",
    description="Questions-réponses pour évaluation RAG"
)

# Ajouter des items
dataset.create_item(
    input={"query": "Qu'est-ce que le RAG ?"},
    expected_output="RAG (Retrieval-Augmented Generation) combine..."
)

# Tester votre chain sur le dataset
for item in dataset.items:
    result = rag_chain.invoke(item.input)

    # Comparer avec expected
    similarity_score = calculate_similarity(result, item.expected_output)

    langfuse.score(
        trace_id=item.trace_id,
        name="similarity",
        value=similarity_score
    )

Monitoring et analytics

Dashboard en temps réel

L’interface Langfuse offre des dashboards pour :

Métriques globales

  • 📊 Volume de requêtes (par heure/jour/mois)
  • 💰 Coûts totaux et par modèle
  • ⏱️ Latence moyenne et P95/P99
  • ✅ Taux de succès et erreurs

Analyses détaillées

  • 🔍 Traces individuelles avec drill-down complet
  • 📈 Évolution des scores d’évaluation
  • 🎯 Comparaison de versions de prompts
  • 👥 Comportements utilisateurs (sessions, feedback)

Alertes et monitoring

# Configurer des alertes via API
langfuse.create_alert(
    name="high-latency",
    condition="avg_latency > 3000",  # Plus de 3s
    notification_channel="email",
    recipients=["[email protected]"]
)

langfuse.create_alert(
    name="low-quality",
    condition="score_helpfulness < 0.5",
    notification_channel="slack",
    webhook_url="https://hooks.slack.com/..."
)

Exports pour analytics avancées

# Exporter données vers votre data warehouse
traces = langfuse.get_traces(
    from_timestamp="2025-01-01",
    to_timestamp="2025-01-31",
    limit=10000
)

# Convertir en DataFrame pour analyse
import pandas as pd

df = pd.DataFrame([
    {
        "id": t.id,
        "timestamp": t.timestamp,
        "model": t.model,
        "tokens": t.usage.total_tokens,
        "cost": t.calculated_total_cost,
        "latency": t.latency_ms,
        "user_id": t.user_id
    }
    for t in traces
])

# Analyses custom
df.groupby("model").agg({
    "cost": "sum",
    "latency": "mean",
    "tokens": "sum"
})

Intégration avec d’autres frameworks

OpenAI SDK Direct

from langfuse.openai import openai

# Drop-in replacement pour OpenAI SDK
client = openai.OpenAI()

completion = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Explique la photosynthèse"}],
    # Métadonnées Langfuse
    langfuse_user_id="user-123",
    langfuse_session_id="session-456",
    langfuse_tags=["biology", "education"]
)

# Automatiquement tracé dans Langfuse !

LlamaIndex

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.callbacks import CallbackManager
from langfuse.llama_index import LlamaIndexCallbackHandler

# Configurer callback
langfuse_handler = LlamaIndexCallbackHandler()
callback_manager = CallbackManager([langfuse_handler])

# Créer index
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(
    documents,
    callback_manager=callback_manager
)

# Interroger
query_engine = index.as_query_engine()
response = query_engine.query("Résume les documents")

Applications custom

from langfuse import Langfuse

langfuse = Langfuse()

# Tracer manuellement
trace = langfuse.trace(
    name="custom-rag-pipeline",
    user_id="user-123",
    metadata={"env": "production"}
)

# Span 1 : Retrieval
with trace.span(name="retrieval") as span:
    docs = vector_store.search(query, k=5)
    span.update(
        output={"num_docs": len(docs)},
        metadata={"similarity_threshold": 0.8}
    )

# Span 2 : Reranking
with trace.span(name="reranking") as span:
    reranked_docs = reranker.rerank(docs, query)
    span.update(output={"num_docs": len(reranked_docs)})

# Span 3 : Generation
with trace.span(name="generation") as span:
    response = llm.generate(
        prompt=f"Context: {reranked_docs}\n\nQuestion: {query}"
    )
    span.update(
        output={"response": response},
        model="gpt-4",
        usage={"total_tokens": 1500},
        calculated_cost_usd=0.03
    )

Production best practices

Gestion des coûts

# Tracker coûts par utilisateur
trace = langfuse.trace(
    name="user-query",
    user_id="user-123",
    tags=["tier-premium"],  # Segmenter par tier
)

# Analyser dans dashboard :
# - Coût moyen par utilisateur
# - Utilisateurs les plus coûteux
# - Coûts par feature

Sampling pour haute volumétrie

import random

# Tracer seulement 10% en production (réduire coûts stockage)
if random.random() < 0.1 or user_is_internal:
    config = {"callbacks": [langfuse_handler]}
else:
    config = {}

result = chain.invoke(input, config=config)

Versioning d’application

# Tracker version de votre app
trace = langfuse.trace(
    name="query",
    release="v2.3.1",  # Version de votre app
    metadata={
        "commit_sha": "abc123",
        "deployed_at": "2025-01-15T10:00:00Z"
    }
)

# Comparer performances entre versions

Conformité et privacy

# Anonymiser données sensibles
def anonymize_pii(text: str) -> str:
    # Remplacer emails, téléphones, etc.
    return re.sub(r'\S+@\S+', '[EMAIL]', text)

# Tracer sans données sensibles
trace = langfuse.trace(
    name="support-query",
    input=anonymize_pii(user_query),
    output=anonymize_pii(bot_response)
)

Cas d’usage réels

Amélioration continue d’un chatbot

# 1. Collecter feedback utilisateur
@app.post("/feedback")
def submit_feedback(trace_id: str, rating: int):
    langfuse.score(
        trace_id=trace_id,
        name="user-rating",
        value=rating / 5.0,  # Normaliser 1-5 → 0-1
        data_type="NUMERIC"
    )

# 2. Identifier prompts problématiques
# Dans dashboard : Filter traces where user-rating < 0.4

# 3. Créer dataset depuis traces mauvaises
dataset = langfuse.create_dataset("low-rated-queries")
for trace in low_rated_traces:
    dataset.create_item(
        input=trace.input,
        expected_output=corrected_response  # Corrigé manuellement
    )

# 4. Tester nouveau prompt
for item in dataset.items:
    result = new_chain.invoke(item.input)
    similarity = calculate_similarity(result, item.expected_output)
    langfuse.score(trace_id=item.trace_id, name="improvement", value=similarity)

# 5. Déployer si amélioration > 20%

A/B testing de prompts

import random

# 2 versions de prompt dans Langfuse
# - "email-response-v1" (actuel)
# - "email-response-v2" (nouveau)

langfuse = Langfuse()

# Sélectionner version aléatoirement
prompt_version = "v2" if random.random() < 0.5 else "v1"
prompt = langfuse.get_prompt(f"email-response-{prompt_version}")

# Tracer avec tag
trace = langfuse.trace(
    name="email-generation",
    tags=[f"prompt-{prompt_version}"]
)

# Après 1 semaine, comparer dans dashboard :
# - Avg user-rating par version
# - Coûts moyens
# - Latence
# - Taux de succès

Comparaison avec alternatives

FonctionnalitéLangfuseLangSmithWeights & BiasesArize Phoenix
Open source✅ MITPartiel✅ Apache 2.0
Self-hosting gratuit
LangChain native
OpenAI SDK
LlamaIndex
Prompt Management✅ Advanced✅ Basic
LLM-as-a-Judge
Annotation Queues
Datasets
Pricing Cloud1M events gratuits5K traces gratuitsUsage-basedFree (self-host)

Limitations et considérations

Limites actuelles :

  • Interface moins polie que LangSmith (mais s’améliore rapidement)
  • Intégration LangChain moins profonde que LangSmith (développé par équipe différente)
  • Self-hosting nécessite expertise DevOps (PostgreSQL, Docker)
  • Moins de templates/examples que LangSmith

Quand choisir Langfuse :

  • Vous avez besoin de self-hosting (conformité, sécurité)
  • Vous utilisez plusieurs frameworks (LangChain + OpenAI + LlamaIndex)
  • Vous voulez open source (transparence, contributions)
  • Vous développez pour enterprise (versioning, CI/CD, compliance)
  • Budget limité mais volumétrie élevée

Quand choisir LangSmith :

  • Vous utilisez exclusivement LangChain/LangGraph
  • Vous privilégiez time-to-value (setup plus rapide)
  • Vous préférez support officiel LangChain
  • Vous êtes une startup avec budget cloud

Pour aller plus loin

Documentation officielle

Ressources complémentaires

Intégrations avancées


Retour à la Formation LangChain