我在六月份写了一篇关于GPT 函数调用(Function calling) 的博客,其中介绍了函数调用的方法,但之前的函数调用,在一轮对话中只能调用一个函数。就在上周,OpenAI在开发者大会上,升级了函数调用的功能,在新的gpt-3.5和gpt-4模型中,可以在单次对话中调用多个函数了,而且在python SDK中也提供了并发函数调用相关的接口,无疑这将大幅减少大语言模型和现实世界之间交互的开发复杂度,接下来就让我用一个具体的示例,带你了解下OpenAI的新特性。
这里假设我需要利用gpt实现一个百度、谷歌、必应三个搜索引擎搜索结果汇总的功能。我现在有以下的几个搜索函数(我们假装已经实现了从分别从百度、谷歌、必应获取搜索结果的逻辑)。
def search_baidu(keyword):
"""从百度搜索引擎中搜索关键词"""
return f"{keyword}是一个技术博主"
def search_google(keyword):
"""从谷歌搜索引擎中搜索关键词"""
return f"{keyword}是一个后端工程师"
def search_bing(keyword):
"""从必应搜索引擎中搜索关键词"""
return f"{keyword}是一个Python爱好者"
接下来我们需要将这三个搜索函数按照openai给定的格式用json字符串描述出来,具体可以参考官方文档,我这里直接给出上面三个函数的json描述。
tools = [
{
"type": "function",
"function": {
"name": "search_baidu",
"description": "从百度搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
},
{
"type": "function",
"function": {
"name": "search_google",
"description": "从google搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
},
{
"type": "function",
"function": {
"name": "search_bing",
"description": "从bing搜索引擎中搜索关键词",
"parameters": {
"type": "object",
"properties": {
"keyword": {
"type": "string",
"description": "搜索关键词",
}
},
"required": ["keyword"],
},
}
}
]
available_functions = { "search_baidu": search_baidu, "search_google": search_google, "search_bing": search_bing }
上面这个的目的是将所有函数的作用和使用方法(入参)描述给gpt,让gpt知道如何去调用。available_functions是为了保存函数名和函数的映射关系,方便我们后续通过函数名去调用函数。
接下来我们实现一个函数,其功能就是给定一个关键词(keyword),返回百度、谷歌、必应三个搜索引擎搜索结果的汇总,这要在之前的函数调用方式下,你必须通过多轮对话获取到所有需要调用的函数,然后将结果汇总后在发给gpt。而在支持了多函数调用后,仅需要一轮对话就可以完成所有的功能,完整的代码如下:
from openai import OpenAI
import json
client = OpenAI(base_url='https://thales.xindoo.xyz/openai/v1/')
def search(keyword):
messages = [{"role": "user", "content": f"汇总下百度、谷歌、必应三个搜索引擎关于'{keyword}'的结果"}]
# 发起首次请求,告诉gpt要做什么,已经有哪些函数可以调动
response = client.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages,
tools=tools,
tool_choice="auto",
)
response_message = response.choices[0].message
tool_calls = response_message.tool_calls
# 检查是否需要调用函数
if tool_calls:
# 解析所有需要调用的函数及参数
messages.append(response_message) # 注意这里要将openai的回复也拼接到消息列表里
# 将所有函数调用的结果拼接到消息列表里
for tool_call in tool_calls:
function_name = tool_call.function.name
function_to_call = available_functions[function_name]
function_args = json.loads(tool_call.function.arguments)
function_response = function_to_call(**function_args)
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response,
}
)
second_response = client.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages,
)
return second_response.choices[0].message.content
print(search("xindoo"))
输出的结果是根据百度、谷歌和必应三个搜索引擎的结果,'xindoo'可能是一个技术博主、后端工程师以及Python爱好者。
这里需要提醒以下两点:
- 目前只有gpt-4-1106-preview和gpt-3.5-turbo-1106两个模式支持单词对话同时调用多个模型的,其他模型均不支持。
- openAI改变了api中传递function的参数,废弃了 functions和 function_call,改用了tools和tool_choice两个新参数,我猜测是为了未来增加更多的工具支持。
这里额外说下,上面的三个函数调用是串行调用,如果每个函数都比较耗时的话,会增加整体的调用时长,而在最新的assistant api中增加了并行执行函数的api,这个我们放到下篇文章中讲解。