LLM-as-Judge: Bias und Ausgabe prüfen

Track KI · M3 Evaluation, Baustein 03, Teil 2 · ca. 50 Min. plus Projektaufgabe

Worum es geht

Ein Judge ist ein fehlbares Modell. In diesem Teil testest du ihn auf zwei typische Verzerrungen: Positionsbias (wer zuerst steht, gewinnt) und Längenbias (wer viel schreibt, wird milder beurteilt). Dazu lernst du, kaputte Judge-Ausgaben von “falsch” zu unterscheiden. Am Ende steht die Projektaufgabe mit einem lokalen Skript.

Was du aus Teil a brauchst: Du kennst den Fake-Judge (regelbasiert, Kernbegriffe zählen, mit eingebautem Längenbias) und das Urteilsschema enthaelt_kernaussage plus begruendung. Außerdem weißt du, dass man einen Judge gegen menschliche Labels kalibriert. Falls nicht: Teil a, Prompt und Kalibrierung.

In dieser Lektion läuft kein echtes LLM. Beide Fake-Judges haben ihre Schwächen absichtlich eingebaut. Zeitplan: etwa 15 Minuten Lesen und 35 Minuten für die drei Übungen, zusammen rund 50 Minuten plus Projektaufgabe.

Was du am Ende kannst:

  • Positionsbias durch Vertauschen der Reihenfolge erkennen und Längenbias messen
  • Judge-Ausgaben validieren und kaputte Antworten von “falsch” unterscheiden
  • begründen, wann eine Judge-Zahl belastbar ist und wann nicht

Von JS/TS her gedacht

Idee JS/TS Python
Kaputte Ausgabe JSON.parse wirft SyntaxError json.loads wirft json.JSONDecodeError (Unterklasse von ValueError)
Vergleichsfunktion (a, b) => ... im sort judge(kern, a, b) als normale Funktion, die du als Argument übergibst
Wahrheitswert prüfen typeof x === "boolean" isinstance(x, bool) (Achtung: isinstance(1, int) und isinstance(True, int) sind beide wahr)

Konzept

Schritt 1: Positionsbias durch Vertauschen finden

Der paarweise Fake-Judge bekommt zwei Antworten A und B und nennt die bessere. Er zählt die Kernbegriffe in beiden. B gewinnt nur, wenn B mindestens zwei Begriffe mehr hat. Sonst gewinnt A. Das ist der eingebaute Positionsbias (position bias): Bei Gleichstand oder knappem Vorsprung gewinnt, wer zuerst steht.

Beim paarweisen Vergleich (“welche Antwort ist besser, A oder B?”) bevorzugen manche Judges eine Position. Der Test ist einfach: Frage jedes Paar zweimal, mit vertauschter Reihenfolge. Ein fairer Judge nennt in beiden Läufen dieselbe Antwort als Sieger (nicht denselben Buchstaben!). Wechselt der Sieger, hat die Position entschieden, nicht der Inhalt.

x gegen y: x hat 3, y hat 2 Begriffe, der Vorsprung ist nur 1. Im ersten Lauf steht x vorn und gewinnt als “A”. Im zweiten Lauf steht y vorn und gewinnt ebenfalls als “A”. Die Buchstaben sind gleich (“A”, “A”), die Sieger aber verschieden (x, dann y): Positionsbias. Bei x gegen z ist der Abstand groß genug, der Inhalt entscheidet, beide Läufe nennen x. Beachte, wie der Sieger über den Buchstaben zurückgerechnet wird: Im zweiten Lauf steht q auf Position A.

Bei einem fairen Judge sind die Buchstaben in den zwei Läufen sogar verschieden (“A”, “B”). Wer die Buchstaben vergleicht, flaggt also die guten Fälle und übersieht die schlechten.

Schritt 2: Längenbias messen und kaputte Ausgaben abfangen

Zuerst der Fake-Judge aus Teil a noch einmal, damit diese Seite für sich allein läuft. Er verlangt bei höchstens 15 Wörtern alle Kernbegriffe, bei mehr Wörtern nur die Hälfte (Längenbias), und antwortet mit einem JSON-String:

Längenbias misst du mit einem Auffüll-Test (padding test): Nimm Antworten, die der Judge als falsch beurteilt, hänge einen inhaltsleeren Füllsatz an und frage erneut. Ein fairer Judge ändert sein Urteil nicht.

Alle vier falschen Antworten kippen nach dem Füllsatz von False auf True: Der Fake-Judge belohnt Länge. Bei einem echten Judge wäre das Ergebnis eine Zahl irgendwo dazwischen, die du messen musst, nicht raten.

Jetzt die Ausgabe. Ein Judge liefert Text, und Text kann kaputt sein: kein JSON, ein String "true" statt Boolean, fehlende Felder, JSON in Markdown-Zaun (```json). Die wichtigste Regel: “nicht lesbar” ist nicht “falsch”. Wer ein kaputtes Urteil als False zählt, drückt die Quote still nach unten (und du suchst Fehler in der Anwendung, die im Prüfer liegen). Zähle ungültige Antworten getrennt, rechne die Quote nur über gültige, und wiederhole (retry) ungültige Aufrufe, statt zu raten.

Vier gültige Urteile (drei True), zwei ungültige: Quote 0.75, 2 ungültig. Würdest du die zwei None als False zählen, wäre die Quote 0.5, ohne dass sich eine einzige Antwort verschlechtert hätte. Ob eine bestimmte Ungültig-Quote noch akzeptabel ist, legst du pro Projekt fest (die Quelle nennt keinen Wert).

Falle

Falle 1: Nur über mehr Fälle mitteln (über die Quelle hinaus). Ein Bias ist systematisch. 1000 Fälle statt 100 machen die Zahl stabiler, aber nicht richtiger. Der Längenbias verschiebt dann einfach 1000 Urteile in dieselbe Richtung.

Falle 2: Ein kaputtes Urteil als “falsch” zählen (Schritt 2). Es ist weder richtig noch falsch, es ist ein Ausfall des Prüfers.

Falle 3: Reproduzierbar mit richtig verwechseln (über die Quelle hinaus). Ein Judge, der bei jedem Lauf dasselbe sagt, ist konsistent, aber nicht automatisch valide. Reproduzierbarkeit zeigt dir der Wiederholungslauf, Validität nur der Vergleich mit menschlichen Labels.

Übungen

Übung 1: Positionsbias finden (mittel bis schwer, ca. 15 Min.)

paare ist eine Liste von Tupeln (kernbegriffe, x, y). judge(kernbegriffe, a, b) gibt "A" oder "B" zurück (die bessere von a und b). Schreibe inkonsistente_paare(paare, judge). Sie gibt die Indizes (aufsteigend, als Liste) aller Paare zurück, bei denen der Sieger wechselt, wenn man die Reihenfolge vertauscht. Verglichen wird, welche Antwort gewinnt, nicht welcher Buchstabe. Der Judge fake_vergleich aus Schritt 1 steht dir zur Verfügung.

Beispiel: Gibt ein Judge immer "A" zurück, wechselt der Sieger bei jedem Paar (erst gewinnt x, dann y), die Antwort ist also [0, 1, ..., n-1]. Ein Judge, der immer die Antwort mit mehr Kernbegriffen wählt, ergibt [], solange es keinen Gleichstand gibt.

Rufe den Judge pro Paar zweimal auf, einmal mit (x, y) und einmal mit (y, x). Übersetze beide Antworten in “x hat gewonnen” oder “y hat gewonnen”. Überleg dir, was "A" im zweiten Lauf bedeutet.

def inkonsistente_paare(paare, judge):
    wechsel = []
    for i, (kern, x, y) in enumerate(paare):
        erst = "x" if judge(kern, x, y) == "A" else "y"
        getauscht = "y" if judge(kern, y, x) == "A" else "x"
        if erst != getauscht:
            wechsel.append(i)
    return wechsel
inkonsistente_paare

Übung 2: Judge-Ausgabe lesen und validieren (mittel bis schwer, ca. 15 Min.)

Schreibe lese_urteil(roh). roh ist die Rohausgabe des Judge. Rückgabe: True oder False (das Urteil enthaelt_kernaussage), und None, wenn die Ausgabe ungültig ist. Regeln:

  • Gültig ist ein JSON-Objekt mit enthaelt_kernaussage als echtem Boolean (der String "true" und die Zahl 1 sind ungültig) und begruendung als Text, der nach strip() nicht leer ist. Weitere Felder werden ignoriert.
  • Ein umschließender Markdown-Zaun (```json oder ``` am Anfang, ``` am Ende) wird entfernt, bevor geparst wird. Fehlt die schließende Markierung, wird nur die öffnende entfernt und der Rest geparst.
  • Text vor oder nach dem JSON (“Hier mein Urteil: …”) wird nicht herausgesucht: Das ist ungültig.
  • Alles andere (None, kein String, leerer String, kein JSON, ein Array statt Objekt) ist ungültig. Die Funktion darf nie eine Exception werfen.

Hinweis: Du prüfst hier von Hand. Pydantic im Standardmodus (lax mode) würde "true" und 1 für ein bool-Feld akzeptieren und in True umwandeln (mit Pydantic 2.10 im Browser und 2.12 lokal ausprobiert). Willst du dort streng prüfen, brauchst du den strict mode. Für diese Übung gilt die Regel oben.

Gehe in Schichten vor: Typ prüfen, Zaun entfernen, parsen, Struktur prüfen. Jede Schicht kann abbrechen und None liefern. Denk daran, dass in Python isinstance(1, int) wahr ist und isinstance(True, int) ebenfalls. Für Booleans zählt der exakte Typ.

def lese_urteil(roh):
    if not isinstance(roh, str):
        return None
    text = roh.strip()
    if text.startswith("```"):
        zeilen = text.splitlines()
        zeilen = zeilen[1:]
        if zeilen and zeilen[-1].strip() == "```":
            zeilen = zeilen[:-1]
        text = "\n".join(zeilen).strip()
    try:
        daten = json.loads(text)
    except Exception:
        return None
    if not isinstance(daten, dict):
        return None
    wert = daten.get("enthaelt_kernaussage")
    grund = daten.get("begruendung")
    if not isinstance(wert, bool):
        return None
    if not isinstance(grund, str) or not grund.strip():
        return None
    return wert
lese_urteil

Übung 3: Wann darfst du der Judge-Zahl trauen? (Multiple Choice, ca. 5 Min.)

Dein Kunde will im Bericht eine einzige Zahl sehen: “Der Assistent beantwortet 82 Prozent der Golden-Set-Fragen korrekt”, gemessen mit einem LLM-Judge. Der Bericht geht ohne weitere Prüfung ins Management. Welche Vorarbeit ist das Minimum, damit du die Zahl mit gutem Gewissen nennen kannst?

  • a) Eine Stichprobe von Hand labeln, den Judge dagegen kalibrieren und Reihenfolge und Länge als Bias testen.
  • b) Dasselbe Modell wie in der Anwendung als Judge nehmen, weil es die Domäne am besten kennt und weniger irrt.
  • c) Den Judge-Lauf zweimal wiederholen und prüfen, dass jedes Mal exakt dieselbe Quote herauskommt.
  • d) Die Zahl der Fälle auf viele Tausend erhöhen, damit sich die Fehler des Judge im Mittel aufheben.

Trage den Buchstaben als String ein.

Unterscheide zwei Fragen: “Sagt der Judge immer dasselbe?” und “Hat der Judge recht?”. Nur eine davon lässt sich ohne menschliches Urteil beantworten. Überleg außerdem, welche Art von Fehler sich beim Mitteln aufhebt und welche nicht.

antwort = "a"
antwort

Projektaufgabe (Abschluss des Bausteins, ohne Browser-Check)

Die Quelle verlangt: Baue den Judge-Prompt, lass ihn über das Golden Set aus Baustein 02 laufen und prüfe für 5 der Urteile von Hand nach, ob du dem Judge zustimmst. In deinem Projekt nimmst du dafür echte Antworten deines RAG-Systems aus M2.

Hilfsmittel: lernlabor/uebung/ki/ki_12_judge_echt.py. Die Datei enthält ein kleines, ausgedachtes Golden Set mit menschlichen Labels, drei Platzhalter (baue_judge_prompt und kalibrierung aus Teil a, Übung 1 und 2, sowie lese_urteil aus Übung 2 dieses Teils). Die Rubrik im Skript hat keine Gewichte: Für den Platzhalter baue_judge_prompt lässt du den Gewichtsteil aus Übung 1 weg und einen Selbsttest.

cd lernlabor && uv run python uebung/ki/ki_12_judge_echt.py
  • Ohne ANTHROPIC_API_KEY oder ohne das Paket anthropic läuft ein Trockenlauf mit dem Fake-Judge. Es entstehen keine Kosten und nichts geht kaputt.
  • Der Schlüssel wird nur aus der Umgebungsvariable gelesen, er steht nie in einer Datei. Das Paket anthropic ist im Lernlabor nicht installiert: nicht von selbst installieren (uv add anthropic, ein Agent fragt vorher bei dir nach).
  • Das Modell kommt aus ANTHROPIC_MODEL (Standard: ein Name aus der Kursquelle, bitte prüfen, ob er noch aktuell ist). Der echte Lauf macht pro Fall einen Aufruf und kostet Geld (Preise bitte beim Anbieter prüfen).

Fertig, wenn:

Merksatz

Teste jeden Judge auf Reihenfolge und Länge, zähle kaputte Ausgaben getrennt von “falsch”, und vertraue einer Judge-Zahl erst, wenn sie gegen menschliche Labels kalibriert ist (Quelle für das Stichproben-Prinzip, die Tests sind über die Quelle hinaus erweitert).

Prüfstein

Dein Judge liefert bei 10 von 100 Fällen kein lesbares JSON, und dein Kollege zählt sie als “falsch”. Wie ändert das die Quote, welche zwei Tests auf Bias verlangst du, und was sagt ein erfolgreicher Wiederholungslauf (immer dieselbe Quote) über die Richtigkeit?


Quelle: quellen/kursbuch-lerninhalte.md, Modul M3, Baustein “03 LLM-as-Judge” (Zeilen 658 bis 683). Aus der Quelle stammen: die Stolperfalle (derselbe Judge wie die Anwendung, Stichproben von Hand), die Übung (Judge-Prompt über das Golden Set laufen lassen, 5 Urteile nachprüfen) und der erzwungene Tool-Aufruf für das Schema. Über die Quelle hinaus (allgemeines Fachwissen): der Positionsbias-Test durch Vertauschen, der Auffüll-Test für Längenbias, die Regel “ungültig ist nicht falsch” samt Retry, die Unterscheidung konsistent gegenüber valide und die Falle “mehr Fälle mitteln keinen Bias weg”. Die Fake-Judges sind von mir ausgedachte Simulationen mit absichtlich eingebauten Verzerrungen, keine Messung echter Modelle. Alle Fälle, Begriffe und Zahlen in den Beispielen sind ausgedacht. Akzeptable Ungültig-Quoten und reale Judge-Genauigkeiten nennt die Quelle nicht (bitte prüfen). Alle Python-Beispiele wurden mit Python 3.13 ausgeführt.