LLM Observability met Langfuse in Python: Complete Handleiding voor 2026

Leer hoe je productie-LLM's instrumenteert met Langfuse en de Python SDK v4. Van @observe decorators en OpenAI/Claude tracing tot RAG-sessies, kostenanalyse, LLM-as-a-judge evaluaties en zelf-hosting met Docker Compose.

Langfuse Tutorial: LLM Observability 2026

Zodra je LLM-applicaties in productie draait, verandert elke onverklaarbare hallucinatie, trage respons of plotselinge kostenpiek in een black-box mysterie. Geloof me, er is weinig frustrerender dan een boze klant die een screenshot stuurt van een fout antwoord—en jij geen idee hebt welke prompt, welke context of welke tokens daar daadwerkelijk achter zaten. LLM observability lost dat op door elke API-call, tool-invocatie, retrieval-stap en tokengebruik vast te leggen in gestructureerde traces die je later kunt doorzoeken, analyseren en evalueren.

Langfuse is in 2026 uitgegroeid tot het meest geadopteerde open-source LLM engineering platform, met meer dan 21.000 GitHub-sterren en een MIT-licentie die onbeperkte self-hosting toestaat. In deze handleiding laten we zien hoe je Langfuse lokaal draait met Docker Compose, hoe je OpenAI, Anthropic Claude en aangepaste Python-functies instrumenteert met de nieuwe v4 SDK, hoe je complete RAG-pipelines traceert, en hoe je production-grade evaluaties opzet met LLM-as-a-judge. Laten we erin duiken.

Wat is Langfuse en waarom observability?

LLM's zijn inherent non-deterministisch. Dat betekent dat debuggen zonder observability tool eigenlijk pure giswerk is. Je ziet een foutief antwoord bij een gebruiker, maar je weet niet welke prompt is verzonden, welke context is opgehaald, hoeveel tokens zijn verbruikt of hoe lang elke tussenstap duurde. Langfuse vangt al die informatie automatisch op in traces die je via een webdashboard kunt inspecteren.

Kernfunctionaliteit van Langfuse in 2026

  • Tracing — volg elke LLM-call, tool-invocatie, retrieval-stap en tussenberekening. Multi-turn gesprekken worden netjes als sessies gegroepeerd.
  • Prompt management — centraal versiebeheer voor prompts met server- en client-side caching, zodat je iteratief kunt tweaken zonder extra latency.
  • Evaluaties — LLM-as-a-judge templates voor hallucinatie, toxiciteit en relevantie, gecombineerd met handmatige annotaties en gebruikersfeedback.
  • Datasets en experimenten — verzamel productie-traces, zet ze om in test-datasets en draai regressietests bij elke deployment.
  • Kosten- en latency-dashboards — opgesplitst per model, gebruiker, sessie of feature.

Langfuse vs LangSmith vs Arize Phoenix

Voordat je commiteert is een eerlijke vergelijking nuttig. Dit zijn de drie belangrijkste spelers in 2026:

  • Langfuse — MIT-licentie, volledige self-hosting gratis, framework-agnostisch, sterke integratie met OpenTelemetry en 50+ frameworks. Beste keuze als je data-ownership wilt en een developer-first workflow.
  • LangSmith — closed-source, $39 per seat per maand, self-hosting alleen in de Enterprise-tier. Interessant als je volledig op LangChain of LangGraph draait en zero-config tracing wilt.
  • Arize Phoenix — Elastic License 2.0, open-source self-host, gebouwd op OpenTelemetry. Uitblinker in drift-detectie en embedding-analyse voor RAG-pipelines. Ideaal naast een bestaande ML-monitoring stack.

Voor de meeste Python-teams die starten met observability is Langfuse de pragmatische keuze: volledige feature-set, geen vendor lock-in en een ecosysteem dat LangChain, LlamaIndex, OpenAI SDK en CrewAI gewoon uit de doos ondersteunt.

Langfuse lokaal draaien met Docker Compose

Langfuse v3 bestaat uit zes containers: de web-UI, een async-worker, PostgreSQL voor metadata, ClickHouse voor trace-analytics, Redis voor queueing en MinIO voor blob storage. Klinkt als veel, ik weet het—maar Docker Compose doet eigenlijk al het werk voor je.

Stap 1: Vereisten installeren

Je hebt Docker Desktop (Mac/Windows) of Docker Engine + Docker Compose (Linux) nodig. Voor productie-VM's raadt Langfuse minimaal 4 cores en 16 GiB geheugen aan. Voor lokale ontwikkeling is 8 GiB voldoende.

Stap 2: Repository clonen en starten

git clone https://github.com/langfuse/langfuse.git
cd langfuse
docker compose up -d

Docker haalt alle images op en start de stack. Na ongeveer twee tot drie minuten (tijd voor een kop koffie) logt de container langfuse-web-1 het bericht "Ready". Je kunt dit controleren met:

docker compose logs -f langfuse-web-1

Stap 3: UI openen en project aanmaken

Open http://localhost:3000 in je browser en maak een account aan via /auth/sign-up. Daarna maak je een nieuwe organisatie, een project, en genereer je een Public Key en Secret Key onder Settings → API Keys. Bewaar die waarden goed—ze zijn zo meteen nodig voor de Python SDK.

Stap 4: Secrets aanpassen voor productie

Voor lokale ontwikkeling werkt de default docker-compose.yml direct, maar voor elke deployment buiten localhost moet je alle regels met # CHANGEME aanpassen. Ik heb ooit een demo-instantie publiek gezet met de defaults erin—doe dat alsjeblieft niet. Vervang hardgecodeerde waarden door environment variables:

NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
SALT=${LANGFUSE_SALT}
ENCRYPTION_KEY=${LANGFUSE_ENCRYPTION_KEY}
TELEMETRY_ENABLED=false

Python SDK v4 installeren en configureren

De Langfuse Python SDK is in maart 2026 herschreven naar v4, met OpenTelemetry als fundament. Dat betekent dat traces nu interoperabel zijn met andere OTel-gebaseerde observability stacks, en dat de API flink consistenter is geworden.

Installatie

pip install langfuse openai anthropic python-dotenv

Of met uv (persoonlijk mijn aanrader voor nieuwe projecten in 2026):

uv add langfuse openai anthropic python-dotenv

Environment variables instellen

Maak een .env-bestand in de root van je project:

LANGFUSE_PUBLIC_KEY=pk-lf-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LANGFUSE_SECRET_KEY=sk-lf-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
LANGFUSE_HOST=http://localhost:3000
OPENAI_API_KEY=sk-proj-...
ANTHROPIC_API_KEY=sk-ant-...

Voor Langfuse Cloud gebruik je https://cloud.langfuse.com (EU) of https://us.cloud.langfuse.com (US) als host.

Basis tracing met de @observe decorator

Het @observe()-decorator is veruit de snelste manier om bestaande Python-functies te instrumenteren. Het vangt automatisch inputs, outputs en duur op, en bouwt een hiërarchische trace van geneste aanroepen. Letterlijk één regel, en je hebt zicht op je hele functie.

from dotenv import load_dotenv
from langfuse import observe, get_client
from langfuse.openai import openai  # drop-in replacement

load_dotenv()
langfuse = get_client()

@observe()
def genereer_product_beschrijving(product_naam: str, kenmerken: list[str]) -> str:
    prompt = f"Schrijf een productbeschrijving voor {product_naam}. Kenmerken: {', '.join(kenmerken)}"
    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "Je bent een ervaren Nederlandse copywriter."},
            {"role": "user", "content": prompt},
        ],
        temperature=0.7,
    )
    return response.choices[0].message.content

if __name__ == "__main__":
    tekst = genereer_product_beschrijving(
        "ergonomisch bureaustoel",
        ["verstelbare armleuningen", "lumbaalsteun", "ademende mesh"],
    )
    print(tekst)
    langfuse.flush()  # belangrijk voor korte scripts

Drie dingen gebeuren hier achter de schermen:

  1. from langfuse.openai import openai is de drop-in vervanger. Elke OpenAI-call wordt automatisch getraceerd met model, tokens, latency en kosten.
  2. @observe() maakt een trace voor de top-level functie en spans voor elke geneste aanroep.
  3. langfuse.flush() stuurt alle buffers direct naar de Langfuse-server. Voor korte scripts is dit verplicht; in lange-lopende services gebeurt het automatisch.

Trace bekijken in het dashboard

Ga in de Langfuse UI naar Traces. Je ziet een regel met de naam van je functie, de totale duur, het aantal tokens en de kosten in dollar. Klik erop voor een waterfall-view met input, output, model parameters, systeem-prompt en elke token die is gebruikt. De eerste keer dat ik dit zag, voelde het een beetje als Chrome DevTools openen voor mijn LLM-app—eindelijk zichtbaar wat er gebeurt.

Anthropic Claude tracen zonder drop-in

Voor providers zonder native integratie gebruik je @observe(as_type="generation"). De functie die de LLM aanroept moet binnen een andere @observe()-functie draaien om een top-level trace te krijgen.

import anthropic
from langfuse import observe, get_client

langfuse = get_client()
claude = anthropic.Anthropic()

@observe(as_type="generation")
def call_claude(prompt: str, model: str = "claude-opus-4-7") -> str:
    response = claude.messages.create(
        model=model,
        max_tokens=1024,
        messages=[{"role": "user", "content": prompt}],
    )
    # Update de huidige span met model-metadata
    langfuse.update_current_generation(
        model=model,
        input=prompt,
        output=response.content[0].text,
        usage_details={
            "input": response.usage.input_tokens,
            "output": response.usage.output_tokens,
        },
    )
    return response.content[0].text

@observe()
def analyseer_sentiment(review: str) -> str:
    return call_claude(f"Classificeer het sentiment (positief/negatief/neutraal): {review}")

analyseer_sentiment("Geweldig product, razendsnelle levering!")
langfuse.flush()

Dankzij update_current_generation() geef je Langfuse de modelnaam en token-usage mee, waarmee het platform kosten correct kan berekenen. Zonder die update verschijnt de call wél in de trace, maar ontbreken de financiële metrics—wat nogal een gemiste kans is als je juist kostenoverzicht zoekt.

RAG-pipelines traceren van begin tot eind

De echte kracht van Langfuse komt pas goed naar voren bij complexe pipelines. Laten we een RAG-flow bouwen met drie stappen: query herschrijven, documenten ophalen en antwoord genereren.

from langfuse import observe, get_client
from langfuse.openai import openai

langfuse = get_client()

@observe(name="query-rewrite")
def herschrijf_query(gebruiker_vraag: str) -> str:
    response = openai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "Herschrijf de vraag naar een zoekopdracht van maximaal 10 woorden."},
            {"role": "user", "content": gebruiker_vraag},
        ],
    )
    return response.choices[0].message.content

@observe(name="vector-retrieval")
def haal_documenten_op(query: str, top_k: int = 5) -> list[dict]:
    # Stel voor: een Qdrant of pgvector oproep
    resultaten = vector_store.search(query, top_k=top_k)
    langfuse.update_current_span(
        input={"query": query, "top_k": top_k},
        output={"doc_ids": [r["id"] for r in resultaten]},
        metadata={"retriever": "qdrant-hnsw", "collection": "handleidingen-nl"},
    )
    return resultaten

@observe(name="rag-pipeline")
def beantwoord_vraag(vraag: str) -> str:
    zoekvraag = herschrijf_query(vraag)
    docs = haal_documenten_op(zoekvraag)
    context = "\n\n".join(d["text"] for d in docs)

    response = openai.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": f"Beantwoord op basis van deze context:\n{context}"},
            {"role": "user", "content": vraag},
        ],
    )
    return response.choices[0].message.content

antwoord = beantwoord_vraag("Hoe activeer ik de lumbaalsteun van de bureaustoel?")
langfuse.flush()

In het dashboard zie je nu één enkele trace met drie geneste spans: query-rewrite, vector-retrieval en de finale generatie. Je kunt doorklikken naar elke stap en zien welke documenten zijn opgehaald, met welke similarity scores, en welke tokens in de uiteindelijke prompt zijn beland. Bij een vreemd antwoord is dat meestal in dertig seconden het probleem opgelost.

Sessies, gebruikers en tags toevoegen

Om traces terug te vinden in productie moet je ze labelen met context—anders kijk je straks naar duizenden anonieme regels. In v4 doe je dit met propagate_attributes:

from langfuse import observe, get_client, propagate_attributes

langfuse = get_client()

@observe()
def chat_endpoint(gebruiker_id: str, sessie_id: str, bericht: str) -> str:
    with propagate_attributes(
        user_id=gebruiker_id,
        session_id=sessie_id,
        tags=["chatbot", "nl", "v2-prompt"],
        metadata={"deployment": "production", "region": "eu-west-1"},
    ):
        return beantwoord_vraag(bericht)

Alle child-spans binnen het with-blok erven deze attributen. In het dashboard filter je nu op user_id om alle interacties van een specifieke gebruiker te zien, of op session_id om een multi-turn gesprek te reconstrueren.

LLM-as-a-judge evaluaties instellen

Tracing is maar de helft van het verhaal. Om kwaliteit te meten heb je evaluaties nodig. Langfuse biedt ingebouwde LLM-as-a-judge templates voor hallucinatie, relevantie, toxiciteit en meer.

In de UI

  1. Ga naar Evaluation → Evaluators → New Evaluator.
  2. Kies een template, bijvoorbeeld Hallucination.
  3. Configureer welke LLM de rol van judge speelt (GPT-4o of Claude Opus werken allebei prima).
  4. Kies een trigger: live traces (elke productie-call), een sampling rate (bijvoorbeeld 10%) of een dataset.
  5. Map de trace-velden op de evaluator: input, output, en optioneel context.

Binnen minuten zie je een score tussen 0 en 1 op elke nieuwe trace verschijnen. Lage scores flag je automatisch voor menselijke review—precies de workflow die je wilt.

Programmatische evaluaties vanuit Python

from langfuse import get_client

langfuse = get_client()

@observe()
def beantwoord_met_eigen_eval(vraag: str) -> str:
    antwoord = beantwoord_vraag(vraag)
    # Eigen scoringlogica (bv. regex check of custom judge)
    relevantie = bereken_relevantie_score(vraag, antwoord)
    langfuse.score_current_trace(
        name="relevantie",
        value=relevantie,
        data_type="NUMERIC",
        comment=f"Score berekend door custom judge op {datetime.utcnow()}",
    )
    return antwoord

Prompt management met versioning

Prompts horen niet hardgecodeerd in je repo. Echt niet. Langfuse biedt een centrale store waar product-mensen en engineers samen prompts itereren zonder deploy—en dat scheelt enorm veel ping-pong op Slack.

Een prompt aanmaken

In de UI ga je naar Prompts → New Prompt. Geef het een naam (bijvoorbeeld support-agent-system), schrijf de inhoud en voeg variabelen toe met dubbele accolades: {{ product_naam }}.

In Python gebruiken

from langfuse import get_client
from langfuse.openai import openai

langfuse = get_client()

prompt = langfuse.get_prompt("support-agent-system", label="production")
gecompileerd = prompt.compile(product_naam="bureaustoel X2")

response = openai.chat.completions.create(
    model="gpt-4o",
    messages=[
        {"role": "system", "content": gecompileerd},
        {"role": "user", "content": "Hoe stel ik de hoogte in?"},
    ],
    langfuse_prompt=prompt,  # link deze generatie aan de prompt-versie
)

Het langfuse_prompt-argument koppelt elke generatie aan een specifieke prompt-versie. In de UI zie je vervolgens per versie hoeveel calls, welke kosten en welke gemiddelde evaluatiescore—ideaal voor A/B-experimenten waarin je wilt weten of die nieuwe systeem-prompt nu echt beter presteert.

Production best practices voor 2026

Sampling instellen om kosten te beheersen

Bij miljoenen calls per dag wil je echt niet elke trace opslaan. Stel een sampling rate in:

from langfuse import Langfuse

langfuse = Langfuse(
    public_key=os.getenv("LANGFUSE_PUBLIC_KEY"),
    secret_key=os.getenv("LANGFUSE_SECRET_KEY"),
    host=os.getenv("LANGFUSE_HOST"),
    sample_rate=0.1,  # 10% van alle traces
)

Voor kritieke paden (betaalflows bijvoorbeeld) override je dit per-trace gewoon naar 100%.

Async flushes voor minimale latency

De SDK buffert traces en verstuurt ze asynchroon in batches. De standaard flush_at=15 en flush_interval=0.5 werkt in de meeste gevallen prima, maar voor high-throughput services kun je dit naar smaak tunen.

Thread-safety met contextvars

De decorator gebruikt Python's contextvars om de huidige trace-context bij te houden. Binnen ThreadPoolExecutor of ProcessPoolExecutor werkt dit niet zonder extra werk—een klassieke valkuil. Gebruik asyncio waar mogelijk, of propageer de context handmatig.

Data privacy en PII-masking

Bij klantdata moet je Personally Identifiable Information maskeren voor het naar Langfuse gaat. Gebruik een pre-processing hook:

from langfuse import Langfuse
import re

def mask_pii(data):
    if isinstance(data, str):
        data = re.sub(r"\b[\w.-]+@[\w.-]+\.\w+\b", "[EMAIL]", data)
        data = re.sub(r"\b\d{4}\s?\d{4}\s?\d{4}\s?\d{4}\b", "[CREDITCARD]", data)
    return data

langfuse = Langfuse(mask=mask_pii)

Langfuse integreren met populaire frameworks

  • LangChain / LangGraph — gebruik from langfuse.langchain import CallbackHandler en geef het mee aan je chain of graph.
  • LlamaIndexfrom langfuse.llama_index import LlamaIndexInstrumentor en roep .start() aan.
  • CrewAI — werkt via OpenTelemetry; stel OTEL_EXPORTER_OTLP_ENDPOINT in naar Langfuse.
  • OpenAI Agents SDK — native integratie sinds v4 via langfuse.openai_agents.
  • LiteLLM — voeg Langfuse toe als callback in de proxy-configuratie voor centraal loggen van 100+ modellen.

Veelgestelde vragen

Moet ik Langfuse Cloud of self-hosted kiezen?

Voor de meeste teams is Langfuse Cloud de snelste start: geen infrastructuur beheren, automatische updates en een vrijblijvende gratis tier. Kies self-hosting als je data-sovereignty eist (denk aan de financiële of gezondheidssector), als je aan GDPR-data-localisatie moet voldoen, of als je volume zo hoog is dat self-hosting gewoon goedkoper uitvalt. Houd er wel rekening mee dat self-hosting PostgreSQL, ClickHouse, Redis en S3/MinIO betekent, plus Kubernetes voor productieschaal.

Wat is het verschil tussen Langfuse v2 en v4?

V4 is in maart 2026 uitgebracht en bouwt op OpenTelemetry. Dat betekent interoperabiliteit met andere OTel-stacks, een consistentere API (start_as_current_observation(), propagate_attributes) en betere async-support. V2 blijft ondersteund, maar nieuwe projecten start je het best direct op v4. De migratiegids op de Langfuse-documentatie beschrijft alle API-wijzigingen stap voor stap.

Hoeveel kost Langfuse Cloud?

De Hobby-tier is gratis voor tot 50.000 events per maand. Core en Pro staan op usage-based pricing (per ingested event). Enterprise-tier voegt SSO, SOC2 en dedicated support toe. Exacte prijzen staan op de Langfuse-website en kunnen per regio verschillen. Self-hosting heeft geen licentiekosten—je betaalt alleen je eigen infrastructuur.

Werkt Langfuse met Claude, Gemini en lokale modellen?

Jazeker. Voor Claude en andere providers zonder drop-in gebruik je @observe(as_type="generation") met update_current_generation(). Voor Gemini en Vertex AI is er een OpenTelemetry-integratie. Lokale modellen via Ollama, vLLM of llama.cpp traceer je simpelweg door de OpenAI-compatible endpoint te gebruiken met langfuse.openai—je wijst base_url gewoon aan naar je lokale server.

Hoe verschilt Langfuse van DeepEval?

DeepEval is een testing-framework: je schrijft unit-tests met metrics zoals hallucinatie en faithfulness, die je lokaal of in CI/CD draait. Langfuse is een observability-platform voor productie: het logt elke call, meet kwaliteit continu en geeft dashboards. De twee zijn eigenlijk complementair—veel teams gebruiken DeepEval in CI en Langfuse in productie, waarbij Langfuse-datasets als input voor DeepEval-tests dienen.

Conclusie

Langfuse vult een cruciaal gat in de Python LLM-stack: de brug tussen experimenteren en productie. Met één pip install, een @observe() decorator en vijf minuten Docker Compose heb je volledige zichtbaarheid in je LLM-applicatie—tracing, evaluatie, prompt management en kostenanalyse in één platform.

Mijn advies: begin klein. Start met Langfuse Cloud, decoreer je bestaande OpenAI-calls, en kijk wat er daadwerkelijk gebeurt in productie. Je zult verrast zijn hoe vaak "dit werkt prima" stiekem "hier zit iets raars" blijkt te zijn. Schaal vervolgens op naar LLM-as-a-judge evaluaties, prompt versioning en uiteindelijk self-hosting wanneer je volume en compliance-eisen dat rechtvaardigen. Zonder observability bouw je blind; met Langfuse bouw je met volledig zicht op kwaliteit, kosten en gedrag van je agents.

Artikelgeschiedenis (1)
  • — SEO meta refreshed (title and description updated)
Editorial Team
Over de Auteur Editorial Team

Our team of expert writers and editors.