使用 Python接入 OpenAI API,梳理清楚对话角

前言

回顾上篇文章写到如何实现最简单的OpenAI对话请求示例:

使用 Python接入 OpenAI API,实现简单的对话生成,介绍其中相应参数含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ini复制代码from openai import OpenAI
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())
client = OpenAI()

response = client.chat.completions.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?"}
]
)
print(response) # 打印的是一个对象
print(response.choices[0].message.content) # 打印的是一个具体的回复的内容

最后返回了一个对象,其实在model=”gpt-3.5-turbo”,增加一行response_format={ “type”: “json_object” }, 将返回值设置为json格式就会输出如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
json复制代码{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"message": {
"role": "assistant",
"content": "The World Series in 2020 was played at Globe Life Field in Arlington, Texas."
}
}
],
"created": 1714051759,
"id": "chatcmpl-6p9XYPYSTTRi0xEviKjjilqrWU2Ve",
"model": "gpt-3.5-turbo-0125",
"object": "chat.completion",
"usage": {
"prompt_tokens": 18,
"completion_tokens": 54,
"total_tokens": 72
}
}

这样返回的内容格式可以更容易给开发者处理。

对话角色

在上面的例子中messages有几个角色分别为:system,user,assistant,对应的解释为:

  • system:用于设置 AI 的行为、背景等,比如设定其为人工智能专家等。
  • assistant:通常是模型的回复,可用于提供上下文。
  • user:模型的使用者,也即聊天内容的发起者,同时可用于提供上下文。

为了让API记住对话的上下文,就要每次在对话的时候都要 携带历史对话的assistant,user的记录,给个示例就明白:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ini复制代码from openai import OpenAI
from dotenv import load_dotenv, find_dotenv

_ = load_dotenv(find_dotenv())
client = OpenAI()

response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": "你是一个ChatGPT助手,你帮助用户回答问题"},
{"role": "user", "content": "说10遍王子请睡觉"},
    {"role": "assistant", "content": "王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。王子请睡觉。"},
{"role": "user", "content": "你上面回答了几次王子请睡觉"}
]
)
print(response.choices[0].message.content) # 我回答了10次“王子请睡觉”。

这就记住了上次的对话,当然携带的内容过多占用的token就会越多,目前ChatGPT3.5最大上下文应该是16K,16k对应16000个token,测试了一下复制了很多assistant和user的对话历史在代码中,token在超过16k的时候报错情况如下:

1
css复制代码openai.BadRequestError: Error code: 400 - {'error': {'message': "This model's maximum context length is 16385 tokens. However, your messages resulted in 17184 tokens. Please reduce the length of the messages.", 'type': 'invalid_request_error', 'param': 'messages', 'code': 'context_length_exceeded'}}

所以在开发多轮对话API的时候要考虑token的占用预估的问题。OpenAI的文档有对token计算的说明,我们以后再研究。

函数调用

函数调用(Function Call)是 OpenAI API 的一项强大功能,它允许开发者向模型提供自定义函数,从而扩展模型的功能并使其能够处理更复杂的任务。借助函数调用,开发者可以:

  • 引入外部数据: 模型可以访问并处理来自外部 API 或数据库的数据,例如天气预报、股票行情等。
  • 实现复杂逻辑: 模型可以执行更复杂的逻辑操作,例如计算、排序、过滤等。
  • 增强交互性: 模型可以与用户进行更具交互性的对话,例如根据用户输入提供个性化建议或完成特定任务。

今天就引入外部数据做一个BTC价格查询的函数调用,详细注释在代码里面:

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
ini复制代码from openai import OpenAI
from dotenv import load_dotenv, find_dotenv
import requests,json


_ = load_dotenv(find_dotenv())
client = OpenAI()

def get_coin_prince(coin):
# Execute query here
url = 'https://data-api.binance.vision/api/v3/ticker/24hr?symbol='+coin.upper()+"USDT" # binance直接请求获得结果数据
response = requests.get(url)
response = response.json()
try:
return response['lastPrice']+'USDT'
except:
return "The coin is not exist"

# 函数描述
tools = [
{
"type": "function",
"function":{
"name": "get_coin_prince",
"description": "Get a coin Price from Binance, The price unit is in US dollars",
"parameters": {
"type": "object",
"properties": {
"coin": {
"coin": "string",
"description": "Abbreviations for Virtual Currency",
}
},
"required": ["coin"],
},
}
}
]

messages=[
{"role": "user", "content": "BTC的价格是多少?"},
],
messages = []
messages.append({"role": "system", "content": "你是一个虚拟币查询机器人"})
messages.append({"role": "user", "content": "btc的价格是多少?"})
response = client.chat.completions.create(
model="gpt-3.5-turbo-0613",
messages=messages,
tools=tools,
  tool_choice="auto", 
)

print(response) # 打印的是一个对象

response_message = response.choices[0].message # 消息对象

tool_calls = response_message.tool_calls # 识别是否触发函数调用
if tool_calls: # 判断是否为函数调用
available_functions = {
"get_coin_prince": get_coin_prince,
} # 本例中只有一个函数,但可以有多个
messages.append(response_message)
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(
coin=function_args.get("coin"),
) # 真实调用本地函数
messages.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": function_response, #本地调用函数内容返回值放进对话 ,接下来给ChatGPT组装内容
}
) # 新一轮的对话的函数调用细节(函数ID,规则,名称,参数)

# 开始第二次对话
second_response = client.chat.completions.create(
model="gpt-3.5-turbo-0125",
messages=messages,
) # 从模型中获取一个新的响应
print(messages) # 调试组装过的对话内容
print(second_response)
res = second_response.choices[0].message.content

print(res)  # BTC的价格为64477.51 USDT。

从原理上面来梳理,就是声明函数,给对话增加额外参数提示可以自动调用函数,识别到有函数调用,就本地调用函数再组装进行第二次对话获得最终结果,第一次接触还是比较难以理解的,顺利弄完了很有成就感,注意以上代码需要有相对应的网络环境。

最后

今天算是真的搞懂了大模型的本地函数调用,找了好多资料都没有我这样的研究过程示例,学到了很开心,今天就介绍到这里,ChatGPT的API基础的都介绍完毕了,后面可能会来解密一下OpenAI API中的详细参数和Token计算,敬请期待~

如果觉得内容不错,欢迎点个赞,你的支持是我更新的动力。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%