问大模型“北京今天多少度”,它只能用训练数据里的旧知识猜,或者老实承认不知道。但如果告诉它“我这里有个查天气的接口”,它会说:“帮我调用 get_weather,城市参数填北京。”你的程序执行调用,把结果递回去,它再组织成一句自然的回答。Function Calling(函数调用)让模型从“只会聊天”进化为“会办事”——它是大模型连接外部世界的第一块基石,也是一切 Agent 系统的地基。本文按“工作原理 → 工具定义 → 调用循环 → 设计最佳实践 → 踩坑 → 总结”组织全文。
1、概述
Function Calling 于 2023 年 6 月由 OpenAI 首次在 API 中引入,随后成为全行业标准能力,各家模型 API 均已支持,官方指南见 OpenAI Function Calling 文档。它的本质是一套消息协议:应用把可用的函数清单告诉模型,模型根据用户意图决定“要不要调、调哪个、参数填什么”,但它自己不执行——执行永远发生在你的应用侧。
这个分工是理解 Function Calling 的关键:模型的角色是“决策者”,负责把自然语言翻译成结构化的调用意图;应用的角色是“执行者”,负责真正跑函数、拿结果。两者通过消息列表接力协作,模型永远摸不到你的服务器。
它解决的核心问题有三个:
- 把自然语言转成程序可执行的结构化调用(JSON 参数,而非自然语言描述)
- 让模型获取训练数据之外的实时信息(天气、股价、库存)
- 让模型触发真实世界的动作(发邮件、写数据库、下单)
需要注意的是,Function Calling 有两个相邻概念容易混淆:狭义的 Function Calling 特指上述 API 机制;广义的 Tool Use 则覆盖工具定义、执行、错误处理、工具生态的完整工程话题。本文聚焦机制本身,工具选型与生态留待工具使用全景的话题展开。
2、工作原理
一次完整的 Function Calling 涉及两轮模型交互。以“北京今天多少度”为例,时序如下:
拆开看四个关键点。第一,应用在请求里携带工具清单——每个工具的名字、描述、参数 Schema,这是模型决策的唯一依据。第二,模型的响应里包含一个“工具调用意图”(tool_calls 字段),含函数名与 JSON 格式的参数,模型此时只是“说”要调,并没有调。第三,应用解析意图、真正执行函数,把结果以 tool 角色消息追加进对话。第四,模型拿到结果,生成面向用户的最终回答。
这四步构成一个控制循环:复杂任务中,模型可能连续发起多次调用(查完天气再查空气),甚至串行依赖(先查城市 ID 再查天气)。控制循环由应用持有,何时终止由模型不再发起调用或应用设置的上限决定。
3、工具定义详解
工具用 JSON Schema 描述,核心是三要素:名字、描述、参数。JSON Schema 是参数定义的标准语言,模型依赖它理解每个参数的类型与含义。
以天气查询为例,Python SDK 的定义方式如下:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市当前天气,返回温度与天气现象",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名,如 北京、上海"
}
},
"required": ["city"]
}
}
}]
解读:定义里最值钱的是 description——模型判断“该不该调这个工具、参数怎么填”几乎完全依赖描述文本。required 列出必填参数,可选参数要写清缺省行为。参数越少越好,能用枚举就用枚举,枚举能从根本上杜绝填错取值。
定义完工具,第一轮请求把工具清单与用户消息一并发出:
from openai import OpenAI
client = OpenAI()
messages = [{"role": "user", "content": "北京今天多少度?"}]
resp = client.chat.completions.create(
model="gpt-4o", messages=messages, tools=tools)
msg = resp.choices[0].message
解读:如果模型认为无需调用工具,msg.tool_calls 为空,直接取 msg.content 即可;如果需要调用,msg.tool_calls 里就有函数名与参数——应用从这里进入执行阶段。
4、完整的调用循环
模型决定调用后,接力棒交给应用。下面的代码完成“解析意图 → 执行 → 回传 → 拿到最终回答”的后半程:
import json
def get_weather(city: str) -> str:
# 真实项目中这里调用天气服务
return json.dumps({"city": city, "temp": 22, "condition": "晴"},
ensure_ascii=False)
if msg.tool_calls:
messages.append(msg) # 先把模型的调用意图入列,保持上下文完整
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args) # 真正执行的是你的函数
messages.append({
"role": "tool", # tool 角色消息:回传执行结果
"tool_call_id": call.id,
"content": result,
})
final = client.chat.completions.create(
model="gpt-4o", messages=messages, tools=tools)
print(final.choices[0].message.content)
解读:两个容易忽略的细节。其一,messages.append(msg) 不能省——模型的调用意图本身就是对话历史的一部分,丢了它,下一轮请求中的 tool 消息就成了无源之水,API 会直接报错。其二,tool 消息靠 tool_call_id 与调用意图配对,一次发起多个调用时全靠 ID 对号入座。
生产级的循环还要包一层“是否继续”:执行完一轮后检查新的响应是否又发起了调用,是则继续执行,直至模型不再调用或到达预设的轮数上限。这个上限必须有,否则模型陷入循环调用时,账单会替你做出反应。
5、设计工具的最佳实践
工具设计质量直接决定模型的调用准确率。同样的接口,描述写得好与坏,模型的调用成功率可以差出数倍——Anthropic 在工程实践中也把工具定义列为 Agent 系统回报率最高的投入点,详见 Building Effective Agents。核心原则如下:
| 原则 | 说明 |
|---|---|
| 名实相符 | 函数名即语义,动词开头,如 send_email |
| 描述说清用途 | 写明何时该调、何时不该调 |
| 参数最少化 | 必填参数控制在一到三个 |
| 枚举约束取值 | 固定选项用 enum,别让模型自由发挥 |
| 结果为模型服务 | 返回精炼文本,错误要带可操作提示 |
其中“结果为模型服务”最常被忽视:工具返回值不是给人看的,是给模型看的。数据库查回来的原始行集,不如先在应用侧加工成“共 3 条,平均 22 度”这样的摘要;报错时返回“城市名不正确,支持的列表:北京、上海”远好于直接抛一个 500 状态码。
工具描述本质上是在给模型写 API 文档——你的文档糊弄模型,模型的参数就糊弄你。
6、踩坑与注意事项
第一,幻觉调用。模型有时会编造不存在的函数名或参数,尤其在工具清单很长、描述含混时。对策:应用侧始终校验函数名与参数合法性,Schema 里能用枚举的绝不放开。
第二,参数幻觉。日期、ID 这类模型猜不准的字段,它倾向于编一个看起来合理的值。对策:让工具返回“可选项列表”分两步调用(先 list_orders 再 get_order),而不是指望模型一次填对订单号。
第三,并行调用失控。模型可能一次发起十几个独立调用,如果你的后端扛不住,要么在请求参数里禁用并行调用,要么在应用侧做限流与去重。
第四,安全隐患。Function Calling 是把模型输出直接对接到真实动作的通道——邮件、支付、删库都可能是工具。必须坚持最小权限:工具只暴露必要能力,敏感操作加确认环节,description 里明确写入“该操作不可撤销”之类的警示,让模型的谨慎程度与操作风险对齐。
第五,消息协议的厂商差异。各家的字段名、消息角色、流式事件格式不完全一致,跨模型迁移时接口层要做适配,不要把某一家 SDK 的消息结构直接写死在业务代码里。
7、总结
回顾全文:Function Calling 是一套“模型决策、应用执行”的消息协议,模型负责把自然语言翻译成结构化调用意图,执行权始终在应用手里;完整链路是“工具清单 → 调用意图 → 本地执行 → 结果回传 → 最终回答”的两轮交互;设计上,函数名、描述与参数 Schema 就是写给模型的 API 文档,枚举约束与精炼返回值是准确率的保障;安全上,最小权限与敏感操作确认是不可省略的闸门。
这是理解一切智能体系统的第一步——Agent 的“手”就是工具调用,“脑”就是控制循环。对于刚上手的开发者,建议从一个只读工具(如查询类接口)开始跑通两轮交互,再逐步加动作类工具,并始终把“校验、限流、确认”三件事做在应用侧,而不是指望模型自觉。

