Leerpad
Je RAG-pijplijn geeft een antwoord op de vraag van een gebruiker. Het oogt redelijk, leest goed en klinkt zelfverzekerd. Maar zodra je het vergelijkt met de documenten die daadwerkelijk zijn opgehaald, vallen je dingen op zoals een datum die nergens in de context voorkomt of een aanbeveling die in tegenspraak is met het bronmateriaal.
Die problemen op schaal opsporen is waar het ingewikkeld wordt. Precies daarom zijn metrics als BLEU en ROUGE ontwikkeld voor machinevertaling en samenvattingstaken, maar ze kunnen je niet vertellen of het antwoord van het model echt is verankerd in de opgehaalde context of dat het de bron tegenspreekt.
Menselijke evaluatie vangt die subtiliteiten wel, maar is te duur op schaal (denk aan 10.000 queries of meer). Er is dus een kloof: geautomatiseerde metrics zijn snel maar oppervlakkig, terwijl menselijke review grondig is maar duur en traag.
Een LLM als jury zit daar tussenin. Je neemt een capabel model, geeft het een rubric die je hebt geschreven en vraagt het om de outputs van je systeem te beoordelen zoals een menselijke reviewer dat zou doen. Het is geen perfecte oplossing (later kom ik op de faalmodi terug), maar het geeft je een praktische manier om kwaliteit te monitoren op een schaal die traditionele metrics of handmatige annotatie op zichzelf niet kunnen evenaren.
In deze tutorial laat ik je stap voor stap zien hoe je een LLM-jury bouwt om een RAG-systeem te evalueren, met werkende code doorlopend, zodat je alles op je eigen machine kunt reproduceren.
Wat is LLM als jury?
Het hoofdidée van LLM-as-a-judge is een taalmodel vragen om tekstoutputs te beoordelen op basis van criteria die je in een prompt vastlegt. Je gebruikt dus een LLM om te checken of de content die een ander model heeft gegenereerd goed is of niet.
De reden dat dit überhaupt werkt (en ik was er in het begin sceptisch over) is dat evaluatie fundamenteel een eenvoudigere taak is dan generatie. Wanneer een model een antwoord helemaal from scratch genereert, jongleert het met allerlei concurrerende randvoorwaarden tegelijk:
- Nauwkeurigheid
- Toon
- Instructies opvolgen
- Verankerd blijven in de context
- Niet te stellig zijn bij onzekere informatie
Maar als je datzelfde model een afgerond antwoord geeft en vraagt om specifieke eigenschappen te checken, wordt de taak veel gerichter omdat het alleen hoeft te beoordelen, niet te creëren. En modellen presteren op die smallere taak doorgaans consistenter.
LLM-as-a-judge-benaderingen vergelijken
In de praktijk kiezen teams meestal een van deze benaderingen, afhankelijk van wat ze willen meten:
- Directe scoring laat de jury één output beoordelen op een numerieke schaal (1 tot 5, 1 tot 10, wat je rubric ook definieert). Je krijgt een getal en, als je daarom hebt gevraagd, een schriftelijke uitleg van de redenering. Dit is het meest gebruikelijke startpunt, al is er een kalibratieprobleem waar ik op terugkom.
- Pairwise comparison zet twee kandidaat-antwoorden naast elkaar en vraagt de jury het beste te kiezen. Dit omzeilt het probleem van numerieke kalibratie volledig omdat de jury alleen een relatieve keuze hoeft te maken, geen absoluut getal. Teams gebruiken dit vaak bij het vergelijken van promptvarianten of modelversies.
- Referentiegebaseerde evaluatie geeft de jury zowel de modeloutput als een gouden-standaardantwoord, en vraagt hoe goed de output overeenkomt. Dit werkt het best voor feitelijke Q&A en gestructureerde outputs waarvoor je al gelabelde ground truth hebt om mee te vergelijken.
- Binaire classificatie brengt het oordeel terug tot slagen of falen op één specifieke eigenschap. Is dit antwoord trouw aan de context? Bevat het persoonlijk identificeerbare informatie? Is het positief? Deze binaire checks draaien sneller, zijn per evaluatie goedkoper en leveren doorgaans consistentere resultaten op dan de numerieke benaderingen.

Afbeelding door de auteur. Vier verschillende benaderingen van LLM als jury.
Voor een direct overzicht van sterke punten, use cases en beperkingen, heb ik alle vier de benaderingen vergeleken in de volgende tabel:
|
Benadering |
Wanneer te gebruiken |
Sterke punten |
Let op |
|
Directe scoring |
Algemene kwaliteitsmonitoring, continue tracking |
Makkelijk te volgen in de tijd, werkt met losse outputs |
Juryleden variëren in hoe ze scores kalibreren |
|
Pairwise comparison |
A/B-testen van modellen, vergelijking van promptvarianten |
Betrouwbaardere rangschikking dan absolute scores |
Verdubbelt je API-calls, geeft geen absoluut kwaliteitsignaal |
|
Referentiegebaseerd |
Feitelijke Q&A, gestructureerde outputs |
Heldere ground truth maakt evaluatie rechttoe rechtaan |
Vereist gelabelde data, bestraft geldige alternatieve formuleringen |
|
Binaire classificatie |
Veiligheidschecks, hallucinatie-detectie, compliance |
Weinig ambiguïteit, eenvoudig om alerts te automatiseren |
Verliest nuance bij grensgevallen |
Voor een bredere kijk op evaluatiebenaderingen buiten het jury-patroon om, behandelt LLM Benchmarks Explained het volledige plaatje.
Hoe werkt een LLM-jury?
Onder de motorkap is het een eenvoudige API-call: je pakt de content in die je wilt laten beoordelen (de output van het model, de oorspronkelijke gebruikersvraag en eventuele opgehaalde context die tijdens de generatie is gebruikt) en wikkelt die in een prompt die het jurymodel vertelt wat jij belangrijk vindt en hoe je de resultaten geformatteerd wilt hebben.
De jury verwerkt dit en geeft een gestructureerde response terug, meestal een score samen met geschreven redenering die uitlegt waarom die score is toegekend. De kwaliteit van je evaluatie hangt vrijwel volledig af van hoe goed je de rubric schrijft. Ik kan dit niet genoeg benadrukken.
Een simpele prompt die zegt "beoordeel dit antwoord van 1 tot 5" levert scores op die alle kanten op gaan, omdat de jury geen gedeeld begrip heeft van wat een 3 betekent tegenover een 4. Daarom moet je concreet uitwerken hoe elk score-niveau eruitziet en waar mogelijk voorbeelden opnemen.
LLM als jury implementeren
Dan nu het praktische deel: we bouwen een volledige evaluatiepijplijn from scratch: een retrieval augmented generation (RAG)-systeem dat vragen beantwoordt uit een kleine knowledge base, een set testqueries die zijn ontworpen om verschillende faalmodi te triggeren, en een LLM-jury die de outputs beoordeelt op betrouwbaarheid (faithfulness) en relevantie.
De omgeving opzetten
Je hebt Python 3.9+ en een OpenAI API-sleutel nodig, die je kunt krijgen in de OpenAI-console. Installeer de dependencies:
pip install openai chromadb langchain langchain-openai langchain-community deepeval
Stel je API-sleutel in:
import os
os.environ["OPENAI_API_KEY"] = "your-key-here"
We gebruiken OpenAI voor zowel de RAG-generator als de LLM-jury, al zijn het verschillende modellen (GPT-4o-mini voor generatie, GPT-4o voor beoordeling). Voor embeddings volstaat text-embedding-3-small voor een tutorial. In een productiesysteem wil je eerst een paar embeddingmodellen benchmarken op je eigen domeindata voordat je je vastlegt.
Wil je eerst meer achtergrond over RAG voordat je in de evaluatiecode duikt, dan behandelt onze cursus Retrieval Augmented Generation (RAG) met LangChain de fundamentals.
Data en vectoropslag voorbereiden
We hebben een kleine knowledge base nodig waaruit het RAG-systeem kan ophalen. Ik gebruik een set tekstfragmenten over het retourbeleid van een fictief bedrijf. De inhoud zelf is hier niet het belangrijkste, maar een begrensde set documenten maakt het makkelijker om precies te zien wanneer het model buiten de gegeven context treedt (de faalmodus die we de jury willen laten detecteren).
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain.schema import Document
# Sample knowledge base
documents = [
Document(
page_content="All customers are eligible for a full refund within 30 days of purchase. "
"The item must be in its original packaging and unused condition. Refunds are "
"processed to the original payment method within 5-7 business days.",
metadata={"source": "return_policy.pdf", "section": "refund_eligibility"}
),
Document(
page_content="Exchanges can be requested within 45 days of purchase for items of "
"equal or lesser value. Size exchanges on clothing are free of charge. "
"For items of greater value, the customer pays the difference.",
metadata={"source": "return_policy.pdf", "section": "exchanges"}
),
Document(
page_content="Electronics have a 15-day return window due to rapid depreciation. "
"Opened software and digital downloads are non-refundable. Defective electronics "
"can be returned within 90 days with proof of defect from an authorized service center.",
metadata={"source": "return_policy.pdf", "section": "electronics"}
),
Document(
page_content="Shipping costs for returns are covered by the company for defective items. "
"For non-defective returns, the customer is responsible for return shipping. "
"Free return shipping labels are available for loyalty program members regardless of reason.",
metadata={"source": "return_policy.pdf", "section": "shipping"}
),
Document(
page_content="Gift purchases can be returned with the gift receipt for store credit only. "
"Without a gift receipt, returns are processed at the lowest sale price in the last 90 days. "
"Gift cards and prepaid cards are non-refundable and cannot be exchanged.",
metadata={"source": "return_policy.pdf", "section": "gifts"}
),
]
# Create vector store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(documents, embeddings, persist_directory="./chroma_db")
print(f"Indexed {len(documents)} documents")
Vijf documenten over retourbeleid is niet veel, maar genoeg om de retrievalproblemen te tonen die we belangrijk vinden: onvolledige context, irrelevante stukken die worden opgehaald en hallucinatiekansen wanneer het model de druk voelt om een volledig antwoord te geven terwijl de context dat niet volledig ondersteunt.
Een basis-RAG-pijplijn bouwen
De RAG-pijplijn haalt relevante stukken op en genereert een antwoord. Niets spectaculairs, gewoon het standaardpatroon ophalen-dan-genereren.
from openai import OpenAI
client = OpenAI()
def rag_query(question: str, top_k: int = 2) -> dict:
"""Run a RAG query: retrieve context, generate answer."""
# Retrieve
results = vectorstore.similarity_search(question, k=top_k)
context = "\n\n".join([doc.page_content for doc in results])
# Generate
system_prompt = """You are a helpful customer support assistant. Answer the
customer's question based ONLY on the provided context. If the context doesn't
contain enough information to answer fully, say so."""
user_prompt = f"""
Context: {context}
Question: {question}
Answer:
"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
temperature=0.3
)
answer = response.choices[0].message.content
return {
"question": question,
"context": context,
"answer": answer,
"sources": [doc.metadata for doc in results]
}
Ik koos voor GPT-4o-mini als generator omdat het goedkoop en snel is, en we willen dat de evaluatie zelf de focus is. De temperatuur staat op 0,3, wat willekeur vermindert maar niet elimineert.
En de systeemprompt instrueert het model expliciet om alleen de gegeven context te gebruiken, wat standaard is in RAG-systemen, al wijken modellen nog vaker dan je denkt van de context af. Precies dat willen we dat de jury markeert.
Laten we het testen:
result = rag_query("Can I return opened software?")
print(f"Question: {result['question']}")
print(f"Answer: {result['answer']}")
print(f"Sources: {result['sources']}")
Een evaluatiedataset genereren
Je hebt testcases nodig die verschillende onderdelen van de pijplijn beproeven. In productie verzamel je die uit echte gebruikersvragen. Voor deze tutorial maak ik een mix die bewust de soorten vragen bevat waar RAG-systemen vaak over struikelen.
eval_questions = [
# Straightforward questions (should be easy)
"What is the refund window for regular purchases?",
"Are exchanges free for clothing size changes?",
# Questions requiring synthesis across chunks
"What are my options if I received a defective laptop 60 days ago?",
# Edge cases likely to cause hallucination
"Can I get a refund for a digital download I purchased yesterday?",
"What happens if I return a gift without the gift receipt?",
# Questions where context might be incomplete
"Do you offer refunds for international orders?",
"Can I return an item I bought on sale?",
# Adversarial or tricky questions
"If I'm a loyalty member, do I get free return shipping even for electronics?",
]
# Generate RAG responses for all questions
eval_results = []
for q in eval_questions:
result = rag_query(q)
eval_results.append(result)
print(f"Q: {q}")
print(f"A: {result['answer'][:150]}...")
print()
De mix doet ertoe. Sommige van deze vragen hebben duidelijke, directe antwoorden in de documenten, terwijl andere het model vragen om informatie over meerdere opgehaalde stukken te combineren (de vraag over de defecte laptop heeft zowel het algemene 30-dagen retourbeleid als de 90-dagenclausule voor defecte elektronica nodig).
En een paar, zoals de vraag over internationale bestellingen, gaan over dingen die de knowledge base simpelweg niet dekt. Juist bij die laatste zie je het vaakst hallucinatie, omdat het model de neiging heeft behulpzaam te zijn en gaten opvult met aannemelijk klinkende informatie die niet is verankerd in de daadwerkelijke context.
Voor meer over het bouwen en testen van RAG-systemen is onze selectie van Top 30 RAG-vragen voor sollicitatiegesprekken en antwoorden een handig naslagwerk.
De LLM-jury opzetten
Hier wordt het interessant. We bouwen twee aparte jury's:
- Faithfulness-jury: Blijft het antwoord binnen de grenzen van de opgehaalde context?
- Relevantie-jury: Gaat het antwoord daadwerkelijk in op wat er is gevraagd?
def judge_faithfulness(question: str, context: str, answer: str) -> dict:
"""Judge whether the answer is faithful to the retrieved context."""
eval_prompt = f"""
You are an impartial judge evaluating whether an AI assistant's
answer is faithful to the provided context.
Faithfulness means every claim in the answer can be traced back to
information in the context. The answer should not contain information
that isn't supported by or inferable from the context.
Score on a scale of 1 to 5:
1 - The answer contains multiple claims not supported by the context
2 - The answer contains at least one significant unsupported claim
3 - The answer is mostly faithful but includes minor unsupported details
4 - The answer is faithful with only trivial extrapolations
5 - Every claim in the answer is directly supported by the context
Context: {context}
Question: {question}
Answer to evaluate: {answer}
Respond in this exact JSON format: {{"score": <int 1-5>, "reason": "<one paragraph explanation>"}}
"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": eval_prompt}],
temperature=0.0,
response_format={"type": "json_object"}
)
import json
return json.loads(response.choices[0].message.content)
def judge_relevance(question: str, answer: str) -> dict:
"""Judge whether the answer is relevant to the question."""
eval_prompt = f"""
You are an impartial judge evaluating whether an AI assistant's answer
is relevant to the user's question.
Relevance means the answer directly addresses what the user asked.
A relevant answer may acknowledge limitations in available information,
but it should not go off-topic or provide unrelated information.
Score on a scale of 1 to 5:
1 - The answer does not address the question at all
2 - The answer partially addresses the question but misses the main point
3 - The answer addresses the question but includes significant irrelevant content
4 - The answer addresses the question well with minor tangents
5 - The answer directly and completely addresses the question
Question: {question}
Answer to evaluate: {answer}
Respond in this exact JSON format: {{"score": <int 1-5>, "reason": "<one paragraph explanation>"}}
"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": eval_prompt}],
temperature=0.0,
response_format={"type": "json_object"}
)
import json
return json.loads(response.choices[0].message.content)
Een paar ontwerpkeuzes om te benoemen. De temperature staat op 0.0 voor maximale consistentie over runs. We gebruiken response_format={"type": "json_object"} zodat de output altijd te parsen is.
De rubric beschrijft elk scoreniveau concreet in plaats van vage labels als "goed" of "slecht". Zonder die specificiteit heb ik gemerkt dat jury's standaard alles een 3 of 4 geven, wat je niets bruikbaars vertelt.
Let op dat GPT-4o de beoordeling doet terwijl GPT-4o-mini de generatie doet. Het is gebruikelijk dat de jury capabeler is dan de generator, omdat de jury sterke instructievolging nodig heeft om de rubric consistent toe te passen.
Als je hetzelfde model voor beide rollen gebruikt, introduceer je bovendien een gedocumenteerde zelfvoorkeursbias waarbij het model zijn eigen outputs gunstiger beoordeelt.
De evaluatielus draaien
Tijd om alles te draaien en de resultaten te verzamelen.
import json
evaluation_report = []
for result in eval_results:
# Run both judges
faithfulness = judge_faithfulness(
result["question"], result["context"], result["answer"]
)
relevance = judge_relevance(
result["question"], result["answer"]
)
evaluation_report.append({
"question": result["question"],
"answer": result["answer"][:200],
"faithfulness_score": faithfulness["score"],
"faithfulness_reason": faithfulness["reason"],
"relevance_score": relevance["score"],
"relevance_reason": relevance["reason"],
})
print(f"Q: {result['question']}")
print(f" Faithfulness: {faithfulness['score']}/5 | Relevance: {relevance['score']}/5")
print(f" Faith reason: {faithfulness['reason'][:100]}...")
print()
# Summary statistics
faith_scores = [r["faithfulness_score"] for r in evaluation_report]
rel_scores = [r["relevance_score"] for r in evaluation_report]
print(f"Average faithfulness: {sum(faith_scores)/len(faith_scores):.2f}")
print(f"Average relevance: {sum(rel_scores)/len(rel_scores):.2f}")
print(f"Questions with faithfulness < 3: {sum(1 for s in faith_scores if s < 3)}")
Elke vraag triggert twee API-calls naar GPT-4o, één per jury. Voor onze acht testvragen zijn dat zestien evaluatiecalls in totaal, wat te overzien is. In productie met duizenden dagelijkse queries wil je dit batchen, asynchroon draaien en waarschijnlijk slechts een steekproef evalueren in plaats van elk antwoord.
Resultaten analyseren en itereren
De numerieke scores geven je een overzicht, maar de redenering vertelt je wat je daadwerkelijk moet fixen.
# Find problematic responses
print("=== LOW FAITHFULNESS (score < 4) ===")
for r in evaluation_report:
if r["faithfulness_score"] < 4:
print(f"\nQ: {r['question']}")
print(f"Score: {r['faithfulness_score']}")
print(f"Reason: {r['faithfulness_reason']}")
print(f"Answer preview: {r['answer'][:150]}...")
print("\n=== LOW RELEVANCE (score < 4) ===")
for r in evaluation_report:
if r["relevance_score"] < 4:
print(f"\nQ: {r['question']}")
print(f"Score: {r['relevance_score']}")
print(f"Reason: {r['relevance_reason']}")
De vraag over internationale bestellingen en die over uitverkoopartikelen zullen vrijwel zeker laag scoren op betrouwbaarheid, omdat de knowledge base die onderwerpen niet behandelt en het model waarschijnlijk een antwoord heeft geïmproviseerd.
De vraag over de defecte laptop is ook interessant omdat die vraagt om informatie te synthetiseren uit het algemene retourbeleid (30 dagen) en de clausule voor defecte elektronica (90 dagen voor defecte items met bewijs), en afhankelijk van welke stukken zijn opgehaald heeft het model mogelijk wel of niet het volledige plaatje.
Wat je met de resultaten doet, hangt af van wat je ontdekt. Lage betrouwbaarheid bij bepaalde vraagcategorieën wijst meestal op twee dingen: ofwel de retriever haalt de verkeerde stukken op (een retrievalprobleem), of de generator gaat verder dan de context die hij heeft gekregen (een generatieprobleem).
Naar de opgehaalde context naast het antwoord kijken vertelt je met welke van de twee je te maken hebt. Lage relevantiescores betekenen daarentegen meestal dat de systeemprompt fine-tuning nodig heeft, of dat de opgehaalde context zo off-topic is dat het model niets bruikbaars heeft om mee te werken.
Voor de operationele kant van evaluaties draaien als onderdeel van je deployment-pijplijn behandelt de cursus LLMOps Concepts de infrastructuur en workflowpatronen.
DeepEval gebruiken voor gestructureerde evaluatie
Eigen judge-functies schrijven werkt, maar voor productie wil je waarschijnlijk een framework dat de boilerplate afhandelt. DeepEval is een van de meer volwassen opties en wordt geleverd met goed geteste metric-implementaties die de meest gebruikelijke evaluatiecriteria dekken.
from deepeval import evaluate
from deepeval.test_case import LLMTestCase
from deepeval.metrics import FaithfulnessMetric, AnswerRelevancyMetric
# Configure metrics
faithfulness_metric = FaithfulnessMetric(
threshold=0.7,
model="gpt-4o",
include_reason=True
)
relevance_metric = AnswerRelevancyMetric(
threshold=0.7,
model="gpt-4o",
include_reason=True
)
# Create test cases from our RAG results
test_cases = []
for result in eval_results:
test_case = LLMTestCase(
input=result["question"],
actual_output=result["answer"],
retrieval_context=[result["context"]]
)
test_cases.append(test_case)
# Run evaluation
evaluate(
test_cases=test_cases,
metrics=[faithfulness_metric, relevance_metric]
)
Wat je van DeepEval krijgt en wat zelf bouwen tijdrovend is:
- Retry-logica voor wanneer de jury ongeldig JSON terugstuurt (gebeurt vaker dan je denkt)
- Resultaatcaching zodat een evaluatie opnieuw draaien je API-rekening niet verdubbelt
- Pytest-integratie waarmee je LLM-evaluaties direct in je CI/CD-pijplijn kunt hangen.
Elke metric genereert ook een self-explanation, wat betekent dat elke score vergezeld gaat van een schriftelijke onderbouwing van de redenering van de jury die je kunt inspecteren als iets vreemd lijkt.
Voor een diepere blik op de volledige mogelijkheden van DeepEval, zie Evaluate LLMs Effectively Using DeepEval.
Best practices voor LLM als jury
Een LLM-jury getallen laten teruggeven is eenvoudig. Zorgen dat die getallen werkelijk iets nuttigs betekenen voor je team vergt meer zorg dan de meesten vooraf verwachten.
Evaluatiekaders en metrics
Naast DeepEval zijn er inmiddels meerdere frameworks om uit te kiezen, en het landschap groeit door. Hier is een praktische vergelijking op basis van waar ealk past:
|
Framework |
Beste voor |
LLM-juryondersteuning |
RAG-specifieke metrics |
Integratie |
|
DeepEval |
Volledige evaluatiesuite, CI/CD-integratie |
Ja, met zelfverklarende scores |
Faithfulness, contextuele precisie/recall, relevantie |
pytest, LangChain |
|
RAGAS |
Specifiek voor RAG-pijplijn-evaluatie |
Ja |
Faithfulness, antwoordrelevantie, contextprecisie, contextrecall |
LangChain, LlamaIndex |
|
MLflow |
Experimenttracking met evaluatie |
Ja (ingebouwd; kan ook met DeepEval/RAGAS worden gecombineerd) |
Via integraties van derden |
MLflow-ecosysteem |
|
Evidently |
Productiemonitoring en drift-detectie |
Ja, met continue tracking |
Via custom evaluators |
Monitoringdashboards |
|
LangSmith |
LangChain-native tracing en evaluatie |
Ja |
Via custom evaluators |
LangChain |
Voor RAG-systemen dekken vier metrics in de praktijk meestal wat je nodig hebt.
- Faithfulness vertelt je of het gegenereerde antwoord verankerd blijft in de opgehaalde context. Als de score 0,6 is, betekent dat grofweg dat 40% van de claims in het antwoord geen steun vindt in de aangeleverde documenten. Dit is je primaire hallucinatie-detector en naar mijn ervaring de belangrijkste metric om doorlopend te volgen.
- Antwoordrelevantie legt vast of het antwoord daadwerkelijk ingaat op wat er is gevraagd, wat een heel andere kwestie is. Een antwoord kan perfect trouw zijn aan de context (elke claim klopt) en toch volledig de kern van de vraag missen.
- Contextprecisie kijkt naar de retrievalkant: worden de relevante documenten bovenaan de resultaten gerankt? Als het document met het antwoord op positie 5 van 5 wordt opgehaald, werkt je retrieval technisch gezien wel, maar de ranking is slecht, en dat beïnvloedt de generatiekwaliteit omdat modellen meer letten op wat eerst in het contextvenster staat.
- Contextrecall gaat de andere kant op en checkt hoeveel van de informatie die nodig is om de vraag te beantwoorden daadwerkelijk is opgehaald. Lage recall is een probleem in je retrievalinfrastructuur, en geen enkele prompt engineering aan de generatiekant compenseert voor context die er simpelweg niet is.
Je wilt deze samen draaien omdat verschillende combinaties van hoge en lage scores naar verschillende oorzaken wijzen.
- Hoge betrouwbaarheid met lage relevantie betekent dat het model de context accuraat weerspiegelt, maar dat de context zelf niet relevant was voor de vraag.
- Lage betrouwbaarheid met hoge relevantie betekent dat het model de vraag begreep maar buiten de gegeven context ging om te antwoorden.
Elke combinatie vertelt je waar je de oplossing moet zoeken.
Voor hands-on oefening met evaluatietracking in MLflow laat Evaluating LLMs with MLflow de integratie zien.
Best practices voor productie-deployment
Productie-deployment vergt meer nadenken dan evaluaties draaien in een notebook, en de praktische issues die op schaal spelen vallen doorgaans in vijf categorieën die het word zijn om te adresseren voordat je je erop vastlegt.
- Plan rondom bekende biases. LLM-jury's beoordelen consequent langere antwoorden hoger (verbaalheidsbias) en hebben de neiging om in pairwise-vergelijkingen het antwoord dat als eerste verschijnt licht te bevoordelen (positie-bias). De standaardmitigatie voor positie-bias is om elke pairwise-vergelijking in beide volgordes te draaien en het resultaat alleen te tellen wanneer de jury in beide consistent is. Voor verbaalheidsbias kun je expliciete taal aan je rubric toevoegen waarin staat dat beknoptheid acceptabel of zelfs gewenst is, al verkleint dit het effect eerder dan dat het het volledig wegneemt.
- Kalibreer tegen menselijke beoordeling voordat je de scores vertrouwt. Verzamel 50 tot 100 voorbeelden die menselijke reviewers al hebben gescoord in jouw domein, laat ze door de jury gaan en check de overeenstemmingsgraad; alles onder 75% duidt erop dat de rubric meer werk nodig heeft aan de criteriadefinities. Vergeet niet deze kalibratiestap periodiek te herhalen omdat zowel het jurymodel als je applicatie in de tijd evolueren; een overeenstemmingsgraad die zes maanden geleden solide leek, kan zijn gedreven doordat je prompts, data of modelversies veranderden.
- Check consistentie los van nauwkeurigheid. Laat dezelfde inputs twee keer door de jury gaan en vergelijk de outputs, want als een response de ene run een 3 krijgt en de volgende een 5, laat je rubric te veel ruimte voor interpretatie en leest de jury hem elke keer anders. In dat geval leveren binaire pass/fail-evaluaties doorgaans betrouwbaardere resultaten over runs dan 5-puntsschalen, dus overweeg om de numerieke scores voor productiemonitoring te versimpelen naar binair, en de fijnmazige schalen te reserveren voor offline analyse waar incidentele inconsistentie minder kostbaar is.
- Beheer kosten op schaal. Evaluatieprompts zijn aanzienlijk langer dan typische generatieprompts omdat ze de oorspronkelijke vraag, de volledige opgehaalde context, het gegenereerde antwoord en de complete rubric met beoordelingscriteria moeten bevatten. Voor systemen met hoog volume is een kosteneffectief patroon om gedetailleerde LLM-evaluaties op een willekeurige 10% steekproef van queries te draaien, terwijl je eenvoudigere geautomatiseerde checks (lengte, formaatinachtneming, aanwezigheid van trefwoorden) toepast op de overige 90% van het verkeer.
- Houd mensen in de loop. Zelfs nadat de jury goed in productie werkt, richt je een regelmatig reviewproces in waarbij iemand uit het team wekelijks een willekeurige steekproef van de jury-evaluaties bekijkt. Let vooral op responses die de jury als perfect (5/5) scoorde, want juist daar zou een gemist issue de meeste ongepaste zekerheid in een fout antwoord geven.
Voor meer over het operationaliseren van LLM-workflows end-to-end behandelt het traject Associate AI Engineer for Data Scientists het volledige deployment-plaatje.
Conclusie
LLM-as-a-judge vult een praktische behoefte in die noch traditionele geautomatiseerde metrics, noch menselijke review op schaal alleen kunnen dekken. Geautomatiseerde metrics missen wat er écht toe doet voor LLM-applicaties; menselijke evaluatie vangt dat wel, maar kan het volume van een productiesysteem niet bijbenen.
Een LLM-jury ertussen geeft je een manier om continu de outputkwaliteit te monitoren met nuance die simpele metrics niet leveren.
We hebben in deze tutorial een complete pijplijn gebouwd: een RAG-systeem met een kleine knowledge base, testqueries die verschillende faalmodi triggeren, custom evaluatiejury's voor betrouwbaarheid en relevantie, en een framework-gebaseerde aanpak met DeepEval die dichter bij productie ligt.
Als er één les is uit dit alles, dan is het dat de rubric allesbepalend is. Investeer daarom tijd in het schrijven van specifieke, concrete evaluatiecriteria met duidelijke voorbeelden voor elk scoreniveau. Een goed ontworpen rubric met een middelmatig model presteert elke keer beter dan een vage rubric met het meest capabele model.
Als je hiervanuit verder wilt bouwen:
- Building a RAG System with LangChain and FastAPI tilt de RAG-pijplijn van notebook naar productieservice.
- Developing LLM Applications with LangChain behandelt de bredere app-ontwikkelworkflow, inclusief chains, agents en memory.
- Introduction to LLMs in Python is het herbekijken waard als je de basis wilt verstevigen voor je dieper gaat.
- 12 LLM-projecten voor elk niveau geeft je meer projectideeën om mee te oefenen.
- What is Retrieval Augmented Generation (RAG)? behandelt de conceptuele basis van RAG dieper.
LLM als jury: veelgestelde vragen
Wat is LLM-as-a-judge precies?
Kort gezegd: je prompt een capabel model zoals GPT-4 of Claude om te scoren of te classificeren wat een ander model heeft geproduceerd, op basis van een rubric die je in gewoon Engels schrijft. Het zal het hebben van mensen die outputs reviewen niet volledig vervangen, maar als je duizenden responses per dag hebt, is het de enige praktische manier om zicht te houden op kwaliteit zonder een leger annotators in te huren.
Wat kan een LLM-jury zoal evalueren?
In principe alles wat je als criterium onder woorden kunt brengen. Trouw aan brondocumenten, toon, veiligheid, of het antwoord de vraag daadwerkelijk beantwoordt en hallucinatie-detectie zijn goede voorbeelden. Sommige teams doen ook pairwise-vergelijkingen, waarbij de jury kiest welk van twee kandidaat-antwoorden de voorkeur heeft. De flexibiliteit is precies de bedoeling.
Hoe nauwkeurig zijn LLM-jury's vergeleken met mensen?
Ongeveer 80% overeenstemming met menselijke beoordelaars is het getal dat steeds terugkomt in onderzoek, en dat is ruwweg wat twee menselijke annotators onderling ook halen. LLM-jury's hebben wel biases. Ze geven de voorkeur aan langere, meer gedetailleerde antwoorden, ook als een korter antwoord beter zou zijn, dus je moet daar omheen ontwerpen.
Welke frameworks ondersteunen LLM-as-a-judge?
DeepEval is populair omdat het integreert met pytest en evaluaties als unittests behandelt, wat natuurlijk past in CI/CD-workflows. RAGAS is specifiek gebouwd voor evaluatie van RAG-pijplijnen. MLflow heeft onlangs integraties met beide toegevoegd, zodat je metrics uit meerdere frameworks in één trackingsysteem kunt verzamelen. LangSmith en Evidently maken de belangrijkste opties compleet.
Josep is een freelance Data Scientist die zich richt op Europese projecten, met expertise in dataopslag, -verwerking, geavanceerde analyses en impactvolle data storytelling.
Als docent geeft hij Big Data in de masteropleiding aan de Universiteit van Navarra en deelt hij inzichten via artikelen op platforms als Medium, KDNuggets en DataCamp. Josep schrijft ook over Data en Tech in zijn nieuwsbrief Databites (databites.tech).
Hij heeft een bachelor in Engineering Physics van de Polytechnische Universiteit van Catalonië en een master in Intelligent Interactive Systems van de Pompeu Fabra-universiteit.

