Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

AI-agents evalueren en testen

Hoe je de kwaliteit van AI-agents meet: trajectevaluatie, nauwkeurigheid van tool-selectie en beoordeling van het eindresultaat met geautomatiseerde en menselijke evaluatie.

Waarom agent-evaluatie anders is

Een gewone LLM-aanroep evalueer je op de output: klopt het antwoord? Een agent heeft een pad, namelijk de reeks stappen die het nam om tot een antwoord te komen. Een agent kan het juiste antwoord bereiken via het verkeerde pad (inefficient, duur) of het verkeerde antwoord via het juiste pad (logisch maar fout startpunt). Je evalueert daarom beide.

Platforms die alleen de eindoutput scoren, missen het grootste deel van de faalmomenten die middenin de uitvoering ontstaan: een verkeerde tool-keuze, een mislukte retry of een planner die de taak verkeerd opknipt. Evalueer dus elke stap in het traject apart.

info

Drie evaluatieniveaus

  1. Traject: waren de stappen logisch en efficient?
  2. Tool-gebruik: werden de juiste tools op het juiste moment aangeroepen?
  3. Eindresultaat: lost het de gebruikersvraag op?

Testset opbouwen

Begin met het verzamelen van representatieve taken. Voor betrouwbare geaggregeerde metrics is een richtgetal van enkele honderden cases gangbaar, maar begin klein met een handvol scherpe gevallen en breid uit.

interface AgentTestCase {
  id: string;
  user_input: string;
  expected_tools: string[];
  expected_outcome_description: string;
  ground_truth?: string;
  difficulty: "easy" | "medium" | "hard";
}

const testCases: AgentTestCase[] = [
  {
    id: "TC001",
    user_input: "Zoek het meest recente rapport over Q3 verkopen en maak een samenvatting",
    expected_tools: ["search_drive", "read_file"],
    expected_outcome_description: "Samenvatting van het Q3-verkooprapport met kernpunten",
    difficulty: "medium",
  },
  {
    id: "TC002",
    user_input: "Stuur een e-mail naar jan@bedrijf.nl met de agenda van aanstaande maandag",
    expected_tools: ["list_upcoming_events", "send_email"],
    expected_outcome_description: "Bevestiging dat e-mail is verstuurd met agenda-inhoud",
    difficulty: "medium",
  },
];
lightbulb

Begin met je echte foutgevallen

De waardevolste testcases komen uit productie. Verzamel de gesprekken waar je agent eerder de mist in ging en zet ze om in testcases. Zo bouw je een testset die jouw echte faalmodi dekt in plaats van bedachte scenario's.

Tool-gebruik evalueren

interface AgentRun {
  test_case_id: string;
  tool_calls: { name: string; input: Record<string, unknown>; output: string }[];
  final_response: string;
  total_tokens: number;
  duration_ms: number;
}

function evaluateToolUsage(run: AgentRun, testCase: AgentTestCase): ToolEvalResult {
  const usedTools = new Set(run.tool_calls.map(tc => tc.name));
  const expectedTools = new Set(testCase.expected_tools);

  const precision = [...usedTools].filter(t => expectedTools.has(t)).length / usedTools.size;
  const recall = [...expectedTools].filter(t => usedTools.has(t)).length / expectedTools.size;
  const f1 = precision + recall > 0 ? (2 * precision * recall) / (precision + recall) : 0;

  return {
    precision,
    recall,
    f1,
    extra_tools: [...usedTools].filter(t => !expectedTools.has(t)),
    missing_tools: [...expectedTools].filter(t => !usedTools.has(t)),
  };
}

Precision laat zien hoeveel van de aangeroepen tools daadwerkelijk verwacht waren, recall hoeveel van de verwachte tools ook echt zijn aangeroepen. De F1-score combineert beide tot een enkel getal dat je makkelijk over runs heen kunt volgen.

LLM-as-judge voor eindresultaat

async function judgeOutcome(
  userInput: string,
  expectedDescription: string,
  actualResponse: string
): Promise<{ score: number; reasoning: string }> {
  const response = await anthropic.messages.create({
    model: "claude-opus-4-8",
    max_tokens: 500,
    messages: [{
      role: "user",
      content: `Beoordeel de kwaliteit van dit agent-antwoord.

Gebruikersvraag: ${userInput}
Verwacht resultaat: ${expectedDescription}
Feitelijk antwoord: ${actualResponse}

Geef een score van 1-5:
1 = Volledig onjuist of irrelevant
2 = Gedeeltelijk relevant maar mist kern
3 = Correct maar onvolledig
4 = Correct en compleet
5 = Uitstekend, overtreft verwachting

Antwoord als JSON: {"score": <1-5>, "reasoning": "<korte uitleg>"}`,
    }],
  });
  return JSON.parse(extractText(response));
}
warning

Absolute scores driften

Een LLM-as-judge is sterk in relatieve vergelijkingen (is A beter dan B?), maar absolute scores op een schaal van 1 tot 5 zijn minder stabiel en kunnen tussen runs verschuiven. Gebruik waar mogelijk een referentieantwoord en overweeg paarsgewijze vergelijking in plaats van losse cijfers wanneer je twee versies tegen elkaar afzet.

Efficiency-metrics

function calculateEfficiency(run: AgentRun): EfficiencyMetrics {
  return {
    tool_calls_count: run.tool_calls.length,
    unique_tools_used: new Set(run.tool_calls.map(tc => tc.name)).size,
    duplicate_calls: run.tool_calls.length - new Set(
      run.tool_calls.map(tc => `${tc.name}:${JSON.stringify(tc.input)}`)
    ).size,
    total_tokens: run.total_tokens,
    duration_ms: run.duration_ms,
    tokens_per_step: run.total_tokens / run.tool_calls.length,
  };
}

Let vooral op duplicate_calls: identieke tool-aanroepen wijzen op een agent die in een lus zit of zijn eigen werk niet onthoudt. Dat is duur en traag, en vaak een teken dat je system prompt of tool-output beter moet.

Testpipeline automatiseren

async function runEvaluationSuite(
  testCases: AgentTestCase[],
  agent: AgentFunction
): Promise<EvaluationReport> {
  const results = await Promise.all(
    testCases.map(async (tc) => {
      const run = await agent(tc.user_input);
      const toolEval = evaluateToolUsage(run, tc);
      const outcomeScore = await judgeOutcome(
        tc.user_input,
        tc.expected_outcome_description,
        run.final_response
      );
      const efficiency = calculateEfficiency(run);
      return { test_case: tc, run, tool_eval: toolEval, outcome_score: outcomeScore, efficiency };
    })
  );

  return {
    total_cases: testCases.length,
    avg_outcome_score: average(results.map(r => r.outcome_score.score)),
    avg_tool_f1: average(results.map(r => r.tool_eval.f1)),
    avg_duplicate_calls: average(results.map(r => r.efficiency.duplicate_calls)),
    results,
  };
}

Regressietests

Voeg bij elke agent-update een regressietest toe, zodat een verbetering op het ene scenario geen onverwachte achteruitgang op een ander veroorzaakt:

describe("Agent regression tests", () => {
  it("lost een e-mail-plus-agenda-taak op", async () => {
    const result = await agent("Stuur de agenda van morgen naar mijn team");
    expect(result.tool_calls.map(tc => tc.name)).toContain("list_upcoming_events");
    expect(result.tool_calls.map(tc => tc.name)).toContain("send_email");
    expect(result.final_response).toMatch(/verstuurd|verzonden/i);
  });
});

Zo zet je evaluatie in je CI/CD-pipeline

  1. Bewaar je testset (AgentTestCase[]) als versiebeheerde fixture in de repo.
  2. Draai runEvaluationSuite als aparte job bij elke pull request die de agent raakt.
  3. Stel drempelwaarden in, bijvoorbeeld avg_tool_f1 >= 0.8 en avg_outcome_score >= 3.5.
  4. Laat de pipeline falen wanneer een metric onder de drempel zakt, en log het volledige rapport als artefact.
  5. Vergelijk per release de scores met de vorige run om sluipende regressie zichtbaar te maken.
Hoe vaak moet ik evaluatie draaien?

Bij elke wijziging aan de system prompt, tool-definities of modelversie. Automatiseer het in je CI/CD en stel een drempelwaarde in, bijvoorbeeld dat de gemiddelde score niet meer dan 10 procent mag dalen.

Is LLM-as-judge betrouwbaar?

Redelijk betrouwbaar voor relatieve vergelijkingen, dus de vraag of A beter is dan B. Voor absolute scores is het minder stabiel. Kalibreer door menselijke beoordelingen te vergelijken met de scores van de judge op een subset, en gebruik waar mogelijk een referentieantwoord.

Hoe evalueer ik agents met side-effects?

Gebruik een sandbox-omgeving met een aparte database, een mock-e-mailserver of een geisoleerde Drive-map. Evalueer nooit in productie, want je agent kan echte e-mails versturen of bestanden wijzigen.

Wat doe ik met lage scores?

Analyseer de mislukte testcases en bepaal waar het misgaat: tool-selectie, parameterkwaliteit of het eindresultaat. Elk vraagt een andere fix, namelijk betere tool-beschrijvingen, strakkere schema's of een herziene system prompt.

Welk model gebruik ik als judge?

Gebruik een capabel model dat losstaat van het model dat je agent aandrijft, zodat de judge niet zijn eigen output beoordeelt. Een sterk redeneermodel zoals de actuele Opus-generatie werkt goed; controleer in de modeloverzichten welke versie op het moment van bouwen de aanbevolen keuze is.

Hoeveel testcases heb ik nodig?

Begin met enkele scherpe gevallen om je pipeline werkend te krijgen, en breid uit richting enkele honderden voordat je geaggregeerde metrics echt vertrouwt. Met te weinig cases zegt een gemiddelde score weinig.