0Pricing
AI Prompt Engineering · Lektion

Assertion-basiertes Prompt-Testing

Ausgaben mit contains(), regulären Ausdrücken, JSON-Schema und LLM-as-judge prüfen.

Assertion-basiertes Prompt-Testing ist eine kostenlose AI Prompt Engineering-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des AI Prompt Engineering-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der AI Prompt Engineering-Kurs umfasst insgesamt 4 Lektionen.

Assertions für LLM-Ausgaben

Assertion-basiertes Testen wendet auf LLMs dasselbe Prinzip an wie beim Unit-Testing: Formulieren Sie explizite Aussagen darüber, was die Ausgabe enthalten muss oder nicht enthalten darf, und lassen Sie den Test sofort fehlschlagen, wenn diese Aussage verletzt wird.

Im Gegensatz zu Unit-Tests mit deterministischen Funktionen befassen sich LLM-Assertions mit probabilistischen Textausgaben – daher sind flexiblere Assertion-Typen erforderlich: contains, matches_schema, satisfies_regex, llm_judge_score_above.

Grundlegende Assertions: contains und not_contains

Die einfachsten Assertions prüfen, ob bestimmte Schlüsselwörter vorhanden sind oder fehlen. Das eignet sich gut für Klassifizierungsaufgaben, strukturierte Ausgaben und Sicherheitsprüfungen.

import openai
client = openai.OpenAI(api_key='sk-...')

def call_prompt(system, user, temperature=0):
    resp = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': system},
            {'role': 'user', 'content': user}
        ],
        temperature=temperature
    )
    return resp.choices[0].message.content

# Keyword presence assertion
def assert_contains(output, keyword, case_sensitive=False):
    text = output if case_sensitive else output.lower()
    kw = keyword if case_sensitive else keyword.lower()
    assert kw in text, f'Expected "{keyword}" in output, got: {output[:100]}'

# Keyword absence assertion
def assert_not_contains(output, forbidden, case_sensitive=False):
    text = output if case_sensitive else output.lower()
    kw = forbidden if case_sensitive else forbidden.lower()
    assert kw not in text, f'Forbidden "{forbidden}" found in output: {output[:100]}'

Validierung von JSON-Schemas

Wenn Ihr Prompt strukturiertes JSON zurückgeben soll, validieren Sie die Ausgabe anhand eines Schemas. Ein Fehler bei der Schema-Validierung bedeutet, dass der Prompt ein Formatproblem hat — entweder hat das Modell zusätzlichen Text ausgegeben oder die JSON-Struktur ist falsch.

import json
from jsonschema import validate, ValidationError

PRODUCT_SCHEMA = {
    'type': 'object',
    'properties': {
        'name': {'type': 'string'},
        'price': {'type': 'number', 'minimum': 0},
        'available': {'type': 'boolean'}
    },
    'required': ['name', 'price', 'available'],
    'additionalProperties': False
}

def assert_valid_json_schema(output, schema):
    try:
        data = json.loads(output.strip())
    except json.JSONDecodeError as e:
        raise AssertionError(f'Output is not valid JSON: {e}\nOutput: {output[:200]}')
    try:
        validate(instance=data, schema=schema)
    except ValidationError as e:
        raise AssertionError(f'JSON does not match schema: {e.message}\nOutput: {output[:200]}')
    return data

# Test
output = call_prompt(
    'Extract product info as JSON: {"name": ..., "price": ..., "available": ...}',
    'Widget Pro costs $49.99 and is in stock.'
)
product = assert_valid_json_schema(output, PRODUCT_SCHEMA)
print('Parsed product:', product)

Regex-Matching

Regex-Assertions validieren das Ausgabeformat präzise — nützlich für Ausgaben, die einem bestimmten Muster folgen sollen, etwa Datumsangaben, Telefonnummern oder strukturierte Codes.

import re

def assert_matches_regex(output, pattern, flags=0):
    if not re.search(pattern, output, flags):
        raise AssertionError(
            f'Output does not match pattern /{pattern}/\nOutput: {output[:200]}'
        )

def assert_output_is_label(output, valid_labels):
    cleaned = output.strip().upper()
    assert cleaned in valid_labels, (
        f'Expected one of {valid_labels}, got: {repr(cleaned)}'
    )

# Examples
output = call_prompt('Classify sentiment as POSITIVE, NEGATIVE, or NEUTRAL:', 'Great product!')
assert_output_is_label(output, {'POSITIVE', 'NEGATIVE', 'NEUTRAL'})

date_output = call_prompt('Extract the date in YYYY-MM-DD format:', 'Meeting on November 15, 2024')
assert_matches_regex(date_output, r'^\d{4}-\d{2}-\d{2}$')

Bewertung durch LLM-as-judge

Verwenden Sie bei offenen Ausgaben einen zweiten LLM-Aufruf, um die Qualität zu bewerten. Dies wird als LLM-as-judge bezeichnet. Das Prüfmodell erhält den ursprünglichen Prompt, die Ausgabe und die Bewertungskriterien und gibt anschließend eine Punktzahl zurück.

def llm_judge_score(original_prompt, output, criteria, max_score=10):
    judge_prompt = (
        f'Evaluate the following AI response on a scale of 1-{max_score}.\n'
        f'Evaluation criteria: {criteria}\n\n'
        f'Original prompt: {original_prompt}\n\n'
        f'AI response: {output}\n\n'
        f'Return only a number from 1 to {max_score}.'
    )
    resp = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': judge_prompt}],
        temperature=0
    )
    score_text = resp.choices[0].message.content.strip()
    return int(score_text)

def assert_llm_score_above(original_prompt, output, criteria, min_score=7):
    score = llm_judge_score(original_prompt, output, criteria)
    assert score >= min_score, f'LLM judge score {score} < minimum {min_score}'

pytest für Prompt-Tests verwenden

pytest ist das Standard-Testframework für Python und eignet sich gut für Prompt-Tests. Jede Testfunktion entspricht einem Testfall. pytest sammelt, führt aus und meldet die Tests automatisch.

# test_sentiment_prompt.py
import pytest
import openai

client = openai.OpenAI(api_key='sk-...')
SYSTEM_PROMPT = 'Classify the sentiment as POSITIVE, NEGATIVE, or NEUTRAL. Return only the label.'

def classify(text):
    resp = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': SYSTEM_PROMPT},
            {'role': 'user', 'content': text}
        ],
        temperature=0
    )
    return resp.choices[0].message.content.strip().upper()

# pytest automatically discovers functions starting with test_
def test_positive_sentiment():
    assert classify('I love this product!') == 'POSITIVE'

def test_negative_sentiment():
    assert classify('Terrible experience.') == 'NEGATIVE'

def test_neutral_sentiment():
    assert classify('It arrived on time.') == 'NEUTRAL'

# Run: pytest test_sentiment_prompt.py -v

Parametrisierte Tests in pytest

Verwenden Sie @pytest.mark.parametrize, um dieselbe Testfunktion mit vielen Eingaben auszuführen, ohne Code zu wiederholen. Das ist der sauberste Weg, eine umfassende Testsuite aufzubauen.

# test_sentiment_parametrized.py
import pytest

TEST_CASES = [
    ('I love this!', 'POSITIVE'),
    ('Worst purchase ever.', 'NEGATIVE'),
    ('It works.', 'NEUTRAL'),
    ('Amazing!', 'POSITIVE'),
    ('Terrible!', 'NEGATIVE'),
    ('OK I guess.', 'NEUTRAL'),
]

@pytest.mark.parametrize('text,expected', TEST_CASES)
def test_sentiment_classification(text, expected):
    result = classify(text)
    assert result == expected, f'For "{text}": expected {expected}, got {result}'

# pytest test_sentiment_parametrized.py -v
# Output shows each test case individually:
# PASSED test_sentiment_parametrized.py::test_sentiment_classification[I love this!-POSITIVE]
# PASSED test_sentiment_parametrized.py::test_sentiment_classification[Worst purchase ever.-NEGATIVE]

Fixtures für gemeinsam genutzten Prompt-Zustand

Verwenden Sie pytest-Fixtures, um aufwendige Initialisierungen zwischen Tests zu teilen — etwa das Laden einer Prompt-Vorlage oder das einmalige Erstellen eines API-Clients pro Testsitzung.

# conftest.py — fixtures available to all test files in the directory
import pytest
import openai

@pytest.fixture(scope='session')
def llm_client():
    return openai.OpenAI(api_key='sk-...')

@pytest.fixture(scope='session')
def sentiment_prompt():
    with open('prompts/sentiment_v3.txt') as f:
        return f.read()

# test_sentiment.py
def test_positive_with_fixture(llm_client, sentiment_prompt):
    resp = llm_client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {'role': 'system', 'content': sentiment_prompt},
            {'role': 'user', 'content': 'I love this!'}
        ],
        temperature=0
    )
    assert 'POSITIVE' in resp.choices[0].message.content.upper()

Umgang mit instabilen Tests

LLM-Ausgaben sind probabilistisch — selbst bei temperature=0 können unterschiedliche Model-Bereitstellungen oder -Versionen verschiedene Ausgaben erzeugen. Behandeln Sie diese Instabilität mit Wiederholungslogik und Toleranzschwellen.

import pytest

def run_with_retry(fn, n=3):
    '''Run fn up to n times, pass if any run succeeds.'''
    failures = []
    for _ in range(n):
        try:
            fn()
            return  # passed
        except AssertionError as e:
            failures.append(str(e))
    raise AssertionError(f'Failed all {n} attempts. Last: {failures[-1]}')

def test_positive_with_retry():
    def check():
        result = classify('I love this!')
        assert result == 'POSITIVE'
    run_with_retry(check, n=3)

# Or use pytest-retry plugin:
# @pytest.mark.flaky(reruns=3)
# def test_positive_sentiment():
#     assert classify('I love this!') == 'POSITIVE'

Testleistung und Kosten

Jeder Testfall ist ein API-Aufruf — bei 100 Testfällen zu 0,005 $ pro Aufruf kostet ein vollständiger Testlauf 0,50 $. Strategien zur Kostenkontrolle:

  • Antworten für statische Testeingaben zwischenspeichern und in CI aus dem Cache ausführen
  • Die vollständige Testsuite jeden Abend ausführen; bei jedem PR nur eine Smoke-Test-Teilmenge mit 10 Fällen ausführen
  • Für die meisten Tests ein günstigeres Modell (gpt-4o-mini) verwenden; gpt-4o nur für die Regressionstestsuite einsetzen
import hashlib, json

RESPONSE_CACHE = {}

def cached_classify(text, use_cache=True):
    key = hashlib.md5(text.encode()).hexdigest()
    if use_cache and key in RESPONSE_CACHE:
        return RESPONSE_CACHE[key]
    result = classify(text)
    RESPONSE_CACHE[key] = result
    return result

# Persist cache to disk for CI
def load_cache(path='test_cache.json'):
    global RESPONSE_CACHE
    try:
        with open(path) as f:
            RESPONSE_CACHE = json.load(f)
    except FileNotFoundError:
        RESPONSE_CACHE = {}

def save_cache(path='test_cache.json'):
    with open(path, 'w') as f:
        json.dump(RESPONSE_CACHE, f, indent=2)

Testausgabeberichte

pytest erzeugt detaillierte Berichte, in denen hervorgehoben wird, welche Testfälle fehlgeschlagen sind und warum. Verwenden Sie pytest --tb=short -v für kurze Fehlermeldungen. Verwenden Sie für CI --junitxml, um JUnit-XML-Berichte zu erzeugen, die mit GitHub Actions, GitLab CI und Jenkins kompatibel sind.

# Run test suite and generate reports
# In terminal:
# pytest tests/prompt/ -v --tb=short --junitxml=test_results.xml

# In Python (for programmatic use):
import subprocess

def run_prompt_tests(test_dir='tests/prompt'):
    result = subprocess.run(
        ['pytest', test_dir, '-v', '--tb=short', '--junitxml=test_results.xml'],
        capture_output=True, text=True
    )
    print(result.stdout)
    if result.returncode != 0:
        print('TESTS FAILED')
        print(result.stderr)
    return result.returncode == 0

passed = run_prompt_tests()

Wissensüberprüfung

Wann würden Sie beim Testen von Prompts eine Bewertung durch LLM-as-judge anstelle einer Exact-Match-Assertion verwenden?

Zusammenfassung: Assertion-basierte Prompt-Tests

Die wichtigsten Assertion-Typen für LLM-Ausgaben:

  • contains / not_contains: Vorhandensein von Schlüsselwörtern — gut für Bezeichnungen und Sicherheitsprüfungen
  • JSON-Schema-Validierung: validiert das Format strukturierter Ausgaben
  • Regex-Matching: validiert bestimmte Muster (Datumsangaben, Codes)
  • LLM-as-judge: bewertet die Qualität offener Textausgaben

Verwenden Sie pytest mit @pytest.mark.parametrize für saubere, skalierbare Testsuites. Speichern Sie Antworten zwischen, um die Kosten zu kontrollieren. Führen Sie bei jedem PR eine Smoke-Test-Teilmenge und jeden Abend die vollständige Testsuite aus. Nächste Lektion: Regressionstests bei Modellaktualisierungen.

Häufig gestellte Fragen

Ist die Lektion „Assertion-basiertes Prompt-Testing“ kostenlos?

Ja — der vollständige Text von „Assertion-basiertes Prompt-Testing“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des AI Prompt Engineering-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der AI Prompt Engineering-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Assertion-basiertes Prompt-Testing“?

Ausgaben mit contains(), regulären Ausdrücken, JSON-Schema und LLM-as-judge prüfen. Du übst AI Prompt Engineering mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um AI Prompt Engineering zu starten?

Keine Vorkenntnisse erforderlich. AI Prompt Engineering auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.

Wie lange dauert die Lektion „Assertion-basiertes Prompt-Testing“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser AI Prompt Engineering-Lektion Code schreiben und ausführen?

Ja. Jede AI Prompt Engineering-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Prompt-Testfälle schreiben
  2. Assertion-basiertes Prompt-Testing
  3. Regressionstests über Modellaktualisierungen hinweg
  4. Eine Prompt-Testsuite erstellen
← Zurück zu AI Prompt Engineering