openai function call stream调用指南
首先支持了 function call 且支持多个function并行调用的模型有:gpt-4-turbo-preview, gpt-4-0125-preview, gpt-4-1106-preview, gpt-3.5-turbo-0125, and gpt-3.5-turbo-1106
所谓并行就是每次要调用的function可以同时进行,而不是串行。
function call支持流式处理,可以做到打印机效果
具体例子可以参考一个开源项目:
点击跳转
https://github.com/XingYu-Zhong/ChineseStockGPT
准备工作
需要准备一份tools的json文件,用来描述每个function的功能和需要输入的参数
ex:
tools = [
{
"type": "function",
"function": {
"name": "get_current_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
},
"required": ["location", "format"],
},
}
},
{
"type": "function",
"function": {
"name": "get_n_day_weather_forecast",
"description": "Get an N-day weather forecast",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"format": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The temperature unit to use. Infer this from the users location.",
},
"num_days": {
"type": "integer",
"description": "The number of days to forecast",
}
},
"required": ["location", "format", "num_days"]
},
}
},
]
调用方式(流式)
需要传入模型名字,消息数组,工具数组,流式参数,同时也可以传入tool_choice这个参数来指定工具效用,一般可以不填,让gpt直接根据messages来选择
stream = await client.chat.completions.create(
model=model,
messages=one_message,
tools=tools,
stream=True
)
流式处理比较麻烦,需要自己处理调用工具的流式数据,这里给出一个简单的例子
func_call_list = []
async for part in stream:
if token := part.choices[0].delta.content:
await msg.stream_token(token)
delta= part.choices[0].delta
if delta.tool_calls:
for tcchunk in delta.tool_calls:
if len(func_call_list) <= tcchunk.index:
func_call_list.append({
"id": "",
"name": "",
"type": "function",
"function": { "name": "", "arguments": "" }
})
tc = func_call_list[tcchunk.index]
if tcchunk.id:
tc["id"] += tcchunk.id
if tcchunk.function.name:
tc["function"]["name"] += tcchunk.function.name
if tcchunk.function.arguments:
tc["function"]["arguments"] += tcchunk.function.arguments
选择工具按理来说是可以不用流式处理这么麻烦的,但是很有可能当前用户的message根本不需要调用工具,然后gpt直接回答流式处理就应该直接输入给前端,所以在第一次就需要流式处理数据。
处理完数据后我们需要判断是否调用了工具,按照上述写法,我们只需要判断func_call_list是否为空即可,因为如果调用了工具,那么func_call_list中至少会有一个元素。
调用工具的话第一件事就要把func_call_list加入message数组里。
message_history.append(
{"role": "assistant", "tool_calls": func_call_list}
)
类似这样,这一步必须加入,要不然后面无法对接上。
然后我们去for tool_call in func_call_list:然后去调用函数获得结果后需要把结果添加到message里
message_history.append(
{
"tool_call_id": tool_call['id'],
"role": "tool",
"name": function_name,
"content": str(function_response),
}
)
最后我们在去调用openai
stream = await client.chat.completions.create(
model=model,
messages=message_history,
tools=tools,
tool_choice=None,
stream=True
)
async for part in stream:
if token := part.choices[0].delta.content or "":
await msg.stream_token(token)
message_history.append({"role": "assistant", "content": msg.content})
await msg.update()
总结
对于function call,我们会调用两次openai接口,如果有function需要调用的情况下,如果没有就只会调用一次openai接口。
参考
- https://cookbook.openai.com/examples/how_to_call_functions_with_chat_models
- https://cookbook.openai.com/examples/how_to_call_functions_for_knowledge_retrieval
- https://platform.openai.com/docs/guides/function-calling