TL;DR

ChatGPT已经自带函数调用能力了,本文给了一个简单的示例。

回顾

笔者曾经在LangChain系列文章里交代利用LangChain赋予ChatGPT上网的能力。

然而OpenAI官方在June 13, 2023的更新里提出了function calling的能力,可以说在这个方向上直接灭掉了LangChain。

先看一下官方有哪些更新。

  • 在Chat Completions API中提供了新的函数调用能力
  • gpt-4gpt-3.5-turbo 模型的小版本迭代
  • gpt-3.5-turbo 扩展到了4倍(16k)的上下文的能力
  • SOTA embeddings 模型降价 75%
  • gpt-3.5-turbo 降价25%
  • gpt-3.5-turbo-0301gpt-4-0314 的退役时间

而最令人激动的,实属 function calling

一个示范

如下图,依然是用人话要股票信息,能够直接给出df数据(完成函数调用)。

如何实现

简单到令人发指。

首先,只需定义functions manifest,如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
functions = [
{
"name": "get_stock_a",
"description": "获取指定A股股票一段时间内的量价信息",
"parameters": {
"type": "object",
"properties": {
"stock_name": {
"type": "string",
"description": "具体的股票名称或代号,如贵州茅台、中国移动",
},
"start_date": {
"type": "string",
"description": "开始日期,格式为2023-01-01",
},
"end_date": {
"type": "string",
"description": "结束日期,格式为2023-01-01",
},
},
"required": ["stock_name", "start_date", "end_date"],
},
}
]

当然这个是不能乱来的,需要遵循一定的规则,具体需要参考官方specification

然后,实现你的自定义方法,这个示例就是实现 get_stock_a 方法,以获取指定A股股票的量价数据。这里我不给出具体实现,有兴趣私聊。

1
2
3
def get_stock_a(stock_name, start_date, end_date):
""" 获取指定A股股票的量价数据 """
print(f"stock_name: {stock_name}, start_date: {start_date}, end_date: {end_date}")

接着,只需在调用Chat Completions API时候,把functions带上。

1
2
3
4
5
6
7
requests.post(f"{openai.api_base}/chat/completions", headers=headers,
json={
"model": GPT_MODEL, # 'gpt-3.5-turbo-0613' or 'gpt-4-0613'
"messages": messages,
"functions": functions
},
)

最后,你会看到类似下面这样的返回,完成一点解析和调用的动作,这个事就成了。

1
2
3
4
5
6
7
8
{
"role": "assistant",
"content": null,
"function_call": {
"name": "get_stock_a",
"arguments": "{\n \"stock_name\": \"招商银行\",\n \"start_date\": \"2023-05-01\",\n \"end_date\": \"2023-06-01\"\n}"
}
}

完整示例

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
import json
import openai
import requests

GPT_MODEL = "gpt-3.5-turbo-0613"

openai.api_key = "" # 你的密钥
openai.api_base = "https://api.openai.com/v1"


def call(messages, functions):
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {openai.api_key}"}
try:
response = requests.post(f"{openai.api_base}/chat/completions", headers=headers,
json={
"model": GPT_MODEL,
"messages": messages,
"functions": functions
},
)
return response.json()["choices"][0]["message"]
except Exception as e:
print("Unable to generate ChatCompletion response")
print(f"Exception: {e}")


functions = [
{
"name": "get_stock_a",
"description": "获取指定A股股票一段时间内的量价信息",
"parameters": {
"type": "object",
"properties": {
"stock_name": {
"type": "string",
"description": "具体的股票名称或代号,如贵州茅台、中国移动",
},
"start_date": {
"type": "string",
"description": "开始日期,格式为2023-01-01",
},
"end_date": {
"type": "string",
"description": "结束日期,格式为2023-01-01",
},
},
"required": ["stock_name", "start_date", "end_date"],
},
}
]


def get_stock_a(stock_name, start_date, end_date):
print(f"stock_name: {stock_name}, start_date: {start_date}, end_date: {end_date}")


def chat_call(message):
messages = [{"role": "system", "content": "不要对函数中应该填入的数值作出自作主张的假设。如果用户的要求不够明确,要求澄清。"}]
messages.append({"role": "user", "content": message})
r = call(messages, functions)

fcall = r["function_call"]
return eval(fcall["name"])(**json.loads(fcall["arguments"]))


chat_call("2023年5月1日到6月1日,招商银行的A股量价给我一份")

如果你足够幸运,你将看到这样一串文本: “stock_name: 招商银行, start_date: 2023-05-01, end_date: 2023-06-01”

Ending

不多说了,黄老板已经说过了,“跑起来掠食,或是努力奔跑免得成为掠食者的食物”。