1. Function calling介绍
在GPT模型中,Function calling(函数调用)是指通过API向模型提供描述性的函数,使其能够智能地选择输出一个包含参数的JSON对象,以便调用一个或多个函数。值得注意的是,Chat Completions API并不直接执行函数调用,而是生成可以用于在代码中执行函数的JSON。
通俗的讲Function calling功能就是我们提供一组函数的定义(包括函数的功能描述、参数描述),把函数的定义传递给GPT模型,由模型根据用户的问题决定要调用那个函数,由于模型无法执行我们定义的外部函数,模型只能以请求响应的方式返回希望调用那个函数(包括函数调用参数),我们程序收到模型请求结果后,在本地执行函数调用,然后把函数调用的结果拼接到提示词中再次传给模型,让模型进一步处理,最后在把结果返回给用户。
2. Function calling的应用场景
以下是其在实际应用中的一些例子:
- 创建调用外部API回答问题的助手,例如定义函数
send_email(to: string, body: string)
或get_current_weather(location: string, unit: 'celsius' | 'fahrenheit')
。 - 将自然语言转换为API调用,例如将“Who are my top customers?”转换为
get_customers(min_revenue: int, created_before: string, limit: int)
然后调用内部API。 - 从文本中提取结构化数据,例如定义函数
extract_data(name: string, birthday: string)
或sql_query(query: string)
。
利用函数调用功能,我们可以实现智能体(AI Agent),让AI可以跟我们本地的系统、数据库互动,例如让AI去查询最新的天气、查询股票价格、让AI去点外卖、订机票等等。
3. 支持Function calling的模型
并非所有版本的模型都经过了函数调用数据的训练。目前支持函数调用的模型有:gpt-4
、gpt-4-turbo-preview
、gpt-4-0125-preview
、gpt-4-1106-preview
、gpt-4-0613
、gpt-3.5-turbo
、gpt-3.5-turbo-1106
和gpt-3.5-turbo-0613
。
此外,模型gpt-4-turbo-preview
、gpt-4-0125-preview
、gpt-4-1106-preview
和gpt-3.5-turbo-1106
支持并行函数调用功能,这允许它们一次性执行多个函数调用,并且能够并行解决这些函数调用效果和结果。
提示:Function calling功能带来的潜在风险在于,它可能导致模型产生错误参数(即幻觉参数)。因此,在代表用户对外界产生实际影响之前(例如发送电子邮件、在线发布、进行购买等),建议在产品层面增加用户确认流程,用户确认之后再执行函数。
4. Function calling例子
4.1 python例子
在OpenAI平台上实现Function calling通常遵循下面的基本步骤,以下将通过查询天气的案例,使用Python代码详细讲解整个过程。
步骤1: 准备可被模型调用的函数和函数定义
首先,我们需要定义一个可以被GPT模型调用的函数。这通常意味着我们需要准备一个我们自己的函数,这个函数能够根据传入的参数执行某些操作,比如与第三方API通信。
import json
# 这是一个假设的函数,用于获取当前的天气情况
def get_current_weather(location, unit="fahrenheit"):
# 在实际应用中,这里会与某个天气API接口进行交互来获取数据
# 这里为了简化示例返回了固定的数据
return json.dumps({
"location": location,
"temperature": "18",
"unit": unit
})
步骤2: 根据问题和tools参数调用模型
接下来,我们需要通过Chat Completion API调用GPT模型,并传入用户的查询(如“当前天气如何”),以及tools
参数,其中包含了我们刚刚定义的get_current_weather
函数的描述。
from openai import OpenAI
client = OpenAI()
# 定义tools参数,其中包含了get_current_weather函数的描述
tools = [{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取给定地点的当前天气",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "城市名称,例如:“旧金山,CA”"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
}]
# 发起调用,传入用户的问题
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "旧金山当前的天气如何?"}],
tools=tools
)
步骤3: 本地执行函数
模型返回的结果会包含模型希望调用的函数,函数信息通常包含在tool_calls
响应参数中,我们就可以根据tool_calls
参数描述的函数调用信息,在本地执行相应的函数调用。
# 解析模型响应中的tool_calls
tool_calls = response.choices[0].message.tool_calls
# 如果模型确定要调用一个函数
if tool_calls:
# 获取模型生成的参数
# ps: 这里只是假设模型仅调用一个函数,实际上有可能调用多个函数
arguments = json.loads(tool_calls[0].function.arguments)
# 执行我们之前定义的函数
# ps: 这里为了演示以硬编码的方式调用本地函数,实际业务你可以根据tool_calls参数动态的选择调用本地函数。
weather_info = get_current_weather(**arguments)
print(weather_info) # 我们可以在这里看到函数调用查询到的天气信息
步骤4: 通过函数返回结果再次调用模型
现在,我们可以将函数返回的结果作为一个新的消息发送给模型,这样模型就可以对这些结果进行处理并生成一个面向用户的响应。
# 使用刚才函数返回的结果继续与模型对话
follow_up_response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": "旧金山当前的天气如何?"},
{"role": "function", "name": "get_current_weather", "content": weather_info}
],
tools=tools
)
说明:
上面例子通过function消息的方式,告诉gpt函数返回内容
{"role": "function", "name": "get_current_weather", "content": weather_info}
实际上,你也可以简单的把函数返回的内容作为参考内容放到sytem消息prompt里面,让AI参考这些信息回答问题。
步骤5: 获取模型最终响应结果
最后,我们可以获取模型的最终响应并将其提供给用户。在此步骤中,模型会根据我们给予的天气信息输出一个面向用户友好的回答。
# 处理得到最终的模型响应
final_output = follow_up_response.choices[0].message.content
print(final_output) # 这个输出就是我们要展示给用户的天气信息
通过以上步骤,我们就完成了一个使用GPT模型以及函数调用的完整的查询天气的例子。
4.2. Function call函数定义说明
4.2.1 Tools参数的字段含义
函数调用(Function Call)在使用时,需要在tools
参数中定义函数的相关信息。tools
参数是一个包含多个函数定义的数组,每个函数定义包含以下字段:
type
: 此字段表示工具的类型,在函数调用中该字段应设为"function"
。function
: 此字段包含函数的详细信息,是一个对象,其具体字段如下:name
: 函数的名称,是一个字符串。description
: 对函数作用的描述,可以帮助模型生成符合用户期望的参数。parameters
: 描述函数的参数定义,是一个对象,该对象包含下列子字段:type
: 定义参数的类型,在大多数情况下应设为"object"
。properties
: 函数的每个参数的具体定义,每个参数定义都是一个对象,通常包含下列子字段:type
: 参数的数据类型(如"string"
,"integer"
,"boolean"
等)。description
: 此参数的描述,以帮助模型理解其用途。enum
(可选): 当type
为"string"
时,enum
字段可以指定一个字符串的数组,表示有效值的集合。
required
: 一个包含必填参数名称的字符串数组。
4.2.2 Tools定义示例
现在我们将提供几个tools
定义的例子,以便于理解每个字段如何使用。
示例 1:获取当前天气信息
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定地点的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "需要查询天气的城市,如'San Francisco, CA'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,'celsius'表示摄氏度,'fahrenheit'表示华氏度"
}
},
"required": ["location", "unit"]
}
}
}
在上述示例中,我们定义了一个名为get_current_weather
的函数,用于获取指定地点的当前天气。参数包括location
(位置)和unit
(单位),其中unit
有两个有效值:”celsius”(摄氏度)和”fahrenheit”(华氏度)。
示例 2:查找特定艺术家的专辑
{
"type": "function",
"function": {
"name": "find_artist_albums",
"description": "查找特定艺术家的所有专辑",
"parameters": {
"type": "object",
"properties": {
"artist_name": {
"type": "string",
"description": "艺术家的名称"
}
},
"required": ["artist_name"]
}
}
}
在这个示例中,我们创建了一个名为find_artist_albums
的函数,它的作用是查找特定艺术家的所有专辑。此函数只需要一个参数:artist_name
(艺术家名称)。
4.3. http请求function call例子
OpenAI是通过http协议的方式开放API,下面讲解如何通过http api使用function call功能,其他编程语言的开发者可以参考这个例子。
4.3.1. 步骤1:通过用户问题和函数声明调用模型
首先我们需要把用户的问题和我们支持的函数清单一起发给GPT模型,让模型根据用户的问题自动分析需要调用那个函数来回答用户问题。
下面例子,告诉GPT我们有一个get_current_weather函数可以用来查询指定城市的天气信息。
curl --location 'https://api.aiproxy.io/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {OPENAI_KEY}' \
--data '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "今天上海天气怎么样?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定地点的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "需要查询天气的城市,如'\''San Francisco, CA'\''"
},
"unit": {
"type": "string",
"enum": [
"celsius",
"fahrenheit"
],
"description": "温度单位,'\''celsius'\''表示摄氏度,'\''fahrenheit'\''表示华氏度"
}
},
"required": [
"location",
"unit"
]
}
}
}
]
}'
请求参数说明:
{
"model": "gpt-3.5-turbo", // 需要调用的gpt模型
"messages": [ // 这里是gpt的消息列表,包括用户的问题
{
"role": "user",
"content": "今天上海天气怎么样?"
}
],
"tools": [
// 这里是你的函数定义,告诉gpt有什么函数可以调用
]
}
tools参数定义参考4.2.2章节。
GPT模型正常处理的情况下,你会得到类似下面的API响应结果:
{
"model": "gpt-3.5-turbo-0613",
"object": "chat.completion",
"usage": {
"prompt_tokens": 122,
"completion_tokens": 27,
"total_tokens": 149
},
"id": "chatcmpl-8mL4hS4zNMocyR2ajKyAvSTcbNaao",
"created": 1706531447,
"choices": [
{
"index": 0,
"delta": null,
"message": {
"role": "assistant",
"tool_calls": [ // tool_calls参数代表GPT希望调用的函数列表
{
"id": "call_1iF09ttX1R9ESR18Ul2nLe1R",
"type": "function",
"function": {
"name": "get_current_weather", // 代表GPT希望通过调用get_current_weather函数,回答用户问题。
"arguments": "{\n \"location\": \"Shanghai, China\",\n \"unit\": \"celsius\"\n}" // 这里是调用get_current_weather函数的输入参数
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
4.3.2. 步骤2:本地执行函数调用
因为GPT模型本身是没法执行具体的函数调用,只是告诉我们希望调用那个函数,因此我们本地程序需要根据模型请求返回的tool_calls参数,执行具体函数调用,不同编程语言执行本地函数的方式不一样,python的例子,可以参考前面章节。
4.3.3. 步骤3:通过函数返回结果再次调用模型
因为函数的调用是在本地执行的,所以我们需要把函数执行的结果和用户的问题再次送入GPT模型,让GPT做出最终的回答。
curl --location 'https://api.aiproxy.io/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-Roc5MX1zEuVxiuaMaETV6wZ2jXcCehjUCzwP9AcNErUiwppQ' \
--data '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "今天上海天气怎么样?"
},
{
"role": "function",
"name": "get_current_weather",
"content": "{\"city\":\"上海\", \"temperature\":\"25摄氏度\"}"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "获取指定地点的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "需要查询天气的城市,如'\''San Francisco, CA'\''"
},
"unit": {
"type": "string",
"enum": [
"celsius",
"fahrenheit"
],
"description": "温度单位,'\''celsius'\''表示摄氏度,'\''fahrenheit'\''表示华氏度"
}
},
"required": [
"location",
"unit"
]
}
}
}
]
}'
说明,上面请求多了一条function消息告诉GPT模型函数的返回值,GPT会根据函数返回信息直接回答用户问题,不会再调用函数。
function消息,代表函数的返回值,格式如下:
{
"role": "function", // 消息类型是function,代表函数返回值
"name": "get_current_weather", // 告诉gpt当前消息是get_current_weather函数的返回值
"content": "{\"city\":\"上海\", \"temperature\":\"25摄氏度\"}" // 函数返回内容,可以是json格式,也可以是其他文本内容。
}
下面是GPT做出的最终回答:
{
"model": "gpt-3.5-turbo-0613",
"object": "chat.completion",
"usage": {
"prompt_tokens": 144,
"completion_tokens": 17,
"total_tokens": 161
},
"id": "chatcmpl-8mLmvvKAjSql7rGF8fvQeddKhWYvr",
"created": 1706534189,
"choices": [
{
"index": 0,
"delta": null,
"message": {
"role": "assistant",
"content": "今天上海的天气是25摄氏度。"
},
"finish_reason": "stop"
}
]
}