35 min

Die Messlatte

Eine Prüfbank ist kein Benchmark-Aufbau, sondern zweiundvierzig feste Erwartungswerte und eine Schleife.

Meilenstein S1 bis S5 laufen strukturell bewertet durch, und das große Modell steht mit 95,2 % gelungener Aufträge als Messlatte fest.

Wie viel Aufbau braucht eine Prüfbank? Kapitel Zusammenspiel sagt: keinen. Feste Erwartungswerte und eine Schleife, mehr nicht. Beides steht in diesem Kapitel, und du kannst es abtippen.

Die fünf Stufen, mit Fällen gefüllt

StufeWas sie prüftFälle
S1Format und Grundverdrahtung, ein Werkzeug zur Wahl8
S2Auswahl aus allen elf Werkzeugen12
S3Zwei unabhängige Aufrufe zugleich6
S4Anfragen, die kein Werkzeug brauchen8
S5Pflichtangabe fehlt in der Anfrage8

Zweiundvierzig Fälle. Wenig für einen Benchmark. Genug für eine Prüfbank, weil du jeden einzelnen von Hand gesetzt hast und ihn im Zweifel nachrechnen kannst, ohne raten zu müssen, was er eigentlich prüfen sollte.

Ein Fall ist eine Zeile JSON. nur schränkt den Katalog ein, was ausschließlich S1 tut; alle anderen Stufen sehen alle elf:

{"id":"S2-7","stufe":"S2",
 "frage":"Set the status of the contact 11d3e7b0-4c52-4a1e-9b8f-0d6a3f2c5e10 to kunde.",
 "erwartet":[{"name":"update_contact",
              "args":{"contact_id":"11d3e7b0-4c52-4a1e-9b8f-0d6a3f2c5e10","status":"kunde"}}]}

Und die unbequemen Stufen erwarten schlicht nichts:

{"id":"S5-2","stufe":"S5","frage":"Delete the contact of Mr Rutkowski.","erwartet":[]}
{"id":"S4-2","stufe":"S4","frage":"What does CRM stand for?","erwartet":[]}

Warum zwei Stufen, wenn die Erwartung dieselbe ist? Weil sie Verschiedenes verlangen. S4 fragt etwas, wofür es kein Werkzeug gibt. S5 fragt etwas, wofür es eines gibt, nennt aber die Pflichtangabe nicht - dort muss das Modell sein eigenes Nichtwissen bemerken, und das ist erfahrungsgemäß deutlich schwerer, als einen Katalog zu überblicken.

Bewertet wird strukturell, nie als Text

Der häufigste Fehler beim Bau einer Prüfbank? Der Textvergleich. Ein Aufruf ist kein String. Er ist ein Name plus ein Wörterbuch, und zwei Argumente in anderer Reihenfolge sind derselbe Aufruf.

def passt(ist, soll):
    if ist["name"] != soll["name"] or ist["args"] is None:
        return False
    # Pflicht: jedes erwartete Argument steht mit dem erwarteten Wert da.
    for k, v in soll["args"].items():
        if str(ist["args"].get(k, "")).strip() != str(v):
            return False
    # Kein zusätzliches, nicht erwartetes Argument (erfundene Filter zählen als falsch).
    return set(ist["args"]) == set(soll["args"])

Die zweite Prüfung ist strenger, als sie aussieht. Absicht. Ein Modell, das auf „zeig mir alle Kontakte“ ein list_contacts mit erfundenem Firmenfilter schickt, hat nicht fast richtig geantwortet - es hat die Trefferliste stillschweigend beschnitten, und ein stillschweigend beschnittenes Ergebnis ist im Betrieb schlimmer als eine Fehlermeldung, weil niemand es bemerkt.

Bei mehreren erwarteten Aufrufen darf ein Ist-Aufruf nicht zwei Soll-Schritte decken:

def bewerte(ist_liste, soll_liste):
    offen = list(ist_liste)
    treffer = 0
    for soll in soll_liste:
        for i, ist in enumerate(offen):
            if passt(ist, soll):
                treffer += 1
                offen.pop(i)
                break
    return treffer, len(offen)

Die vier Kennzahlen

Welche Zahlen sagen dir etwas? Die vier aus dem Kapitel, plus eine fünfte, die aus S4 und S5 fällt:

k = {
    "formkorrekt": ...,          # Anteil der Ausgaben, aus denen sich ein Aufruf lesen lässt
    "richtig_je_schritt": ...,   # Werkzeug UND Argumente getroffen, über alle Schritte
    "auftraege_gelungen": ...,   # alle Schritte eines Falls richtig, nichts überzählig
    "schritte_je_auftrag": ...,  # gemittelte Zahl abgesetzter Aufrufe
    "stillgehalten": ...,        # Anteil der S4/S5-Fälle ohne jeden Aufruf
}

Die dritte ist die härteste und die einzige, die am Ende zählt. Gerechnet wird sie als alle Schritte gelungen, nie als drei von vier. Ein Agent, der bei drei von vier Schritten richtig liegt, hat den Auftrag nicht erledigt.

stillgehalten steht daneben, weil du sonst Über-Aufrufen nicht siehst. Ein Modell, das auf jede Anfrage etwas aufruft, sieht in S1 bis S3 hervorragend aus.

Die erste Messung

python3 messen.py --url http://localhost:8009/v1/chat/completions \
  --modell qwen3.8-27b --temp 0.0 --n 1 --tag gross

Ergebnis für qwen3.8-27b, ein 27-Milliarden-Parameter-Modell auf demselben Rechner:

KennzahlWert
Formkorrekte Aufrufe100,0 %
Richtige Aufrufe je Schritt96,9 %
Aufträge gelungen95,2 %
Schritte je Auftrag0,81
Stillgehalten (S4 + S5)93,8 %
Median je Anfrage6,42 s

Nach Stufen: S1 100 %, S2 91,7 %, S3 100 %, S4 100 %, S5 87,5 %.

Zwei Fälle gingen schief, und beide sind lehrreich. Bei S2-9 sollte der Raum besprechung gebucht werden, das Modell schickte room: "raum" - den Kalenderschlüssel aus dem Auftakt statt den Namen aus der Anfrage. Bei S5-8 („Put the appointment into the calendar tomorrow morning“) rief es list_calendars und get_events auf, statt nachzufragen. Es fing an zu planen, wo eine Rückfrage richtig gewesen wäre.

Das ist die Messlatte. Neunzig Prozent aufwärts, und teuer.

Warum diese Zahl vor dem Feintuning stehen muss

Sie ist der einzige Grund, warum die nächsten drei Kapitel etwas bedeuten. Ein nachtrainiertes Modell mit 88 % ist ein Erfolg, wenn der Ausgangswert bei 74 lag, und ein Rückschritt, wenn er bei 95 lag. Ohne die Zahl bleibt alles Weitere Gefühl.