Ga naar hoofdinhoud

LangFuse-tutorial: LLM-engineeringplatform voor monitoring en evaluaties

Bouw een document-vraag-en-antwoordtool en ontdek hoe LangFuse het debuggen vereenvoudigt, kosten bijhoudt en prompts organiseert in complexe LLM-toepassingen.
· 15 min lezen

Verken met AI

LLM-toepassingen bouwen wordt al snel een rommeltje. Je begint te gokken naar kosten. Fouten debuggen wordt lastiger als je niet kunt zien wat er gebeurde. Prompts raken verspreid over je codebase. LangFuse lost dit op met een platform dat alles traceert, prompts beheert en kwaliteit meet.

In deze tutorial bouw je een document-vraag-en-antwoordtool en zie je hoe LangFuse je ontwikkeling organiseert. Je leert LLM-calls te traceren voor debugging, prompts te beheren met versiebeheer en evaluatieaanpakken te verkennen.

Wat is LangFuse?

LangFuse is een open-sourceplatform dat teams helpt betere LLM-toepassingen te bouwen. Terwijl jij je op features richt, verzorgt LangFuse het monitoren, testen en debuggen.

Het platform dekt drie hoofdgebieden:

  1. Observability: Zie wat er gebeurt wanneer je LLM draait. Volg kosten, spot fouten en begrijp hoe gebruikers met je app omgaan. Nooit meer raden waarom iets stuk ging of hoeveel OpenAI je vorige maand in rekening bracht.
  2. Promptbeheer: Sla je prompts centraal op en versioneer ze, in plaats van ze door je codebase te verspreiden. Je team kan verschillende versies testen zonder aan productiecode te zitten.
  3. Evaluatie: Meet hoe goed je LLM presteert. Richt geautomatiseerde tests in die controleren of antwoorden kloppen, on-topic blijven of aan kwaliteitsnormen voldoen.

Wanneer moet je LangFuse gebruiken?

Je wilt LangFuse zodra je LLM-toepassing verder gaat dan simpele experimenten. Hier zijn situaties waarin het waardevol wordt:

  • Meerdere teamleden werken aan prompts en je hebt versiebeheer nodig
  • Productietoepassingen waarbij je kosten moet volgen en prestaties monitoren
  • Complexe workflows zoals RAG-systemen, multi-agentopstellingen of conversatieketens
  • Kwaliteitszorgen waarbij je systematisch wilt testen in plaats van handmatig steekproeven nemen

Als je een snelle prototype of persoonlijk project bouwt, heb je het waarschijnlijk nog niet nodig. Maar zodra je app echte gebruikers bedient of je team groter wordt dan één persoon, wordt LangFuse nuttig.

Adoptie en integratie

LangFuse werkt met je bestaande AI-stack. Het integreert met grote frameworks zoals LangChain, LlamaIndex, AutoGen en CrewAI. Je kunt het met elke modelprovider gebruiken — OpenAI, Anthropic, Google Gemini, Mistral of Hugging Face-modellen werken allemaal out-of-the-box.

Het platform biedt native SDK’s voor Python en JavaScript, plus OpenTelemetry-ondersteuning voor maatwerkopstellingen. Of je nu no-code agent builders gebruikt of complexe multi-agentframeworks gebruikt, LangFuse haakt in zonder je kernarchitectuur te veranderen.

Bedrijven zoals Samsara, Twilio en Khan Academy gebruiken het in productie, en het voldoet aan enterprise-beveiligingsstandaarden. Het project heeft meer dan 16.500 GitHub-sterren en wordt gesteund door Y Combinator. Je kunt de gehoste versie gebruiken of het op je eigen infrastructuur draaien.

Nu je begrijpt wat LangFuse doet en wanneer het helpt, gaan we er iets mee bouwen. Zoals beloofd maken we een eenvoudige document-vraag-en-antwoordtool die alle drie de features in actie laat zien.

Vereisten

Je hebt wat Python- en LLM-ervaring nodig voordat je in LangFuse duikt.

Moet je hebben:

  • Python 3.8+ en basiskennis van API’s
  • Ervaring met de OpenAI API (we gebruiken GPT-4)
  • LangChain-basics — prompts, chains en kernconcepten

Handig om te hebben:

  • Inzicht in embeddings en retrievalpatronen
  • Ervaring met prompt engineering

Setup:

  • OpenAI API-sleutel
  • Python-pakketmanager (uv of pip)
  • We maken samen een gratis LangFuse-account aan

Nieuw in AI-ontwikkeling? Begin dan eerst met onze complete AI-leergids.

Voor context over waar LangFuse past in het bredere ecosysteem, bekijk onze overzicht van LLMOps-tools.

Laten we nu iets bouwen. We beginnen met een eenvoudige document-vraag-en-antwoordfunctie en kijken hoe LangFuse bijhoudt wat er gebeurt als je die draait. Dit basisvoorbeeld laat je alle drie de hoofdfeatures zien — observability, promptbeheer en evaluatie — op een manier die opschaalt naar complexere projecten.

Een document-vraag-en-antwoordtool bouwen met LangFuse

Je bouwt een eenvoudig document-vraag-en-antwoordsysteem dat vragen beantwoordt uit een PDF. Dit geeft je iets echts om te traceren, te beheren en te evalueren. Hoewel we het voorbeeld simpel houden, werken dezelfde patronen voor complexe RAG-systemen en multi-agentworkflows.

De omgeving instellen

Maak eerst een map voor het project en installeer de benodigde packages:

mkdir langfuse-qa-demo
cd langfuse-qa-demo
uv add langfuse langchain langchain-openai langchain-community pypdf python-dotenv

Meld je aan voor een gratis LangFuse Cloud-account op langfuse.com. Maak na het inloggen een nieuwe organisatie en een project aan. Je ziet een scherm met je API-sleutels:

LangFuse-dashboard met API-sleutels voor een nieuw project met velden Public Key en Secret Key

Kopieer je Public Key en Secret Key. Je hebt ook je OpenAI API-sleutel nodig van platform.openai.com.

Maak nu een .env-bestand in je projectmap:

OPENAI_API_KEY=your_openai_key_here
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://cloud.langfuse.com

Als je LangFuse liever op je eigen infrastructuur draait, kun je Docker Compose gebruiken voor lokale tests. De self-hostinggids legt uit hoe je PostgreSQL, ClickHouse en de LangFuse-services opzet. Gebruik voor productiedeployments Kubernetes of cloudspecifieke Terraform-templates.

Een document-qa-chain bouwen in LangChain

Je bouwt een retrieval chain die een PDF laadt, in stukken splitst, in een vectordatabase opslaat en vragen beantwoordt op basis van de opgehaalde context.

Maak een bestand met de naam qa_chain.py:

from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.vectorstores import InMemoryVectorStore
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from dotenv import load_dotenv

load_dotenv()

Dit importeert alles wat je nodig hebt. De aanroep load_dotenv() laadt je API-sleutels uit het .env-bestand.

# Load and split PDF
loader = PyPDFLoader("ai-security-landscape.pdf")
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)

Hier laden we het document ai-security-landscape.pdf, een van de whitepapers van Amazon Web Services. Om dit voorbeeld te draaien, moet je het bestand zelf downloaden en met de juiste naam aan je werkmap toevoegen.

De PyPDFLoader leest de PDF en RecursiveCharacterTextSplitter hakt die in stukken van 1000 tekens met 200 tekens overlap. De overlap helpt context te behouden over chunkgrenzen heen.

Maak vervolgens de vectorstore:

# Create vector store
embeddings = OpenAIEmbeddings()
vectorstore = InMemoryVectorStore.from_documents(documents=splits, embedding=embeddings)

InMemoryVectorStore slaat documentembeddings in het geheugen op. In productie zou je Pinecone, Weaviate of een andere vectordatabase gebruiken.

Bouw nu de QA-chain:

# Build QA chain
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
   ("system", "Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.\n\n{context}"),
   ("human", "{input}"),
])

question_answer_chain = create_stuff_documents_chain(llm, prompt)
qa_chain = create_retrieval_chain(vectorstore.as_retriever(), question_answer_chain)

create_stuff_documents_chain stopt opgehaalde documenten in de promptcontext. create_retrieval_chain combineert retrieval en beantwoorden in één chain.

Met de chain gereed koppelen we hem aan LangFuse voor tracing.

Je eerste call tracen met LangFuse

Voeg een LangFuse callback handler toe aan het script:

from langfuse.langchain import CallbackHandler

langfuse_handler = CallbackHandler()

response = qa_chain.invoke(
   {"input": "What are the main security risks discussed?"},
   config={"callbacks": [langfuse_handler]}
)

print(response["answer"])
The main security risks discussed in relation to generative artificial intelligence (AI) are context window overflow, agent mismanagement, indirect prompt injections and data leaks from overprivileged agents, logging, and caching.

Trace sent to LangFuse! Check your dashboard at https://cloud.langfuse.com

Wanneer je dit draait, legt LangFuse de volledige execution trace vast. Ga naar je LangFuse-dashboard en je ziet dat het is bijgewerkt met nieuwe details:

LangFuse-dashboard met de eerste getraceerde uitvoering van de Q&A-chain met timing- en tokenmetrics

Merk op dat, hoewel we hier een eenvoudig voorbeeld gebruiken, LangFuse werkt met complexe RAG-pijplijnen die meerdere documenttypen verwerken, re-ranking gebruiken of parallel meerdere retrievalstrategieën draaien.

Met je Q&A-systeem draaiend en LangFuse-tracing op zijn plaats, ben je klaar om te begrijpen wat LangFuse vastlegt en hoe je die informatie gebruikt om je toepassing te verbeteren.

Observability in LangFuse

Je hebt zojuist een trace naar LangFuse gestuurd. Laten we nu begrijpen wat dat oplevert en waarom het ertoe doet.

Waarom LLM-observability belangrijk is

Traditionele logging werkt niet goed voor LLM-apps. Je kunt loggen dat een functie draaide, maar je ziet niet wat je model daadwerkelijk deed met de invoer van de gebruiker. Haalde het de juiste documenten op? Werd de prompt correct geformatteerd? Welke stap duurde vijf seconden?

LLM-observability volgt het hele execution pad. Je ziet elke input, output en tussenstap. Meldt een gebruiker foute antwoorden, dan open je hun trace en bekijk je exact wat er gebeurde. Je hoeft niet te raden. Je weet het.

Dit wordt belangrijker naarmate je app complexer wordt. Een simpele Q&A-chain heeft 4–5 stappen. Productie-RAG-systemen hebben documentloaders, chunkingstrategieën, meerdere retrievers, re-rankers en generatiestappen. Zonder observability wordt debuggen onmogelijk. Mét observability wijs je problemen precies aan.

Traces en sessies begrijpen

Open je LangFuse-dashboard en klik op “Tracing” in de linkerzijbalk. Je ziet je eerste trace in een tabel staan.

De tabel toont wanneer de trace draaide, hoe deze heet (retrieval_chain) en wat erin en eruit ging. Elke rij staat voor een volledige uitvoering van je Q&A-chain.

Klik op de trace om te zien wat er binnenin gebeurde. Je krijgt een gedetailleerde uitsplitsing.

Het linkerpaneel toont je uitvoeringstree. LangFuse volgde elke stap in je chain. De root retrieval_chain duurde in totaal 4,75 seconden. Daaronder zie je elke uitgevoerde operatie: document retrieval (retrieve_documents), vector search (VectorStoreRetriever) en answer generation (stuff_documents_chain). Elke stap toont zijn uitvoeringstijd.

Deze hiërarchie laat zien waar je chain tijd besteedde. Als je app traag draait, weet je of het de embedding lookup, de vector search of de LLM-call is.

Het rechterpaneel toont de tracedata. Bovenaan zie je de belangrijkste metrics: latency (4,75 s), kosten (~$0,016) en gebruikte tokens (426 input, 55 output). Daaronder zie je de daadwerkelijke input en output. De context-sectie toont de vier documentchunks die je retriever uit de vectorstore haalde.

Onderaan visualiseert een flowdiagram hoe je chain de operaties koppelt. Dit helpt bij het debuggen van complexe chains met meerdere takken.

Dan nu sessies. Een trace legt één vraag-en-antwoordcyclus vast. Maar echte apps hebben gesprekken. Een gebruiker kan drie gerelateerde vragen over beveiligingsrisico’s stellen. Je wilt die traces groeperen.

Dat is wat sessies doen. Ze bundelen meerdere traces in één conversatiedraad. Meldt een gebruiker een bug, dan open je hun hele sessie en zie je elke vraag en elk antwoord.

Laten we dit in actie zien. Maak eerst een helperbestand build_chain.py dat je chain-buildlogica verpakt:

from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.vectorstores import InMemoryVectorStore
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from dotenv import load_dotenv

load_dotenv()

def build_qa_chain():
   # Load and split PDF
   loader = PyPDFLoader("ai-security-landscape.pdf")
   docs = loader.load()

   text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
   splits = text_splitter.split_documents(docs)

   # Create vector store
   embeddings = OpenAIEmbeddings()
   vectorstore = InMemoryVectorStore.from_documents(documents=splits, embedding=embeddings)

   # Build QA chain
   llm = ChatOpenAI(model="gpt-4")
   prompt = ChatPromptTemplate.from_messages([
       ("system", "Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.\n\n{context}"),
       ("human", "{input}"),
   ])

   question_answer_chain = create_stuff_documents_chain(llm, prompt)
   qa_chain = create_retrieval_chain(vectorstore.as_retriever(), question_answer_chain)

   return qa_chain

Dit verpakt alles in een functie die je kunt importeren. Maak nu sessions_demo.py aan:

from build_chain import build_qa_chain
from langfuse.langchain import CallbackHandler

# Build the Q&A chain
qa_chain = build_qa_chain()

# Set up LangFuse handler
langfuse_handler = CallbackHandler()
This imports your chain and sets up the LangFuse handler. Now add session tracking for the first conversation:
# Session 1: User asking about security risks
response1 = qa_chain.invoke(
   {"input": "What are the main security risks discussed?"},
   config={
       "callbacks": [langfuse_handler],
       "metadata": {"langfuse_session_id": "conversation-001"}
   }
)
print(f"Q1: {response1['answer']}\n")

Het veld metadata met langfuse_session_id tagt deze trace met een sessie-ID. Draai dit en je ziet:

Q1: The main security risks discussed are context window overflow, agent mismanagement, and indirect prompt injections.

Voeg nu een vervolgvraag toe met dezelfde sessie-ID:

response2 = qa_chain.invoke(
   {"input": "How can these risks be mitigated?"},
   config={
       "callbacks": [langfuse_handler],
       "metadata": {"langfuse_session_id": "conversation-001"}
   }
)
print(f"Q2: {response2['answer']}\n")

Deze tweede vraag hoort bij hetzelfde gesprek, dus gebruikt het opnieuw conversation-001. Je ziet een langer antwoord:

Q2: Risks can be mitigated through several strategies including:
1. Input Management: Limit and sanitize inputs going into the model.
2. Real-time Monitoring: Use systems that alert when the context window is nearing capacity.
3. Principle of Least Privilege: Implement this for all agents and external integrations.
....

Simuleer nu een tweede gebruiker met andere vragen. Voeg dit toe aan je script:

# Session 2: Different user asking about AI applications
response3 = qa_chain.invoke(
   {"input": "What AI applications are mentioned in the document?"},
   config={
       "callbacks": [langfuse_handler],
       "metadata": {"langfuse_session_id": "conversation-002"}
   }
)
print(f"Q3: {response3['answer']}\n")

response4 = qa_chain.invoke(
   {"input": "What are the benefits of these applications?"},
   config={
       "callbacks": [langfuse_handler],
       "metadata": {"langfuse_session_id": "conversation-002"}
   }
)
print(f"Q4: {response4['answer']}\n")

Deze twee vragen gebruiken conversation-002. Draai het complete script:

uv run python sessions_demo.py

De laatste twee outputs zullen zijn:

Q3: The document does not specifically mention any AI applications. However, it discusses the concept of generative AI and its security considerations.

Q4: The context does not provide specific benefits of these applications.

Ga nu naar je LangFuse-dashboard en klik op “Sessions” in de linkerzijbalk. Je ziet beide gesprekken vermeld:

Er verschijnen twee sessies: conversation-002 (~4 seconden) en conversation-001 (~14 seconden). LangFuse groepeerde je traces op sessie-ID.

Klik op conversation-001 om het volledige gesprek te zien:

LangFuse-sessiedetailweergave met twee gerelateerde traces met vragen over beveiligingsrisico's en mitigatiestrategieën

Je ziet beide traces op volgorde: Vraag 1 over beveiligingsrisico’s en Vraag 2 over mitigatie. De sessie toont totale kosten ($0,05121 voor beide traces samen) en duur (totaal 14 seconden). Elke trace verschijnt met zijn input, output en opgehaalde context.

Deze sessieweergave laat je precies zien wat een gebruiker ervoer tijdens de interactie. Meldt iemand een probleem, dan hoef je niet door losse traces te zoeken. Je opent hun sessie en ziet de volledige conversatiestroom.

Gebruikers volgen

Tot nu toe heb je sessies gevolgd, maar hoe zit het met individuele gebruikers? Echte LLM-apps moeten weten welke gebruiker welk verzoek deed. Wanneer iemand zich aanmeldt, genereer je een unieke ID voor hem of haar. Vervolgens koppel je die ID aan elke LLM-call die ze doen.

LangFuse laat je traces groeperen op gebruikers-ID bovenop sessiegroepering. Dit geeft je een extra manier om je observabilitydata te organiseren.

Zo voeg je gebruikerstracking toe. Maak user_tracking.py aan:

from build_chain import build_qa_chain
from langfuse.langchain import CallbackHandler

# Build the Q&A chain
qa_chain = build_qa_chain()

# Set up LangFuse handler
langfuse_handler = CallbackHandler()

# Simulate a new user asking a question
user_response = qa_chain.invoke(
   {"input": "What security controls are recommended for AI systems?"},
   config={
       "callbacks": [langfuse_handler],
       "metadata": {
           "langfuse_session_id": "conversation-003",
           "langfuse_user_id": "user-12345"
       }
   }
)
print(f"Response: {user_response['answer']}")

Het veld langfuse_user_id tagt deze trace met een gebruikers-ID. Draai het script:

uv run python user_tracking.py

Je ziet:

Response: The recommended security controls for AI systems include:

1. Adaptive defenses: Incorporate machine learning-powered filters that continuously learn and adapt to new adversarial techniques.
2. Defense-in-depth: Introduce layered security mechanisms, such as refusal classifiers and real-time input monitoring, to fortify AI system integrity.
3. Data classification: Properly classify your data according to sensitivity.
......

Ga nu naar je LangFuse-dashboard. Klik op “Users” in de linkerzijbalk en je ziet user-12345 staan. Klik op de gebruiker om al diens traces over verschillende sessies te zien.

Deze gebruikersweergave helpt je vragen te beantwoorden als “Wat kostte gebruiker X ons deze maand?” of “Welke vragen blijft gebruiker Y stellen?” Je kunt individueel gedrag volgen en issues debuggen die specifiek zijn voor bepaalde gebruikers.

Promptbeheer in LangFuse

Je Q&A-systeem heeft een probleem. De prompt zit verstopt in je code. Om de manier waarop het systeem reageert te veranderen, moet je qa_chain.py bewerken, lokaal testen en opnieuw deployen. Wil een teamgenoot een andere aanpak proberen, dan moet die je codestructuur begrijpen en een nieuwe branch maken. Je kunt verschillende promptversies niet naast elkaar vergelijken of terugdraaien naar de versie van vorige week zonder in de gitgeschiedenis te graven.

Promptbeheer lost dit op door prompts buiten je code op te slaan. Je bewerkt ze in een UI, ze worden automatisch geversioneerd en je haalt ze op tijdens runtime. Je code blijft hetzelfde terwijl je prompts verbeteren.

Het collaboratieve promptprobleem

Kijk naar de prompt in je Q&A-chain:

prompt = ChatPromptTemplate.from_messages([
   ("system", "Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.\n\n{context}"),
   ("human", "{input}"),
])

Dit werkt voor soloprojecten. Maar als je met een team werkt of in productie draait, veroorzaken hardcoded prompts problemen:

  • Deploymentfrictie: Elke promptwijziging vereist een code-deploy. Je kunt geen A/B-tests met verschillende prompts doen zonder je applicatiecode te wijzigen. Wil je “Wees kort en feitelijk” proberen in plaats van “Houd het antwoord beknopt”, dan moet je committen, pushen en redeployen.
  • Geen versiecontrole: Git volgt codewijzigingen, maar laat je niet zien welke promptversie beter presteerde in productie. Je ziet geen metrics voor “prompt v1” versus “prompt v2” en je kunt niet snel terugrollen naar de vorige versie zonder code-deploy.
  • Teambottlenecks: Niet-technische teamleden kunnen niet op prompts itereren. Je productmanager heeft ideeën voor betere bewoordingen, maar moet een engineer vragen om de wijziging te maken. Dit vertraagt experimenten.
  • Testmoeilijkheden: Promptvarianten vergelijken betekent meerdere versies van je code draaien. Je kunt beknopte versus gedetailleerde antwoorden niet eenvoudig naast elkaar testen zonder handmatig werk.

Het promptbeheer van LangFuse pakt deze issues aan door prompts als data te behandelen, niet als code.

Prompts maken en versiebeheer

LangFuse slaat je prompts centraal op met automatische versiebeheer. Je kunt prompts maken via de UI of via de SDK.

Prompts maken in de UI

Ga naar je LangFuse-dashboard en klik op “Prompts” in de linkerzijbalk. Klik op “New Prompt” om er een te maken.

Je ziet een formulier waarin je kunt:

  • Je prompt een naam geven (gebruik document-qa-prompt)
  • Het type kiezen (text of chat)
  • Je prompttemplate schrijven met variabelen met {{variable}}-syntaxis
  • Configuratie instellen zoals model en temperatuur
  • Labels toevoegen om versies te markeren (zoals production of staging)

Voor ons Q&A-systeem maak je een chatprompt met twee berichten:

Systeembericht:

Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.

{{context}}

Gebruikersbericht:

{{input}}

LangFuse detecteert de variabelen {{context}} en {{input}} automatisch.

Stel vervolgens de config in op:

{
 "model": "gpt-4",
 "temperature": 0.0
}

Voeg het label production toe om dit als je productieversie te markeren.

Zodra je bewaart, maakt LangFuse versie 1 van je prompt. Elke keer dat je bewerkt en bewaart, ontstaat er een nieuwe versie. De oude versies blijven beschikbaar.

Prompts maken via SDK

Je kunt prompts ook maken met de Python SDK:

from langfuse import Langfuse

langfuse = Langfuse()

langfuse.create_prompt(
   name="document-qa-prompt",
   type="chat",
   prompt=[
       {"role": "system", "content": "Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.\n\n{{context}}"},
       {"role": "user", "content": "{{input}}"}
   ],
   labels=["production"]
)

Dit maakt dezelfde prompt programmatisch aan. Roep je create_prompt() opnieuw aan met dezelfde naam, dan voegt LangFuse een nieuwe versie toe in plaats van te overschrijven.

Versiebeheer met labels

LangFuse volgt elke promptwijziging met versienummers (v1, v2, v3). Je kunt ook labels aan versies toekennen. Labels helpen je bij deployments:

  • production - De versie die je productie-app gebruikt
  • staging - De versie in je stagingomgeving
  • latest - Verwijst automatisch naar de nieuwste versie

Verbeter je prompt door een nieuwe versie te maken. Test die eerst in staging door het staging-label te geven. Werkt het goed, verplaats dan het label production naar de nieuwe versie. Je productie-app pakt de wijziging automatisch op bij de volgende promptfetch.

Varianten testen in de playground

De LangFuse-UI bevat een playground waarin je prompts kunt testen voordat je ze in code gebruikt. Je kunt:

  • Verschillende variabelewaarden proberen
  • Meerdere versies naast elkaar vergelijken
  • Zien hoe wijzigingen de output beïnvloeden
  • Testresultaten delen met je team

Zo kunnen niet-technische teamleden met bewoordingen experimenteren zonder code aan te raken.

Beheerde prompts integreren in code

Zo gebruik je door LangFuse beheerde prompts in je applicatie. Deze workflow houdt je code stabiel terwijl je prompts evolueren.

Stap 1: Haal de prompt op

Haal in je applicatiecode de prompt op uit LangFuse in plaats van die hard te coderen:

from langfuse import Langfuse

langfuse = Langfuse()

# Get the production version
prompt = langfuse.get_prompt("document-qa-prompt", type="chat")

Dit haalt de versie met het label production op. LangFuse cachet prompts aan de clientzijde, dus er is geen latentie-impact op je LLM-calls.

Je kunt ook specifieke versies of labels ophalen:

# Get version 2 specifically
prompt = langfuse.get_prompt("document-qa-prompt", version=2, type="chat")

# Get staging version
prompt = langfuse.get_prompt("document-qa-prompt", label="staging", type="chat")

# Get latest version (automatically maintained by LangFuse)
prompt = langfuse.get_prompt("document-qa-prompt", label="latest", type="chat")

Stap 2: Compileer de prompt met variabelen

De opgehaalde prompt is een template met {{variables}}. Compileer die door variabelen te vervangen met echte waarden:

compiled_messages = prompt.compile(
   context="Retrieved document context goes here",
   input="What are the main security risks?"
)

Dit retourneert een lijst met chatberichten die klaar zijn voor je LLM:

[
   {
       "role": "system",
       "content": "Use the given context to answer the question. If you don't know the answer, say you don't know. Keep the answer concise.\n\nRetrieved document context goes here"
   },
   {
       "role": "user",
       "content": "What are the main security risks?"
   }
]

Stap 3: Gebruik de gecompileerde prompt met je LLM

Geef de gecompileerde berichten door aan je LLM-call. De exacte code hangt ervan af of je LangChain, direct de OpenAI SDK of een ander framework gebruikt.

Stap 4: Koppel de prompt aan traces

Koppel bij een LLM-call met een door LangFuse beheerde prompt deze aan de trace. Zo zie je welke promptversie voor elke LLM-call is gebruikt en kun je metrics per promptversie volgen:

from langfuse.langchain import CallbackHandler

langfuse_handler = CallbackHandler()

response = qa_chain.invoke(
   {"input": "What are the main security risks?"},
   config={"callbacks": [langfuse_handler]}
)

In je LangFuse-dashboard zie je traces gegroepeerd per promptnaam en -versie. Zo zie je welke promptversies meer kosten, trager draaien of betere resultaten opleveren.

Stap 5: Itereer zonder codewijzigingen

Hier betaalt promptbeheer zich uit. Om je prompt te verbeteren:

  1. Ga naar de LangFuse-UI
  2. Bewerk je prompt (maakt automatisch een nieuwe versie aan)
  3. Test in de playground
  4. Ken het label staging toe om in je stagingomgeving te testen
  5. Verplaats het label production om te deployen

Je code verandert nooit. De volgende keer dat die get_prompt() aanroept, haalt hij de nieuwe versie op.

Stap 6: Rollback indien nodig

Presteert de nieuwe prompt slechter, rol dan terug door het label production terug te zetten naar de vorige versie. Geen codedeploy nodig.

Deze workflow scheidt prompt engineering van software engineering. Je team kan snel op prompts itereren terwijl je applicatiecode stabiel blijft.

Verder lezen: LangFuse ondersteunt ook A/B-testen om verkeer automatisch tussen promptversies te splitsen, webhooks die je systemen informeren bij promptwijzigingen en teamfeatures zoals comments en goedkeuringsworkflows. Deze geavanceerde functies helpen grotere teams prompts op schaal te beheren. Bekijk de documentatie over promptbeheer voor details.

Evaluatie in LangFuse

Je hebt een Q&A-systeem gebouwd, observability toegevoegd en promptbeheer ingericht. Maar hoe weet je of je antwoorden goed zijn? Waarschijnlijk test je nu handmatig. Je stelt een vraag, leest het antwoord en beoordeelt of het klopt. Dat werkt voor een paar voorbeelden, maar het schaalt niet.

Wat gebeurt er als je je prompt verandert? Dan moet je alles opnieuw testen. En edgecases? Je kunt niet elke vraag onthouden die je eerder testte. En in productie kun je niet meten of versie 2 van je prompt beter presteert dan versie 1.

Evaluatie lost dit op door de prestaties van je LLM systematisch te meten. LangFuse geeft je tools om antwoorden automatisch te scoren, promptversies met echte metrics te vergelijken en kwaliteitsproblemen te vangen voordat gebruikers ze zien.

Systematische LLM-evaluatieaanpakken

Handmatig testen vertelt je of één specifiek antwoord goed oogt. Systematische evaluatie vertelt je of je hele systeem goed werkt over honderden of duizenden voorbeelden.

Zo werkt systematische evaluatie in LangFuse:

  1. Bouw een testdataset: Verzamel 20–50 vragen die echt gebruik representeren, inclusief edgecases waarbij het antwoord niet in de documenten staat.
  2. Kies scoringsmethoden: Bepaal welke kwaliteitsdimensies tellen voor je Q&A-systeem (we behandelen dit in de volgende sectie).
  3. Draai je systeem op de dataset: Verwerk elke vraag en log de resultaten naar LangFuse als traces.
  4. Score de antwoorden: Pas je scoringsmethoden automatisch toe op elk antwoord.
  5. Analyseer resultaten: LangFuse aggregeert scores over je testset en laat zien welke voorbeelden faalden.
  6. Itereer en vergelijk: Verander je prompt, draai de testset opnieuw en vergelijk metrics om te zien of je verbeterde.

Dit proces vervangt “Ik denk dat deze prompt beter is” door “deze prompt scoort 0,85 op relevantie vs. 0,72 voor de oude versie.”

Voor een complete gids over evaluatieaanpakken en hoe je ze in LangFuse implementeert, bekijk de overzichtsdocumentatie over evaluatie.

LLM-as-a-Judge-evaluatie begrijpen

Je moet honderden antwoorden scoren. Dit handmatig doen kost uren. LLM-as-a-judge automatiseert dit door een andere LLM te gebruiken om de output van je hoofd-LLM te beoordelen.

Hoe het werkt

Je geeft een beoordelende LLM drie dingen: de originele vraag, het antwoord van je Q&A-systeem en beoordelingscriteria. De judge leest het antwoord en kent een score toe.

Voor een Q&A-systeem let je op verschillende kwaliteitsdimensies. Hier zijn drie judges die je zou kunnen maken:

Correctness judge: “Beoordeel of dit antwoord de informatie in de gegeven context correct weergeeft. Score 1 als volledig correct, 0,5 als gedeeltelijk correct, 0 als incorrect of gehallucineerd.”

Dit vangt antwoorden die je documenten tegenspreken of dingen verzinnen.

Relevance judge: “Beoordeel of dit antwoord direct ingaat op de gestelde vraag. Score 1 als zeer relevant, 0,5 als enigszins relevant, 0 als off-topic.”

Dit zorgt dat je systeem on topic blijft in plaats van accurate maar irrelevante informatie te geven.

Groundedness judge: “Beoordeel of dit antwoord alleen is gebaseerd op de gegeven context. Score 1 als volledig verankerd in de context, 0,5 als gedeeltelijk verankerd, 0 als het informatie bevat die niet in de context staat.”

Dit voorkomt hallucinaties waarbij de LLM feiten toevoegt die niet in je documenten staan.

Elke judge bekijkt een specifieke kwaliteitsdimensie. Je draait alle drie judges op elk antwoord en krijgt een compleet kwaliteitsprofiel. Je kunt meer judges toevoegen voor stijl (is het beknopt?), toon (is het professioneel?) of omgang met weigeringen (zegt het “Ik weet het niet” wanneer passend?).

Judges instellen in LangFuse

LangFuse laat je LLM-judges configureren via de UI of API. Het instelproces:

  1. Definieer je judgeprompt: Schrijf duidelijke scoringsinstructies met specifieke criteria per score.
  2. Kies het judgemodel: Gebruik GPT-4, Claude of een ander LLM. Sterkere modellen scoren accurater maar kosten meer.
  3. Configureer de judge in LangFuse: Maak een scoringtemplate die LangFuse automatisch op traces toepast.
  4. Test je judge: Draai hem op enkele voorbeelden waarvan je de juiste score kent. Pas de prompt aan als de judge te streng of te soepel is.
  5. Pas toe op je dataset: LangFuse scoort al je traces automatisch zodra je judge goed werkt.

De judges draaien asynchroon, dus vertragen je hoofdapplicatie niet. Je bekijkt scores in het LangFuse-dashboard nadat je tests klaar zijn.

Wanneer judges goed werken

LLM-judges werken het best voor subjectieve criteria die moeilijk met code te meten zijn. Dingen als toon, relevantie en stijl. Ze zijn minder betrouwbaar voor objectieve feiten. Moet je verifiëren dat “2+2=4”, schrijf dan een deterministische check in plaats van een judge te gebruiken.

Voor ons Q&A-systeem werken judges goed voor relevantie en groundedness, maar je kunt ze combineren met deterministische checks voor zaken als “lengte van het antwoord is onder 100 woorden.”

Beperkingen om te overwegen

LLM-judges zijn niet perfect. Ze kunnen het oneens zijn, vooroordelen hebben richting bepaalde antwoordstijlen en fouten maken door hoge scores te geven aan zelfverzekerd klinkende maar foute antwoorden.

Dit ondervang je door je judges te testen op voorbeelden waarvan je de juiste score weet, meerdere judges te gebruiken en hun scores te middelen, en steekproeven te controleren om judgefouten te vangen.

Implementatie-opmerking: Volledige evaluatieworkflows omvatten het opzetten van datasets, het configureren van judges, batchevaluaties draaien en resultaten analyseren. Dit vereist meer setup dan we in deze tutorial kunnen behandelen en toch praktisch blijven. De LLM-as-a-judge-documentatie biedt volledige implementatiedetails, inclusief codevoorbeelden en best practices. Voor aangepaste scoringsaanpakken buiten LLM-judges, zie de gids voor custom scores.

De workflow die we hier schetsen toont je de evaluatiemindset. Zodra je traces en prompts in LangFuse beheert, volgt systematische evaluatie vanzelf. Je bouwt testsets stapsgewijs op, begint met simpele judges en verfijnt je aanpak naarmate je leert welke metrics voor jouw toepassing tellen.

Verder lezen: LangFuse ondersteunt ook datasetbeheer voor het organiseren van testcases, aangepaste scoringsfuncties voor deterministische checks en continue evaluatie die automatisch draait op productieverkeer. Deze features helpen je de kwaliteit te behouden terwijl je systeem schaalt. Bekijk de evaluatie-overzicht voor het volledige plaatje.

Conclusie

Je hebt een document-vraag-en-antwoordsysteem gebouwd en geleerd hoe de drie pijlers van LangFuse samenwerken. Observability liet je zien wat er gebeurt wanneer je LLM draait. Je ziet elke trace, elke kost, elke token. Promptbeheer haalde je prompts uit de code en bracht ze in een versiegecontroleerd systeem waar je kunt itereren zonder deployments. Evaluatie gaf je manieren om kwaliteit systematisch te meten in plaats van te raden. Deze features zijn verbonden: traces voeden evaluatiedatasets, promptversies linken aan prestatietrends en het hele systeem helpt je begrijpen of veranderingen zaken beter of slechter maken.

Begin klein met je eigen projecten. Kies één feature. Voeg bijvoorbeeld tracing toe om te zien waar je app geld uitgeeft, of verplaats één hardcoded prompt naar het beheersysteem van LangFuse. De patronen die je leerde met ons eenvoudige Q&A-voorbeeld schalen naar complexe RAG-pijplijnen, multi-agentsystemen en productietoepassingen die duizenden gebruikers bedienen. Bekijk de LangFuse-documentatie voor verdiepingen in specifieke features, of probeer de voorbeeldnotebooks die implementaties uit de praktijk laten zien. De volgende keer dat je met LLM’s bouwt, heb je de tools om te zien wat er gebeurt, te sturen wat er verandert en te meten wat ertoe doet.

Bexruz (Bex) Tuychiev

Auteur

Bexruz (Bex) Tuychiev

Ik ben een contentmaker op het gebied van data science met meer dan 2 jaar ervaring en een van de grootste achterbannen op Medium. Ik schrijf graag uitgebreide artikelen over AI en ML met een vleugje sarcastische stijl, want je moet toch íets doen om ze wat minder saai te maken. Ik heb meer dan 130 artikelen en een DataCamp-cursus gemaakt, en er is nog een nieuwe in de maak. Mijn content is door meer dan 5 miljoen paar ogen gezien, van wie 20k volgers werden op zowel Medium als LinkedIn. 

Onderwerpen