30 min

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
KennzahlGroßes ModellRoher Spezialist
Formkorrekte Aufrufe100,0 %100,0 %
Richtige Aufrufe je Schritt96,9 %78,1 %
Aufträge gelungen95,2 %73,8 %
Stillgehalten (S4 + S5)93,8 %68,8 %
Median je Anfrage6,42 s0,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:

FallAnfrageWas 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.0temp 1.0, top_p 0.95, top_k 64
Läufe42126
Richtige Aufrufe je Schritt78,1 %61,5 %
Aufträge gelungen73,8 %65,1 %
Stillgehalten68,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.