慕雪的小助
慕雪小助手的总结
DeepSeek & LongCat

欢迎阅读慕雪撰写的AI Agent专栏,本专栏目录如下

  1. 【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?
  2. 【AI】AI对26届计算机校招的影响
  3. 【Agent.01】AI Agent智能体开发专题引言
  4. 【Agent.02】市面上常见的大模型有哪些?
  5. 【Agent.03】带你学会写一个基础的Prompt
  6. 【Agent.04】AI时代的hello world:调用OpenAI接口,与大模型交互
  7. 【Agent.05】OpenAI接口Function Calling工具调用详解

本文将详细介绍OpenAI格式中的Function Calling工具调用的格式和逻辑。

1. 写在前面

Function Calling(函数调用)是大模型时代最革命性的功能之一,它让AI从"只会聊天"变成了"能干活"。在这之前,AI就像一个只能说话的顾问,只能说话,不能干活。现在通过Function Calling,AI变成了一个能够操作工具、执行任务的助手,能实现的能力就更多了!

这个功能最早由OpenAI在2023年6月推出,直接引爆了整个AI Agent开发的浪潮。现在市面上几乎所有的大模型都支持Function Calling功能,包括GPT系列、Claude、Gemini、Qwen、Kimi、DeepSeek等等。只要是你叫的上名字的大模型,都支持Function Calling。

Function Calling是Agent开发的基石。所有Agent框架,本质上都是在利用Function Calling的能力,让AI大模型ReAct,自主进行多轮工具调用,来完成特定任务的。

所以,了解Function Calling,是我们正式开始学习Agent开发之前,必不可少的一环。

在之前,本站已经在MCP的博客中,简单介绍了Function Calling的格式,和MCP工具进行了对比。但在那篇文章中,我们是用了OpenAI的SDK实现的功能,并没有使用直接请求的方式。要详细学习,还是得自己会直接HTTP请求OpenAI接口!

本文将通过requests库直接请求OpenAI接口,带大家认识一下最基础的Function Calling格式长啥样,以及要如何使用Function Calling的结果。现在这些工作,都已经被Agent SDK帮我们实现了。但我们还是得从头开始,自己试试,才能在Agent开发中,更好的理解Agent到底是怎么调用工具的,以及哪里出了问题。

2. 什么是Function Calling?

Function Calling是一种让大模型能够调用外部工具(函数)的机制。简单来说,就是AI可以根据用户的意图,自动选择合适的函数并调用,从而完成实际任务。

2.1. 传统对话 vs Function Calling

下面是一个伪Function Calling调用的示意和Chat模式的对比:

传统对话模式

plaintext
1
2
用户:帮我查一下今天的天气
AI:对不起,我无法获取实时天气信息,因为我是一个语言模型...

Function Calling模式

plaintext
1
2
3
4
用户:帮我查一下今天的天气
AI:[调用get_weather函数,参数:location="北京", date="today"]
函数返回:北京今天晴天,温度25°C
AI:北京今天天气晴朗,温度25°C,适合外出活动。

看到了吗?通过Function Calling,AI从"只会说不会做"变成了"既能说又能做"。

有人可能会说:这个功能不是可以让AI在回答中以特定格式,给出函数参数,我们通过解析回答来实现吗?

从理论上来说,当然是可以实现的。但是有一个东西叫“大模型幻觉”,还有另外一个事实是,即便是最新的大模型,依旧可能会在Prompt遵循上有自己的“创造性转化和创新性发展”,导致你本来想让它以A格式返回,模型偏不,给你输出了一个B模式,那你代码里面实现的回答解析逻辑就蛋糕了,没法用喽!

即便现在最新的大模型,依旧有这个问题出现。如果你调用过美团LongCat的API或者claude的API,你可能会发现有的时候,这两个AI会在回答中,以XML格式(如<longcat_tool_use><function_call>块)返回工具调用的参数,而不是在Function Calling参数中返回。导致我们预先设计的逻辑直接失效,非常蛋疼。

上面介绍了这些,只是想让大家知道,单纯依赖Chat回答来实现工具调用是不可行的!我们必须要有一个机制,能让大模型直接返回统一格式的工具调用结果,方便实现通用的解析方法和能力使用。

2.2. Function Calling的核心价值

AI生成的核心价值,仅供参考:

  1. 扩展AI能力边界:让AI能够获取实时信息、操作数据库、调用API等
  2. 提高响应准确性:通过调用可靠的数据源,减少AI"胡说八道"的情况
  3. 实现复杂工作流:可以组合多个函数调用,完成复杂任务
  4. 降低开发门槛:开发者只需要定义好函数接口,不需要复杂的训练过程

3. Function Calling的工作原理

好了,上面叭叭了那么多没用的,现在开始正式来认识一下Function Calling的流程吧。

Function Calling的核心流程可以概括为:定义函数 -> 用户输入 -> AI选择函数,生成参数 -> 调用函数 -> AI处理结果 -> 返回包含函数信息的回答

这里需要明确的一点是,调用函数这一环节,是需要我们自己通过解析AI返回的函数工具参数,然后实现的调用,并不能由AI主动触发某个函数!如果你看到了某个AI工具(比如cursor)会主动触发工具调用,那是因为开发者在cursor的代码中实现了这套逻辑。

3.1. 完整工作流程图

下面是单轮工具调用的流程:

用户输入

AI分析用户意图

需要调用函数?

直接回答用户

选择合适的函数

生成函数调用参数

返回函数调用信息

执行函数调用

获取函数执行结果

将结果传回AI

AI生成最终回答

输出给用户

3.2. Function Calling的请求和返回格式

了解了流程后,我们再来看看流程中是怎么实现的。

3.2.1. 函数定义(Function Schema)

名词解释:在 XML/JSON 中,schema 用于定义数据格式的约束(如 XML Schema Definition, XSD),规定哪些字段允许出现、数据类型是什么、是否必填等,确保数据交换时的一致性。

首先,我们需要告诉AI有哪些函数可以调用。这通过定义函数的schema来实现:

json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定地点的天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "要查询天气的地点"
},
"date": {
"type": "string",
"description": "查询日期,可以是'today'、'tomorrow'或具体日期"
}
},
"required": ["location"]
}
}
}
]
}

单个function中的关键字段:

  • name: 函数名称,必须是有效的标识符,且不可以出现重复。
  • description: 函数功能描述,AI通过这个理解函数用途,知道什么时候要调用它。
  • parameters: 参数定义,包括参数名称、类型、描述等,AI通过类型和描述理解参数字段含义,知道要给他传入什么值。
  • required: 必需参数列表

这个tools的schema是放到OpenAI请求的body中的,和messages参数一起发送

json
1
2
3
4
5
{
"messages": [],
"tool_choice": "auto",
"tools": []
}

这里多了一个参数tool_choice,可选值为auto/none/required:

  • auto:自动识别是否需要进行工具调用(可以调用,可以不调用);
  • none:AI会认为没有工具,不进行工具调用;
  • required:AI必须进行工具调用;

3.2.2. AI的函数选择

当用户输入后,AI会分析用户意图,决定是否需要调用函数以及调用哪个函数,并在返回值的Body中包含如下字段:

json
1
2
3
4
5
6
7
8
9
10
11
12
{
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\": \"北京\", \"date\": \"today\"}"
}
}
]
}

其中arguments部分就是AI给这个工具生成的调用参数了。我们需要在代码中,对arguments参数里面的json字符串进行二次解析,获取参数的字段值。

3.2.3. 函数执行和结果处理

我们在接收到AI的函数调用请求后,需要调用并执行对应的函数,并将结果,以如下格式,在messages数组中追加,返回给AI:

json
1
2
3
4
5
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": "{\"temperature\": 25, \"condition\": \"晴天\", \"humidity\": \"60%\"}"
}

其中,AI是通过tool_call_id字段将调用结果和调用来源对应上,能够理解这是get_weather工具的调用结果。content字段就是工具调用的返回值了,这个返回值没有任何格式限定,可以随意给出,只要AI能够理解就OK。一般的工具调用结果都会用json格式返回给AI,比较方便AI理解。

接下来,就是AI总结这个工具结果,返回回答给用户了。

3.3. 实际代码示例(单轮)

以下是使用 Python + requests 直接请求 OpenAI API 的示例。其中,我们用了一个假返回值的函数get_weather来模拟了一个天气请求的结果,返回给AI。

这是可行的,因为对于AI来说,我们的工具是怎么实现的是一个黑盒状态,AI不关心我们返回的工具调用结果是哪里来的,也不关注我们工具调用结果的格式。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import requests
import json

def get_weather(location, date):
"""模拟获取天气信息的函数"""
# 这里应该调用真实的天气API
return {
"location": location,
"date": date,
"temperature": 25,
"condition": "晴天",
"humidity": "60%"
}

def chat_with_function_calling(user_message):
# 配置API信息
api_key = "your-openai-api-key" # 替换为你的OpenAI API密钥
base_url = "https://api.siliconflow.cn/v1"
model = "Qwen/Qwen3-8B"

# 设置请求头
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}

# 定义可用的函数
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定地点的天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "要查询天气的地点"
},
"date": {
"type": "string",
"description": "查询日期,可以是'today'、'tomorrow'或具体日期"
}
},
"required": ["location"]
}
}
}
]

# 第一次调用AI的请求体
request_data = {
"model": model,
"messages": [{"role": "user", "content": user_message}],
"tools": tools,
"tool_choice": "auto"
}

# 发送第一次请求
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=request_data
)

if response.status_code != 200:
print(f"API请求失败: {response.status_code} - {response.text}")
return "抱歉,服务暂时不可用,请稍后重试。"

response_data = response.json()
message = response_data["choices"][0]["message"]

# 检查是否需要调用函数
if "tool_calls" in message and message["tool_calls"]:
# 执行函数调用
tool_call = message["tool_calls"][0]
function_name = tool_call["function"]["name"]
function_args = json.loads(tool_call["function"]["arguments"])

if function_name == "get_weather":
result = get_weather(function_args["location"], function_args.get("date", "today"))

# 构建第二次请求的消息数组
messages = [
{"role": "user", "content": user_message},
message,
{
"role": "tool",
"tool_call_id": tool_call["id"],
"name": function_name,
"content": json.dumps(result)
}
]

# 第二次请求的数据
second_request_data = {
"model": model,
"messages": messages
}

# 发送第二次请求
second_response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=second_request_data
)

if second_response.status_code == 200:
second_response_data = second_response.json()
return second_response_data["choices"][0]["message"]["content"]
else:
print(f"第二次API请求失败: {second_response.status_code} - {second_response.text}")
return "抱歉,处理函数结果时出现错误。"

return message["content"]

# 使用示例
if __name__ == "__main__":
print(chat_with_function_calling("帮我查一下北京今天的天气"))

在本地pip install了requests库之后,配置你的硅基流动API KEY,运行如上代码,可以正常得到结果!可以看到AI的回答,正是我们Function Calling里面工具返回的结果,调用成功了!

image.png

你可以在上述代码中添加打印,详细看看每一步我们发送的请求体格式、OpenAI接口返回的结果格式,进一步了解两边的交互过程。本文就不演示啦!

4. Function Calling的高级应用

上面,我们已经了解了单轮Function Calling应该如何实现,现在可以来看看如何实现多轮工具调用

4.1. 多函数调用

AI可以一次性调用多个函数,或者根据前一个函数的结果决定是否需要调用更多函数。所以我们可以定义多个函数(工具):

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 定义多个函数
tools = [
{
"type": "function",
"function": {
"name": "search_flights",
"description": "搜索航班信息",
"parameters": {
"type": "object",
"properties": {
"origin": {"type": "string", "description": "出发城市"},
"destination": {"type": "string", "description": "目的地城市"},
"date": {"type": "string", "description": "出发日期"}
},
"required": ["origin", "destination", "date"]
}
}
},
{
"type": "function",
"function": {
"name": "book_flight",
"description": "预订航班",
"parameters": {
"type": "object",
"properties": {
"flight_id": {"type": "string", "description": "航班ID"},
"passenger_name": {"type": "string", "description": "乘客姓名"}
},
"required": ["flight_id", "passenger_name"]
}
}
}
]

4.2. 多轮函数调用示例

下面我们实现一个完整的多轮函数调用示例,使用4.1节中定义的航班搜索和预订工具:

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import requests
import json

def search_flights(origin, destination, date):
"""模拟搜索航班信息"""
# 这里应该调用真实的航班API
return [
{
"flight_id": "CA1234",
"origin": origin,
"destination": destination,
"date": date,
"departure_time": "08:30",
"arrival_time": "11:45",
"price": 1280,
"available_seats": 15
},
{
"flight_id": "MU5678",
"origin": origin,
"destination": destination,
"date": date,
"departure_time": "14:20",
"arrival_time": "17:35",
"price": 980,
"available_seats": 8
}
]

def book_flight(flight_id, passenger_name):
"""模拟预订航班"""
# 这里应该调用真实的预订API
import random
success_rate = random.random()
if success_rate > 0.2: # 80%成功率
return {
"success": True,
"booking_id": f"BK{flight_id}{int(__import__('time').time())}",
"flight_id": flight_id,
"passenger_name": passenger_name,
"status": "confirmed"
}
else:
return {
"success": False,
"error": "座位已售罄,请选择其他航班"
}

def multi_tool_function_calling(user_message):
"""多轮函数调用示例"""
# 配置API信息
api_key = "your-siliconflow-api-key"
base_url = "https://api.siliconflow.cn/v1"
model = "Qwen/Qwen3-8B"

# 设置请求头
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}

# 定义多个工具
tools = [
{
"type": "function",
"function": {
"name": "search_flights",
"description": "搜索航班信息",
"parameters": {
"type": "object",
"properties": {
"origin": {"type": "string", "description": "出发城市"},
"destination": {"type": "string", "description": "目的地城市"},
"date": {"type": "string", "description": "出发日期"}
},
"required": ["origin", "destination", "date"]
}
}
},
{
"type": "function",
"function": {
"name": "book_flight",
"description": "预订航班",
"parameters": {
"type": "object",
"properties": {
"flight_id": {"type": "string", "description": "航班ID"},
"passenger_name": {"type": "string", "description": "乘客姓名"}
},
"required": ["flight_id", "passenger_name"]
}
}
}
]

# 工具函数映射
tool_functions = {
"search_flights": search_flights,
"book_flight": book_flight
}

# 第一轮对话
messages = [{"role": "user", "content": user_message}]

request_data = {
"model": model,
"messages": messages,
"tools": tools,
"tool_choice": "auto"
}

response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=request_data
)

if response.status_code != 200:
print(f"API请求失败: {response.status_code} - {response.text}")
return "抱歉,服务暂时不可用,请稍后重试。"

response_data = response.json()
message = response_data["choices"][0]["message"]
messages.append(message)

# 处理第一轮工具调用
if "tool_calls" in message and message["tool_calls"]:
tool_results = []

# 执行所有工具调用
for tool_call in message["tool_calls"]:
function_name = tool_call["function"]["name"]
function_args = json.loads(tool_call["function"]["arguments"])

if function_name in tool_functions:
try:
result = tool_functions[function_name](**function_args)
tool_results.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": function_name,
"content": json.dumps(result)
})
print(f"✅ 调用 {function_name}: {function_args}")
print(f"📋 返回结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
except Exception as e:
tool_results.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": function_name,
"content": json.dumps({"error": str(e)})
})
print(f"❌ 调用 {function_name} 失败: {e}")

# 将工具调用结果添加到消息中
messages.extend(tool_results)

# 第二轮对话:处理工具调用结果
second_request_data = {
"model": model,
"messages": messages,
"tools": tools,
"tool_choice": "auto" # 可能需要进一步调用工具
}

second_response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=second_request_data
)

if second_response.status_code == 200:
second_response_data = second_response.json()
second_message = second_response_data["choices"][0]["message"]
messages.append(second_message)

# 检查是否有第二轮工具调用
if "tool_calls" in second_message and second_message["tool_calls"]:
print("\n🔄 进行第二轮工具调用...")

second_tool_results = []
for tool_call in second_message["tool_calls"]:
function_name = tool_call["function"]["name"]
function_args = json.loads(tool_call["function"]["arguments"])

if function_name in tool_functions:
try:
result = tool_functions[function_name](**function_args)
second_tool_results.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": function_name,
"content": json.dumps(result)
})
print(f"✅ 第二轮调用 {function_name}: {function_args}")
print(f"📋 返回结果: {json.dumps(result, ensure_ascii=False, indent=2)}")
except Exception as e:
second_tool_results.append({
"role": "tool",
"tool_call_id": tool_call["id"],
"name": function_name,
"content": json.dumps({"error": str(e)})
})
print(f"❌ 第二轮调用 {function_name} 失败: {e}")

# 将第二轮工具调用结果添加到消息中
messages.extend(second_tool_results)

# 第三轮对话:生成最终回答
third_request_data = {
"model": model,
"messages": messages
}

third_response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=third_request_data
)

if third_response.status_code == 200:
third_response_data = third_response.json()
return third_response_data["choices"][0]["message"]["content"]
else:
print(f"第三次API请求失败: {third_response.status_code} - {third_response.text}")
return "抱歉,生成最终回答时出现错误。"
else:
# 没有第二轮工具调用,直接返回结果
return second_message["content"]
else:
print(f"第二次API请求失败: {second_response.status_code} - {second_response.text}")
return "抱歉,处理工具结果时出现错误。"

return message["content"]

# 使用示例
if __name__ == "__main__":
# 测试多轮工具调用
test_cases = [
"帮我搜索明天从北京到上海的航班",
"我想预订一张从北京到上海明天航班的机票,乘客姓名是张三。直接预订第一个机票就行",
"帮我查询并预订一张从广州到成都后天航班的机票,乘客是李四。直接预订第一个机票就行"
]

for i, test_message in enumerate(test_cases, 1):
print(f"\n{'='*60}")
print(f"🚀 测试用例 {i}: {test_message}")
print(f"{'='*60}")

result = multi_tool_function_calling(test_message)
print(f"\n🎯 最终回答:")
print(result)
print("\n" + "="*60)

运行这个示例,你可以看到AI如何智能地组合多个工具调用来完成复杂任务。比如用户说"帮我预订机票",AI会先搜索航班,然后再根据搜索结果进行预订。

但是需要注意的是,我们必须加一个“直接预订第一个机票就行”的限定词,否则AI会咨询我们应该预订哪一张,就要等待用户确认了。这个逻辑在Demo中并没有实现。

4.3. 示例代码测试结果

以下是运行如上代码的终端输出结果,因为让AI帮忙加了些日志,所以看上去更详细。

首先是第一个测试用例,只是让AI进行了查询,没有让他进行预订。可以看到AI确实也没有去调用预订飞机的函数

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
============================================================
🚀 测试用例 1: 帮我搜索明天从北京到上海的航班
============================================================
✅ 调用 search_flights: {'origin': '北京', 'destination': '上海', 'date': '2023-10-06'}
📋 返回结果: [
{
"flight_id": "CA1234",
"origin": "北京",
"destination": "上海",
"date": "2023-10-06",
"departure_time": "08:30",
"arrival_time": "11:45",
"price": 1280,
"available_seats": 15
},
{
"flight_id": "MU5678",
"origin": "北京",
"destination": "上海",
"date": "2023-10-06",
"departure_time": "14:20",
"arrival_time": "17:35",
"price": 980,
"available_seats": 8
}
]

🎯 最终回答:
这里有两个航班选项:

1. 中国国航 CA1234,08:30 起飞,11:45 到达,票价 1280 元,剩余座位 15 个
2. 中国东航 MU5678,14:20 起飞,17:35 到达,票价 980 元,剩余座位 8 个

您需要预订其中一个航班吗?或者需要我帮您进一步筛选航班信息?

然后是第二个测试用例,我们要求AI进行预订了,可以看到AI先进行了查询,然后开始进行预订。因为我们在代码中写了可能预订失败的随机逻辑,可以看到AI在预订是失败的时候,也进行了提示,符合工具返回结果的预期值。

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
============================================================
🚀 测试用例 2: 我想预订一张从北京到上海明天航班的机票,乘客姓名是张三。直接预订第一个机票就行
============================================================
✅ 调用 search_flights: {'origin': '北京', 'destination': '上海', 'date': '2023-10-06'}
📋 返回结果: [
{
"flight_id": "CA1234",
"origin": "北京",
"destination": "上海",
"date": "2023-10-06",
"departure_time": "08:30",
"arrival_time": "11:45",
"price": 1280,
"available_seats": 15
},
{
"flight_id": "MU5678",
"origin": "北京",
"destination": "上海",
"date": "2023-10-06",
"departure_time": "14:20",
"arrival_time": "17:35",
"price": 980,
"available_seats": 8
}
]

🔄 进行第二轮工具调用...
✅ 第二轮调用 book_flight: {'flight_id': 'CA1234', 'passenger_name': '张三'}
📋 返回结果: {
"success": false,
"error": "座位已售罄,请选择其他航班"
}

🎯 最终回答:


很抱歉,CA1234航班已售罄。您是否想尝试预订其他可用的航班,例如MU5678(14:20起飞,17:35到达,票价980元)?或者还有其他需求需要协助吗?

============================================================

当第三个用例,预订成功的时候,输出的结果又不一样了:

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
============================================================
🚀 测试用例 3: 帮我查询并预订一张从广州到成都后天航班的机票,乘客是李四。直接预订第一个机票就行
============================================================
✅ 调用 search_flights: {'origin': '广州', 'destination': '成都', 'date': '后天'}
📋 返回结果: [
{
"flight_id": "CA1234",
"origin": "广州",
"destination": "成都",
"date": "后天",
"departure_time": "08:30",
"arrival_time": "11:45",
"price": 1280,
"available_seats": 15
},
{
"flight_id": "MU5678",
"origin": "广州",
"destination": "成都",
"date": "后天",
"departure_time": "14:20",
"arrival_time": "17:35",
"price": 980,
"available_seats": 8
}
]

🔄 进行第二轮工具调用...
✅ 第二轮调用 book_flight: {'flight_id': 'CA1234', 'passenger_name': '李四'}
📋 返回结果: {
"success": true,
"booking_id": "BKCA12341760838361",
"flight_id": "CA1234",
"passenger_name": "李四",
"status": "confirmed"
}

🎯 最终回答:


您的航班已成功预订!以下是预订详情:

- **航班号**: CA1234
- **出发地**: 广州
- **目的地**: 成都
- **日期**: 后天(2023-10-07)
- **起飞时间**: 08:30
- **到达时间**: 11:45
- **票价**: 1280元
- **预订号**: BKCA12341760838361

订单状态:✅ 已确认
请核对乘客姓名是否正确(李四),如有其他需求可随时告知!

============================================================

5. 工具设计最佳实践

到这里,关于Function Calling的格式和实践我们都已经了解啦,想必你已经知道如何通过OpenAI接口让大模型进行工具调用了。

下面给出一些函数设计方面的提示:

  1. 单一原则:一个函数只干一件事。不要设计通过某个参数让一个函数可以做两件事的逻辑。这种情况,需要拆分成两个函数
  2. 名称和参数命名规范:清晰规范的参数命名,能让AI直接通过参数和函数名称理解这个函数的作用,避免AI迷失在description的大海里面,到时候给你瞎调用
  3. 返回值简化:Function Call函数的返回值会算作上下文Token的一部分,在设计函数返回值的时候,一定要合理的简化函数的返回值,避免提供重复或者冗余的信息,这种会在多轮工具调用的时候占用Token,影响AI回答效果。
  4. 长返回值截断:对于某些工具可能会返回的超级长返回值,一定要设计截断逻辑,对返回值进行削减。建议工具返回值不要超过20K Tokens(这个值已经很大了,超过这个值就很容易直接一个工具干爆大模型上下文窗口)

在实际的设计环节,会包含如上最佳实践,让大家能够看到遵循和不遵循如上最佳实践的效果差异。

6. The end

Function Calling是AI Agent开发的核心技术,掌握它就等于掌握了让AI"干活"的能力。

随着AI技术的发展,Function Calling的能力已经越来越强,支持的工具类型也越来越丰富。现在一个claude code工具里面都的Agent,能为我们实现的能力已经非常非常牛逼了,比如我前几天更新的chrome-devtools-mcp的博客,AI已经能直接帮忙我们操作浏览器了,效果还不错!

根据本专栏目录,下一篇文章将介绍MCP协议,它是Function Calling的标准化扩展,让AI Agent开发更加规范和高效。这篇文章本站已有,请移步阅读:什么是MCP?