Zum Hauptinhalt springen
Lektion 16 von 20 · Produktion, Evaluierung und Optimierung

Erstellen benutzerdefinierter Bewertungen

Lektion 16 von 20 Aktualisiert am 3. November 2026 Fortgeschritten
Auf dieser Seite
  1. Bewertungen an Ihr Fachgebiet anpassen
  2. Eine geschäftliche Anforderung in eine Rubrik übersetzen
  3. Wenn die Maschine besser urteilt als ein Mensch
  4. Bewertungen wiederholbar machen
  5. Kernaussagen

Lektion aktualisiert am 3. November 2026 basierend auf der OpenAI-Dokumentation: temperature und top_p werden nur mit einem Reasoning-Effort von none akzeptiert, während GPT-5.6-Modelle standardmäßig mit medium rechnerisch arbeiten. Aufrufe, die diese Parameter setzen, müssen daher reasoning={"effort": "none"} (API Responses) oder reasoning_effort="none" (Chat Completions) angeben. Quellen: Using GPT-6 · GPT-5.6 Terra.

Aktualisierung vom 2. November 2026 — Evals-Plattform. OpenAI hat das Ende ihrer Evals-Plattform angekündigt: Bestehende Bewertungen waren seit dem 31. Oktober 2026 schreibgeschützt, und sowohl das Dashboard als auch die API, einschließlich der Bewerter (graders), werden am 30. November 2026 geschlossen. OpenAI hat einen Migrationsleitfaden zu Promptfoo veröffentlicht. Die Bewertungsprinzipien in dieser Lektion bleiben gültig; lediglich das Werkzeug ändert sich.

Bewertungen an Ihr Fachgebiet anpassen

Generische Bewerter reichen nicht aus, um die Qualität in Ihrem spezifischen Fachgebiet zu beurteilen. Ein medizinischer Chatbot, ein juristischer Assistent und ein Marketing-Schreibwerkzeug haben jeweils unterschiedliche Qualitätskriterien, und diese Kriterien sind nicht nur zahlreicher, sondern auch unterschiedlich gewichtet. Ein Tonfehler in einem Newsletter ist eine Belästigung; dieselbe Annäherung in einer Dosierungsanweisung ist ein schwerwiegender Fehler. Ein generischer Bewerter behandelt beide gleich, was im Grunde bedeutet, dass nichts bewertet wird.

Deshalb benötigen Sie benutzerdefinierte Bewertungen, die auf einer expliziten Rubrik basieren, die Ihre geschäftlichen Prioritäten in Gewichte übersetzt. Das ist die Rubrik, die wir erstellen werden.

Eine geschäftliche Anforderung in eine Rubrik übersetzen

Eine Rubrik ist eine Liste gewichteter Kriterien, die sich zu 1 summieren. Der nützlichste Übungsschritt ist nicht das Codieren der Struktur, was etwa fünfzehn Zeilen dauert, sondern das Ausfüllen: Die Entscheidung, dass Kundenvorteile 0,25 wiegen, während die Länge nur 0,10 wiegt, zwingt zu einer expliziten Diskussion mit den Produktverantwortlichen. Das Ergebnis ist eine gemeinsame Definition von „eine gute Antwort”, die niemand nachträglich bestreiten kann.

from dataclasses import dataclass

@dataclass
class Kriterium:
    name: str
    beschreibung: str
    gewicht: float  # 0 bis 1, Summe der Gewichte = 1

@dataclass
class Rubrik:
    name: str
    kriterien: list[Kriterium]

    def validieren(self) -> bool:
        total = sum(c.gewicht for c in self.kriterien)
        return abs(total - 1.0) < 0.01

# Beispiel: Rubrik für einen Marketing-Schreibassistenten
rubrik_marketing = Rubrik(
    name="Qualität des Marketingschreibens",
    kriterien=[
        Kriterium("ton", "Der Ton ist professionell und ansprechend", 0.20),
        Kriterium("cta", "Enthält eine klare Handlungsaufforderung", 0.15),
        Kriterium("vorteile", "Hebt Kundenvorteile hervor", 0.25),
        Kriterium("laenge", "Respektiert die angeforderte Länge", 0.10),
        Kriterium("grammatik", "Keine Grammatik- oder Rechtschreibfehler", 0.15),
        Kriterium("marke", "Respektiert die Markenrichtlinien", 0.15),
    ],
)

Die validieren-Methode sollte in Tests aufgerufen werden, nicht nur geschrieben. Rubriken entwickeln sich weiter: Sie fügen ein Compliance-Kriterium hinzu, entfernen eines, das nicht mehr nützlich ist, und die Summe driftet auf 1,15, ohne dass etwas die Anomalie meldet. Die Scores bleiben berechenbar, sind aber falsch, und wenn sie über Versionen hinweg verglichen werden, führen sie zu fehlerhaften Schlussfolgerungen.

Der Bewerter übersetzt dann die Rubrik in einen Prompt, fordert für jedes Kriterium einen Score von 0–5 an und setzt einen gewichteten Gesamtscore zusammen. Jeder Score wird mit einem kurzen Kommentar versehen: Das ermöglicht es Ihnen, bei der Überprüfung schlechter Fälle zu verstehen, ob das Modell schlecht reagiert hat oder ob Ihr Kriterium schlecht formuliert war.

import openai
import json

client = openai.OpenAI()

def bewerten_multi_kriterien(
    frage: str,
    antwort: str,
    rubrik: Rubrik,
) -> dict:
    """Bewertet eine Antwort anhand einer mehrkriteriellen Rubrik."""

    kriterien_text = "\n".join(
        f"- {c.name} (gewicht: {c.gewicht}): {c.beschreibung}"
        for c in rubrik.kriterien
    )

    prompt = (
        "Bewerten Sie diese Antwort nach den folgenden Kriterien.\n"
        "Für jedes Kriterium geben Sie einen Score von 0 bis 5 an.\n\n"
        f"Gestellte Frage : {frage}\n"
        f"Zu bewertende Antwort : {antwort}\n\n"
        f"Kriterien :\n{kriterien_text}\n\n"
        "Antworten Sie in JSON mit einem Objekt scores, das für jedes Kriterium "
        "einen Score (0-5) und einen kurzen Kommentar enthält."
    )

    response = client.responses.create(
        model="gpt-5.6-terra",
        reasoning={"effort": "none"},  # temperature erfordert effort none
        input=prompt,
        temperature=0.0,
    )

    try:
        ergebnis = json.loads(response.output_text)
        scores = ergebnis.get("scores", {})

        # Berechnung des gewichteten Scores
        score_gewichtet = 0
        for kriterium in rubrik.kriterien:
            if kriterium.name in scores:
                score_roh = scores[kriterium.name]["score"] / 5.0
                score_gewichtet += score_roh * kriterium.gewicht

        return {
            "score_gesamt": score_gewichtet,
            "scores_details": scores,
            "rubrik": rubrik.name,
        }
    except (json.JSONDecodeError, KeyError):
        return {"score_gesamt": 0, "fehler": "Parsing fehlgeschlagen"}

Achten Sie auf den Berechnungsschleife: Ein Kriterium, das in der Antwort des Richters fehlt, wird stumm ignoriert und trägt null zum gewichteten Score bei. Eine exzellente Antwort könnte daher bei 0,75 enden, einfach weil der Richter einen Schlüssel vergessen hat. Wenn Ihre Scores unter einer unerklärlichen Obergrenze stecken, prüfen Sie scores_details, bevor Sie das bewertete Modell beschuldigen.

Wenn die Maschine besser urteilt als ein Mensch

Es ist ein kostspieliger Abkürzungsversuch, einen LLM dazu zu bringen, Code zu bewerten: Das einzige Urteil, das zählt, ist die Ausführung. Der folgende Bewerter fügt den generierten Code und einen Test zusammen, schreibt ihn in eine temporäre Datei, führt ihn aus und zählt die Erfolge. Der resultierende Score benötigt keine Interpretation – ein Test besteht oder er besteht nicht.

import subprocess
import tempfile

def bewerten_code_python(
    code_generiert: str,
    tests: list[str],
) -> dict:
    """Bewertet Python-Code, indem er ihn gegen Tests ausführt."""
    test_ergebnisse = []

    for i, test in enumerate(tests):
        code_vollstaendig = f"{code_generiert}\n\n{test}"

        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".py", delete=False
        ) as f:
            f.write(code_vollstaendig)
            f.flush()

            try:
                resultat = subprocess.run(
                    ["python3", f.name],
                    capture_output=True,
                    text=True,
                    timeout=10,
                )
                erfolg = resultat.returncode == 0
                test_ergebnisse.append({
                    "test": i,
                    "erfolg": erfolg,
                    "ausgabe": resultat.stdout[:200],
                    "fehler": resultat.stderr[:200] if not erfolg else "",
                })
            except subprocess.TimeoutExpired:
                test_ergebnisse.append({
                    "test": i,
                    "erfolg": False,
                    "fehler": "Timeout (10s)",
                })

    anzahl_erfolge = sum(1 for r in test_ergebnisse if r["erfolg"])
    return {
        "score": anzahl_erfolge / len(tests) if tests else 0,
        "tests_bestanden": anzahl_erfolge,
        "tests_gesamt": len(tests),
        "details": test_ergebnisse,
    }

Der timeout=10 ist keine Komfortvorkehrung: Ein Modell erzeugt regelmäßig unendliche Schleifen, und ohne diese Begrenzung hängen Ihre Bewertungskampagnen am ersten fehlgeschlagenen Fall fest. Denken Sie auch daran, dass Sie Code ausführen, den Sie nicht geschrieben haben – außerhalb einer isolierten Entwicklungsumgebung erfordert diese Funktion eine Sandbox.

Die Zusammenfassung illustriert einen Mischfall, bei dem ein Teil des Urteils mechanisch und ein anderer nicht ist. Die Länge ist messbar: Das berechnete Verhältnis bestraft eine Zusammenfassung, die zu kurz ist, und eine, die zu lang ist, symmetrisch, was den Trick des Modells vermeidet, den Quelltext zu kopieren, um nichts wegzulassen. Die Treue ist hingegen nicht durch eine Regel messbar und geht zurück an den LLM-Richter, mit einer direkten Frage zu erfundenen Informationen.

def bewerten_zusammenfassung(
    text_original: str,
    zusammenfassung: str,
    ziel_laenge: int = 100,
) -> dict:
    """Bewertet die Qualität einer Zusammenfassung."""
    woerter_zusammenfassung = len(zusammenfassung.split())
    verhaeltnis_laenge = min(
        woerter_zusammenfassung / ziel_laenge,
        ziel_laenge / max(woerter_zusammenfassung, 1),
    )

    # Treue via LLM
    response = client.responses.create(
        model="gpt-5.6-terra",
        input=(
            f"Ist die folgende Zusammenfassung dem Originaltext treu ? "
            f"Enthält sie erfundene Informationen ?\n\n"
            f"Originaltext (Auszug) : {text_original[:2000]}\n\n"
            f"Zusammenfassung : {zusammenfassung}\n\n"
            f"Antworten Sie in JSON mit den Schlüsseln : treue (0-5), "
            f"halluzinationen (ja/nein), kommentar (kurz)."
        ),
        temperature=0.0,
    )

    try:
        treue = json.loads(response.output_text)
    except json.JSONDecodeError:
        treue = {"treue": 0, "halluzinationen": "unbekannt"}

    return {
        "score_laenge": verhaeltnis_laenge,
        "score_treue": treue.get("treue", 0) / 5.0,
        "halluzinationen": treue.get("halluzinationen", "unbekannt"),
        "woerter_zusammenfassung": woerter_zusammenfassung,
        "ziel_laenge": ziel_laenge,
    }

Bewertungen wiederholbar machen

Eine Bewertung, die Sie manuell wiederholen, wird irgendwann nicht mehr wiederholt. Die Pipeline kapselt die Rubrik ein und hält eine Historie von Kampagnen, wodurch eine einmalige Messung in eine Zeitreihe verwandelt wird: Sie sehen, dass der Durchschnittsscore von 0,81 auf 0,79 gefallen ist, nachdem der Prompt am Dienstag geändert wurde, eine Information, die eine isolierte Ausführung Ihnen nicht hätte geben können.

class PipelineEval:
    """Wiederverwendbare Bewertungspipeline."""

    def __init__(self, name: str, rubrik: Rubrik):
        self.name = name
        self.rubrik = rubrik
        self.historie: list[dict] = []

    def bewerten_batch(
        self,
        datensatz: list[dict],
        modell: str,
        prompt_system: str,
    ) -> dict:
        """Bewertet einen vollständigen Batch und gibt Metriken zurück."""
        ergebnisse = []

        for fall in datensatz:
            response = client.responses.create(
                modell=modell,
                instructions=prompt_system,
                input=fall["input"],
            )

            bewertungsergebnis = bewerten_multi_kriterien(
                fall["input"],
                response.output_text,
                self.rubrik,
            )
            ergebnisse.append(bewertungsergebnis)

        scores = [r["score_gesamt"] for r in ergebnisse]
        bericht = {
            "pipeline": self.name,
            "modell": modell,
            "anzahl_faelle": len(datensatz),
            "score_mittel": sum(scores) / len(scores),
            "score_min": min(scores),
            "score_max": max(scores),
        }

        self.historie.append(bericht)
        return bericht

Ein Tipp zum Starten: Erstellen Sie Ihre erste Rubrik auf Basis von nur zwanzig Fällen, lassen Sie sie parallel vom LLM-Richter und von einem Fachexperten bewerten, und vergleichen Sie dann die beiden Reihen. Die Diskrepanzen werden Ihnen sagen, welche Kriterien schlecht beschrieben sind, lange bevor sie Ihnen etwas über die Qualität des Modells sagen.

Kernaussagen

  • Erstellen Sie Bewertungsrubriken, die spezifisch für Ihr geschäftliches Fachgebiet sind, mit expliziten Gewichten
  • Stellen Sie sicher, dass die Summe der Gewichte bei 1 bleibt, während sich die Rubrik entwickelt
  • Kombinieren Sie automatisierte Bewertungen (Code, Länge) und LLM-as-Judge (Qualität)
  • Testen Sie generierten Code, indem Sie ihn ausführen, mit einem Timeout und einer isolierten Umgebung
  • Erstellen Sie eine wiederverwendbare Pipeline, die eine Historie von Kampagnen speichert