GLM 5.2 函数调用:工具使用、并行调用与智能体工作流
Jul 20, 2026

GLM 5.2 函数调用:工具使用、并行调用与智能体工作流

GLM 5.2 支持 OpenAI 兼容的函数调用与并行工具调用。本文讲清如何定义工具、处理响应、构建多步智能体,并跑通真实的智能体循环。

大多数语言模型 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"

这就是全部所需配置。下面每个代码片段都使用这个 clientMODEL 常量。

第 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。每个条目有三个字段:idfunction.namefunction.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
TTFT1.54 s
Terminal-Bench v2.178%
SWE-bench Pro62.1%
输入价格$1.40 / M tokens
输出价格$4.40 / M tokens
缓存命中价格$0.26 / M tokens
并行工具调用支持
工具选择模式auto、required、none、forced
模型 IDglm-5.2
Base URLhttps://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_callsNone模型选择直接回答检查你的提示词;如果始终需要调用,用 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 请求、数据库查询),始终用 ThreadPoolExecutorasyncio 并行执行。

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

Sources

Start Using GLM 5 Today

Try GLM 5 free — reasoning, coding, agents, and image generation in one platform.