AI output evalueren waarom hoe Tutorial Python

AI-output evalueren: waarom en hoe? [Tutorial in Python]

Classificaties én teksten beoordelen met LLM’s

Het beoordelen van AI-output is nodig om kwaliteit en betrouwbaarheid te waarborgen.
Of je nu werkt met classificaties of met gegenereerde teksten: zonder evaluatie weet je niet of je model of prompt doet wat je verwacht.

Met evaluatie meet je hoe goed antwoorden aansluiten bij:

  • de oorspronkelijke vraag, of
  • een vooraf bepaalde referentie

Zo leg je een stevige basis voor verbetering en optimalisatie van modellen en prompts.

In deze longread lopen we twee veelvoorkomende evaluatievormen door:

  1. Evalueren van classificaties
  2. Evalueren van gegenereerde teksten

Beide voorbeelden zijn praktisch opgezet en direct toepasbaar.


Setup: OpenAI client

Voor beide evaluaties gebruiken we dezelfde basis: een OpenAI client voor API-communicatie.

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv("api_key.env")

OPENAI_API_KEY = os.getenv("OPEN_AI_API_KEY")

print(f"Length API key: {len(OPENAI_API_KEY)}")
print(f"Last characters: ...{OPENAI_API_KEY[-30:]}")

client = OpenAI(api_key=OPENAI_API_KEY, base_url="https://api.openai.com/v1/")

print(client)
Length API key: 164
Last characters: ...xtvqYjTUfLU8_RN4xfoZMy0nQDfCkA
<openai.OpenAI object at 0x78eddf0e97f0>

Deel 1 – Evalueren van classificaties

Met een LLM kun je teksten automatisch indelen in categorieën, bijvoorbeeld:

  • klantenservice tickets
  • feedback
  • e-mails
  • supportvragen

Maar hoe weet je of de labels ook kloppen?

Wat we doen in dit deel

  1. Een classificatiefunctie maken
  2. Een referentie-dataset opzetten
  3. Voorspellingen doen
  4. De resultaten evalueren

Met een LLM kunnen we bijvoorbeeld teksten classificeren naar onderwerpen.

In dit voorbeeld kijken we naar het classificeren van klantenservice tickets naar onderwerpen.

1.1. Aanmaken functie om te classificeren

In onderstaande code doen we het volgende:

  • Aanmaken van system prompt.
  • Aanmaken van functie om een klantenservice ticket te kunnen labelen/classificeren naar onderwerp.
system_prompt = (
    "You are a strict ticket classifier. "
    "Classify each ticket into exactly one of these labels: ['Billing', 'Shipping', 'Technical']"
    "Respond with only the label, nothing else."
)

def predict_label(text: str) -> str:
    prompt = f"Ticket:\n{text}\n\nLabel:"
    response = client.responses.create(
        model="gpt-4o-mini",
        instructions=system_prompt,
        input=prompt,
        temperature=0,
    )
    return response.output_text

We testen de functie:

ticket = "I can't log in to my account after resetting my password."

predict_label(ticket)
'Technical'

Nu kan het natuurlijk zo zijn dat dit niet voor alle tickets goed gaat, door allerlei redenen.

Als je deze functie in de praktijk toe gaat passen, wil je gevoel hebben bij hoe vaak het een label voorspelt dat je ook verwacht.

Dit kunnen we testen met een referentie-dataset.

1.2. Aanmaken van referentie-dataset

In onderstaande code doen we het volgende:

  • We maken een referentie-dataset aan met een Pandas DataFrame.
  • Daarin 2 kolommen: de ticket-tekst, en het label dat we zouden verwachten.
import pandas as pd

data = [
    ("Invoice not received for last month subscription", "Billing"),
    ("My card was charged twice, please refund", "Billing"),
    ("App crashes when I click export", "Technical"),
    ("Cannot log in after password reset", "Technical"),
    ("Where is my package? tracking link shows pending", "Shipping"),
    ("Delivery arrived damaged, need replacement", "Shipping"),
    ("Refund for the wrong invoice line item", "Billing"),
    ("Exported CSV has weird characters", "Technical"),
    ("Parcel stuck in customs, any update?", "Shipping"),
    ("Payment failed with error 402", "Technical"),
    ("Printer integration stopped working", "Technical"),
    ("Courier missed delivery window again", "Shipping"),
]

df = pd.DataFrame(data, columns=["text", "true_label"])

df
texttrue_label
0Invoice not received for last month subscriptionBilling
1My card was charged twice, please refundBilling
2App crashes when I click exportTechnical
3Cannot log in after password resetTechnical
4Where is my package? tracking link shows pendingShipping
5Delivery arrived damaged, need replacementShipping
6Refund for the wrong invoice line itemBilling
7Exported CSV has weird charactersTechnical
8Parcel stuck in customs, any update?Shipping
9Payment failed with error 402Technical
10Printer integration stopped workingTechnical
11Courier missed delivery window againShipping

1.3. Doen van voorspellingen op referentie-dataset

In onderstaande code doen we het volgende:

  • We gebruiken de eerdere gemaakte functie, en voorspellen voor iedere tekst een label.
  • Dit voergen we toe aan het DataFrame.
  • We bewaren de werkelijke labels in y_true, en de voorspelde labels in y_pred.
y_true = df["true_label"].to_list()
y_pred = [predict_label(ticket_text) for ticket_text, _true_label in data]

df["pred_label"] = y_pred

df
texttrue_labelpred_label
0Invoice not received for last month subscriptionBillingBilling
1My card was charged twice, please refundBillingBilling
2App crashes when I click exportTechnicalTechnical
3Cannot log in after password resetTechnicalTechnical
4Where is my package? tracking link shows pendingShippingShipping
5Delivery arrived damaged, need replacementShippingShipping
6Refund for the wrong invoice line itemBillingBilling
7Exported CSV has weird charactersTechnicalTechnical
8Parcel stuck in customs, any update?ShippingShipping
9Payment failed with error 402TechnicalBilling
10Printer integration stopped workingTechnicalTechnical
11Courier missed delivery window againShippingShipping

1.4. Evalueren van resultaten

Nu kunnen we berekenen hoe goed het model heeft voorspeld.

In onderstaande code doen we het volgende:

  • Importeren functies uit package sklearn, om onder andere de nauwkeurigheid te berekenen.
  • Accuracy: berekent het percentage correcte voorspellingen (y_true vs y_pred).
  • Confusion matrix: toont per label hoeveel voorspellingen correct/fout waren (rijen = werkelijk, kolommen = voorspeld).
  • Classification report: geeft precisie, recall en F1-score per label, plus gemiddelden
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix

labels = ['Billing', 'Shipping', 'Technical']
print("Accuracy:", round(accuracy_score(y_true, y_pred), 3))
print()
print("Confusion matrix (rows=true, cols=pred):")
print(confusion_matrix(y_true, y_pred, labels=labels))
print()
print("Classification report:")
print(classification_report(y_true, y_pred, labels=labels, zero_division=0))
Accuracy: 0.917

Confusion matrix (rows=true, cols=pred):
[[3 0 0]
 [0 4 0]
 [1 0 4]]

Classification report:
              precision    recall  f1-score   support

     Billing       0.75      1.00      0.86         3
    Shipping       1.00      1.00      1.00         4
   Technical       1.00      0.80      0.89         5

    accuracy                           0.92        12
   macro avg       0.92      0.93      0.92        12
weighted avg       0.94      0.92      0.92        12

We kunnen de confusion matrix nog iets beter leesbaar tonen met behulp van onderstaande code.

Je ziet daarin duidelijk waar het goed of niet goed gaat.

df_confusion_matrix = pd.crosstab(
    pd.Series(y_true, name="Actual"),
    pd.Series(y_pred, name="Predicted"),
    dropna=False,
)

df_confusion_matrix
PredictedBillingShippingTechnical
Actual
Billing300
Shipping040
Technical104

Afhankelijk van je toepassing en de resultaten kun je hiermee beoordelen in welke mate het model bruikbaar is.

Als je tot de conclusie komt dat het model niet goed genoeg presteert, kun je bijvoorbeeld proberen je prompts te verbeteren.


Wat leer je hieruit?

  • Waar gaat het model goed?
  • Welke labels worden verwisseld?
  • Is dit “goed genoeg” voor productie?

Zo niet, dan is prompt-iteratie vaak de eerste stap.


Deel 2 – Evalueren van gegenereerde teksten

Bij tekstgeneratie is “goed” subjectiever.
Twee antwoorden kunnen anders geformuleerd zijn, maar inhoudelijk gelijk.

Hier gebruik je een LLM om een andere LLM te beoordelen.


Evalueren teksten

Het beoordelen van AI-output is nodig om kwaliteit en betrouwbaarheid te waarborgen.

Met evaluatie meet je hoe goed antwoorden aansluiten bij de vraag of bij een referentie.

Zo leg je de basis voor verbetering en optimalisatie van je model/prompts.

In dit deel behandelen we het volgende:

  1. Aanmaken van een testcase
  2. Beoordelen van de testcase
  3. Meerdere testcases testen

2.1 Aanmaken van een testcase

Wanneer je een antwoord op een vraag/prompt in menselijke taal hebt, kan dezelfde booschap met verschillende woorden/zinnen toch hetzelfde zijn.

Wanneer je hier een model op wilt testen, of er een tekst met dezelfde betekenis wordt gegenereerd, kun je dit bijvoorbeeld op 2 manieren doen:

  1. Handmatig, als mens.
  2. Geautomatiseerd, waarbij je ook weer een LLM gebruikt.
    • Dit LLM vergelijkt dan de genereerde tekst met een tekst die je zou verwachten.

We bekijken dit vanuit een voorbeeld.

In onderstaande code doen we het volgende:

  • Aanmaken van een voorbeeld testcase:
    • De input is de prompt/vraag.
    • "actual" toont de voorspelling van een LLM.
    • "expected" toont de referentie-tekst.
hr_testcase = {
    "input": "How many vacation days per year do employees receive?",
    "actual": "Employees receive 25 days of paid vacation per calendar year.",
    "expected": "The policy grants 25 annual paid vacation days.",
}

Hier zijn "actual" en "expected" verschillend, maar hebben ze toch dezelfde boodschap.

2.2 Beoordelen van de testcase

Met package deepeval biedt hulpmiddelen om LLM output te evalueren.

Een voorbeeld daarvan is GEval: een manier om gegenereerde teksten te beoordelen met een LLM.

In onderstaande code doen we het volgende:

  • Importeren van diverse onderdelen uit package deepeval.
  • Aanmaken van een evaluatie-metric met het GEval object.
    • Daarin benoemen we een criteria, dit is de test die wordt uigevoerd.
    • We gebruiken een OpenAI model als LLM.
from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams, LLMTestCase
from deepeval.models.llms.openai_model import GPTModel

# 2) Eén GEval-metric hergebruiken (scheelt laadtijd)
geval_correctness = GEval(
    name="Correctness",
    criteria="Judge if ACTUAL conveys the same essential business answer as EXPECTED, regardless of phrasing.",
    evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.EXPECTED_OUTPUT],
    strict_mode=False,
    model=GPTModel(model="gpt-4o-mini"),
)

In onderstaande code doen we het volgende:

  • Aanmaken van een test-case met het LLMTestCase object.
  • Daarin verwijzen we naar de teksten uit de eerdere test-case.
  • We testen de case met de zojuist aangemaakte metric en bekijken het resultaat.
test_case = LLMTestCase(
    input=hr_testcase["input"],
    actual_output=hr_testcase["actual"],
    expected_output=hr_testcase["expected"],
)
result = geval_correctness.measure(test_case)

print("Test passed:", bool(result))
Output()
Test passed: True

We zien dat de test geslaagd is: volgens het model lijken de teksten goed genoeg op elkaar.

2.3 Meerdere testcases testen

In de praktijk zul je meerdere cases testen om een goed oordeel te kunnen vellen.

In onderstaande code doen we het volgende:

  • Aanmaken van een lijst met meerdere testcases.
  • Met een for-loop, elk van de cases testen met de eerder gemaakte metric.
  • Bewaren van de resultaten.
hr_testcases = [
    {
        "input": "How many vacation days per year do employees receive?",
        "actual": "Employees receive 25 days of paid vacation per calendar year.",
        "expected": "The policy grants 25 annual paid vacation days."
    },
    {
        "input": "What is the probation period for new employees?",
        "actual": "The probation period is 3 months from the start date.",
        "expected": "New hires have a 3-month probationary period."
    },
    {
        "input": "Are employees allowed to work from home?",
        "actual": "Yes, employees can work remotely up to two days per week.",
        "expected": "Remote work is permitted for a maximum of two days weekly."
    },
    {
        "input": "Does the company offer parental leave?",
        "actual": "Yes, new parents are entitled to 12 weeks of paid parental leave.",
        "expected": "The handbook provides 12 weeks of paid leave for parents."
    },
    {
        "input": "What benefits are offered for professional development?",
        "actual": "The company provides an annual training budget of €1,000 for each employee.",
        "expected": "Employees receive a yearly allowance of €1,000 for training and courses."
    },
    {
        "input": "When are annual performance reviews conducted?",
        "actual": "Performance reviews take place every January.",
        "expected": "The company conducts annual reviews in January each year."
    },
    {
        "input": "What is the company’s policy on sick leave?",
        "actual": "Employees must notify their manager on the first day of illness and provide a doctor’s note after three days.",
        "expected": "The policy requires immediate reporting of illness and a medical note after three days."
    },
    {
        "input": "Are part-time employees eligible for health insurance?",
        "actual": "Yes, part-time staff working at least 20 hours per week are covered by health insurance.",
        "expected": "Employees working 20+ hours weekly qualify for health insurance benefits."
    },
    {
        "input": "Does the company support flexible working hours?",
        "actual": "Yes, employees may adjust their start and end times within core hours (10 AM–3 PM).",
        "expected": "Flexible schedules are allowed as long as core hours from 10 to 3 are respected."
    },
    {
        "input": "What is the company’s retirement plan contribution?",
        "actual": "Employees working 20+ hours weekly qualify for health insurance benefits.",
        "expected": "The handbook states a 5% employer contribution to the retirement plan."
    },
]

results = []
passed_count = 0
for i, row in enumerate(hr_testcases, start=1):
    test_case = LLMTestCase(
        input=row["input"],
        actual_output=row["actual"],
        expected_output=row["expected"]
    )
    result = geval_correctness.measure(test_case)
    passed_count += bool(result)
    results.append({
        "idx": i,
        "passed": bool(result),
        "input": row["input"],
        "actual": row["actual"],
        "expected": row["expected"],
    })
Output()
Output()
Output()
Output()
Output()
Output()
Output()
Output()
Output()
Output()

We kunnen de bewaarde resultaten bekijken:

results
[{'idx': 1,
  'passed': True,
  'input': 'How many vacation days per year do employees receive?',
  'actual': 'Employees receive 25 days of paid vacation per calendar year.',
  'expected': 'The policy grants 25 annual paid vacation days.'},
 {'idx': 2,
  'passed': True,
  'input': 'What is the probation period for new employees?',
  'actual': 'The probation period is 3 months from the start date.',
  'expected': 'New hires have a 3-month probationary period.'},
 {'idx': 3,
  'passed': True,
  'input': 'Are employees allowed to work from home?',
  'actual': 'Yes, employees can work remotely up to two days per week.',
  'expected': 'Remote work is permitted for a maximum of two days weekly.'},
 {'idx': 4,
  'passed': True,
  'input': 'Does the company offer parental leave?',
  'actual': 'Yes, new parents are entitled to 12 weeks of paid parental leave.',
  'expected': 'The handbook provides 12 weeks of paid leave for parents.'},
 {'idx': 5,
  'passed': True,
  'input': 'What benefits are offered for professional development?',
  'actual': 'The company provides an annual training budget of €1,000 for each employee.',
  'expected': 'Employees receive a yearly allowance of €1,000 for training and courses.'},
 {'idx': 6,
  'passed': True,
  'input': 'When are annual performance reviews conducted?',
  'actual': 'Performance reviews take place every January.',
  'expected': 'The company conducts annual reviews in January each year.'},
 {'idx': 7,
  'passed': True,
  'input': 'What is the company’s policy on sick leave?',
  'actual': 'Employees must notify their manager on the first day of illness and provide a doctor’s note after three days.',
  'expected': 'The policy requires immediate reporting of illness and a medical note after three days.'},
 {'idx': 8,
  'passed': True,
  'input': 'Are part-time employees eligible for health insurance?',
  'actual': 'Yes, part-time staff working at least 20 hours per week are covered by health insurance.',
  'expected': 'Employees working 20+ hours weekly qualify for health insurance benefits.'},
 {'idx': 9,
  'passed': True,
  'input': 'Does the company support flexible working hours?',
  'actual': 'Yes, employees may adjust their start and end times within core hours (10 AM–3 PM).',
  'expected': 'Flexible schedules are allowed as long as core hours from 10 to 3 are respected.'},
 {'idx': 10,
  'passed': True,
  'input': 'What is the company’s retirement plan contribution?',
  'actual': 'Employees working 20+ hours weekly qualify for health insurance benefits.',
  'expected': 'The handbook states a 5% employer contribution to the retirement plan.'}]

En we kunnen wat metrics berekenen en details zichtbaar maken.

In onderstaande code doen we het volgende:

  • Berekenen van de nauwkeurheid (welk percentage is geslaagd voor de test?).
  • Tonen van de gefaalde test(en).
accuracy = passed_count / len(hr_testcases)
print(f"Evaluated {len(hr_testcases)} cases → accuracy: {accuracy:.2%}")

print("Failed tests:")
for result in results:
    if result["passed"] == False:
        print(f"[{result['idx']}] | Input: {result['input']}")
        print(f"Expected: {result['expected']}")
        print(f"Actual: {result['actual']}")
        print()
Evaluated 10 cases → accuracy: 100.00%
Failed tests:

Je ziet dat dit een methode is om relatief snel gegenereerde teksten te kunnen testen met behulp van een LLM.

Samenvatting m.b.t. evalueren van AI output

AI Classificaties evalueren

  • Gebruik een referentie-dataset
  • Vergelijk voorspellingen met bekende labels
  • Meet met accuracy en confusion matrix
  • Verbeter via prompt engineering

AI Teksten evalueren

  • Verwacht semantische gelijkheid, geen letterlijke
  • Gebruik een LLM als beoordelaar
  • Test meerdere cases tegelijk
  • Kijk niet alleen naar “pass/fail”, maar ook waarom

Zo maak je AI-output toetsbaar, uitlegbaar en betrouwbaar.

Wil je AI expert worden?

Tijdens onze 5-daagse AI Opleiding of 7-daagse AI Engineer Opleiding leer je het hele AI spectrum kennen; van klassieke machine learning modellen tot generative AI met o.a. ChatGPT. Je leert programmeren in Python zodat je op uiteenlopende vlakken aan de slag kunt met AI. Of ben je op zoek naar een introductie in AI? Bekijk dan onze AI cursus basis eens. We bieden ook AI consultancy aan op allerlei thema’s.

by: