使用 Python接入 OpenAI API,梳理清楚对话角色&函数调用
前言
回顾上篇文章写到如何实现最简单的OpenAI对话请求示例:
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格式就会输出如下内容
{
"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的记录,给个示例就明白:
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的时候报错情况如下:
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价格查询的函数调用,详细注释在代码里面:
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 = []
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计算,敬请期待~
如果觉得内容不错,欢迎点个赞,你的支持是我更新的动力。
转载自:https://juejin.cn/post/7361629576416215081