背景
最近 OpenAI 发布了一些模型及 API 的更新,主要有以下内容:
-
Chat Completions 接口中新增对
function calling
(函数调用)的支撑; - gpt-3.5 发布的新的支撑 16k 上下文的模型
gpt-3.5-turbo-16k
,直接变成了原来的 4 倍; - 发布了新的支撑函数调用的模型
gpt-3.5-turbo-0613
和gpt-4-0613
;
其间对开发者而言,最兴奋的便是 function calling
的功用了,这个根本便是 ChatGPT Plugin 底层完结的根底了,有了这个才能咱们也能够打造自己的“插件商城”了。下面咱们就一起看一下 function calling
的具体功用。
概述
GPT 自身的约束
OpenAI 的 GPT 自身具有很强的智能,但也有知识陈旧以及短少“四肢”问题。比如,gpt-4
模型的练习数据都是 2021 年 9 月之前的数据,所以他并不知道 2023 年苹果发布了 visionOS 体系;另外,现在的 ChatGPT 并不能履行一些动作,比如帮助发送邮件,现在能做的仅仅是编写邮件。
具体表现为:
- LLM 的知识是依据模型练习时运用的数据及知识,关于后续的新的事实其并不知道;
- LLM 相似于“缸中之脑”,其有很高的智能但是短少一些举动的才能;
现在的解决计划
上述问题社区中也有一些解决计划,如 LangChain 之类的东西。除此之外,OpenAI 官方也推出了 Plugin 的功用,答应开发者能够给 LLM 供给一些才能来解决上述问题,甚至是增强其才能供给了更多模态的信息输出。
上述的解决计划中也都有各自的问题:
- LangChain 的解决计划究竟不是官方计划,不可避免的存在一些安稳性的危险,除此之外,LangChain 现在仅支撑 Python 以及 JS 代码,其他的言语没有现成的解决计划;
- OpenAI Plugin 的方式是正解,但是只能在 ChatGPT 这个封闭的生态中运用,关于一些笔直范畴的 LLM 运用开发而言,并不能直接运用;
Function Calling
简略讲 Function Calling
功用其实便是在 Chat Completions 接口(下文一致称 Chat 接口)中新增了一个 functions
的可选入参,该参数是一个列表能够支撑传入多个函数的声明,在声明中告诉 GPT 有这些函数能够被调用,他能够凭借这些函数完结自己不能完结的的一些事情,或者说把自己的使命完结的愈加超卓。
从这视点来理解 Function Calling
和 ChatGPT 中的 Plugin 并没有本质差异。前者是面临开发者的根底才能,后者是面临用户的根底交互体会。
Function Calling 的呈现根本上能够很好的解决之前留传的一些问题:
- 突破编程言语的约束,现在发布的功用是云端接口,只需能够进行网络恳求的言语都能够完结此功用;
- 提升函数调用的安稳性,官方出品安稳性有确保,但并不是没有安稳性的问题仍是要增加对应容错逻辑;
- 提示 API 易用性,笔直范畴的 LLM 运用开发少不了 API 调用,这部分的运用越简略其上层的 LLM 运用月昌盛;
下面咱们就来具体看一下 Function Calling
到底是什么,怎么用,以及一些常见的运用案例。
Chat Completions
Function Calling
是 Chat 接口中一个参数,想要了解 Function Calling
咱们需求先了解一下 Chat 接口的一些根本概念。
接口简介
Chat 接口ChatGPT 谈天运用中运用的接口,差异于之前的 Completions 接口,它将用户和 GPT 的对话以列表的方式进行进行办理,并不是将一切的对话的一致拼接在一个字符串中。除此之外,Chat 接口还明确了 role 的概念,体系的上下文信息经过 system 来标明。
现在接口仅支撑 gpt-3.5 和 gpt-4 的系列模型。其方式大致如下:
import openai
openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Who won the world series in 2020?"},
{"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
{"role": "user", "content": "Where was it played?"}
]
)
messages 标明的是对话列表,包括对话的角色以及对应的内容,此次的更新中新增了 function
这个角色,现在共有以下内容:
- system:标明体系上下文信息,确认当时对话的根本风格,一般接口只包括一条该内容;
- user:用户的发问的问题;
- assistant:GPT 回来的答复内容;
- function:函数履行的成果,需求将此信息传递给 GPT,以便 GPT 愈加次内容做出进一步的答复;
Function Calling 根本界说
这次的 Function Calling 功用并不是新接口,而是在 Chat 接口上扩展而来的。在恳求接口以及接口回来上都有调整。主要内容如下:
- 接口恳求:新增 functions 参数和messages平级,role 中新增 function 枚举标识是函数履行的成果;
- 接口回来:choices 的 message 中新增 function_call 字段,包括函数履行有必要的参数信息,与文本内容 content 平级,但根本不会一起呈现;
Curl 恳求方式大致如下(下文会具体解说字段意义):
curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
"model": "gpt-3.5-turbo-0613",
"messages": [
{"role": "user", "content": "What is the weather like in Boston?"}
],
"functions": [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
}'
回来的 JONS 方式大致如下(下文会具体解说字段意义):
{
"id": "chatcmpl-123",
...
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_current_weather",
"arguments": "{ \"location\": \"Boston, MA\"}"
}
},
"finish_reason": "function_call"
}]
}
Function Calling 调用流程
Function Calling 整个功用的调用次序大致如下:
- 声明函数并恳求 API:界说当时函数的称号,描绘,以及对应的参数信息,并履行 OpenAI 的 Chat 接口;
- 履行本地函数:承受接口回来,并解析对应的函数参数信息,依据对应的参数信息调用本地函数;
- 上报履行成果:将本地函数履行的成果上报给 OpenAI Chat 接口进行汇总、总结;
具体的时序图如下:
下面就以用户询问当时的气候的场景来具体解说下 Function Calling
是如何运行的。
根本运用
第 1 步:声明函数并恳求 API
其间 functions 是一个列表包括多个函数目标,每个目标中包括:
- name:函数的名字,但函数被射中时接口会回来此处界说的称号;
- description:函数的描绘,OpenAI 会依据此描绘依据场景决议是否需求调用此函数,所以这里一定要描绘清楚;
- parameters:函数参数,当时界说的函数需求哪些传参,这些参数的类型以及可空性都需求在这里声明;
首要咱们先界说一个获取当时气候的函数声明,而且这个函数需求两个参数:
- location:方位信息,即想要查询那个地方的气候信息,类型是一个字符串,非空;
- unit:气候的单位,类型是一个字符串,是两个枚举摄氏度与华氏度,可空;
其声明大致如下:
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
运用上述函数声明恳求 OpenAI 接口的 Python 代码大致如下:
def chat():
# Step 1: send the conversation and available functions to GPT
messages = [{"role": "user", "content": "What's the weather like in Boston?"}]
functions = [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
},
"required": ["location"],
},
}
]
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=messages,
functions=functions,
function_call="auto", # auto is default, but we'll be explicit
)
留意:
- 这里运用的模型是
gpt-3.5-turbo-0613
,现在支撑此功用的模型还有gpt-4-0613
其他的模型可能并不支撑函数调用。 -
function_call
传参数标明 OpenAI 如何运用函数,如果 functions 参数不为空时默认值为 auto,如果为空时默认值为 none;
第 2 步:履行本地函数
若声明的函数被射中之后,GPT 会回来本地函数履行所需的必要参数(第一步中界说的),在履行本地函数之前需求先将其解析处理。GPT 的 Response 和之前相比会有以下不同之处:
-
content
:content 内容为空,不仔包括对应的文本信息; -
function_call
:在 choices 列表中新增 function_call 字段,该字段中包括需求调用的函数信息; -
finish_reason
:finish_reason 对应的值为function_call
,标明是因为函数调用而停止的;
其回来的内容方式大致如下:
{
"id": "chatcmpl-123",
...
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"function_call": {
"name": "get_current_weather",
"arguments": "{ \"location\": \"Boston, MA\"}"
}
},
"finish_reason": "function_call"
}]
}
其解析的代码逻辑大致如下:
def parse_response(response):
response_message = response["choices"][0]["message"]
# 检测是否需求调用函数
if response_message.get("function_call"):
# 调用函数
available_functions = {
"get_current_weather": get_current_weather,
} # only one function in this example, but you can have multiple
function_name = response_message["function_call"]["name"]
fuction_to_call = available_functions[function_name]
function_args = json.loads(response_message["function_call"]["arguments"])
function_response = fuction_to_call(
location=function_args.get("location"),
unit=function_args.get("unit"),
)
代码的大致逻辑如下:
- 首要判别
function_call
是否存在,若存在则标明需求调用函数; - 运用 JSON 解析对应的函数名以及对应的参数信息;
留意,OpenAI 回来的 JSON 信息并不能始终确保其有效性,需求自己做容错处理。
其间 get_current_weather
这个函数的具体完结如下:
def get_current_weather(location, unit="fahrenheit"):
"""Get the current weather in a given location"""
weather_info = {
"location": location,
"temperature": "72",
"unit": unit,
"forecast": ["sunny", "windy"],
}
return json.dumps(weather_info)
首要函数在调用时 location 时必传的,unit 有默认值能够不必传递。这里为了便利起见并没有真实的去恳求气候的接口,而是写死了一个 mock 的数据。在自己的业务中,需求将其替换成自己的真实业务逻辑。
下一步便是需求将这里的履行数据上报给 OpenAI 进行进一步的整合了。
第 3 步:将成果上报给 OpenAI
这一步咱们会将函数的履行成果上传给 OpenAI,需求留意的有:
- 对话的 messages 中需求包括上一步 OpenAI 回来的 function_call 信息;
- 上报成果的 role 为 function,而且还需求经过 name 特点奉告对应的函数名;
具体的代码如下:
def chat_function_call():
# 第 3 步: 将函数履行的成果上报给 GPT
messages.append(response_message) # extend conversation with assistant's reply
messages.append(
{
"role": "function",
"name": function_name,
"content": function_response,
}
) # extend conversation with function response
second_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=messages,
) # get a new response from GPT where it can see the function response
return second_response
完整的的 curl 代码大致如下:
curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
"model": "gpt-3.5-turbo-0613",
"messages": [
{"role": "user", "content": "What is the weather like in Boston?"},
{"role": "assistant", "content": null, "function_call": {"name": "get_current_weather", "arguments": "{ \"location\": \"Boston, MA\"}"}},
{"role": "function", "name": "get_current_weather", "content": "{\"temperature\": "22", \"unit\": \"celsius\", \"description\": \"Sunny\"}"}
],
"functions": [
{
"name": "get_current_weather",
"description": "Get the current weather in a given location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"]
}
}
]
}'
对应的回来成果如下:
{
"id": "chatcmpl-123",
...
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "The weather in Boston is currently sunny with a temperature of 22 degrees Celsius.",
},
"finish_reason": "stop"
}]
}
解析上面的 message 中的 content 内容,就能够展示呼应的信息给到用户了。
总结
Chat Completions 接口中新增的 Function Calling
功用能够很好解决 GPT 知识陈旧以及短少四肢的问题,是对 GPT 才能的一次大升级。其主要流程为:
- 声明函数并恳求 API:界说当时函数的称号,描绘,以及对应的参数信息,并履行 OpenAI 的 Chat 接口;
- 履行本地函数:承受接口回来,并解析对应的函数参数信息,依据对应的参数信息调用本地函数;
- 上报履行成果:将本地函数履行的成果上报给 OpenAI Chat 接口进行汇总、总结;
个人估测 Function Calling
是 ChatGPT Plugin 功用的底层完结技能支撑,有了这个功用 LLM 运用开发变得愈加安稳可靠,一起也会有人依据这个这个技能开发出一套第三方的 GPT Plugin 商店;
除此之外,想必在 LLM 开发层也会有对应的动作:
- LangChain 相似的东西会快速跟进支撑(现已支撑了);
- 像 Google Bart 之类自研 LLM 的厂家会及时跟进开发相似的功用;
- 开源的 LLM 应该也会很快的推出依据
Function Calling
相似的微调计划,使开源的 LLM 也能装上“四肢”;
后面会依据 Function Calling
功用解说更多的实际案例,概况见:OpenAI 函数调用(2) – 打造自己 LLM 运用,欢迎重视。
参考资料
- OpenAI新功用 blog:Function calling and other API updates
- OpenAI 开发文档
- How_to_call_functions_with_chat_models