Function Calling协议与调用过程深度剖析
全面解析大模型工具调用(Function Calling)的核心协议、消息结构、完整调用流程及最佳实践,助你构建强大的AI Agent。
Function Calling(函数调用)是大模型从“对话者”升级为“行动者”的关键能力。它允许模型在需要时,结构化地请求调用外部工具(函数、API),并将结果整合到对话中,从而极大扩展了模型的能力边界。本文将深入解析其协议细节与完整调用流程,并对比不同协议格式中的实现差异。
一、Function Calling/Tools 是什么?
要知道,大模型很聪明,但它有几个天生的限制:
- 无法访问实时信息:它不知道今天的天气、最新的新闻
- 无法执行操作:它不能帮你发邮件、不能帮你操作文件
- 知识有截止日期:它不知道今天发生了什么
- 无法访问私有数据:它不知道你的数据库、你的文件系统
工具就是大模型的"手"和"脚"。 有了工具,智能体就能:查询数据库、调用外部 API、操作文件系统、发送消息——做任何你想让它做的事情。
工具让大模型从"只能聊天"变成"能做实事"。
Function Calling 不是让模型直接执行代码,而是让模型生成一个结构化的请求,表明“我需要调用名为 X 的函数,并传入参数 Y 和 Z”。真正的函数执行由你的应用程序(客户端)完成。
核心价值:
- 连接真实世界:获取实时数据(天气、股价)、操作外部系统(发送邮件、创建日历事件)。
- 精确性与可控性:模型输出结构化的 JSON 参数,比自然语言描述更精确、不易出错。
- 安全性:应用层可以审核、过滤、限制模型请求调用的函数和参数,再执行。
你可以通过阅读快速补全基础概念的信息差 来快速了解他在整个大模型交互中的定位。
Function Calling 的工作原理
整个过程可以分为四个步骤:
第一步:工具描述 你需要告诉大模型有哪些工具可用。每个工具需要提供:
- 工具名称(name)
- 工具描述(description)
- 参数定义(parameters)
第二步:用户提问 用户向智能体提出问题,比如"帮我整理桌面"。
第三步:大模型决策 大模型分析用户的问题,决定是否需要调用工具。如果需要,它会返回:
- 工具名称(比如
list_files) - 工具参数(比如
path: "/Users/xxx/Desktop")
第四步:执行并返回 你的代码执行这个工具,把结果返回给大模型。大模型拿到结果后,继续思考并给出最终回答。
整个流程就像这样:
用户: "帮我整理桌面"
↓
大模型思考: "我需要先扫描桌面上的文件"
↓
大模型返回: {
"tool": "list_files",
"parameters": {
"path": "/Users/xxx/Desktop"
}
}
↓
你的代码执行: list_files("/Users/xxx/Desktop")
↓
你的代码返回: "桌面上有 15 个文件:..."
↓
大模型继续思考: "我需要把这些文件分类..."
二、协议核心:消息结构的变化
Function Calling 引入了新的消息角色和字段,使对话流变得更加丰富。不同协议格式在实现上有显著差异。
1. 工具定义(Tools)
在发起对话前,你需要向模型描述可用的工具。这通过请求中的 tools 参数完成。
工具定义示例(OpenAI 格式):
{
"tools": [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定位置的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如:北京"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["location"]
}
}
}
]
}
工具定义示例(Anthropic 格式):
{
"tools": [
{
"name": "get_current_weather",
"description": "获取指定位置的当前天气",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如:北京"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["location"]
}
}
]
}
2. 模型响应中的工具调用请求
当模型判断需要调用工具时,其回复中会包含工具调用请求。不同格式的表示方式差异很大。
OpenAI Chat Completions / Responses 格式:
模型回复的 assistant 消息中包含 tool_calls 字段。
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_current_weather",
"arguments": "{\"location\": \"北京\", \"unit\": \"celsius\"}"
}
}
]
}
Anthropic Messages 格式:
模型回复的 assistant 消息中包含 tool_use 内容块。
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_abc123",
"name": "get_current_weather",
"input": {"location": "北京", "unit": "celsius"}
}
]
}
关键区别:
- 结构:OpenAI 使用
tool_calls数组,Anthropic 使用content数组中的tool_use块。 - 参数:OpenAI 的
arguments是 JSON 字符串,Anthropic 的input是 JSON 对象。 - ID:两者都使用唯一 ID 关联调用和结果。
3. 工具执行结果返回
你的应用程序执行函数后,需要将结果返回给模型。不同格式使用不同的消息角色。
OpenAI Chat Completions / Responses 格式:
使用 tool 角色消息。
{
"role": "tool",
"tool_call_id": "call_abc123",
"name": "get_current_weather",
"content": "{\"temperature\": 22, \"unit\": \"celsius\", \"description\": \"晴朗\"}"
}
Anthropic Messages 格式:
使用 user 角色消息,包含 tool_result 内容块。
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_abc123",
"content": "{\"temperature\": 22, \"unit\": \"celsius\", \"description\": \"晴朗\"}"
}
]
}
关键区别:
- 消息角色:OpenAI 使用专门的
tool角色,Anthropic 使用user角色。 - 内容结构:OpenAI 使用简单的
content字符串,Anthropic 使用tool_result内容块。 - 关联方式:OpenAI 通过
tool_call_id,Anthropic 通过tool_use_id。
4. 模型的最终回复
模型收到工具结果后,会将其整合,并生成最终面向用户的自然语言回复。
最终的 assistant 消息示例(两种格式类似):
{
"role": "assistant",
"content": "北京现在的天气是晴朗,气温22摄氏度。"
}
三、完整调用流程(时序图)
下面是一个完整的 Function Calling 交互流程:
序列图
participant 用户
participant 客户端应用
participant 大模型API
participant 外部函数
用户->>客户端应用: 发送问题:“北京今天天气怎么样?”
客户端应用->>大模型API: 请求(messages + tools定义)
大模型API-->>客户端应用: 响应(assistant消息,含工具调用请求)
客户端应用->>外部函数: 调用 get_current_weather(location=“北京”)
外部函数-->>客户端应用: 返回天气数据
客户端应用->>大模型API: 发送工具执行结果
大模型API-->>客户端应用: 最终响应(assistant消息,自然语言回复)
客户端应用-->>用户: 显示:“北京天气晴朗,22℃。”
关键步骤解析:
- 工具定义先行:每次请求都可以(也应该)携带
tools定义,告诉模型它有哪些“武器”可用。 - 模型决策:模型根据用户问题和工具描述,自主决定是否调用、调用哪个工具、传入什么参数。这是模型的“推理”能力体现。
- 客户端执行:重要! 模型不执行代码,它只输出调用意图。执行权在你的应用手中。这保证了安全性和可控性。
- 结果回传:将执行结果以特定格式回传,模型才能知道执行结果。
- 整合回复:模型基于工具返回的数据,生成最终的回答。
四、并行函数调用(Parallel Function Calling)
现代模型支持一次请求中调用多个函数,这些调用可以并行执行以节省时间。
OpenAI 格式示例(并行调用):
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_abc",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"location\": \"北京\"}"}
},
{
"id": "call_def",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"location\": \"上海\"}"}
}
]
}
Anthropic 格式示例(并行调用):
{
"role": "assistant",
"content": [
{"type": "tool_use", "id": "toolu_abc", "name": "get_weather", "input": {"location": "北京"}},
{"type": "tool_use", "id": "toolu_def", "name": "get_weather", "input": {"location": "上海"}}
]
}
你需要为每个工具调用分别执行函数,并返回对应的结果(通过 ID 关联)。
五、最佳实践与陷阱
最佳实践:
- 清晰的函数描述:
description和参数描述要准确,这是模型决定何时调用的主要依据。 - 严格的参数校验:模型生成的参数可能有误,务必校验类型、范围,防止注入攻击。
- 优雅的错误处理:函数执行可能失败。将错误信息作为工具结果返回,模型通常能理解并告知用户。
- 控制工具数量:提供的工具过多会增加模型决策难度和 token 消耗。按需提供。
- 管理对话历史:完整的工具调用链都需要保留在对话历史中,以保证上下文连贯。
- 注意格式差异:不同协议格式的工具调用实现差异很大,务必查阅官方文档。
常见陷阱:
- 幻觉调用:模型可能“幻想”出一个不存在的工具名或参数。确保你的应用只响应
tools定义中存在的函数。 - 无限循环:模型可能陷入“调用-结果-再调用”的循环。设置最大调用轮次限制。
- 参数格式错误:模型输出的参数可能格式有误。做好解析失败的处理。
- 格式混淆:在不同协议格式间切换时,容易混淆工具调用和结果返回的格式。
六、与协议角色的关系
在 Function Calling 流程中,角色有了新的内涵:
system:可以定义工具使用的总体策略(如:“优先使用本地数据库,再调用远程API”)。user:提出需要工具才能解决的需求。assistant:扮演“调度员”角色,决定调用哪个工具,并解析结果。tool/tool_result:代表工具的执行结果。它扩展了对话的参与者。
在不同协议格式中的差异:
- OpenAI 格式:使用专门的
tool角色消息返回结果。 - Anthropic 格式:使用
user角色消息中的tool_result内容块返回结果。 - 角色复用:Anthropic 的设计复用了
user角色,而 OpenAI 引入了新角色。
七、总结
Function Calling 协议通过引入工具定义、工具调用请求和工具执行结果,构建了一个完整的“模型-工具”交互循环。不同协议格式(OpenAI Chat Completions/Responses 与 Anthropic Messages)在实现细节上差异显著,但核心思想一致:让模型能够安全、可控地与外部世界交互。
掌握这一协议,意味着你不再仅仅是在使用一个聊天机器人,而是在编排一个能够思考和行动的智能系统。
系列文章导航:
- 大模型基础协议概览:OpenAI Chat Completions、OpenAI Responses与Anthropic Messages格式解析
- 上下文角色深度解析:System、User、Assistant与Developer
- Function Calling协议与调用过程深度剖析(本文)
本文为“大模型底层通信协议深究”系列第三篇。