🎁 新人 免费注册,送 10 次调用,最高 $1,免绑卡。
Agent 循环中的 GLM 5.2 工具调用:“兼容 OpenAI”没有告诉你的事

Agent 循环中的 GLM 5.2 工具调用:“兼容 OpenAI”没有告诉你的事

目录
  1. 同一轮,三种返回方式
  2. 文本与工具调用一起返回
  3. 它会把推理过程直接暴露出来
  4. GLM 5.2 单轮工具调用的成本
  5. 什么时候该用 GLM 5.2,以及如何用好它
  6. 免责声明
  7. 资料来源

把现有的 OpenAI 风格 Agent 循环接到 GLM 5.2 上,大部分逻辑都能直接工作:发送 tools,收到 tool_calls,执行工具,再发回结果。但随后会出现一个 SDK 示例从未展示过的情况。Assistant 在返回工具调用的同一轮里,还会返回一行文本:

{
  "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

  • GLM 5.2 单次热缓存工具调用的成本为 $0.0009,gpt-5.5 为 $0.0042,claude-opus-4-8 为 $0.0051(测量日期:2026-06-30)。
  • GLM 5.2 热缓存轮次的延迟中位数为 6.6s,gpt-5.5 为 1.9s,opus-4-8 为 3.1s:成本低,但速度也慢。
  • GLM 5.2 会在返回 tool_callsfinish_reason: "tool_calls" 的同一轮中返回可见文本;按 OpenAI 的约定,此时 content 为 null。
  • GLM 5.2 每个热缓存轮次约有 27 个推理 token;相同任务中,gpt-5.5 和 claude-opus-4-8 均为 0。

目前主要有两套工具调用约定,理解差异很重要。OpenAI 的方式是:发送函数 schema,收到 tool_calls 后,针对每个调用返回一条 tool 消息,并通过 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"})

Anthropic 的结构不同:工具使用 input_schema,模型输出 tool_use block,调用方再用 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 使用的是 OpenAI 这套协议。

按 OpenAI 的约定,当 finish_reasontool_calls 时,message.content 应为 null。很多 Agent 循环都依赖这个前提:用“内容还是工具调用”来做分支、把 content 记为最终答案,或者直接断言它为空。GLM 会同时返回两者,这个假设首先会失效。

本文行为来自对 glm-5.2 的真实工具调用请求,并使用 gpt-5.5claude-opus-4-8 执行相同任务作为对照。简而言之,GLM 5.2 使用 OpenAI 的 API 形式,但在几个方面更像 Claude,而不是 GPT。基于 OpenAI 行为编写的循环反而最容易出问题。

同一轮,三种返回方式

相同的 prompt、相同的两个工具、三个模型:

GLM (glm-5.2)OpenAI (gpt-5.5)Anthropic (claude-opus-4-8)
API 形式OpenAI chat-completionsOpenAI chat-completionsAnthropic messages
工具调用轮次中的文本content 前置文本(非 null)contentnulltool_use 前有一个 text block
当前轮次的推理暴露:reasoning_content + reasoning_tokens隐藏;仅在 usage 中提供 reasoning_tokens仅在启用后以 thinking block 返回
并行工具调用支持,带 index支持支持,包含多个 tool_use block
结束信号finish_reason: "tool_calls"finish_reason: "tool_calls"stop_reason: "tool_use"
工具调用 ID 前缀call_…call_…toolu_…

真正会让循环出错的是两行:工具调用轮次中带有文本,以及当前轮次会暴露推理。其他方面都没有意外。

文本与工具调用一起返回

GLM 5.2 经常会在 tool_calls 旁边返回一段简短的 Assistant content 前置文本,同时将 finish_reason 设为 tool_calls。这不是错误,也不是偶发现象。

下面是三个模型在同一轮中的响应,仅保留差异部分:

// 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 将 content 留为 null;GLM 会填入内容;Anthropic 一直会在这里放一个 text block。也就是说,GLM 使用 OpenAI 的传输格式,却保留了 Anthropic 在执行工具前先描述动作的习惯。基于 OpenAI 行为编写的循环因此容易措手不及。修复并不复杂,但需要明确处理:不要再假设工具调用轮次一定没有内容。

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

如果循环会把 content 作为 Assistant 回复展示给用户,那么现在每次工具调用前都会出现一句“我来查一下”。是否展示由你决定。关键在于,这应该是显式决策,不能再依赖模型保持沉默。

它会把推理过程直接暴露出来

GLM 5.2 是推理模型,工具调用期间也不会暂停推理。工具调用轮次会附带推理内容,而且 GLM 5.2 会以文本形式将其暴露出来。在非 streaming 响应中,token 统计清楚地体现了这一点:

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

这次请求的可见输出只有两个简短的函数调用,但推理占了 completion 的近一半。三个模型在这里的行为完全不同。GLM 5.2 会通过 reasoning_content 返回推理文本,并提供 token 数量。OpenAI 会在 usage 中对 reasoning_tokens 计费,但不会返回文本。Anthropic 只会通过 thinking block 展示,而且必须先启用 extended thinking。默认情况下,GLM 5.2 暴露的内容最多。

这会带来两个影响。首先是成本:工具调用轮次中的推理 token 也要付费,而 Agent 循环通常包含很多轮。reasoning effort 是控制这项成本的主要参数,详见 GLM 5.2:Reasoning Effort 才是成本开关。每一轮都要统计推理 token,而不只是最终回答。

其次是 streaming 顺序。使用 streaming 请求时,GLM 会先发送推理,再发送前置文本,最后发送工具调用:

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

按标准 OpenAI chat completions 编写的解析器不认识 reasoning_content 字段,通常会静默忽略开头这一段。大多数情况下没问题。但如果 UI 的“思考中……”状态依赖第一个 content delta 来切换,就会出错。网络流中最先到达的是推理,而不是 content,状态指示器将无法切换。

GLM 5.2 单轮工具调用的成本

行为只是一半,账单则是另一半,而 Agent 循环会多次执行相同的轮次。使用固定前缀(约 2,000 个 token 的 system prompt 加工具定义),每次调用只更改用户消息,并测量十个热缓存轮次后,结果如下:

每个热缓存工具调用轮次GLM glm-5.2OpenAI gpt-5.5Anthropic claude-opus-4-8
成本$0.0009$0.0042$0.0051
延迟(中位数)6.6s1.9s3.1s
Prompt 缓存率≈96%≈81%≈97%
推理 token≈2700
冷缓存到热缓存的成本差3.4×2.8×4.9×

GLM 5.2 成本最低:每个热缓存轮次约比 GPT-5.5 便宜 4.5×,比 Opus 便宜 5.4×。但它的速度也最慢,延迟是另外两者的 2 到 3.5 倍,因为 GLM 每轮都会消耗推理 token,而另外两个模型在此任务中都没有消耗。取舍很明确:GLM 用延迟换成本,reasoning effort 则用于调节两者。

缓存让循环中的这些模型变得可负担。system prompt 和工具定义占据了每次 prompt 的大部分,而且每轮完全相同。前缀进入缓存后,单轮成本会降低 2.8× 到 4.9×。能否获得这种收益取决于两点。GLM 和 OpenAI 会自动缓存前缀;Anthropic 只缓存使用 cache_control 标记的内容。此外,GLM 的缓存会晚一拍生效,因此三步任务可能全程按原价计费,而三十步任务则能在缓存命中后降低成本。具体机制见 开放权重 LLM 的缓存机制

什么时候该用 GLM 5.2,以及如何用好它

综合这些特点,GLM 5.2 在表中成本最低、速度最慢,而且每轮都会推理。它适合的场景也由此变得清晰。

它适合长时间、多步骤的 Agent 循环:成本优先,并且每轮多等几秒可以接受。例如后台编程 Agent、CI、批处理自动化,以及无人值守任务。持续推理虽然让它变慢,却也让它能处理真正的编程和规划任务,而不只适合简单路由。缓存生效后,任务越长,优势越明显:三十步任务可以摊薄前缀成本,并以较低成本持续运行;三步任务则可能全程按原价付费,还要白白承受延迟。因此,长任务适合 GLM 5.2;交互式、单次调用则应保留速度更快的模型,因为每轮六秒的延迟会很明显。

要用好 GLM 5.2,只需养成五个习惯,无须离开 OpenAI API 形式:

  • 工具调用轮次可能同时携带 content,不要断言它为空。
  • 预期传输中出现 reasoning_content,并在 usage 中出现 reasoning_tokens;两者都要纳入预算,再通过 reasoning-effort 参数权衡质量和成本。
  • 在 streaming 模式下,不要用第一个 content delta 驱动 UI 状态,因为推理会先到达。
  • 原样回传 tool_call_id;将其视为不透明值,不要解析或重新生成。
  • index 累积 streaming 返回的 arguments,直到调用结束;不要假设 chunk 数量。

有两件事不需要额外防御:GLM 和其他模型一样,会通过 index 输出并行工具调用;整个调用往返也能正常结束。追加 Assistant 轮次,再为每个调用追加一条包含结果的 tool 消息,最终就会以 finish_reason: "stop" 结束。同时要确保各轮之间可缓存前缀的字节完全一致。system prompt 和工具定义占据了每次 prompt 的大部分,只有稳定的前缀才能让 GLM 缓存预热后持续节省成本。

这些都不复杂。区别只在于“请求成功”和“Agent 循环正确”并不是一回事。对 GLM 来说,两者之间主要隔着两个错误假设:工具调用轮次没有文本,以及工具调用期间不会推理。去掉这两个假设,并保持前缀稳定,同一套循环就能同时支持 GLM、GPT 和 Claude。在延迟不是首要优化目标的场景下,GLM 还能把成本降到另外两者的一小部分。

免责声明

以上成本、延迟和缓存数据测量于 2026-06-30。每个模型均执行了十个热缓存工具调用轮次,使用的模型分别为 glm-5.2gpt-5.5claude-opus-4-8。成本来自上报的 usage;延迟为端到端耗时的中位数,会随负载和 reasoning effort 变化。模型行为和价格都会变化,这些数据仅供参考。在用于实际决策前,请根据自己的流量重新测量。

资料来源

← 返回博客