OpenAI function calling 实践
OpenAI 的 function calling 是什么?
function calling
即函数调用,是 OpenAI 引入的一项功能,可以增强模型在执行特定任务时的能力。这项功能允许模型在对话过程中调用预定义的函数,完成特定的任务或获取外部系统的信息。这种功能使模型不仅能生成文本,还能通过调用外部函数执行操作,扩展其能力。
OpenAI 的模型在无 function calling 时存在的不足?
- 无法自动执行某些变成或者工具调用,例如自动化的数据处理、API 调用、数据库查询等,导致需要手动执行操作,增加了工作量。
- 无法获取实时信息,如当前的天气、股票价格、最近的新闻等,导致提供了过时的信息。
- 无法获取特定领域的知识,如专业领域的医学知识、金融知识、法律知识,导致无法准确地回答一些专业性很强的问题。
总的来说,function calling
增强了模型在处理复杂任务、精确性、实时性和用户交互等方面的能力,缺乏这一功能将限制模型在这些领域的表现。
1. 无函数调用时对话处理过程
无函数调用的时候,对话处理过程如下:
- Client 发送请求到 Chat Server
- Chat Server 发送 Prompt 给 OpenAI Server
- OpenAI Server 响应文本给 Chat Server
- Chat Server 将响应结果返回给用户
- 重复执行
首次请求如下:
1
2
3
4
5
6
7
{
"messages": [
{ "role": "user", "content": "今天天气怎么样?" }
],
"model": "gpt-4o",
"stream": false
}
再次请求如下:
1
2
3
4
5
6
7
8
9
{
"messages": [
{ "role": "user", "content": "今天天气怎么样?" },
{ "role": "assistant", "content": "很抱歉,我无法实时获取天气信息。" },
{ "role": "user", "content": "北京今天天气怎么样?" }
],
"model": "gpt-4o",
"stream": false
}
实现的 nextjs 代码如下:
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
import { NextApiRequest, NextApiResponse } from 'next'
export default async function createMessage(req: NextApiRequest, res: NextApiResponse) {
const { messages } = req.body
const apiKey = process.env.OPENAI_API_KEY // https://api.openai.com/v1
const baseUrl = process.env.OPENAI_BASE_URL // sk-xxx
const body = JSON.stringify({
messages,
model: 'gpt-4o',
stream: false
})
try {
const response = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`,
},
body
})
const data = await response.json()
res.status(200).json({ data })
} catch (error: any) {
res.status(500).json({ error: error.message })
}
}
2. 有函数调用时对话处理过程
有函数调用的时候,对话处理过程如下:
- Client 发送 user prompt 到 Chat Server
- Chat Server 将用户提示词和函数定义一并发送给 OpenAI Server
- OpenAI Server 根据用户的提示词,判断是用普通文本响应还是函数调用的格式响应
- 如果是函数调用则返回调用的函数名及参数,Chat Server 执行该函数,并将结果再次发送给 OpenAI
- 如果需要调用多个函数,则重复执行 3-4 步
- OpenAI Server 根据提供的数据,使用普通文本响应
首次请求如下:
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
{
"messages": [
{ "role": "user", "content": "今天天气怎么样?" }
],
"model": "gpt-4o",
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "getLocation",
"description": "Get the user's current location",
"parameters": { "type": "object", "properties": {}, "required": [] }
}
},
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": { "latitude": { "type": "number" }, "longitude": { "type": "number" } },
"required": ["latitude", "longitude"]
}
}
}
],
"tool_choice": "auto"
}
再次请求如下(携带位置信息):
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
{
"messages": [
{ "role": "user", "content": "今天天气怎么样?" },
{
"role": "function",
"name": "getLocation",
"content": "{\n \"latitude\": 40.7128,\n \"longitude\": -74.006\n}"
}
],
"model": "gpt-4o",
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "getLocation",
"description": "Get the user's current location",
"parameters": { "type": "object", "properties": {}, "required": [] }
}
},
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": { "latitude": { "type": "number" }, "longitude": { "type": "number" } },
"required": ["latitude", "longitude"]
}
}
}
],
"tool_choice": "auto"
}
最后一次请求如下(携带位置和天气信息):
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
{
"messages": [
{ "role": "user", "content": "今天天气怎么样?" },
{
"role": "function",
"name": "getLocation",
"content": "{\n \"latitude\": 40.7128,\n \"longitude\": -74.006\n}"
},
{
"role": "function",
"name": "getCurrentWeather",
"content": "{\n \"temperature\": 22,\n \"condition\": \"sunny\"\n}"
}
],
"model": "gpt-4o",
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "getLocation",
"description": "Get the user's current location",
"parameters": { "type": "object", "properties": {}, "required": [] }
}
},
{
"type": "function",
"function": {
"name": "getCurrentWeather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": { "latitude": { "type": "number" }, "longitude": { "type": "number" } },
"required": ["latitude", "longitude"]
}
}
}
],
"tool_choice": "auto"
}
实现的 nextjs 代码如下:
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
import { NextApiRequest, NextApiResponse } from 'next'
export default async function createMessage(req: NextApiRequest, res: NextApiResponse) {
const { messages } = req.body
const apiKey = process.env.OPENAI_API_KEY
const baseUrl = process.env.OPENAI_BASE_URL
try {
while (true) {
const body = JSON.stringify({
messages,
model: 'openai/gpt-4o-2024-08-06',
stream: false,
tools: functions,
tool_choice: 'auto'
})
const response = await fetch(`${baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`
},
body
})
const data = await response.json()
const { finish_reason, message } = data.choices[0]
if (finish_reason === 'tool_calls') {
const functionName = message.tool_calls[0].function.name
const fucntionArguments = message.tool_calls[0].function.arguments
const functionArgsArr = Object.values(fucntionArguments)
const functionToCall = availableFunctions[functionName]
const functionResponse = await functionToCall.apply(null, functionArgsArr)
messages.push({
role: 'function',
name: functionName,
content: `${JSON.stringify(functionResponse, null, 2)}`
})
} else if (finish_reason === 'stop') {
res.status(200).json({ data })
return
}
}
} catch (error: any) {
res.status(500).json({ error: error.message })
}
}
async function getLocation() {
return { latitude: 40.7128, longitude: -74.0060 }
}
async function getCurrentWeather(latitude: any, longitude: any) {
return { temperature: 22, condition: "sunny" }
}
const functions = [
{
type: 'function',
function: {
name: 'getLocation',
description: "Get the user's current location",
parameters: {
type: 'object',
properties: {},
required: []
}
}
},
{
type: 'function',
function: {
name: 'getCurrentWeather',
description: 'Get the current weather for a location',
parameters: {
type: 'object',
properties: {
latitude: { type: 'number' },
longitude: { type: 'number' }
},
required: ['latitude', 'longitude']
}
}
}
]
const availableFunctions = {
getCurrentWeather,
getLocation
}
完整的代码:nextjs-chatgpt-tutorial
3.问题
GPT 大模型如何知道获取天气需要调用 getLocation()
和 getCurrentWeather()
函数的?
- 自然语言理解能力:通过对用户输入“今天天气怎么样?”的理解,知道用户想要获取天气信息。
- 上下文分析:模型分析了所有的请求上下文,包括可用的工具函数。它理解了
getLocation()
和getCurrentWeather()
函数的功能和参数。 - 任务分解:模型将“获取天气”任务分解为两个步骤:首先获取位置,然后根据位置获取天气。
- 工具选择能力:模型基于对任务的理解,选择了最适合的工具组合来完成任务。
- 参数依赖分析:模型分析到
getCurrentWeather()
函数需要经度和纬度作为输入,而getLocation
函数刚好可以提供。因此,它推断出需要现调用getLocation()
,再调用getCurrentWeather()
。 - 训练数据中的模式:模型训练过类似的场景,学习到了处理天气查询时常用的步骤和工具组合。
- 推理能力:基于以上所有信息,模型推理后得出需要依次调用这两个函数才能回答用户的问题。
GPT 模型的上下文理解、任务分解、工具选择和逻辑推理能力,通过深入理解任务需求和可用工具的功能,从而做出智能决策。
本文由作者按照 CC BY 4.0 进行授权