一、什么是 Function Calling:给大模型装上“手和眼”
在日常与大模型(如 GLM 系列)的交互中,我们通常会发现它是一个“封闭”的系统——它只能依靠训练数据中的知识来回答问题。如果你问它“今天北京的天气怎么样?”或者“帮我查一下航班 CA1234 的状态”,由于缺乏实时外部数据,模型往往只能用客套话拒绝,或者产生幻觉(胡编乱造一个结果)。
Function Calling(工具调用)机制的出现,就是为了打破这种封闭性。简单来说,它相当于给大模型安装了“手和眼”,允许模型在对话过程中主动向外部发出请求。当模型判断出需要实时信息时,它不再瞎编,而是输出一段结构化的指令,告诉你的代码:“嘿,我需要调用天气查询工具,参数是北京,请帮我执行并告诉我结果。”
二、GLM 工具调用的底层逻辑:不是直接执行,而是“报菜名”
很多人对 Function Calling 有一个根本的误解:以为大模型会自己去执行 Python 代码或者联网。实际上,无论是 GLM 还是其他主流大模型,工具调用的本质是“意图识别与结构化输出”。
在底层原理上,GLM 经过了特定的微调(SFT)和对齐训练。当你在 Prompt 中传入可用的工具列表(通常包含工具名称、描述和参数的 JSON Schema)时,GLM 的注意力机制会将其作为重要的上下文。它的工作流分为三步:
- 意图判断:模型评估用户的问题是否能通过已知的工具解决。
- 参数提取:如果需要调用工具,模型利用其强大的信息抽取能力,从用户的自然语言中提取出符合 JSON Schema 规范的参数。
- 状态等待:模型输出一个特殊的
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 返回的工具调用指令后,整个流程并没有结束,这就好比机器齿轮咬合,还需要转完最后一圈。你的代码必须执行真实的函数,并把返回结果“喂”回给模型,让它基于新信息给出最终的自然语言回复。
这部分流程通常包含以下几个关键动作:
- 解析指令:从模型的 Response 中提取出要调用的函数名(如
get_weather)和参数字典(如{"location": "上海市"})。 - 本地执行:在你的代码里动态运行对应的 Python 函数。
- 拼接历史:将模型的工具调用记录,以及你本地函数执行的结果,按照角色(
tool)追加到messages对话列表中。 - 二次请求:携带更新后的
messages再次请求 GLM API。
# 假设上一轮请求后,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(智能体)开发的第一块、也是最重要的一块基石。