OpenAI Assistants-API简明教程

news2024/9/25 3:19:17

  OpenAI在11月6号的开发者大会上,除了公布了gpt4-v、gpt-4-turbo等新模型外,还有一个assistants-api,基于assistants-api开发者可以构建自己的AI助手,目前assistants-api有三类的工具可以用。首先就是之前大火的代码解释器(Code Interpreter),这个在chatgpt-plus会员上线的时候大火了一把。其次就是文件检索(Retrieval),利用Retrieval你可以在assistants中外挂自己的知识库。还有就是函数调用了,这个就不在多说了。assistants-api目前还处于beta版本,但从OpenAI的规划来看,后续应该是会支持DALLE3、gpt4-v甚至是plugin的,我们可以期待下。

  使用assistants-api和使用chat-api有啥区别?首先就是chat api只能使用模型的chat能力的,而且如果你之前使用过,就会发现chat对话历史都需要自行维护,很不方便。而assistants-api除了chat的能力外,它还可以调用强大的解释器(Code Interpreter),还可以调用外部函数(Functions Calling), 而且还可以外挂自己的知识库(Retrieval),主要你还不需要维护对话历史,只需要关注对话本身即可。 如果后续assistants-api支持了plugin、DALLE3和gpt4-v之后,你完全可以认为它就是一个api版本的chatGPT-Plus,当然功能可以可以完全定制,相信看到这里你肯定也蠢蠢欲动,想定制自己的AI助手了。

  在正式开始开发之前,我们先来了解下Assistants-API的几个核心对象。
在这里插入图片描述

对象作用
Assistant助手,可以使用指定模型根据的一个实体,如果把助手比作某个人的化,这里就是指具备某些能力的一个具体的人
Thread没有合适的翻译,这里就不翻译了,可以认为这个是和助手的沟通的上下文对话信息, 就好比你和某宝客服沟通,整个对话就可以认为是一个Thread
Run也没有合适的翻译,可以认为是你向助手发起一次对话,整个对话响应的过程及工程中的状态变化,就可以当成一个run,一个run里不仅仅可以有模型的回复,还可以有函数调用、代码解释器调用、文件召回……
Run StepRun各个步骤的详情,可以看到整个助手的运行过程,主要是方便问题排查和助手优化

  知道了这些概念,我们就可以着手实现自己的Assistant了,为了能更好理解整个Assistant的开发流程,我们还是用一个具体的示例来完成整个功能的开发。假设我们需要开发一个花店财务助手,它的主要功能是根据我们每天卖出去的花,统计成本和收入,最后将收入和成本保存到数据库里。

  这里我提前准备了一个excel表格(flower_prices.xlsx),来记录所有花的成本和售价(虚构数据、不代表真实价格)。
在这里插入图片描述
  下面正式开始我们花店财务助手的开发和使用。

创建助手

  这里首先需要将我们的flower_prices.csv转成Assistant能使用的file,使用如下代码即可:

from openai import OpenAI
client = OpenAI(base_url='https://thales.xindoo.xyz/openai/v1/')
# 将文件上传至openAI保存
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。

# 创建助手,将code_interpreter,retrieval,function都开启
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
thread = client.beta.threads.create(
  messages=[
    {
      "role": "user",
      "content": "我卖出去了红玫瑰3支、郁金香2支、百合6支,计算下总成本和总收入,给出具体的计算过程"
    }
  ]
)

  这里看到Thead并没有和Assistant关联到一起,猜测这里只是在本地代码里创建了一个Thread对象,实际上在OpenAI那边还没有任何操作,这个可能是OpenAI利用懒加载来减轻对服务端的压力。

创建Run

# 创建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中获取结果
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状态
cancelledRun已成功取消。
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)
    # 将函数调用的结果回传给Assistant
    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的最新状态。 
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
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的话,也可以先到官方https://platform.openai.com/assistants 先行体验,试用完成后可以再将页面配置完整翻译成代码,然后再嵌入到自己的应用中。

完整的代码我已上传至Github上https://github.com/xindoo/openai-examples/blob/main/flower_assistant.ipynb,后续OpenAI其他API的使用示例我也会上传到这个仓库,有兴趣可以关注下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1227905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

隐式转换导致索引失效的原因

Num1 int Num2 varchar Str1不能为null Str2可null 例子1: 结果:124非常快,0.001~0.005秒出结果。3最慢,4~5秒出结果。 查询执行计划:124索引扫描。3全表扫描。 解释:首先四个23都产生隐式转换&#x…

第7天:信息打点-资产泄漏amp;CMS识别amp;Git监控amp;SVNamp;DS_Storeamp;备份

第7天:信息打点-资产泄漏&CMS识别&Git监控&SVN&DS_Store&备份 知识点: 一、cms指纹识别获取方式 网上开源的程序,得到名字就可以搜索直接获取到源码。 cms在线识别: CMS识别:https://www.yun…

【Gradle-13】SNAPSHOT版本检查

1、什么是SNAPSHOT SNAPSHOT版本是指尚未发布的版本,是一个「动态版本」,它始终指向最新的发布工件(gav),也就是说同一个SNAPSHOT版本可以反复用来发布。 这种情况在大型app多团队的开发中比较常见,比如us…

【Linux系统化学习】进程的状态 | 僵尸进程 | 孤儿进程

个人主页点击直达:小白不是程序媛 Linux专栏:Linux系统化学习 目录 操作系统进程的状态 运行状态 阻塞状态 进程阻塞的现象 挂起阻塞状态 Linux进程状态 Linux内核源代码怎么说 R(running状态)运行状态 S(sl…

关于DBMS_STATS.GATHER_DATABASE_STATS_JOB_PROC的一些发现

任务在哪 这个是11g以后的自动收集统计信息的后台任务,10g之前是在dba_scheduler_jobs里查看 SQL> SELECT CLIENT_NAME ,STATUS ,MEAN_INCOMING_TASKS_7_DAYS,MEAN_INCOMING_TASKS_30_DAYS FROM DBA_AUTOTASK_CLIENT WHERE…

云课五分钟-0Cg++默认版本和升级-std=c++17

前篇: 云课五分钟-0B快速排序C示例代码-注释和编译指令 视频: 云课五分钟-0Cg默认版本和升级-stdc17 文本: 在Linux系统中,可以通过以下步骤升级g: 打开终端,使用root权限或者sudo权限登录。输入以下命令…

[AI]ChatGPT4 与 ChatGPT3.5 区别有多大

ChatGPT 3.5 注册已经不需要手机了,直接邮箱认证就可以,这可真算是好消息,坏消息是 ChatGPT 4 还是要收费。 那么 GPT-3.5 与 GPT-4 区别有多大呢,下面简单测试一下。 以从 TDengine 订阅数据为例,TDengine 算是不太小…

【C++】一文全解C++中的异常:标准库异常体系&自定义异常体系(含代码演示)

前言 大家好吖,欢迎来到 YY 滴C系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! 目录 一.C语言传统的处理错误的方式二.C异常…

python-opencv 培训课程笔记(1)

python-opencv 培训课程笔记(1) 博主参加了一次opencv库的培训课程,把课程所学整理成笔记,供大家学习,第一次课程包括如下内容: 1.读取图像 2.保存图像 3.使用opencv库显示图像 4.读取图像为灰度图像 …

每天一道算法题(六)——返回一组数字中所有和为 0 且不重复的三元组

文章目录 前言1、问题2、示例3、解决方法4、效果5、注意点 前言 注意:答案中不可以包含重复的三元组。 1、问题 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] n…

基于PHP+MySql的酒店信息管理系统的设计与实现

一、系统开发环境 运行环境:phpstudy或者wampserver, 开发工具:vscodephpstorm 数据库:mysql 二、酒店管理系统功能 1.前台功能: 首页客房推荐,周边特色介绍 酒店在线预订 订单查询,可以…

linux中利用fork复制进程,printf隐藏的缓冲区,写时拷贝技术,进程的逻辑地址与物理地址

1.prinf隐藏的缓冲区 1.思考:为什么会有缓冲区的存在? 2.演示及思考? 1).演示缓存区没有存在感 那为什么我们感觉不到缓冲区的存在呢?我们要打印东西直接就打印了呢? 我们用代码演示一下: 比如打开一个main.c,输入内容如下: #include <stdio.h>int main(){printf…

【机器学习】划分训练集和测试集的方法

在机器学习中&#xff0c;我们的模型建立完成后&#xff0c;通常要根据评估指标来对模型进行评估&#xff0c;以此来判断模型的可用性。而评估指标主要的目的是让模型在未知数据上的预测能力最好。因此&#xff0c;我们在模型训练之前&#xff0c;要对训练集和测试集进行划分。…

python-opencv 培训课程笔记(2)

python-opencv 培训课程笔记&#xff08;2&#xff09; 1.图像格式转换 先看一下cvtColor函数的例子 #默认加载彩图 pathrD:\learn\photo\cv\cat.jpg# imread(path,way) #way0 灰度图。way1 彩图 #默认彩图 imgcv2.imread(path) img_dogcv2.imread(path_dog) #图片格式的转化…

Lesson 04 模板入门

C&#xff1a;渴望力量吗&#xff0c;少年&#xff1f; 文章目录 一、泛型编程1. 引入2. 函数模板&#xff08;1&#xff09;函数模板概念&#xff08;2&#xff09;函数模板格式&#xff08;3&#xff09;函数模板的原理&#xff08;4&#xff09;函数模板的实例化&#xff08…

git rebase 和 git merge的区别?以及你对它们的理解?

文章目录 前言是什么分析区别后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;git操作相关 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&#xff0c;感谢…

MVSNet论文笔记

MVSNet论文笔记 摘要1 引言2 相关基础2.1 多视图立体视觉重建&#xff08;MVS Reconstruction&#xff09;2.2 基于学习的立体视觉&#xff08;Learned Stereo&#xff09;2.3 基于学习的多视图的立体视觉&#xff08;Learned MVS&#xff09; Yao, Y., Luo, Z., Li, S., Fang,…

阿坤老师的独特瓷器(Java详解)

一、题目描述 示例&#xff1a; 输入&#xff1a; 5 3 4 5 6 2 5 3 7 6 5 输出&#xff1a; 3 二、题解 思路分析&#xff1a; 题目要求我们计算出“独特瓷器”的个数&#xff0c;而“独特瓷器”是指对于瓷器A&#xff0c;没有另一个瓷器B&#xff0c;直径和高度都大于A。则…

系列三、双亲委派 沙箱安全 机制

一、概述 当一个类收到了类加载的请求&#xff0c;它首先不会尝试自己去加载这个类&#xff0c;而是把这个请求委派给父类去完成&#xff0c;每一层的类加载器都是如此&#xff0c;因此所有的请求最终都应该传送到启动类加载器中&#xff0c;只有当父类加载器反馈自己无法完成…

算法通关村——数字中的统计、溢出、进制转换处理模板

数字与数学基础问题 1、数字统计 1.1、符号统计 LeetCode1822. 给定一个数组&#xff0c;求所有元素的乘积的符号&#xff0c;如果最终答案是负的返回-1&#xff0c;如果最终答案是正的返回1&#xff0c;如果答案是0返回0. 这题其实只用看数组中0和负数的个数就好了&#x…