🎁 Novità Registrati gratis, 10 chiamate le offriamo noi. Fino a $1, senza carta.
Chiamate a tool con GLM 5.2 negli agent loop: cosa nasconde 'OpenAI-compatible'

Chiamate a tool con GLM 5.2 negli agent loop: cosa nasconde 'OpenAI-compatible'

Indice
  1. Lo stesso turno, in tre modi
  2. Il testo viaggia insieme alla tool call
  3. Ragiona ad alta voce
  4. Quanto costa un turno con chiamata a un tool su GLM 5.2
  5. Quando conviene usare GLM 5.2 e come farlo bene
  6. Disclaimer
  7. Fonti

Prendi un agent loop già scritto in stile OpenAI, puntalo su GLM 5.2 e quasi tutto funziona: mandi i tools, ricevi i tool_calls, li esegui, rimandi i risultati. Poi succede una cosa che gli esempi dell’SDK non mostrano mai. L’assistant restituisce una riga di testo nello stesso turno delle chiamate ai tool:

{
  "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\"}"}}
      ]
    }
  }]
}

TL;DR

  • Un turno di tool-call a caldo su GLM 5.2 è costato $0.0009 contro $0.0042 su gpt-5.5 e $0.0051 su claude-opus-4-8 (misurato il 2026-06-30).
  • La latenza mediana del turno a caldo di GLM 5.2 era di 6.6s contro 1.9s su gpt-5.5 e 3.1s su opus-4-8: il turno economico è anche quello lento.
  • GLM 5.2 restituisce testo visibile nello stesso turno di tool_calls con finish_reason: "tool_calls"; il contratto di OpenAI lascia content null in quel caso.
  • Ogni turno a caldo di GLM 5.2 trasportava circa 27 reasoning token; gpt-5.5 e claude-opus-4-8 ne trasportavano 0 sullo stesso task.

Dominano due convenzioni, e conviene tenerle entrambe presenti. In quella di OpenAI mandi gli schemi delle funzioni, ricevi i tool_calls e rispondi con un messaggio tool per ogni chiamata, agganciato tramite 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"})

Quella di Anthropic ha una forma diversa: i tool portano un input_schema, il modello emette blocchi tool_use e tu rispondi con un blocco tool_result:

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 parla il dialetto di OpenAI.

Nel contratto di OpenAI, message.content è null quando finish_reason è tool_calls. Molti agent loop ci si appoggiano: ramificano su “contenuto oppure chiamate ai tool”, loggano content come risposta finale, oppure verificano che sia vuoto. GLM ti consegna entrambi in una volta, ed è proprio questo assunto a saltare per primo.

Il comportamento qui descritto è stato registrato da richieste di tool calling reali verso glm-5.2, con gpt-5.5 e claude-opus-4-8 fatti girare sullo stesso task come riferimento. In breve: GLM 5.2 usa la superficie dell’API di OpenAI, ma su un paio di aspetti si comporta più come Claude che come GPT, e a inciampare è proprio il loop pensato per OpenAI.

Lo stesso turno, in tre modi

Stesso prompt, stessi due tool, tre modelli:

GLM (glm-5.2)OpenAI (gpt-5.5)Anthropic (claude-opus-4-8)
Superficie APIOpenAI chat-completionsOpenAI chat-completionsAnthropic messages
Testo nel turno della tool-callpreambolo in content (non nullo)content è nullun blocco text prima di tool_use
Reasoning in quel turnoesposto: reasoning_content + reasoning_tokensnascosto; solo reasoning_tokens in usagesolo come blocco thinking, se lo abiliti
Tool call parallelesì, con indexsì, più blocchi tool_use
Segnale di completamentofinish_reason: "tool_calls"finish_reason: "tool_calls"stop_reason: "tool_use"
Prefisso id della tool-callcall_…call_…toolu_…

I loop si rompono su due righe: il testo nel turno della tool-call e il reasoning che compare in quel turno. Il resto è rassicurantemente noioso.

Il testo viaggia insieme alla tool call

GLM 5.2 emette abitualmente un breve preambolo nel content dell’assistant insieme alle tool_calls, con finish_reason: "tool_calls". Non è un errore e non capita ogni tanto.

Ecco lo stesso turno dai tre modelli, ridotto alla parte che cambia:

// 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 lascia content a null; GLM lo riempie; Anthropic ci ha sempre messo un blocco text. Quindi GLM prende il formato di trasporto di OpenAI e ci aggiunge l’abitudine di Anthropic di raccontare prima di agire, e a farsi cogliere impreparato è il loop scritto per OpenAI. La correzione è piccola, ma va fatta di proposito. Smetti di considerare il turno di una tool-call come privo di contenuto:

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})

Se il tuo loop mostra content all’utente come risposta dell’assistant, ora vedrai una riga tipo “let me check that” prima di ogni tool call. Decidi se la vuoi. Il punto è che la decisione è tua, non qualcosa che il silenzio del modello prende al posto tuo.

Ragiona ad alta voce

GLM 5.2 è un modello di reasoning, e questo non si ferma durante l’uso dei tool. Un turno con chiamata a un tool porta con sé il reasoning, e GLM 5.2 lo espone come testo. In una risposta non-streaming il conteggio dei token lo rende evidente:

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

Quasi metà del completion era reasoning, su una richiesta il cui output visibile sono due brevi function call. Qui è dove i tre modelli divergono. GLM 5.2 ti dà il reasoning come reasoning_content più un conteggio di token. OpenAI fattura i reasoning_tokens nel campo usage ma non mostra mai il testo. Anthropic lo mostra solo come blocchi thinking, e solo se abiliti l’extended thinking. GLM 5.2 è il più esposto dei tre per default.

Due conseguenze. La prima è il costo: paghi quei reasoning token sui turni con chiamata a un tool, e un loop di un agente è fatto di molti turni. Il reasoning effort è la manopola che sposta quel numero, come abbiamo visto in GLM 5.2: Reasoning Effort Is the Cost Lever. Conta i reasoning token a ogni turno, non solo sulla risposta finale.

La seconda è l’ordine nello streaming. Quando fai streaming della richiesta, GLM invia prima il reasoning, poi il testo di preambolo, poi le chiamate ai tool:

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

Un parser scritto per le chat completions standard di OpenAI non conosce il campo reasoning_content e ignora silenziosamente quella raffica iniziale. Di solito va bene. Smette di andare bene se la tua UI mostra uno stato “thinking…” agganciato al primo content delta: la prima cosa che arriva sul filo è reasoning, non content, e l’indicatore non scatta mai.

Quanto costa un turno con chiamata a un tool su GLM 5.2

Il comportamento è metà della storia; l’altra metà è il conto, e un loop di un agente ripete lo stesso turno molte volte. Con un prefisso fisso (un system prompt di circa 2.000 token più le definizioni dei tool) e il messaggio utente variato a ogni chiamata, misurato su dieci turni caldi:

per turno caldo con chiamata a un toolGLM glm-5.2OpenAI gpt-5.5Anthropic claude-opus-4-8
Costo$0.0009$0.0042$0.0051
Latenza (mediana)6.6s1.9s3.1s
Prompt in cache≈96%≈81%≈97%
Reasoning token≈2700
Costo cold → warm3.4×2.8×4.9×

GLM 5.2 è quello economico: circa 4,5× più economico di GPT-5.5 e 5,4× più economico di Opus per turno caldo. È anche quello lento, da due a tre volte e mezzo la loro latenza, perché spende reasoning token a ogni turno mentre gli altri due non ne hanno spesi su questo compito. È questo il compromesso: GLM compra costo con la latenza, e il reasoning effort è la manopola che lo regola.

È il caching a rendere sostenibile qualunque di questi in un loop. Il system prompt e le definizioni dei tool sono la maggior parte di ogni prompt e sono identici a ogni turno, quindi una volta che il prefisso è in cache il turno costa da 2,8× a 4,9× meno. Due cose determinano se lo ottieni. GLM e OpenAI mettono in cache il prefisso automaticamente; Anthropic mette in cache solo ciò che marchi con cache_control. E la cache di GLM si scalda con un attimo di ritardo, quindi un compito di tre passi può pagare il prezzo pieno mentre uno di trenta gira in cache. I dettagli sono in Open-Weight LLM Caching.

Quando conviene usare GLM 5.2 e come farlo bene

Mettiamo insieme i pezzi. Nella tabella GLM 5.2 è il modello economico e quello lento, e ragiona a ogni turno. Da questo profilo si capisce dove dà il meglio.

Dove ha senso usarlo: loop agentici lunghi e multi-step, dove il costo pesa più di tutto e qualche secondo per turno è accettabile. Agenti di coding in background, CI e automazioni batch, job che girano senza supervisione. Il ragionamento che lo rende lento è anche il motivo per cui regge su coding e planning reali, invece di limitarsi al routing banale. Il caching rafforza l’argomento una volta superato il warm-up: un task da trenta step ammortizza il prefisso e costa poco, mentre uno da tre step rischia di pagare il prezzo pieno e di subire la latenza per niente. Quindi usa GLM 5.2 sui job lunghi, e tieni un modello più veloce per le chiamate interattive one-shot, dove sei secondi a turno si sentono.

Come far girare bene GLM 5.2. Cinque abitudini rendono un loop pronto per GLM senza uscire dalla superficie dell’API OpenAI:

  • Considera che un turno con tool call può contenere content. Non dare per scontato che sia vuoto.
  • Aspettati reasoning_content sul filo e reasoning_tokens in usage; prevedili entrambi, e usa la manopola del reasoning-effort per bilanciare qualità e costo.
  • In streaming, non legare lo stato della UI al primo delta di content: il reasoning arriva prima.
  • Rimanda indietro tool_call_id identico; trattalo come opaco, non fare parsing e non rigenerarlo.
  • Accumula gli arguments in streaming per index finché la call non si chiude; non assumere un numero fisso di chunk.

Due cose contro cui non devi difenderti: GLM emette tool call parallele con un index come gli altri, e il round-trip si chiude normalmente. Aggiungi il turno dell’assistant, aggiungi un messaggio tool per ogni call con il suo risultato, e finisce con finish_reason: "stop". Nel frattempo mantieni stabile a livello di byte il prefisso cacheabile tra un turno e l’altro: il system prompt e le definizioni dei tool sono la parte più grossa di ogni prompt, ed è proprio un prefisso stabile a permettere alla cache di GLM di abbattere il costo una volta che si scalda.

Niente di esotico. È la differenza tra “la richiesta va a buon fine” e “il loop dell’agente è corretto”, e su GLM questa differenza sta quasi tutta in due assunzioni: che un turno con tool call sia silenzioso, e che non stia pensando. Elimina queste due, tieni stabile il prefisso, e un unico loop regge GLM, GPT e Claude allo stesso modo, con GLM che fa il lavoro a una frazione del costo ovunque la latenza non sia ciò che stai ottimizzando.

Disclaimer

I dati su costo, latenza e cache riportati sopra sono stati misurati il 2026-06-30 su dieci turni con tool call a caldo per ogni modello, usando glm-5.2, gpt-5.5 e claude-opus-4-8. Il costo è preso dall’usage riportato; la latenza è la mediana wall-clock e varia con il carico e il reasoning effort. Il comportamento dei modelli e i prezzi cambiano nel tempo, quindi prendi questi numeri come indicativi e rimisurali sul tuo traffico prima di farci affidamento.

Fonti

← Torna al blog