Der Ausgangszustand
Bevor irgendetwas verbessert wird, muss jede Runde nachlesbar sein. Rohausgabe, gelesener Aufruf, Ergebnis, finish_reason.
Meilenstein Elf Werkzeuge aus den Sim-Welten stehen als Aufrufliste da, und ein Lauf des großen Modells liegt vollständig protokolliert vor.
Bevor du irgendetwas verbesserst, musst du sehen können, was passiert. Das ist Stufe eins aus dem Kapitel Zusammenspiel, und sie kostet einen Tag. Wer sie überspringt, misst später gegen ein Gefühl.
Woher die Werkzeuge kommen
Warum nicht einfach ein paar Werkzeuge ausdenken? Weil du hinterher nicht sagen
kannst, ob ein Aufruf richtig war. Also nimmt der Kurs die, die es schon gibt. Im mcp-Container dieses Repos laufen
zwei Sim-Welten, die für die Parcours-Aufgaben gebaut wurden:
| Datei | Welt | Werkzeuge |
|---|---|---|
mcp/tools/sim_calendar.ts | Der Kalender von Simhaven | list_calendars, get_events, create_event, invite, book_room |
mcp/tools/sim_crm.ts | Das CRM derselben Stadt | list_contacts, get_contact, create_contact, update_contact, delete_contact, get_leads_csv |
Elf Werkzeuge. Für das Modell sind sie eine Liste von Schemata, also holt man
sie einmal in genau der Form heraus, in der eine Chat-Schnittstelle sie
erwartet. Ein Auszug aus werkzeuge.json:
{
"type": "function",
"function": {
"name": "update_contact",
"description": "Change fields of one contact. Only the fields you pass are touched - everything else stays as it is. `notes` REPLACES the note; if you want to keep what is there, read it first and pass the combined text. `stand` is set to now.",
"parameters": {
"type": "object",
"properties": {
"contact_id": { "type": "string", "description": "Id of the contact, a uuid." },
"status": { "type": "string", "description": "One of: neu, kontaktiert, kunde, verloren." }
},
"required": ["contact_id"]
}
}
}
Die Beschreibungen sind wörtlich die aus dem Server. Sie zu glätten wäre
verlockend und falsch. Der Spezialist soll lernen, was im Betrieb wirklich in
seinem Prompt steht, und im Betrieb steht dort ein Satz über ein Feld namens
notes, das den bestehenden Eintrag ersetzt statt ihn zu ergänzen - mit der
Warnung dahinter, dass du erst lesen musst, wenn du behalten willst.
Der Auftakt, den alle drei Stände teilen
Ein großes Modell mit Werkzeugen ist ein Planer. Auf „zeig mir die Termine im
Kalender brandt“ ruft es gern erst list_calendars auf, um den Schlüssel
nachzusehen - vernünftiges Agentenverhalten, und für diese Prüfbank trotzdem der
falsche Zug, weil gemessen werden soll, wie eine Absicht zum Aufruf wird, und
nicht die Planung darum herum.
Also bekommen alle drei Stände denselben Auftakt, wörtlich:
AUFTAKT = (
"You are the tool layer of Simhaven. The calendar keys are brandt, keller, "
"ruben, raum and organisator; contact ids appear literally in the request. "
"Answer a request with exactly the tool calls it asks for, no exploratory "
"lookups. If no tool fits, or a required argument is missing, answer in "
"words instead."
)
Drei Sätze, und der letzte ist der wichtigste. Er erlaubt, nichts zu tun. Ohne ihn wäre jede Absage ein Regelverstoß, und die Prüfstufen S4 und S5 würden etwas messen, worum niemand gebeten hat.
Was protokolliert wird
Was gehört in so ein Protokoll? Vier Dinge je Runde, und keines darfst du weglassen:
- Die Rohausgabe, unverändert. Nicht die geparste Fassung, nicht die bereinigte. Scheitert der Parser, siehst du nur daran, warum.
- Der gelesene Aufruf, also Name und Argumente nach dem Parsen.
- Das Ergebnis in der Form, in der das Modell es zu sehen bekäme.
finish_reasonund Dauer. Ein Aufruf, den die Tokengrenze abgeschnitten hat, sieht im Protokoll aus wie ein Formatfehler.finish_reasonist der einzige Unterschied.
Im Messskript des Kurses sieht das so aus:
protokoll.append({
"tag": tag, "fall": fall["id"], "stufe": fall["stufe"], "runde": k,
"roh_inhalt": (msg.get("content") or "")[:400],
"ist": ist, "soll": soll, "form_ok": form_ok,
"treffer": treffer, "schritte_soll": len(soll),
"schritte_ist": len(ist), "gelungen": gelungen,
"finish_reason": finish, "dauer_s": round(dauer, 3), "fehler": fehler,
})
Eine Zeile je Anfrage, als JSON auf die Platte. Das reicht. Du brauchst dafür keine Benchmark-Infrastruktur, und wer eine baut, bevor die erste Zahl steht, hat sich verlaufen.
Was das Protokoll sofort verrät
Der erste Lauf des großen Modells gegen die elf Werkzeuge braucht im Median 6,42 Sekunden je Anfrage, im besten Fall 2,28 und im schlechtesten 25,42. Die Spanne ist der eigentliche Befund dieses Kapitels. Ein Modell, das denkt, bevor es aufruft, ist bei einer einfachen Übersetzung nicht viermal langsamer als ein Spezialist - im schlechtesten Fall ist es hundertmal langsamer.
Ob dich das stört, hängt davon ab, wo der Aufruf sitzt. In einem Stapellauf über Nacht fällt es nicht auf. Vor einem wartenden Menschen schon, und erfahrungsgemäß merkt man es dort auch als Erstes.
Damit steht der Ausgangszustand. Was fehlt, ist die Zahl, gegen die man ihn hält.