🎁 Neu Kostenlos registrieren, 10 Aufrufe gratis. Bis zu 1 $, ohne Karte.
GLM 5.2 Tool Calls in Agent-Loops: Was 'OpenAI-kompatibel' verschweigt

GLM 5.2 Tool Calls in Agent-Loops: Was 'OpenAI-kompatibel' verschweigt

Inhalt
  1. Der gleiche Zug, dreimal anders
  2. Text reist mit dem Tool-Call mit
  3. Es denkt laut mit
  4. Was ein GLM-5.2-Tool-Call-Turn kostet
  5. Wann GLM 5.2 die richtige Wahl ist und wie man es sauber betreibt
  6. Disclaimer
  7. Quellen

Richtet man einen bestehenden OpenAI-Agent-Loop auf GLM 5.2 aus, läuft das meiste einfach: man schickt tools, bekommt tool_calls zurück, führt sie aus, schickt die Ergebnisse. Dann passiert etwas, das in keinem SDK-Beispiel auftaucht. Der Assistant liefert im selben Turn wie die Tool Calls noch eine Zeile Text:

{
  "choices": [{
    "finish_reason": "tool_calls",
    "message": {
      "role": "assistant",
      "content": "I'll look up both pieces of information for you at the same time!",
      "tool_calls": [
        {"id": "call_…", "type": "function",
         "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}},
        {"id": "call_…", "type": "function",
         "function": {"name": "get_time", "arguments": "{\"city\":\"Tokyo\"}"}}
      ]
    }
  }]
}

Zwei Konventionen dominieren, und man sollte beide im Blick behalten. Bei OpenAI schickt man Function-Schemas, bekommt tool_calls zurück und antwortet mit einer tool-Message pro Call, identifiziert über tool_call_id:

resp = openai.chat.completions.create(model="…", tools=tools, tool_choice="auto", messages=messages)
# assistant.tool_calls → [{"id": "call_…", "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}]
messages.append(resp.choices[0].message)
messages.append({"role": "tool", "tool_call_id": "call_…", "content": "18C, clear"})

Bei Anthropic sieht es anders aus: Tools tragen ein input_schema, das Modell gibt tool_use-Blöcke aus, und man antwortet mit einem tool_result-Block:

resp = anthropic.messages.create(model="…", tools=tools, messages=messages)
# resp.content → [{"type": "tool_use", "id": "toolu_…", "name": "get_weather", "input": {"city": "Paris"}}]
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": [
    {"type": "tool_result", "tool_use_id": "toolu_…", "content": "18C, clear"}]})

GLM 5.2 spricht den OpenAI-Dialekt.

In OpenAIs Vertrag ist message.content gleich null, sobald finish_reason auf tool_calls steht. Viele Agent-Loops verlassen sich darauf: Sie verzweigen über “Content oder Tool Calls”, loggen content als finale Antwort oder prüfen per Assertion, dass es leer ist. GLM gibt dir beides gleichzeitig, und genau diese Annahme bricht als Erstes.

Das beschriebene Verhalten stammt aus echten Tool-Calling-Requests an glm-5.2; als Referenz liefen gpt-5.5 und claude-opus-4-8 auf derselben Aufgabe. Kurz gesagt: GLM 5.2 nutzt die OpenAI-API-Oberfläche, verhält sich aber in ein paar Punkten eher wie Claude als wie GPT — und stolpern wird der Loop, der auf OpenAI trainiert ist.

Der gleiche Zug, dreimal anders

Gleicher Prompt, dieselben zwei Tools, drei Modelle:

GLM (glm-5.2)OpenAI (gpt-5.5)Anthropic (claude-opus-4-8)
API-OberflächeOpenAI chat-completionsOpenAI chat-completionsAnthropic messages
Text im Tool-Call-Zugcontent-Präambel (nicht null)content ist nullein text-Block vor tool_use
Reasoning in diesem Zugsichtbar: reasoning_content + reasoning_tokensverborgen; nur reasoning_tokens in usagenur als thinking-Block, wenn du ihn aktivierst
Parallele Tool-Callsja, mit indexjaja, mehrere tool_use-Blöcke
Fertig-Signalfinish_reason: "tool_calls"finish_reason: "tool_calls"stop_reason: "tool_use"
Präfix der Tool-Call-IDcall_…call_…toolu_…

Zwei Zeilen sind es, an denen Loops brechen: Text im Tool-Call-Zug und Reasoning, das in genau diesem Zug auftaucht. Der Rest ist beruhigend langweilig.

Text reist mit dem Tool-Call mit

GLM 5.2 gibt regelmäßig eine kurze Assistant-content-Präambel zusammen mit den tool_calls aus, mit finish_reason: "tool_calls". Das ist kein Fehler und kommt auch nicht nur gelegentlich vor.

Hier derselbe Zug von allen dreien, gekürzt auf den Teil, der sich unterscheidet:

// OpenAI gpt-5.5: content is null on a tool-call turn
"message": { "content": null,
             "tool_calls": [ {/* get_weather */}, {/* get_time */} ] }

// GLM glm-5.2: content carries a preamble
"message": { "content": "I'll look up both pieces of information for you at the same time!",
             "tool_calls": [ {/* get_weather */}, {/* get_time */} ] }

// Anthropic claude-opus-4-8: a text block sits before the tool_use blocks
"content": [ { "type": "text", "text": "I'll get both pieces of information for you." },
             { "type": "tool_use", /* get_weather */ },
             { "type": "tool_use", /* get_time */ } ]

OpenAI lässt content auf null; GLM füllt es; Anthropic hat dort schon immer einen text-Block platziert. GLM übernimmt also OpenAIs Wire-Format und kombiniert es mit Anthropics Angewohnheit, vor dem Handeln zu erzählen. Ein Loop, der gegen OpenAI geschrieben ist, wird davon kalt erwischt. Der Fix ist klein, aber du musst ihn bewusst setzen. Behandle einen Tool-Call-Zug nicht länger als content-frei:

resp = client.chat.completions.create(model="glm-5.2", messages=msgs, tools=tools)
msg = resp.choices[0].message

# GLM may return assistant text in the same turn as the tool calls.
if msg.content:
    log.debug("preamble: %s", msg.content)   # keep or drop, but don't assume it's empty

msgs.append(msg)
for call in msg.tool_calls:
    result = dispatch(call.function.name, json.loads(call.function.arguments))
    msgs.append({"role": "tool", "tool_call_id": call.id, "content": result})

Wenn dein Loop content als Antwort des Assistants an den Nutzer rendert, zeigst du jetzt vor jedem Tool-Call eine “Lass mich das mal nachsehen”-Zeile. Entscheide, ob du das willst. Wichtig ist: Die Entscheidung liegt bei dir und wird dir nicht durch das Schweigen des Modells abgenommen.

Es denkt laut mit

GLM 5.2 ist ein Reasoning-Modell, und das gilt auch bei der Tool-Nutzung. Ein Tool-Call-Turn bringt sein Reasoning mit, und GLM 5.2 legt es als Text offen. In einer Non-Streaming-Antwort macht es die Token-Abrechnung explizit:

"usage": {
  "prompt_tokens": 224,
  "completion_tokens": 68,
  "completion_tokens_details": { "reasoning_tokens": 30 },
  "total_tokens": 292
}

Fast die Hälfte der Completion war Reasoning – bei einem Request, dessen sichtbare Ausgabe aus zwei kurzen Function Calls besteht. Genau hier laufen die drei Modelle auseinander. GLM 5.2 liefert das Reasoning als reasoning_content plus Token-Zahl. OpenAI berechnet reasoning_tokens in usage, zeigt den Text aber nie. Anthropic zeigt ihn nur als thinking-Blöcke, und auch nur, wenn man Extended Thinking einschaltet. Von den dreien gibt GLM 5.2 standardmäßig am meisten preis.

Zwei Konsequenzen. Erstens die Kosten: Du zahlst diese Reasoning-Tokens bei jedem Tool-Call-Turn, und eine Agent-Schleife besteht aus vielen Turns. Der Reasoning-Effort ist der Regler, der die Zahl bewegt – das haben wir in GLM 5.2: Reasoning Effort Is the Cost Lever behandelt. Zähle die Reasoning-Tokens bei jedem Turn, nicht nur bei der finalen Antwort.

Zweitens die Streaming-Reihenfolge. Beim Streamen schickt GLM zuerst das Reasoning, dann den Preamble-Text, dann die Tool-Calls:

reasoning_content  (many deltas)
content            (a few deltas)
tool_calls         (id + name, then arguments)

Ein Parser, der gegen die normalen OpenAI Chat Completions geschrieben wurde, kennt das Feld reasoning_content nicht und ignoriert diesen ersten Schwall stillschweigend. Meistens kein Problem. Zum Problem wird es, wenn deine UI einen „thinking…“-Status am ersten Content-Delta festmacht, denn das Erste auf der Leitung ist Reasoning, nicht Content – und die Anzeige springt nie um.

Was ein GLM-5.2-Tool-Call-Turn kostet

Das Verhalten ist nur die halbe Wahrheit; die andere Hälfte ist die Rechnung, und eine Agent-Schleife führt denselben Turn viele Male aus. Mit einem festen Prefix (ein System-Prompt von rund 2.000 Tokens plus die Tool-Definitionen) und einer pro Call variierten User-Message, gemessen über zehn warme Turns:

pro warmem Tool-Call-TurnGLM glm-5.2OpenAI gpt-5.5Anthropic claude-opus-4-8
Kosten$0.0009$0.0042$0.0051
Latenz (Median)6.6s1.9s3.1s
Prompt gecacht≈96%≈81%≈97%
Reasoning-Tokens≈2700
Kalt → warm Kosten3.4×2.8×4.9×

GLM 5.2 ist das günstige Modell: pro warmem Turn rund 4,5× billiger als GPT-5.5 und 5,4× billiger als Opus. Es ist auch das langsame, mit der zwei- bis dreieinhalbfachen Latenz, weil es bei jedem Turn Reasoning-Tokens verbraucht, während die beiden anderen für diese Aufgabe keine brauchten. Das ist der Deal: GLM erkauft Kosten mit Latenz, und der Reasoning-Effort ist der Regler dafür.

Caching ist es, was so etwas in einer Schleife überhaupt bezahlbar macht. System-Prompt und Tool-Definitionen machen den Großteil jedes Prompts aus und sind bei jedem Turn identisch. Ist der Prefix einmal gecacht, wird der Turn 2,8× bis 4,9× billiger. Zwei Dinge entscheiden, ob du das auch siehst. GLM und OpenAI cachen den Prefix automatisch; Anthropic cacht nur, was du mit cache_control markierst. Und GLMs Cache wird einen Tick zu spät warm, sodass eine Aufgabe mit drei Schritten den vollen Preis zahlen kann, während eine mit dreißig Schritten gecacht läuft. Die Mechanik steht in Open-Weight LLM Caching.

Wann GLM 5.2 die richtige Wahl ist und wie man es sauber betreibt

Setzen wir die Teile zusammen. GLM 5.2 ist das günstige Modell in dieser Tabelle, gleichzeitig das langsame, und es reasoniert in jedem Turn. Genau dieses Profil zeigt, wo es seinen Platz verdient.

Wo es passt: lange, mehrstufige Agent-Loops, in denen die Kosten dominieren und ein paar Sekunden pro Turn vertretbar sind. Coding-Agents im Hintergrund, CI- und Batch-Automatisierung, unbeaufsichtigt laufende Jobs. Das Reasoning, das es langsam macht, ist auch der Grund, warum es bei echtem Coding und Planning besteht und nicht nur beim trivialen Routing. Caching verstärkt den Vorteil nach der Warm-up-Phase: Eine Aufgabe mit dreißig Schritten amortisiert den Prefix und läuft günstig, während eine mit drei Schritten den vollen Preis zahlen und die Latenz ohne Gegenwert schlucken kann. Nimm GLM 5.2 also für die langen Jobs und halte ein schnelleres Modell für die interaktiven Single-Shot-Calls bereit, bei denen sechs Sekunden pro Turn spürbar sind.

So betreibst du GLM 5.2 sauber. Fünf Gewohnheiten machen einen Loop GLM-tauglich, ohne die OpenAI-API-Fläche zu verlassen:

  • Behandle einen Tool-Call-Turn so, als könnte er content mitführen. Geh nicht davon aus, dass er leer ist.
  • Rechne mit reasoning_content auf der Leitung und reasoning_tokens in usage; plane für beides ein und nutze den Reasoning-Effort-Regler, um Qualität gegen Kosten abzuwägen.
  • Beim Streaming darfst du den UI-State nicht am ersten Content-Delta festmachen, denn das Reasoning kommt zuerst.
  • Gib tool_call_id unverändert zurück; behandle sie als opak, parse oder generiere sie nie neu.
  • Sammle gestreamte arguments nach index, bis der Call geschlossen wird; geh nicht von einer festen Chunk-Anzahl aus.

Zwei Dinge musst du nicht abfangen: GLM gibt parallele Tool-Calls mit einem index aus wie die anderen, und der Round-Trip schließt normal ab. Hänge den Assistant-Turn an, hänge pro Call eine tool-Message mit dessen Ergebnis an, und es endet mit finish_reason: "stop". Halte dabei den cachebaren Prefix über die Turns hinweg byte-stabil; System-Prompt und Tool-Definitionen machen den größten Teil jedes Prompts aus, und ein stabiler Prefix ist genau das, was den Cache von GLM die Kosten tragen lässt, sobald er warmgelaufen ist.

Nichts davon ist exotisch. Es ist die Lücke zwischen „der Request war erfolgreich” und „der Agent-Loop ist korrekt”, und bei GLM ist diese Lücke im Wesentlichen zwei Annahmen breit: dass ein Tool-Call-Turn stumm ist und dass nicht reasoniert wird. Lass diese beiden fallen, halte den Prefix stabil, und ein einziger Loop trägt GLM, GPT und Claude gleichermaßen – wobei GLM es zu einem Bruchteil der Kosten erledigt, überall dort, wo Latenz nicht das ist, was du optimierst.

Disclaimer

Die Zahlen zu Kosten, Latenz und Cache oben wurden am 2026-06-30 über zehn warme Tool-Call-Turns pro Modell gemessen, mit glm-5.2, gpt-5.5 und claude-opus-4-8. Die Kosten stammen aus der gemeldeten Usage; die Latenz ist der Median der Wall-Clock-Zeit und verschiebt sich mit Last und Reasoning-Effort. Modellverhalten und Preise ändern sich, behandle die Zahlen also als Richtwerte und miss gegen deinen eigenen Traffic nach, bevor du dich darauf verlässt.

Quellen

← Zurück zum Blog