创建助手
这里首先需要将我们的flower_prices.csv转成Assistant能使用的file,使用如下代码即可:
from openai import OpenAI
client = OpenAI(base_url='https://thales.xindoo.xyz/openai/v1/')
file = client.files.create(
file=open("flower_prices.csv", "rb"),
purpose='assistants'
)
接下来我们定义保存账单信息的function,具体可以参考下我上篇博客OpenAI的多函数调用
def save_bill(totalCost, totalIncome):
'''保存总成本和总的收入'''
print(totalCost, totalIncome)
return "success"
function = {
"type": "function",
"function": {
"name": "save_bill",
"description": "保存总成本和总的收入",
"parameters": {
"type": "object",
"properties": {
"totalCost": {
"type": "number",
"description": "总成本",
},
"totalIncome": {
"type": "number",
"description": "总收入",
}
},
"required": ["totalCost", "totalIncome"],
},
}
}
available_functions = { "save_bill": save_bill}
创建助手(assistant)
这里需要调用API将所有的开关、文件和函数调用信息都传给OpenAI,创建一个属于我们自己的assistant。
assistant = client.beta.assistants.create(
name="花店财务助手",
description="按照每种花的售出量,统计成本和收入,计算出总利润",
model="gpt-4-1106-preview",
tools=[{"type": "code_interpreter"}, {"type": "retrieval"}, function],
file_ids=[file.id]
)
创建Thread
如上文讲到Thread是用户和Assistant对话的上下文信息,用户和Assistant初次对话肯定是需要创建上下文的,代码和很简单,如下:
thread = client.beta.threads.create(
messages=[
{
"role": "user",
"content": "我卖出去了红玫瑰3支、郁金香2支、百合6支,计算下总成本和总收入,给出具体的计算过程"
}
]
)
这里看到Thead并没有和Assistant关联到一起,猜测这里只是在本地代码里创建了一个Thread对象,实际上在OpenAI那边还没有任何操作,这个可能是OpenAI利用懒加载来减轻对服务端的压力。
创建Run
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id
)
创建Run的方法也很简单,可以看到只需要传thread_id和assistant_id两个参数即可,而这两个id都是字符串,尤其是assistant_id 你都是可以在OpenAI网站后台看到的,相信这里大家已经猜到了,Assistant和Thread不用每次都创建新的。
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id
)
获取run的状态
Run创建好之后,需要让OpenAI运行起来,这里就需要调用Retrieve方法,来获取Run的运行结果,这里如果你打印出run的话,你可能会看到类似的信息。
Run(id='run_A9phobcoIOG3euibElksTu8a', assistant_id='asst_hW7NrPZP8q8KvE9oiuceg5mM', cancelled_at=None, completed_at=None, created_at=1700400089, expires_at=1700400689, failed_at=None, file_ids=['file-uhMIBtm4BPXlJlY1UzGIPlGn'], instructions=None, last_error=None, metadata={}, model='gpt-4-1106-preview', object='thread.run', required_action=None, started_at=1700400089, status='in_progress', thread_id='thread_nvsTyK6DQdmKoVxOseSSKZF4', tools=[ToolAssistantToolsCode(type='code_interpreter'), ToolAssistantToolsRetrieval(type='retrieval'), ToolAssistantToolsFunction(function=FunctionDefinition(name='save_bill', parameters={'type': 'object', 'properties': {'totalCost': {'type': 'number', 'description': '总成本'}, 'totalIncome': {'type': 'number', 'description': '总收入'}}, 'required': ['totalCost', 'totalIncome']}, description='保存总成本和总的收入'), type='function')])
这里获取到的是run的最新状态,有可能run还没有执行完,所以可能需要一直循环调取,等待run的状态发生变化。Run有以下的一些状态。
具体的状态和含义如下表:
状态 | 定义 |
---|
queued | 当Runs首次创建或者调用了retrive获取状态后,就会变成queued等待运行。正常情况下,很快就会变成in_progress状态。 |
in_progress | 说明run正在执行中,这时候可以调用run step来查看具体的执行过程 |
completed | 执行完成,可以获取Assistant返回的消息了,也可以继续想Assistant提问了 |
requires_action | 如果Assistant需要执行函数调用,就会转到这个状态,然后你必须按给定的参数调用指定的方法,之后run才可以继续运行 |
expired | 当没有在expires_at之前提交函数调用输出,run将会过期。另外,如果在expires_at之前没获取输出,run也会变成expired状态 |
cancelling | 当你调用client.beta.threads.runs.cancel(run_id=run.id, thread_id=thread.id)方法后,run就会变成cancelling,取消成功后就会变成callcelled状态 |
cancelled | Run已成功取消。 |
failed | 运行失败,你可以通过查看Run中的last_error对象来查看失败的原因。 |
这里需要特别注意requires_action状态,这个是需要要求代码本地去执行一些函数的,执行完成后将结果返回给Assistant,之后run才能继续运行下去。
run触发函数调用
如果run.status是requires_action,我们需要调用本地工具,当然现在只有函数调用,然后将函数调用的结果返给Assistant,方便它继续执行,代码如下:
if run.status == 'requires_action':
tool_outputs=[]
for call in run.required_action.submit_tool_outputs.tool_calls:
if call.type != "function":
continue
function = available_functions[call.function.name]
output = {
"tool_call_id": call.id,
"output": function(**call.function.arguments),
}
tool_outputs.append(output)
run = client.beta.threads.runs.submit_tool_outputs(
thread_id=thread.id,
run_id=run.id,
tool_outputs=tool_outputs
)
获取Assistant的消息
接下来我只需要轮询retrive接口,获取run的最新状态,如果状态是completed,就可以读取Assistant的返回结果了。
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id
)
if run.status == 'completed':
messages = client.beta.threads.messages.list(
thread_id=thread.id
)
print(messages)
这里注意下messages是倒序排列的,所以最新的消息是在最上面的。
发起新信息
上面的流程是从Assistant创建到发起首次消息的流程,如果我们需要紧接着之前的流程继续对话,只需要在thread中添加新的消息,然后然后创建并执行run即可,代码如下:
message = client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="另外还有2支向日葵,补充下这份账单"
)
run = client.beta.threads.runs.create(
thread_id=thread.id,
assistant_id=assistant.id
)
run = client.beta.threads.runs.retrieve(
thread_id=thread.id,
run_id=run.id
)
结语
以上就是Assistants-API整体的开发流程,了解了这些流程后,大家可以很容易构建出像ChatGPT-Plus的私人助理。当然Assistants-API目前还是在beta阶段,有很多功能不完善,比如不支持流式返回、不支持图片生成、不支持插件调用……,甚至run的状态还需要轮询来获取……。另外我在写本文demo的时候,发现Retrivel的文本内容召回成功率非常低,导致账单计算成功率很低(也可能是我给的文本格式的问题)。还有就是code_interpreter运行成功率也很低,经常出现运行不起来的情况,难怪还是beta版本,只能期待后续官方能优化下。
另外有些像assistant、thread、run、run step的查看和管理的接口我这里没有讲到,具体大家可自行查阅下官网文档。 如果大家需要试用Assistants-API的话,也可以先到官方platform.openai.com/assistants 先行体验,试用完成后可以再将页面配置完整翻译成代码,然后再嵌入到自己的应用中。
完整的代码我已上传至Github上github.com/xindoo/open…,后续OpenAI其他API的使用示例我也会上传到这个仓库,有兴趣可以关注下。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
个人中心
购物车
优惠劵
今日签到
有新私信
私信列表
搜索
幸运之星正在降临...
点击领取今天的签到奖励!
恭喜!您今天获得了{{mission.data.mission.credit}}积分
-
限制以下商品使用:
限制以下商品分类使用:
不限制使用:
所有商品和商品类型均可使用