Der rohe Spezialist
Zweiunddreißigmal schneller und zwanzig Punkte schlechter. Und bei fehlender Pflichtangabe erfindet er sie.
Meilenstein Der Spezialist läuft als eigener Dienst, die zweite Messung steht, und die Temperaturfrage aus Kapitel 06 ist entschieden.
Jetzt kommt das kleine Modell dazwischen. Kapitel FunctionGemma beschreibt den Anschluss vollständig, dieses Kapitel setzt ihn und misst, was dabei herauskommt.
Vorweg, damit du dich nicht wunderst. Die Messung fällt schlechter aus. Sie soll schlechter ausfallen - ein Modell, das auf einem fremden Werkzeugsatz sofort mit einem 27B mithält, bräuchte kein Training, und dann hätte dieser Kurs kein Thema.
Der Dienst
Wohin gehört der Spezialist? Neben die Anwendung, nicht hinein. Ein eigenes Modellvolumen, eine eigene Gesundheitsprüfung, und er darf ausfallen, ohne die Anwendung mitzunehmen. Die Bauform beschreibt der Baustein Sidecars.
docker run -d --name functiongemma --gpus all \
-v llm-stack_hf-cache:/hf-cache -e HF_HUB_CACHE=/hf-cache \
-p 127.0.0.1:8008:8000 \
vllm/vllm-openai:cu130-nightly \
--model google/functiongemma-270m-it --served-model-name functiongemma-270m \
--host 0.0.0.0 --port 8000 --dtype bfloat16 --max-model-len 32768 \
--gpu-memory-utilization 0.035 --max-num-seqs 8 \
--enable-auto-tool-choice --tool-call-parser functiongemma
Der ENTRYPOINT dieses Images ist bereits vllm serve, deshalb steht hier kein
serve mehr in der Zeile - ein zweites bricht den Aufruf mit
unrecognized arguments ab. Und drei Zeilen tragen den Rest. --gpu-memory-utilization 0.035 reserviert
dreieinhalb Prozent der Karte, weil das Modell samt Kontext nicht mehr braucht
und der Rest den großen Modellen gehört. --enable-auto-tool-choice schaltet
die Werkzeugschnittstelle frei. Und --tool-call-parser functiongemma schließt
die Notation aus Kapitel 06 an - der Server liest
<start_function_call>call:name{…}<end_function_call> und gibt es als
gewöhnliche tool_calls heraus, sodass dein Harness nicht merkt, dass hier ein
Modell mit eigener Sprache sitzt.
Arbeitest du mit llama.cpp, brauchst du dort --jinja für die mitgelieferte
Chat-Vorlage und <start_function_response> in den Stoppsequenzen. Beides
erledigt vLLM von selbst.
Ein Blick auf den gerenderten Prompt
Was bekommt das Modell eigentlich zu lesen? Sieh einmal nach. Die Chat-Vorlage aus den Gewichten macht aus einem gewöhnlichen Werkzeugschema das hier:
<bos><start_of_turn>developer
You are the tool layer of Simhaven. …<start_function_declaration>declaration:get_events{description:<escape>Read events.<escape>,parameters:{properties:{calendar:{description:<escape>Calendar key.<escape>,type:<escape>STRING<escape>}},type:<escape>OBJECT<escape>}}<end_function_declaration><end_of_turn>
<start_of_turn>user
Show brandt.<end_of_turn>
<start_of_turn>model
Alles so, wie Kapitel 06 es beschreibt: Rolle developer, <escape> um jeden
Zeichenkettenwert, Typnamen versal. Und die Antwort darauf, roh aus dem
Vervollständigungs-Endpunkt geholt:
<start_function_call>call:get_events{}<end_function_call>
Das Modell hört nach <end_function_call> von selbst auf. Die Beobachtung ist
zweimal wichtig. Sie bestätigt, dass der Anschluss steht, und sie legt fest, wo
im Kapitel 5 deine Trainingsantwort enden muss.
Die zweite Messung
Dieselbe Prüfbank, dieselben zweiundvierzig Fälle, derselbe Auftakt:
python3 messen.py --url http://localhost:8008/v1/chat/completions \
--modell functiongemma-270m --temp 0.0 --n 1 --tag roh-t0
| Kennzahl | Großes Modell | Roher Spezialist |
|---|---|---|
| Formkorrekte Aufrufe | 100,0 % | 100,0 % |
| Richtige Aufrufe je Schritt | 96,9 % | 78,1 % |
| Aufträge gelungen | 95,2 % | 73,8 % |
| Stillgehalten (S4 + S5) | 93,8 % | 68,8 % |
| Median je Anfrage | 6,42 s | 0,20 s |
Nach Stufen: S1 87,5 %, S2 66,7 %, S3 83,3 %, S4 87,5 %, S5 50,0 %.
Die erste Zeile ist die gute Nachricht, und sie ist keine Kleinigkeit. Hundert Prozent formkorrekt heißt, dass Vorlage, Parser und Notation stimmen. Was jetzt noch schiefgeht, liegt am Verständnis und nicht an der Verdrahtung. Wäre die Zahl eingebrochen, müsstest du zuerst dort suchen.
Und die letzte Zeile? 0,20 statt 6,42 Sekunden. Zweiunddreißigmal schneller, auf demselben Rechner.
Woran es scheitert
Die interessanten Fehler stehen in S5, und sie sehen alle gleich aus. Die Anfrage nennt die Pflichtangabe nicht. Das Modell setzt trotzdem etwas ein:
| Fall | Anfrage | Was der Spezialist schickte |
|---|---|---|
| S5-1 | „Read that one contact for me, please.“ | get_contact{contact_id: 12345} |
| S5-2 | „Delete the contact of Mr Rutkowski.“ | delete_contact{contact_id: "Mr. Rutkowski"} |
| S5-7 | „Change the status of the contact to kunde.“ | update_contact{contact_id: "<contact_id>", status: "kunden"} |
Der dritte Fall ist der schönste. Das Modell schreibt den Platzhalter aus dem
Schema als Wert hin und verhunzt nebenbei den Aufzählungswert - Fehlerbild B-5
in Reinform. Ein delete_contact mit erfundener Kennung wäre im Betrieb kein
Schönheitsfehler.
Dazu ein Muster aus S2, das dieselbe Wurzel hat:
S2-7 soll: update_contact{contact_id: …, status: "kunde"}
ist: update_contact{contact_id: …, status: "kanada"}
S2-10 soll: list_contacts{status: "neu"}
ist: list_contacts{}
S3-6 soll: invite{event_id: …, calendar: "brandt"} + invite{…, calendar: "keller"}
ist: invite{event_id: …, calendar: <die Termin-Kennung>}
kunde wird zu kanada, ein Pflichtfilter fällt weg, und die Terminkennung
landet im Kalenderfeld. Das Modell hat verstanden, welches Werkzeug gemeint ist.
Es weiß nur nicht, was in die Felder gehört, weil es diese Felder noch nie
gesehen hat.
Hier kann ein Feintuning etwas ausrichten. Das Werkzeug ist meistens schon richtig gewählt, die Argumente sind es nicht.
Die Temperaturfrage, entschieden
Kapitel 06 lässt eine Frage offen. Der Hersteller nennt 1.0, die Faustregel
des Kapitels sagt niedrig. Wer soll recht haben? Der Widerspruch gehört auf die
Prüfbank, also kommt er auf die Prüfbank: dreimal je Fall bei den
Herstellerwerten, gegen einen deterministischen Lauf.
temp 0.0 | temp 1.0, top_p 0.95, top_k 64 | |
|---|---|---|
| Läufe | 42 | 126 |
| Richtige Aufrufe je Schritt | 78,1 % | 61,5 % |
| Aufträge gelungen | 73,8 % | 65,1 % |
| Stillgehalten | 68,8 % | 68,8 % |
Für diesen Werkzeugsatz ist die Antwort eindeutig. 8,7 Punkte kostet die Herstellertemperatur. Am stärksten leidet S3, das von 83,3 auf 50,0 fällt, und das passt: Zwei zusammengehörige Aufrufe hintereinander sauber hinzuschreiben ist die Sorte Ausgabe, die Sampling als Erstes zerlegt.
Damit ist die Faustregel bestätigt und die Herstellerangabe nicht widerlegt. Sie
gilt vermutlich für den Werkzeugsatz, an dem das Modell trainiert wurde. Für
deinen gilt, was deine Prüfbank sagt. Der Rest des Kurses fährt auf 0.0.