一、什么是 Function Calling:给大模型装上“手和眼”

在日常与大模型(如 GLM 系列)的交互中,我们通常会发现它是一个“封闭”的系统——它只能依靠训练数据中的知识来回答问题。如果你问它“今天北京的天气怎么样?”或者“帮我查一下航班 CA1234 的状态”,由于缺乏实时外部数据,模型往往只能用客套话拒绝,或者产生幻觉(胡编乱造一个结果)。

Function Calling(工具调用)机制的出现,就是为了打破这种封闭性。简单来说,它相当于给大模型安装了“手和眼”,允许模型在对话过程中主动向外部发出请求。当模型判断出需要实时信息时,它不再瞎编,而是输出一段结构化的指令,告诉你的代码:“嘿,我需要调用天气查询工具,参数是北京,请帮我执行并告诉我结果。”

二、GLM 工具调用的底层逻辑:不是直接执行,而是“报菜名”

很多人对 Function Calling 有一个根本的误解:以为大模型会自己去执行 Python 代码或者联网。实际上,无论是 GLM 还是其他主流大模型,工具调用的本质是“意图识别与结构化输出”

在底层原理上,GLM 经过了特定的微调(SFT)和对齐训练。当你在 Prompt 中传入可用的工具列表(通常包含工具名称、描述和参数的 JSON Schema)时,GLM 的注意力机制会将其作为重要的上下文。它的工作流分为三步:

  1. 意图判断:模型评估用户的问题是否能通过已知的工具解决。
  2. 参数提取:如果需要调用工具,模型利用其强大的信息抽取能力,从用户的自然语言中提取出符合 JSON Schema 规范的参数。
  3. 状态等待:模型输出一个特殊的 tool_calls 响应,然后暂停生成,把控制权交还给你的业务代码。
提示:大模型在这里扮演的是一个“路由器”和“参数解析器”的角色,真正去执行网络请求或数据库操作的,永远是你在本地的业务代码。

三、实战演练:如何构造一个 GLM 工具调用请求

要想启用 GLM 的工具调用,你需要在请求 API 时带上 tools 参数。理解这个参数的结构是掌握 Function Calling 的核心。下面是一个使用 Python 构造工具调用的真实场景模拟。

我们需要向模型描述一个查询天气的工具。这个描述必须足够清晰,让模型不仅能看懂,还能严格按照格式提取参数:

import json

# 1. 定义业务中真实的函数
def get_weather(location: str, unit: str = "celsius"):
    # 实际业务中,这里应该调用真实的天气 API
    return f"{location} 今天晴,气温 25 度{unit}。"

# 2. 用 JSON Schema 向 GLM 描述这个工具(极其重要的一步)
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的实时天气信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市或地区名,例如:北京市"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"] # 告诉模型 location 是必填项
            }
        }
    }
]

# 3. 模拟用户提问
messages = [{"role": "user", "content": "我今天要去上海出差,需要穿外套吗?"}]

# 伪代码:向 GLM 发起请求
# response = glm_client.chat.completions.create(
#     model="glm-4", messages=messages, tools=tools
# )

在这个例子中,description 字段是模型判断“要不要用这个工具”的依据,而 parameters 则是模型“怎么填参数”的蓝图。

四、闭环控制:如何把工具结果喂回给大模型

当你的代码收到了 GLM 返回的工具调用指令后,整个流程并没有结束,这就好比机器齿轮咬合,还需要转完最后一圈。你的代码必须执行真实的函数,并把返回结果“喂”回给模型,让它基于新信息给出最终的自然语言回复。

这部分流程通常包含以下几个关键动作:

# 假设上一轮请求后,GLM 返回了如下结构(已解析为字典):
tool_call_response = {
    "name": "get_weather",
    "arguments": '{"location": "上海市", "unit": "celsius"}'
}

# 本地动态执行函数
args = json.loads(tool_call_response["arguments"])
weather_result = get_weather(**args)

# 将工具调用的全过程拼入 messages 历史中
messages.append({
    "role": "assistant", 
    "content": None, 
    "tool_calls": [{"id": "call_123", "type": "function", "function": tool_call_response}]
})
# 追加工具执行结果
messages.append({
    "role": "tool", 
    "tool_call_id": "call_123", # 必须与上一步的 id 对应
    "content": weather_result   # "上海市 今天晴,气温 25 度celsius。"
})

# 再次发起请求,让 GLM 总结输出
# final_response = glm_client.chat.completions.create(
#     model="glm-4", messages=messages, tools=tools
# )
# 此时 GLM 会结合天气结果回复用户:"上海今天 25 度,比较温暖,不需要穿厚外套..."
提示:tool_call_id 是连接请求与响应的生命线。大模型在处理多轮对话或并行调用时,必须依赖这个 ID 来确认“这段工具运行的结果是对应刚才哪一次请求的”,漏掉它会导致 API 直接报错。

五、总结:从“对答如流”到“知行合一”

梳理下来,GLM 的 Function Calling 机制并不神秘,它本质上是基于大模型强大的指令遵循代码生成(JSON 也是代码的一种)能力衍生出的工程化标准。

在这个机制下,大模型不再仅仅是一个“懂很多知识的嘴”,而是成为了一个智能中枢。它学会了在自身能力不足时向外求助,学会了如何精确地传递参数,也学会了如何整合外部系统的反馈。对于开发者而言,熟练掌握这套“提出工具 -> 执行工具 -> 反馈结果”的闭环逻辑,就是踏入 AI Agent(智能体)开发的第一块、也是最重要的一块基石。