AI Agent Evaluation in productie: van traces naar testsuites
Automatische vertaling Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.
Een final answer kan aangeven dat een refund is voltooid, terwijl de trace laat zien dat verify_identity nooit is uitgevoerd, issue_refund 17 keer is geretried, of dat de agent succes meldde voordat de database was gewijzigd. Grading op alleen het antwoord verbergt deze failures.
Voor engineers die tool-using agents in productie beheren, is de oplossing om herhaalbare traces om te zetten in afgebakende regression cases: deterministische checks bewaken toolvolgorde, argumenten, loops en invariants; gekalibreerde judges behandelen beslissingen die interpretatie vereisen. Het resultaat is een versioned suite die dezelfde failure vóór de volgende release detecteert.
Zie voor de korte vergelijking van tools Best AI Agent Evaluation Tools.
Waarom agent evals anders zijn
Traditionele LLM evals scoren meestal één input-output-paar: relevantie, faithfulness, correctheid, safety en misschien stijl. Agents voegen planning, tool calls, retries en termination checks toe, waarbij elke stap een nieuwe failure-mogelijkheid vormt.
Neem een refund agent. De transcriptie kan goed eindigen terwijl de trace onjuist is:
lookup_order -> issue_refund -> final_answer
De output-eval slaagt. Een trajectory eval moet falen omdat verify_identity nooit vóór issue_refund is uitgevoerd. Voor tool-using agents zijn answer-only evals smoke tests: ze detecteren volledige uitval en missen vrijwel al het andere.
Er is een tweede probleem: fouten stapelen zich op. Als een workflow 20 vereiste stappen heeft, elke stap onafhankelijk slaagt en elke stap dezelfde betrouwbaarheid van 95% heeft, komt het end-to-end success rate uit op ongeveer 36%:
De agent kan dus solide lijken in geïsoleerde checks en toch bij de meeste volledige runs falen. De breuk zit meestal ergens in het midden. Om die te vinden heb je visibility op componentniveau nodig, niet nog een blik op het antwoord.
Twee onderzoeksteams hebben dit gekwantificeerd.
tau-bench geeft een agent taken voor airline- en retail-customer-service. De agent praat met een gesimuleerde gebruiker, roept APIs aan en moet het domeinbeleid volgen. Na het gesprek controleert de grader of de database de geannoteerde goal state heeft bereikt. Een plausibele transcriptie met de verkeerde rows faalt alsnog.
Onder die grading loste GPT-4o slechts 35,2% van de airline-taken op en iets meer dan 60% van de retail-taken. Het paper introduceerde ook pass^k: voer dezelfde taak k keer uit en tel de taak alleen als pass wanneer de agent in alle k runs slaagt.
Retail, de eenvoudigere split, daalde bij k = 8 tot onder 25%. Bij meer dan driekwart van die taken faalde dezelfde agent minstens één keer wanneer hij dezelfde taak acht keer uitvoerde. Een eval met één run kan die inconsistentie niet blootleggen.
MAST onderzoekt waarom agents falen. De auteurs bouwden een taxonomy met 14 modes op basis van 150 handmatig geannoteerde traces en pasten die vervolgens toe op meer dan 1.600 traces uit 7 populaire multi-agent frameworks. De taxonomy bevat vage role definitions (system design), een agent die negeert wat een andere agent rapporteerde (inter-agent misalignment) en het verklaren van succes zonder het resultaat te controleren (no verification). Deze failures wijzen op prompts, orchestration logic en ontbrekende checks in de harness. Een sterker base model kan geen verification step uitvoeren die nooit is gebouwd. Het evaluatiedoel moet daarom ook de harness rond het model omvatten.
De adoption gap
De State of Agent Engineering-survey van LangChain (1.340 respondenten, uitgevoerd eind 2025) suggereert dat veel teams al over het ruwe materiaal voor betere evals beschikken. Volgens de survey had 89% enige vorm van observability, draaide 52,4% offline evals en draaide 37,3% online evals.
De survey meldt ook dat 57,3% van de respondenten al agents in productie heeft. Op de vraag wat productie blokkeert, noemde 32% quality en 20% latency. Dit is een vendor survey onder zijn respondenten en geen census van agentteams, maar de resultaten laten wel een nuttige kloof zien tussen trace collection en systematische evaluatie.
Teams blijven daardoor in een ongemakkelijke tussenfase hangen: ze kunnen een slechte run achteraf inspecteren en vervolgens toch tweemaal dezelfde failure shippen.
Elke gediagnosticeerde production failure moet een trace, een label, een dataset row en een scorer achterlaten. Een herhaalbare failure hoort in de regression suite.
Kies metrics op basis van failure mode
De juiste metric hangt af van de failure mode, niet van het framework. Een bruikbare indeling heeft drie niveaus:
- Outcome evals beantwoorden of de taak is geslaagd.
- Trajectory evals beantwoorden of het pad geldig, efficiënt en policy-compliant was.
- Component evals beantwoorden welke tool, retriever, sub-agent of decision step is stukgegaan.
Elk niveau kan offline draaien op vaste, replayable cases vóór een release of online op sampled production traces na de response. De sectie over guardrails behandelt deze splitsing verderop in detail. Offline evals kunnen goldens vereisen: opgeslagen cases die een input koppelen aan de outcome, tool invariants en argumenten die een correcte run moet produceren. Online evals geven de voorkeur aan invariants, distributions en async checks die buiten het request path blijven.
| Vraag | Metric family | Offline / online contract | Deterministisch of judge? | Let op |
|---|---|---|---|---|
| Heeft de agent de juiste tools aangeroepen? | Tool correctness: exact, in-order of any-order match | Exact goldens offline; required-tool invariants en anomalies online | Deterministisch | Exact match bestraft geldige alternatieve paden |
| Heeft de agent de juiste inputs gebruikt? | Argument correctness, schema validation, parameter match | Verwachte argumenten offline; schema-, range- en policy-checks online | Beide | De juiste tool met verkeerde argumenten blijft fout |
| Heeft de agent stappen verspild? | Step efficiency, retry count, loop detection, cost en latency | Step- en loop-budgets offline; cost- en latency-drift online | Meestal deterministisch | Hoge task completion kan dure omwegen verbergen |
| Is de taak daadwerkelijk geslaagd? | Task completion, outcome grading, final state diff | Simulator of golden state offline; final state, user signal of async judge online | Judge of state check | Grade waar mogelijk de environment state |
| Behield de agent context over turns heen? | Multi-turn fidelity, role adherence, conversation completeness | Scripted long-horizon cases offline; sampled long sessions online | Judge | Single-turn tests zeggen niets over turn 14 |
| Stopte de agent op het juiste moment? | Termination correctness, premature success, endless work | Scenario-tests offline; loop-, timeout- en false-success monitors online | Beide | ”Done” kan een gehallucineerde state zijn |
| Interpreteerde de agent tool results correct? | Tool-result understanding, downstream state checks | Adversarial tool outputs offline; downstream state checks en sampled review online | Beide | Grade de downstream state, niet de exit code van tool |
Begin met deterministische metrics. Ze zijn goedkoop, snel en driften niet.
Tool-call correctness
Tool correctness vergelijkt de aangeroepen tools met de verwachte tools. Kies de strictness bewust:
- Exact match: de sequence moet exact overeenkomen. Gebruik dit wanneer order policy is, bijvoorbeeld
lookup_order -> verify_identity -> issue_refund. - In-order match: vereiste tools moeten in de juiste relatieve volgorde voorkomen, maar extra onschadelijke calls zijn toegestaan.
- Any-order match: vereiste tools moeten voorkomen, maar de order mag variëren.
Een kleine lokale scorer is voldoende om te beginnen:
from collections import Counter
def tool_correctness(called: list[str], expected: list[str], mode: str = "in_order") -> float:
if not expected:
return 1.0
if mode == "exact":
return float(called == expected)
if mode == "any_order":
matched = sum((Counter(called) & Counter(expected)).values())
return matched / len(expected)
rows = [[0] * (len(expected) + 1) for _ in range(len(called) + 1)]
for i, tool in enumerate(called):
for j, wanted in enumerate(expected):
if tool == wanted:
rows[i + 1][j + 1] = rows[i][j] + 1
else:
rows[i + 1][j + 1] = max(rows[i][j + 1], rows[i + 1][j])
return rows[-1][-1] / len(expected)
called = ["lookup_order", "check_refund_policy", "issue_refund"]
expected = ["lookup_order", "verify_identity", "issue_refund"]
print(round(tool_correctness(called, expected, "exact"), 3)) # 0.0
print(round(tool_correctness(called, expected, "in_order"), 3)) # 0.667
De in_order-score is longest-common-subsequence recall: welk deel van de vereiste sequence in de juiste volgorde behouden bleef. Let op wat deze score negeert. Junk calls verlagen de score niet, waardoor een agent hier 1.0 kan scoren en toch tweemaal zoveel calls kan doen als nodig. Wanneer extra calls geld kosten of state muteren, houd dan ook precision bij (gematchte vereiste calls gedeeld door het totale aantal calls) en interpreteer beide samen. Recall detecteert de ontbrekende stap; precision detecteert het rondzwerven.
DeepEval’s Tool Correctness metric biedt dezelfde opties via should_consider_ordering en should_exact_match.
Argument correctness
De juiste tool aanroepen met de verkeerde argumenten is vaak erger dan de verkeerde tool aanroepen, omdat de trace er normaal uitziet.
Valideer in eenvoudige gevallen JSON Schema en exacte waarden. Sla voor semantische cases de verwachte argumenten op en grade de deltas:
{
"trace_id": "tr_2417",
"input": "Reschedule order A-100 for next Friday.",
"expected_tools": ["lookup_order", "reschedule_delivery"],
"expected_arguments": {
"reschedule_delivery": {
"order_id": "A-100",
"date": "2026-06-19"
}
}
}
Een tool-name metric kan 2026-06-17 niet detecteren wanneer het beleid 2026-06-19 vereist. De dataset moet dus ook argumenten opslaan.
De bijbehorende score is parameter-match: het aandeel van de verwachte (tool, key, value)-triples dat de agent correct heeft uitgevoerd.
def argument_correctness(called_args: dict, expected_args: dict) -> float:
total = matched = 0
for tool, params in expected_args.items():
for key, want in params.items():
total += 1
if called_args.get(tool, {}).get(key) == want:
matched += 1
return matched / total if total else 1.0
Exacte gelijkheid is correct voor IDs, enums en dates die al naar één format zijn genormaliseerd. Voor vrije tekst, floats en dates in het format dat het model produceerde is ze ongeschikt, omdat == een correct antwoord als fout markeert. Grade zulke velden op hun eigen manier: een normalized string match, een date parse of een numeric tolerance. De metric blijft hetzelfde; alleen de comparator per field verandert.
Efficiency, loops en dead ends
Een agent die de taak voltooit na vijf redundante tool calls laat nog steeds een planningprobleem zien en kost meer om uit te voeren.
Begin met deze goedkope signalen:
- Redundant-call rate: identieke tool calls met identieke argumenten die meer dan twee keer worden herhaald.
- Trace-shape anomalies: plotselinge spikes in depth, tool-call count, token count, latency of cost.
- Path convergence: hoe dicht de run bij het kortst bekende geldige pad voor de taak ligt.
- Termination correctness: of de agent te vroeg stopte, na succes bleef doorwerken of succes verklaarde zonder de vereiste state change.
- Plan adherence: als de agent vóór het handelen een plan schrijft, controleer dan of de trace dat plan volgde. Een goed plan dat wordt genegeerd en een slecht plan dat perfect wordt gevolgd falen beide, om tegenovergestelde redenen; het verschil tussen plan en trace laat zien welke situatie zich voordoet.
Voer deze checks waar mogelijk vóór een judge uit. Een loop detector bestaat uit enkele regels over de trace. Daarvoor is geen model nodig.
Task completion en outcome grading
Bij grading op de outcome is de vraag: “heeft de gebruiker gekregen waar die om vroeg?”
Twee patronen werken het best:
- Referenceless task-completion judging: haal het doel uit de input en judge of de trace plus het final answer dat doel heeft bereikt. Dit werkt online omdat production traffic zelden golden outputs heeft.
- Environment-state grading: vergelijk de uiteindelijke database rows, files, tickets, bookings of records met een geannoteerde goal state. Dit is robuuster dan transcript matching, omdat agents geldige paden kunnen vinden die je niet vooraf hebt uitgeschreven.
De tweede optie is beter wanneer je die kunt bouwen. De final state is het contract. De transcriptie is alleen evidence.
Twee caveats houden dit eerlijk. Een 2025 audit van agentic benchmarks stelde vast dat tau-bench sommige taken uitsluitend op basis van de database state grade. Bij sommige taken vereist de geannoteerde outcome geen state change en geen specifieke tekst. Een agent die niets doet kan dan slagen: 38% op de airline-split en 6,0% op retail, bij elke k. Anthropic rapporteerde een Opus 4.5-run die een booking-taak in tau2-bench, de opvolgende benchmark, “failed”. De agent had een policy loophole gevonden die voor de gebruiker feitelijk de betere outcome was. State grading is beter dan transcript matching, maar de goal state is nog steeds een annotation en annotations bevatten fouten. Audit daarom ook cases die te gemakkelijk slagen, niet alleen cases die falen.
Component evals
Outcome- en trajectory metrics vertellen je dat de run is gefaald en ongeveer waar. Component evals scoren één span: was de retrieved chunk relevant, retourneerde de sub-agent het schema dat zijn caller verwachtte, kon de eigen response van de tool worden geparsed? Koppel de score aan de span in plaats van aan de run, zodat “welke tool degradeerde deze week?” een query is en geen nieuwe run vereist.
Drie checks dekken het grootste deel:
- Per-span scoring: voer de metric uit die bij het type span past. Retrieval spans krijgen recall en precision tegen de geannoteerde chunk, sub-agent spans krijgen schema validation plus hun eigen tool-correctness score, tool spans krijgen error rate en latency.
- Tool-result interpretation: geef de agent een correcte maar ongemakkelijke tool output (een lege lijst, een partial match, een stale timestamp) en controleer wat hij daarna doet. Een tool kan correct zijn terwijl de agent de output verkeerd leest; die failure wordt pas twee stappen later zichtbaar.
- Failure attribution: de zichtbare failure ligt meestal downstream van de echte failure. Attribueer aan de vroegste span waarvan de output al verkeerd was, niet aan de stap die de error opwierp.
Ook hier komt de compounding math uit het begin van pas. Als 20 stappen er afzonderlijk goed uitzien, kan de run toch meestal falen. Per-span pass rates laten zien welke stap op 95% draait en welke op 70%.
De trace-to-eval flywheel
Ontgin production failures voordat je extra eval cases bedenkt.
De loop:
- Leg de volledige trace vast.
- Label wat is gefaald.
- Groepeer vergelijkbare failures.
- Behoud één representatieve golden per cluster.
- Versioneer de dataset.
- Voer deze uit in CI.
- Blijf sampled production traces online scoren.
De bijbehorende repository trace2evals implementeert de volledige loop voor een defecte support agent. De repository legt OpenTelemetry GenAI spans vast, detecteert failures met deterministische rules, dedupliceert cases tot een versioned golden dataset en draait elke golden opnieuw in CI. De standaard backend vervangt het model door deterministische rules die de beslissingen van de buggy agent opnieuw uitvoeren, zodat make demo de volledige loop offline reproduceert zonder API key. Voer uv sync --extra live uit en stel een API key in; dezelfde commands sturen dan een echt model aan.
Ontgin failures met error analysis
Hamel Husain en Shreya Shankar beschrijven voor precies deze stap een error-analysis-workflow; Hamels field guide werkt die uit. De eerste twee stappen lenen hun namen uit kwalitatief onderzoek, maar de methode is eenvoudig: lees traces, maak notities en benoem patronen.
- Open coding: lees 30 tot 50 echte traces en maak vrije notities over wat er misging.
- Axial coding: cluster die notities in 5 of 6 benoemde failure categories.
- Label alles aan de hand van de taxonomy.
- Bouw metrics voor de grootste buckets.
Begin niet met labels zoals reasoning_issue of tool_problem. Die zijn te vaag om te testen. Gebruik labels zoals missing_identity_verification, date_argument_mismatch, retried_same_tool_after_429 of stopped_before_database_update. Zo’n specifiek label vertelt je precies wat de regression test moet assert.
Deduplicate voordat je promoveert
De trace-mining-loop heeft een valkuil: elke slechte trace voor altijd toevoegen. Zo ontstaat een dataset die groot, duur en smal is. De suite slaagt dan op near-duplicates uit maart, terwijl de nieuwe vorm van dezelfde bug in juni wordt gemist.
Groepeer eerst. Promoveer één representatieve golden per cluster. Sla de gerelateerde trace IDs op in metadata, zodat een reviewer het production evidence later kan inspecteren.
Als een failure cluster na een fix terugkeert, heeft de regression case niet gegeneraliseerd. Cluster opnieuw en verbreed de golden in plaats van 15 point examples toe te voegen.
Versioneer de dataset
Versioneer datasets zoals je prompts en code versioneert. Wanneer iets betekenisvol verandert (model, prompt, tool schema, judge prompt of app behavior), wil je dezelfde dataset version vóór en na de wijziging draaien.
De CI gate moet het volgende pinnen:
- dataset version
- app version
- prompt version
- judge model
- judge prompt
- evaluator code version
Als een van deze onderdelen verandert, wordt je before/after comparison onduidelijk. Een goldens-v3.json-bestand in git volstaat op kleine schaal. Tool-native snapshots in Langfuse, Phoenix, Braintrust of LangSmith helpen zodra de dataset collaborative wordt.
Gate CI
Een falende metric moet leiden tot een falende build, anders is de eval suite slechts een dashboard dat niemand leest.
De test moet de huidige agent opnieuw uitvoeren met de golden input. De test mag niet alleen de oude failed trace replayen (schets; de uitvoerbare versie staat in de bijbehorende repository):
@pytest.mark.parametrize("golden", GOLDENS, ids=[item["id"] for item in GOLDENS])
def test_agent_regression(golden: dict) -> None:
answer, fresh_trace = run_agent_and_capture_trace(golden["input"])
refired = set(flag_failures(fresh_trace)) & set(golden["failure_modes"])
assert not refired, f"failure mode regressed: {sorted(refired)}"
assert tool_correctness(
called=[call["name"] for call in fresh_trace["tool_calls"]],
expected=golden["expected_tools"],
mode=golden.get("tool_match", "in_order"),
) >= golden.get("tool_threshold", 1.0)
Dit onderscheid is eenvoudig verkeerd te implementeren. De taak van de dataset is om te detecteren dat de volgende versie van de agent dezelfde oude failure herhaalt, niet om de failure zelf te archiveren.
Kalibreer de judge voordat je erop vertrouwt
LLM-as-judge helpt. Het is ook eenvoudig om jezelf ermee voor de gek te houden.
G-Eval evalueert drie meta-evaluation benchmarks. Dat zijn SummEval, opgebouwd uit nieuws-samenvattingen van CNN/DailyMail; Topical-Chat, een knowledge-grounded dialogue benchmark; en QAGS, dat factual consistency test op samenvattingen van CNN/DailyMail en XSum. Met GPT-4 als backbone bereikte G-Eval-4 een Spearman-correlatie van 0,514 met human judgments op SummEval. De scoring function weegt rating levels op basis van token probability ().
Het paper schatte de token probabilities van GPT-4 door 20 keer te samplen, omdat het model deze in het experiment niet beschikbaar stelde. Een hosted model biedt mogelijk geen bruikbare logprobs. Behoud daarom de rubric, maar suggereer niet dat je de probability weighting uit het paper hebt gereproduceerd. Deze resultaten vergelijken het protocol uit het paper met de NLG-baselines op deze benchmarks. Ze ondersteunen het testen van een expliciete rubric judge, maar geen algemene vervanging van automatische metrics of een production-agent trajectory benchmark.
MT-Bench liet zien dat GPT-4 ongeveer even vaak met human preferences overeenkwam als mensen onderling. Dat resultaat hielp om LLM judging mainstream te maken. Later onderzoek bracht position-, length- en self-preference-biases aan het licht. Judge scores kunnen ook verschuiven wanneer de prompt of modelversie verandert.
JudgeBench bouwde response pairs waarin één antwoord objectief fout is op het gebied van verifiable knowledge, reasoning, math en code. Met een eenvoudige judge prompt scoorde GPT-4o 50,9%, nauwelijks beter dan een coin flip; de sterkere Arena-Hard prompt uit het paper bracht hetzelfde model slechts op 56,6%. Het model verwisselen onder die sterkere prompt maakt meer verschil: Claude 3.5 Sonnet, de beste general-purpose judge in de test, bereikte 64,3%, en o3-mini met hoge reasoning effort 80,9%. Confident maar incorrect answers blijven moeilijk voor een judge die niet reasoned voordat hij grade.
Behandel de judge als een meetinstrument: kalibreer hem tegen human labels voordat hij iets gradeert en controleer opnieuw wanneer het judge model of de prompt verandert.
Wanneer een judge nodig is, maak de verdict structured. Schema-Guided Reasoning (SGR) geeft de verdict een schema voor de output shape en inspectability. Structured Outputs of constrained decoding kan object shape, required fields en value constraints afdwingen voor fields zoals evidence, passed_criteria, failed_criteria, failure_mode en score.
Zet evidence fields vóór de score als dat het record eenvoudiger inspecteerbaar maakt. De field order is presentatie, geen garantie voor reasoning. Een schema-valid verdict kan nog steeds unsupported evidence of een unreliable score bevatten. Gebruik calibration tegen human labels, deterministic validators en transcript review om judge reliability te testen. CI kan een stabiel JSON object diffen, maar dat controleert inspectability en shape; het bewijst niet dat rubric stages zijn gevolgd.
Een structured verdict kan ook de cost curve veranderen. Behandel een goedkoper model als kandidaat, niet als automatische vervanging. Laat het model dezelfde human-labeled calibration set verwerken. Vergelijk agreement, false-pass rate en false-fail rate met de grotere judge. Gebruik het alleen voor routine cases als het de thresholds van je applicatie haalt. Behoud de grotere judge voor disagreements, high-risk cases en calibration runs.
Checklist voor judge hygiene:
- Geef waar mogelijk de voorkeur aan binary pass/fail. Schalen met vijf punten nodigen uit tot schijnprecisie.
- Label 30 tot 50 trajectories met de hand voordat je de definitieve rubric schrijft.
- Meet judge-human agreement met Cohen’s kappa, een confusion matrix en positive/negative recall. Een judge die altijd “pass” zegt, heeft geen bruikbare discriminatie; kappa kan nul of undefined zijn wanneer de noemer van de expected agreement nul is. Stel expliciet beleid op voor undefined kappa voordat je deze metric als deployment gate gebruikt.
- Decompose coarse criteria. “Heeft de agent identity geverifieerd vóór de refund-tool call?” is beter dan “Was de trajectory goed?”
- Emit de verdict via een SGR schema met evidence, failed criteria, failure mode en score.
- Gebruik waar mogelijk een judge uit een andere model family dan de generator.
- Randomize de pairwise order en average beide richtingen.
- Penalize unsupported length in de rubric. Een langer antwoord is niet automatisch beter.
- Pin het judge model, de prompt, dataset, schema en app version.
- Recalibrate na wijzigingen aan model, prompt, tool, policy of schema.
Gebruik voor high-stakes scores een kleine jury in plaats van één grote judge. PoLL testte een panel kleinere judges uit disjuncte model families en poolde hun verdicts. Over zes datasets volgde het panel human judgments beter dan één GPT-4 judge. Het vermeed ook de self-preference bias van één judge en kostte meer dan zeven keer minder. Behoud human review voor beslissingen die geld, toegang, safety of compliance beïnvloeden.
Als een judge op jouw taak een kappa van 0,55 met mensen heeft, gebruik hem dan niet om deploys te blokkeren. Gebruik hem om review queues te sorteren. Als de score rond 0,75 ligt en de failure cost gematigd is, is een CI gate veel beter te verdedigen.
Guardrails blokkeren inline; online evals observeren achteraf
Mensen halen deze twee door elkaar omdat beide scores produceren. Het verschil zit in de plaatsing: inline in het request path, vóór release of na de response.
Guardrails draaien inline. Ze zijn snel en zichtbaar voor de gebruiker. Een guardrail kan een tool call blokkeren, PII redacteren, prompt injection afwijzen of een retry afdwingen voordat de response je systeem verlaat. Een false positive is een production bug. Een false negative is stiller en erger, omdat niets in het request path dit meldt. Schema-, range- en policy-checks zijn deterministisch. Injection- en PII-detectie zijn classifiers; behandel misses daarom als te verwachten en laat een async eval controleren wat ze doorlaten.
Offline evals draaien vóór release. Ze zijn reproduceerbaar. Ze gaten prompts, models, tools, retrievers en policies tegen een vaste dataset.
Online evals draaien na de response, meestal op sampled traffic. Ze kunnen tragere LLM judges gebruiken omdat ze niet in het latency path staan. Hun taak is drift detecteren, nieuwe failure clusters vinden en de volgende offline dataset voeden.
Een verkeerde plaatsing doet in beide gevallen pijn:
- Een judge in het request path voegt latency en een nieuwe bron van flakiness toe.
- Een guardrail die tot async scoring wordt gedegradeerd, laat policy violations gebruikers bereiken.
Score voor high-volume systemen een kleine sample met een sterkere judge en een bredere sample met goedkopere classifiers. Alert op clusters en confidence bounds, niet op één noisy point estimate.
Tooling-keuzes
Geen enkele tool beheert de volledige loop. Vergelijk een trace/dataset store en een CI/eval runner afzonderlijk; één product kan beide dekken, maar je hoeft ze niet bij dezelfde vendor af te nemen.
Dit is een author snapshot, gecontroleerd op 2026-08-16. Elke link verwijst naar de actuele documentatie die ik voor de capability claim heb gebruikt. Plans, licenses, API keys, provider access en infrastructure requirements blijven van toepassing.
| Tool | Kies deze wanneer… | Gecontroleerde capability en voorwaarde |
|---|---|---|
| DeepEval | Python en pytest de CI gate moeten vormen. | deepeval test run voert eval test files uit en falende metrics laten de build falen. Voor het markeren van een officiële Confident AI baseline is CONFIDENT_API_KEY vereist. |
| Inspect AI | Je safety-, frontier- of sandboxed agent-tasks nodig hebt. | inspect eval en de Python API voeren tasks uit; limits, agents, sandboxes en model-provider access worden afzonderlijk geconfigureerd. Het is een eval runner, geen production trace store. |
| Phoenix | Je self-hosted tracing en evals nodig hebt, met data in je eigen infrastructuur. | Phoenix documenteert gratis self-hosting zonder feature limitations, plus deterministic en LLM evaluations. Je beheert de deployment zelf. |
| Langfuse | Je een open-source trace-, dataset- en experiment-workflow wilt. | De core is self-hostable; low-scale Docker Compose biedt geen high availability, scaling of backups, terwijl sommige add-ons een license vereisen. De CI experiment action kan een dataset version pinnen en falen bij regression. |
| LangSmith | Je LangChain/LangGraph al gebruikt en de platform boundary accepteert. | Cloud-, hybrid- en self-hosted modes bestaan; hybrid en self-hosted deployment zijn Enterprise-opties. Dataset creation en eval workflows blijven gekoppeld aan de geselecteerde deployment. |
| Braintrust | Managed PR feedback en vergelijkbare experiment snapshots belangrijker zijn dan self-hosting. | De CI/CD-documentatie toont een GitHub Action die resultaten naar een pull request post; CI heeft een BRAINTRUST_API_KEY en de managed service nodig. |
| Promptfoo | Prompt- of red-team regressions vóór deployment moeten draaien. | De CI-documentatie behandelt CLI- en GitHub Action-paden; de action heeft een config, GitHub token en provider secrets nodig wanneer de geselecteerde provider dat vereist. Het is geen trace store. |
De trade-off notes beschrijven waar kosten vandaan komen, niet wat de prijs is. Pricing pages veranderen en vendors tellen verschillende zaken: traces, observations, spans, scores, users, retention of processed data. Controleer live pricing opnieuw voordat je commit.
Aanbevelingen per constraint:
- Kies Phoenix wanneer self-hosting, privacy en OTel-compatible tracing harde requirements zijn en je team de deployment kan beheren.
- Kies Langfuse wanneer je ook dataset versioning en experiments nodig hebt en je de storage stack kunt beheren of de vereiste add-ons kunt aanschaffen.
- Kies DeepEval wanneer Python/pytest CI pass-fail de primaire contract is.
- Kies Inspect AI wanneer het belangrijkste werk safety- of frontier-agent evaluation in configureerbare sandboxes is.
- Kies LangSmith wanneer LangChain/LangGraph integration zwaarder weegt dan de Enterprise requirement voor hybrid of self-hosted deployment.
- Kies Braintrust wanneer managed pull-request feedback en experiment comparison een API-key-backed service rechtvaardigen.
- Kies Promptfoo wanneer prompt- of red-team checks de belangrijkste regression surface zijn en een trace store buiten scope valt.
Tool choice is secundair. Als production failures geen test cases worden, betaal je vooral voor trace storage.
Praktische rollout-checklist
Bouw eerst de evidence pipeline voordat je de metric stack uitbreidt. Begin met bepalen waar de examples vandaan komen.
-
Verzamel eerst historical runs. Als de agent al bestaat, haal dan traces, support tickets, bug reports, thumbs-down sessions, manual QA transcripts en dogfooding notes op voordat je de implementatie wijzigt. Als de agent nog niet bestaat, log dan vanaf dag één elke prototype- en manual test-run.
-
Instrumenteer de trace shape. Leg messages, tool calls, argumenten, tool outputs, errors, token counts, latency, cost, user feedback, app version, prompt version, model version, tool schema version en de final environment state vast. Gebruik OpenTelemetry GenAI conventions of OpenInference-style spans als je portability wilt. Gebruik Langfuse, LangSmith, Phoenix of Braintrust als je direct een trace UI en dataset workflow wilt.
-
Maak seed cases van echte failures. Lees de traces voordat je ze met een model samenvat. Sla voor elke bruikbare failure de input, source trace ID, expected state, expected tool invariants, failure mode, severity en reviewer note op. Langfuse kan dataset items koppelen aan production traces; LangSmith kan datasets maken uit traced runs. Behoud de source link zodat de case auditable blijft.
-
Genereer cold-start cases als er geen history is. Vraag een LLM om tasks op te stellen uit product requirements, policies, tool schemas, state machines en support macros. Dek happy paths en failures af, zoals verkeerde permissions, ontbrekende identity checks, stale tool results, ambigue dates, retries na rate limits en tegenstrijdige tool output.
-
Vertrouw synthetic cases pas na human review. Synthetic examples zijn nuttig voor coverage, niet voor truth. Markeer ze met
source: syntheticen vereist dat een reviewer de expected outcome goedkeurt. Draai waar mogelijk een bekende good reference path en gebruik verschillende model families voor het genereren van de case en het judgen van het resultaat. -
Bouw een kleine balanced dataset. Neem successes, failures, refusals, boundary cases, long-turn cases, policy-sensitive cases en geldige alternate paths op. Maak de golden niet “de exacte oude transcriptie”. Sla op wat een golden hierboven opslaat, plus de failure mode waardoor de case in de suite terechtkwam.
-
Voeg eerst deterministische checks toe. Required tool order wanneer order policy is, required arguments, schema validation, final-state diffs, loop limits, token- en latency ceilings en task-specific invariants moeten vóór elke judge draaien.
-
Voeg één SGR-shaped judge toe. Gebruik die alleen voor het deel dat interpretatie vereist. Kalibreer hem tegen human labels. Als de judge goede en slechte examples niet van elkaar kan onderscheiden op de calibration set, verbeter dan eerst de rubric voordat je hem aan CI koppelt.
-
Verbind de loop. Draai de kleine offline suite in CI, de grotere suite vóór release, score sampled production traffic online en promoveer terugkerende online failure clusters naar de offline dataset.
Je eerste eval suite zal op saaie manieren verkeerd zijn. Ship hem toch. Een suite die je elke dag draait is eenvoudiger te verbeteren dan een perfect design document dat nooit een slechte PR blokkeert.
References
- trace2evals companion demo repository
- LangChain - State of Agent Engineering
- Anthropic - Demystifying Evals for AI Agents
- Hamel Husain - A Field Guide to Rapidly Improving AI Products
- OpenTelemetry semantic conventions for generative AI systems
- DeepEval Tool Correctness
- DeepEval Task Completion
- DeepEval unit testing in CI/CD
- Inspect running evals
- OpenAI Structured model outputs
- Schema-Guided Reasoning: vLLM, xgrammar, and Pydantic
- Phoenix self-hosting
- Langfuse datasets and versioning
- Langfuse experiments in CI/CD
- LangSmith platform setup
- LangSmith - Create and manage datasets programmatically
- Braintrust - Create experiments
- Phoenix LLM evals
- Promptfoo GitHub Action integration
- tau-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains
- Why Do Multi-Agent LLM Systems Fail?
- Establishing Best Practices for Building Rigorous Agentic Benchmarks
- G-Eval: NLG Evaluation using GPT-4 with Better Human Alignment
- Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena
- Replacing Judges with Juries: Evaluating LLM Generations with a Panel of Diverse Models
- JudgeBench: A Benchmark for Evaluating LLM-based Judges
- Self-Preference Bias in LLM-as-a-Judge