大多数语言模型 API 都逼你在聊天机器人和工作流引擎之间二选一。GLM 5.2 绕开了这个取舍:它的 OpenAI 兼容函数调用接口让你把一组工具交给模型,然后坐等它自己决定调用哪些、按什么顺序、带什么参数——包括在单轮里调用多个工具。本教程会带你走完整个过程,从定义第一个工具,到运行一个完整的多步智能体循环,直到模型没有更多工作可做才终止。
前置条件
- 一个 Z.ai API key(在 z.ai 注册)
- Python 3.9+,并安装
openai包(pip install openai) - 对 OpenAI chat completions API 的基本了解
GLM 5.2 的函数调用与 OpenAI 使用相同的线上格式,所以你现有的任何工具调用代码,只需改两个环境变量就能指向 Z.ai 端点,零重构。
第 1 步:配置客户端
因为 GLM 5.2 讲的是 OpenAI 协议,openai Python SDK 开箱即用。把它指向 Z.ai 的 base URL 并填入你的 API key:
from openai import OpenAI
client = OpenAI(
api_key="YOUR_Z_AI_KEY",
base_url="https://api.z.ai/v1",
)
MODEL = "glm-5.2"
这就是全部所需配置。下面每个代码片段都使用这个 client 和 MODEL 常量。
第 2 步:定义你的工具
工具在 tools 列表里用 JSON Schema 描述。每个条目包含 type: "function" 和一个嵌套的 function 对象,指定函数名、模型在推理时读取的通俗描述,以及校验模型允许传回内容的 parameters schema。
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": (
"Returns current weather conditions for a given city. "
"Use this when the user asks about weather or temperature."
),
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name, e.g. 'Tokyo' or 'San Francisco'",
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature unit. Defaults to celsius.",
},
},
"required": ["city"],
},
},
},
{
"type": "function",
"function": {
"name": "search_web",
"description": "Fetches top search results for a query. Use for factual lookups.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query string.",
},
"num_results": {
"type": "integer",
"description": "Number of results to return. Default 5.",
},
},
"required": ["query"],
},
},
},
]
写描述要像给初级同事写 docstring 一样。模型会逐字阅读这些描述来决定哪个工具适配哪种情况,所以精确性比简洁更重要。
第 3 步:发起初始 API 调用
传入 tools 列表并设置 tool_choice 来控制模型处理工具选择的方式:
tool_choice 值 | 行为 |
|---|---|
"auto" | 模型在有用时调用工具;也可以直接回答 |
"required" | 模型必须至少调用一个工具 |
{"type":"function","function":{"name":"X"}} | 强制调用特定函数 |
"none" | 本次调用禁用工具 |
对大多数智能体循环来说,"auto" 是正确的默认值:
messages = [
{"role": "user", "content": "What is the weather in Tokyo and Berlin right now?"},
]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
print(message)
如果模型决定调用工具,message.content 会是 None 或空字符串,而 message.tool_calls 会包含一个调用对象列表。
第 4 步:处理响应中的工具调用
检查 message.tool_calls。每个条目有三个字段:id、function.name 和 function.arguments(一个 JSON 字符串,不是字典——始终要解析它):
import json
def run_tool(name: str, arguments: dict) -> str:
"""Dispatch to the real implementation and return a string result."""
if name == "get_weather":
city = arguments["city"]
units = arguments.get("units", "celsius")
# Replace with a real weather API call
return json.dumps({"city": city, "temp": 22, "units": units, "condition": "sunny"})
elif name == "search_web":
query = arguments["query"]
num = arguments.get("num_results", 5)
# Replace with a real search API call
return json.dumps({"query": query, "results": [f"Result {i} for {query}" for i in range(num)]})
else:
return json.dumps({"error": f"Unknown tool: {name}"})
# Add the assistant's message to history first
messages.append(message)
# Execute each tool call and append a tool-role message for each
if message.tool_calls:
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
result = run_tool(name, args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
这里有两件事是必须的:助手消息必须先于工具结果追加,而且每条工具结果消息必须引用触发它的那次调用的精确 tool_call_id。模型用这些 ID 把结果与调用对应起来,尤其是涉及并行调用时。
第 5 步:并行工具调用
注意,上面东京/柏林的天气问题可以用两个并行的调用回答。GLM 5.2 会在单个响应里同时发出这两个调用,而不是先问一个、等结果、再问另一个。你的循环已经能正确处理这种情况,因为它遍历的是 message.tool_calls——并行调用只是这个列表里的多个条目。
# The model may return something like:
# tool_calls = [
# ToolCall(id="call_abc", function=Function(name="get_weather", arguments='{"city":"Tokyo"}')),
# ToolCall(id="call_xyz", function=Function(name="get_weather", arguments='{"city":"Berlin"}')),
# ]
如果每个工具调用都要访问慢速的外部 API,你可以用 concurrent.futures.ThreadPoolExecutor 并行执行:
from concurrent.futures import ThreadPoolExecutor
def execute_tool_call(tc):
name = tc.function.name
args = json.loads(tc.function.arguments)
result = run_tool(name, args)
return tc.id, result
if message.tool_calls:
with ThreadPoolExecutor() as executor:
futures = {executor.submit(execute_tool_call, tc): tc for tc in message.tool_calls}
for future in futures:
call_id, result = future.result()
messages.append({
"role": "tool",
"tool_call_id": call_id,
"content": result,
})
这个模式把延迟按并行调用数量成比例压缩——两次 500ms 的天气请求变成一次 500ms 等待,而不是串行的 1000ms。
第 6 步:构建完整的智能体循环
一个完整的智能体循环会一直运行,直到模型返回一个没有工具调用的响应——这意味着它已经收集齐所需信息,准备好回答用户了:
import json
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI
client = OpenAI(api_key="YOUR_Z_AI_KEY", base_url="https://api.z.ai/v1")
MODEL = "glm-5.2"
def agent_loop(user_message: str, tools: list, max_iterations: int = 10) -> str:
messages = [{"role": "user", "content": user_message}]
for iteration in range(max_iterations):
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
message = response.choices[0].message
messages.append(message)
# No tool calls → model is done
if not message.tool_calls:
return message.content
# Execute tool calls (parallel)
def execute(tc):
name = tc.function.name
args = json.loads(tc.function.arguments)
return tc.id, run_tool(name, args)
with ThreadPoolExecutor() as ex:
for call_id, result in ex.map(execute, message.tool_calls):
messages.append({
"role": "tool",
"tool_call_id": call_id,
"content": result,
})
raise RuntimeError(f"Agent did not finish within {max_iterations} iterations")
answer = agent_loop(
"Compare the current weather in Tokyo and Berlin, then search for the best time to visit each city.",
tools=tools,
)
print(answer)
max_iterations 守卫防止循环失控。实践中,GLM 5.2 在 Terminal-Bench v2.1 上 78% 的得分——一个考察真实多步 shell 任务的基准——意味着它能快速收敛,避免无谓的额外调用。1M token 上下文窗口(1,048,576 tokens)意味着即使包含数百对工具调用/结果的长智能体历史也不会溢出,不会被强制在任务中途截断。
第 7 步:从工具结果产出结构化输出
协议里工具结果是纯字符串,但模型天然擅长处理 JSON。从你的工具实现里返回结构化 JSON,能让模型获得丰富的推理数据:
# Instead of: return "The temperature is 22 degrees"
# Return:
return json.dumps({
"city": "Tokyo",
"temperature": 22,
"units": "celsius",
"humidity": 68,
"condition": "partly cloudy",
"wind_kph": 14,
})
对于返回大 payload 的数据库查询或 API 响应,GLM 5.2 的 1M 上下文意味着你可以返回完整响应而不是截断它,让模型自己挑出相关字段,而不必在你的工具包装层里预过滤。
与智能体使用相关的 GLM 5.2 规格
| 属性 | 值 |
|---|---|
| 上下文窗口 | 1,048,576 tokens(1M) |
| 速度 | 每秒 158 tokens |
| TTFT | 1.54 s |
| Terminal-Bench v2.1 | 78% |
| SWE-bench Pro | 62.1% |
| 输入价格 | $1.40 / M tokens |
| 输出价格 | $4.40 / M tokens |
| 缓存命中价格 | $0.26 / M tokens |
| 并行工具调用 | 支持 |
| 工具选择模式 | auto、required、none、forced |
| 模型 ID | glm-5.2 |
| Base URL | https://api.z.ai/v1 |
以每秒 158 tokens 的速度,GLM 5.2 在 Artificial Analysis 的前沿模型吞吐量排名中位列第三。在智能体循环里,速度会复利放大:如果每轮模型调用从 1–2 秒降到 0.3–0.5 秒,一个十步智能体就能在五秒内完成而不是二十秒。再配合 $1.40/M 的输入价格和重复系统提示词、工具 schema 的 $0.26/M 缓存,即使高吞吐生产负载,单次智能体运行的成本也保持可预测。
关于如何优化长智能体会话的开销,详见 GLM 5.2 定价。
常见问题
| 问题 | 可能原因 | 解决办法 |
|---|---|---|
定义了工具但 tool_calls 是 None | 模型选择直接回答 | 检查你的提示词;如果始终需要调用,用 tool_choice="required" |
function.arguments 上出现 json.JSONDecodeError | 模型生成了格式错误的 JSON | 加 try/except;记录并跳过错误调用;把错误作为工具结果反馈回去 |
| 工具结果没有匹配到调用 | tool_call_id 缺失或错误 | 原样复制 tool_call.id;不要自己生成 |
| 循环永不终止 | 智能体一直调用工具 | 加 max_iterations;检查工具结果里是否含有模型试图修复的错误 |
| 超出上下文窗口 | 历史过长 | GLM 5.2 的 1M 上下文让这很少发生;万一发生,总结较早的工具结果 |
| 模型忽略某个工具 | 描述写得差 | 重写 description 字段,更明确地说明何时该用它 |
FAQ
GLM 5.2 的函数调用能配合 OpenAI Python SDK 用吗?
能。把 base_url="https://api.z.ai/v1" 和 api_key 设为你的 Z.ai key,然后像用 OpenAI 一样使用 SDK。不需要其他改动。TypeScript、cURL 以及任何能设置 base URL 的 HTTP 客户端同理。
模型能在一次响应里多次调用同一个函数吗?
能。如果模型需要查五个城市的天气,它可以在单个 tool_calls 列表里发出五次 get_weather 调用。执行它们,并在下一轮模型调用前返回全部五条 tool-role 消息。
我的工具抛异常了怎么办?
用 try/except 包住你的工具实现,并把一段描述性错误字符串作为工具结果返回。模型会读取错误并决定是用不同参数重试、改调其他工具,还是向用户解释它无法完成任务。
怎么防止模型调用我没定义的工具?
GLM 5.2 只能调用你传给 API 的 tools 列表里出现的函数。它无法凭空发明新的函数名。如果还想进一步限制——例如只能调用某个函数——使用强制的 tool_choice 格式:{"type": "function", "function": {"name": "your_function"}}。
并行工具调用有延迟成本吗?
模型在单次响应中返回多个调用不会增加延迟。你的执行延迟取决于怎么跑工具:串行执行是各次耗时相加,并行执行则以上限为最慢的单次调用。对 I/O 型工具(HTTP 请求、数据库查询),始终用 ThreadPoolExecutor 或 asyncio 并行执行。
1M 上下文窗口如何影响智能体设计?
大多数智能体框架都会加截断逻辑来避免超出上下文限制。有了 GLM 5.2 的 1,048,576-token 窗口,包含数百对工具调用/结果的历史可以完整装下、无需截断。这简化了你的智能体代码,并确保模型在思考下一步时始终拥有完整历史。
函数调用能配合流式输出用吗?
能。在 create 调用里设置 stream=True。工具调用片段会通过 delta.tool_calls 增量到达;累加它们直到流结束,然后像非流式场景一样分发并循环。线上格式与 OpenAI 的流式工具调用协议一致。
下一步
- 阅读 GLM 5.2 概览 获取完整基准背景与架构细节
- 查看 GLM 5.2 定价 了解不同智能体工作负载的成本估算
- 如果你想用一个 API key 访问多个模型,可以探索 OpenRouter 作为备选端点
试用 GLM 5.2——无需 API key:glm5.app/chat。




