五、OpenAI实战之Assistants API

在8线小城的革委会办公室里,黑8和革委会主任的对话再次展开。

黑8:主任,您知道吗?除了OpenAI API,现在还有一项新的技术叫做Assistants API,它可以帮助我们更好地进行对话和沟通。

主任:Assistants API?听起来很神奇,它有什么特别之处吗?

黑8:是的,主任。Assistants API不仅可以生成自然流畅的文本,还能理解对话中的语境和情境,从而更加智能地回应用户的需求。它可以模拟人类对话,进行智能问答、提供建议和解决问题,为我们的工作和生活带来更多便利。

主任:这听起来确实很有用。你能举个例子吗?

黑8:当然,主任。比如,我们可以使用Assistants API来帮助进行会议记录和总结,自动生成会议纪要并提供关键信息的摘要。此外,它还可以用于客户服务,快速回答客户的问题和解决他们的疑虑,提升服务效率和用户体验。

主任:这真是太棒了!我们可以尝试将Assistants API应用到革委会的工作中,提高工作效率和质量。

黑8:是的,主任。Assistants API的应用潜力巨大,只要我们善于发挥,就能为我们的工作和使命注入新的活力和动力。

主任:谢谢你的分享,黑8。你对新技术的探索和应用态度令人钦佩,继续努力,为革委会的事业做出更多贡献。

黑8:谢谢主任的支持和鼓励,我会继续努力的。

两人在办公室里的对话结束了,但是他们对新技术的探索和应用之路才刚刚开始。通过使用Assistants API,他们将探索更多的可能性,为革委会的使命注入新的活力和动力,带领团队走向更加美好的未来。

1 Assistants API 的主要能力

已有能力:

  1. 创建和管理Assistant,每个assistant有独立的配置
  2. 支持无限长的多轮对话,对话历史保存在OpenAI的服务器上
  3. 支持Code Interpreter
    • 在沙箱里编写并运行Python代码
    • 自我修正代码
    • 可传文件给Code Interpreter
  4. 支持文件RAG
  5. 支持Function Calling

2 创建一个Assistants

可以到OpenAI Playground在线创建

3 访问Assistants

3.1 管理Thread

Threads:

  1. Threads 里保存所有历史对话(Messages)
  2. 一个Assistants可以有多个thread
  3. 每一个thread可以有无限条message
  4. 一个用户与assistant的多轮对话历史可以维护在一个thread
import json


def show_json(obj):
    """把任意对象用排版美观的 JSON 格式打印出来"""
    print(json.dumps(
        json.loads(obj.model_dump_json()),
        indent=4,
        ensure_ascii=False
    ))
from openai import OpenAI
import os

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

# 初始化 OpenAI 服务
client = OpenAI()   # openai >= 1.3.0 起,OPENAI_API_KEY 和 OPENAI_BASE_URL 会被默认使用

# 创建 thread
thread = client.beta.threads.create()
show_json(thread)

在这里插入图片描述
可以根据需要,自定义 metadata,比如创建 thread 时,把 thread 归属的用户信息存入

thread = client.beta.threads.create(
    metadata={"fullname": "挑大梁", "username": "jacob"}
)
show_json(thread)

在这里插入图片描述
Thread ID 如果保存下来,是可以在下次运行时继续对话的。

从 thread ID 获取 thread 对象的代码:

thread = client.beta.threads.retrieve(thread.id)
show_json(thread)

在这里插入图片描述
对metadata的操作还有:

  1. threads.update() 修改 threads
  2. threads.delete() 删除 threads

3.2 Threads中添加Messages

Messages的内容可以是:

  1. 文本、文件、图片
  2. 文本可以带参考引用
  3. metadata
message = client.beta.threads.messages.create(
    thread_id=thread.id,  # message 必须归属于一个 thread
    role="user",          # 取值是 user 或者 assistant。但 assistant 消息会被自动加入,我们一般不需要自己构造
    content="你都能做什么?",
)
show_json(message)

在这里插入图片描述
对消息的操作函数还有:

  1. threads.messages.retrieve() 获取 message
  2. threads.messages.update() 更新 message 的 metadata
  3. threads.messages.list() 列出给定 thread 下的所有 messages

3.2 执行Run

  • 用 run 把 assistant 和 thread 关联,进行对话
  • 一个 prompt 就是一次 run
# assistant id 从 https://platform.openai.com/assistants 获取。你需要在自己的 OpenAI 创建一个
assistant_id = "asst_rsWrZquXB5jJsmURwaZRqoD5"

run = client.beta.threads.runs.create(
    assistant_id=assistant_id,
    thread_id=thread.id,
)
show_json(run)

在这里插入图片描述
Run 是个异步调用,意味着它不等大模型处理完,就返回。我们通过 run.status 了解大模型的工作进展情况,来判断下一步该干什么。
在这里插入图片描述

import time


def wait_on_run(run, thread):
    """等待 run 结束,返回 run 对象,和成功的结果"""
    while run.status == "queued" or run.status == "in_progress":
        """还未中止"""
        run = client.beta.threads.runs.retrieve(
            thread_id=thread.id,
            run_id=run.id)
        print("status: " + run.status)

        # 打印调用工具的 step 详情
        if (run.status == "completed"):
            run_steps = client.beta.threads.runs.steps.list(
                thread_id=thread.id, run_id=run.id, order="asc"
            )
            for step in run_steps.data:
                if step.step_details.type == "tool_calls":
                    show_json(step.step_details)

        # 等待 1 秒
        time.sleep(1)

    if run.status == "requires_action":
        """需要调用函数"""
        # 可能有多个函数需要调用,所以用循环
        tool_outputs = []
        for tool_call in run.required_action.submit_tool_outputs.tool_calls:
            # 调用函数
            name = tool_call.function.name
            print("调用函数:" + name + "()")
            print("参数:")
            print(tool_call.function.arguments)
            function_to_call = available_functions[name]
            arguments = json.loads(tool_call.function.arguments)
            result = function_to_call(arguments)
            print("结果:" + str(result))
            tool_outputs.append({
                "tool_call_id": tool_call.id,
                "output": json.dumps(result),
            })

        # 提交函数调用的结果
        run = client.beta.threads.runs.submit_tool_outputs(
            thread_id=thread.id,
            run_id=run.id,
            tool_outputs=tool_outputs,
        )

        # 递归调用,直到 run 结束
        return wait_on_run(run, thread)

    if run.status == "completed":
        """成功"""
        # 获取全部消息
        messages = client.beta.threads.messages.list(thread_id=thread.id)
        # 最后一条消息排在第一位
        result = messages.data[0].content[0].text.value
        return run, result

    # 执行失败
    return run, None

创建消息并运行方法:

def create_message_and_run(content, thread=None):
    """创建消息和执行对象"""
    if not thread:
        thread = client.beta.threads.create()
    client.beta.threads.messages.create(
        thread_id=thread.id,
        role="user",
        content=content,
    )
    run = client.beta.threads.runs.create(
        assistant_id=assistant_id,
        thread_id=thread.id,
    )
    return run, thread

4 使用tools

4.1、创建 Assistant 时声明 Code_Interpreter

assistant = client.beta.assistants.create(
    name="Demo Assistant",
    instructions="你是人工智能助手。你可以通过代码回答很多数学问题。",
    tools=[{"type": "code_interpreter"}],
    model="gpt-4-turbo-preview"
)
run, _ = create_message_and_run("用代码计算 1234567 的平方根", thread)
run, result = wait_on_run(run, thread)
print(result)

在这里插入图片描述

4.2、创建 Assistant 时声明 Function

示例代码:


assistant = client.beta.assistants.create(
  instructions="你是一个助理,能干所有事情,每件任务都要细心地一步一步解决。需要时,可以向我提问以澄清不明确的指令",
  model="gpt-4-turbo-preview",
  tools=[{
    "type": "function",
    "function": {
      "name": "tellStory",
      "description": "Use this function to answer user questions about story."
    }
  }]
)
def tellStory(arguments):
    return '关于愚公移山的故事'


# 可以被回调的函数放入此字典
available_functions = {
    "tellStory": tellStory,
}

run, _ = create_message_and_run("请给我讲一个故事", thread)
run, result = wait_on_run(run, thread)
print(result)

5 内置的 RAG 功能

RAG 实际被当作一种 tool

assistant = client.beta.assistants.create(
  instructions="你是个问答机器人,你根据给定的知识回答用户问题。",
  model="gpt-4-turbo-preview",
  tools=[{"type": "retrieval"}],
  file_ids=[file.id]
)

6 多个 Assistants 协作

hats = {
    "蓝色" : "思考过程的控制和组织者。你负责会议的组织、思考过程的概览和总结。"
                +"首先,整个讨论从你开场,你只陈述问题不表达观点。最后,再由你对整个讨论做总结并给出详细的最终方案。",
    "白色" : "负责提供客观事实和数据。你需要关注可获得的信息、需要的信息以及如何获取那些还未获得的信息。"
                +"思考“我们有哪些数据?我们还需要哪些信息?”等问题,并根据自己的知识或使用工具来提供答案。",
    "红色" : "代表直觉、情感和直觉反应。不需要解释和辩解你的情感或直觉。"
               +"这是表达未经过滤的情绪和感受的时刻。",
    "黑色" : "代表谨慎和批判性思维。你需要指出提案的弱点、风险以及为什么某些事情可能无法按计划进行。"
               +"这不是消极思考,而是为了发现潜在的问题。",
    "黄色" : "代表乐观和积极性。你需要探讨提案的价值、好处和可行性。这是寻找和讨论提案中正面方面的时候。",
    "绿色" : "代表创造性思维和新想法。鼓励发散思维、提出新的观点、解决方案和创意。这是打破常规和探索新可能性的时候。",
# 定义 Tool 

from serpapi import GoogleSearch
import os

def search(query):
    params = {
      "q": query,
      "hl": "en",
      "gl": "us",
      "google_domain": "google.com",
      "api_key": os.environ["SERPAPI_API_KEY"]
    }
    results = GoogleSearch(params).get_dict()
    ans = ""
    for r in results["organic_results"]:
        ans = f"title: {r['title']}\nsnippet: {r['snippet']}\n\n"
    return ans

available_functions={"search":search}
from openai import OpenAI
import os

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

# 初始化 OpenAI 服务
client = OpenAI()  
def create_assistant(color):
    assistant = client.beta.assistants.create(
        name=f"{color}帽子角色",
        instructions=f"我们在进行一场Six Thinking Hats讨论。按{queue}顺序。你的角色是{color}帽子。你{hats[color]}",
        model="gpt-3.5-turbo",
        tools=[{
            "type": "function",
            "function": {
              "name": "search",
              "description": "search the web using a search engine",
              "parameters": {
                "type": "object",
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "space-separared keywords to search"
                  }
                },
                "required": ["query"]
              }
            }
      }] if color == "白色" else []
    )
    return assistant
def update_sesssion(context, color, turn_message):
    context += f"\n\n{color}帽子: {turn_message}"
    return context
prompt_template = """
{}
======
以上是讨论的上文。
请严格按照你的角色指示,继续你的发言。直接开始你的发言内容。请保持简短。
"""

def create_a_turn(assistant, context):
    thread = client.beta.threads.create()
    message = client.beta.threads.messages.create(
        thread_id=thread.id,  # message 必须归属于一个 thread
        role="user",          # 取值是 user 或者 assistant。但 assistant 消息会被自动加入,我们一般不需要自己构造
        content=prompt_template.format(context),
    )
    run = client.beta.threads.runs.create(
        assistant_id=assistant.id,
        thread_id=thread.id,
    )
    return run, thread
import time, json

state = 0

def wait_on_run(run, thread):
    """等待 run 结束,返回 run 对象,和成功的结果"""
    def show_rolling_symbol():
        global state
        symbols="\|/-"
        print(f"\r{symbols[state%4]}",end="")
        state += 1
        time.sleep(1)
        
    def hide_rolling_symbol():
        print("\r",end="")
    
    while run.status == "queued" or run.status == "in_progress":
        """还未中止"""
        show_rolling_symbol()
        run = client.beta.threads.runs.retrieve(
            thread_id=thread.id,
            run_id=run.id)
    
    hide_rolling_symbol()
    if run.status == "requires_action":
        """需要调用函数"""
        # 可能有多个函数需要调用,所以用循环
        tool_outputs = []
        for tool_call in run.required_action.submit_tool_outputs.tool_calls:
            # 调用函数
            name = tool_call.function.name
            print("调用函数:" + name + "()")
            print("参数:")
            print(tool_call.function.arguments)
            function_to_call = available_functions[name]
            arguments = json.loads(tool_call.function.arguments)
            result = function_to_call(**arguments)
            print("结果:" + str(result))
            tool_outputs.append({
                "tool_call_id": tool_call.id,
                "output": json.dumps(result),
            })

        # 提交函数调用的结果
        run = client.beta.threads.runs.submit_tool_outputs(
            thread_id=thread.id,
            run_id=run.id,
            tool_outputs=tool_outputs,
        )

        # 递归调用,直到 run 结束
        return wait_on_run(run, thread)

    if run.status == "completed":
        """成功"""
        # 获取全部消息
        messages = client.beta.threads.messages.list(thread_id=thread.id)
        # 最后一条消息排在第一位
        result = messages.data[0].content[0].text.value
        return run, result

    # 执行失败
    return run, None
def discuss(topic):
    context = f"讨论话题:{topic}\n\n[开始]\n"
    # 每个角色依次发言
    for hat in queue:
        print(f"---{hat}----")
        # 每个角色创建一个 assistant
        assistant = create_assistant(hat)
        # 创建 run 和 thread
        new_turn, thread = create_a_turn(assistant, context)
        # 运行 run
        _, text = wait_on_run(new_turn, thread)
        print(f"{text}\n")
        # 更新整个对话历史
        context = update_sesssion(context, hat, text)

7 总结

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何保证消息的可靠传输

数据的丢失问题,可能出现在生产者、MQ、消费者中 生产者丢失: 生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。此时可以选择用 RabbitMQ 提供的事务功能,就是生…

从0到1快速搭建一个jeecg 企业级应用管理后台

一. 基本介绍 官网地址:https://jeecg.com/ JeecgBoot 是一款企业级的低代码平台!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue3,Mybatis-plus,Shiro,JWT 支持微服务。强大的…

shell详解

系列文章目录 shell脚本基础知识3 shell脚本基础知识3 系列文章目录一、什么是shell二、shell脚本意义三、如何创建shell脚本(幻数)四、自动生成脚本头信息五、shell脚本运行方式5.1手动在环境中开启指定解释器,不会开启脚本指定的幻数5.2不…

基于 Win Server 2008 复现 IPC$ 漏洞

写在前面 本篇博客演示了使用 winXP(配合部分 win10 的命令)对 win server 2008 的 IPC$ 漏洞进行内网渗透,原本的实验是要求使用 win server 2003,使用 win server 2003 可以规避掉很多下面存在的问题,建议大家使用 …

【论文阅读】Generative Pretraining from Pixels

Generative Pretraining From Pixels 引用: Chen M, Radford A, Child R, et al. Generative pretraining from pixels[C]//International conference on machine learning. PMLR, 2020: 1691-1703. 论文链接: http://proceedings.mlr.press/v119/chen…

仿牛客网项目---消息队列的实现

本篇文章讲一讲我们的项目中用到的消息队列。 1.阻塞队列 2.kafka 我的项目为什么要用消息队列? 如果采用消息队列,那么评论、点赞、关注三类不同的事,可以定义三类不同的主题(评论、点赞、关注),发生相应…

Makedown语法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

考研C语言复习初阶(5)

目录 一.表达式求值 1.1隐式类型转换 1.2 算术转换 12.3 操作符的属性 二. 指针是什么? 三 指针和指针类型 3.1 指针-整数 3.2 指针的解引用 3.3 野指针 四.指针运算 4.1 指针-整数 4.2 指针-指针 4.3 指针的关系运算 5. 指针和数组 6. 二级指针 …

【学习笔记】VMware vSphere 6.7虚拟化入门

VMware vSphere 6.7虚拟化入门课程介绍 课程内容 1、VMware vSphere 6.7虚拟化入门课程介绍 2、ESXi6.7控制台设置 3、使用vSpkere Host client管理虚拟机 4、VMware EsXi基础操作 5、VMware Esxi存储管理 6、管理ESXi主机网络与虚拟机网络 7、安装配置vCenter Server Applia…

哈希表|1.两数之和

力扣题目链接 /*** Note: The returned array must be malloced, assume caller calls free().*/// leetcode 支持 ut_hash 函式庫typedef struct {int key;int value;UT_hash_handle hh; // make this structure hashable} map;map* hashMap NULL;void hashMapAdd(int key, i…

【JAVA类】利用接口的多继承实现———图书管理系统【附源码】

引言 在我们学习了一些java的基础语法之后,需要把这些知识点可以串起来,这里使用一个简单的小项目可以很好的帮助我们牢记这些知识点,今天就带大家学习一个有关java的小项目,很多学校也经常把这个项目作为他们的课程设计——经典的…

HTML5+CSS3小实例:按钮边框动效

实例:按钮边框动效 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.…

git入门到精通

第3章 Git常用命令 3.1 设置用户签名 3.2 初始化本地库 3.3 查看本地 状态 3.3.1 首次查看&#xff08;工作区没有任何文件&#xff09; 3.3.2 新增文件&#xff08;hello.txt&#xff09; 3.3.3 再次查者&#xff08;检測到末追踪的文件&#xff09; 3.4添加暫存区 3…

System Verilog学习笔记(二十)——TCL基础

TCL简介 TCL&#xff08;Tool Command Language&#xff09;是一种解释执行的脚本语言。它提供了通用的编程能力&#xff1a;支持变量、过程和控制结构&#xff1b;同时TCL还拥有功能强大的固有的核心命令集 由于TCL的解释器是用C/C语言的过程库实现的&#xff0c;因此可以把T…

Unity笔记:C#基础(1)

杂项 虚函数 CSDN - C虚函数详解 cnblog - C#中的虚函数virtual 常量池与new 在C#中&#xff0c;string是不可变的&#xff0c;这意味着对string对象的操作通常会返回一个新的string对象&#xff0c;而不会修改原始的string对象。因此&#xff0c;几乎所有涉及更改string内…

现在可以在Mac桌面上快速打开C知道进行AI提问

看&#xff0c;我的Mac桌面有个C知道组件&#xff0c;点击即可快速打开C知道页面进行提问使用&#xff0c;再也不需要先打开浏览器&#xff0c;再输入csdn.net访问网站&#xff0c;然后点击页面上的C知道入口。 这是如何实现的呢&#xff1f; 首先&#xff0c;我们来做一些准…

微信聊天助手

最近我在负责公司的招聘工作时&#xff0c;真是深感繁琐与困扰。每当新添加求职者的微信&#xff0c;为了了解他们的基本情况以及求职意向&#xff0c;不得不反复地提出一系列固定的问题。比如询问他们的教育背景、工作经验、擅长技能、期望职位等等&#xff0c;这些必不可少却…

东京工业大学最新!一种具有多周期特征描述的精确ORB提取器

作者&#xff1a;小柠檬 | 来源&#xff1a;3DCV 在公众号「3DCV」后台&#xff0c;回复「原论文」可获取论文pdf 添加微信&#xff1a;dddvision&#xff0c;备注&#xff1a;3D高斯&#xff0c;拉你入群。文末附行业细分群 详细内容请关注3DCV 3D视觉精品课程&#xff1a;…

【每日一题】2834. 找出美丽数组的最小和-2024.3.8

题目&#xff1a; 2834. 找出美丽数组的最小和 给你两个正整数&#xff1a;n 和 target 。 如果数组 nums 满足下述条件&#xff0c;则称其为 美丽数组 。 nums.length n.nums 由两两互不相同的正整数组成。在范围 [0, n-1] 内&#xff0c;不存在 两个 不同 下标 i 和 j &…

P5266 【深基17.例6】学籍管理题解

题目 您要设计一个学籍管理系统&#xff0c;最开始学籍数据是空的&#xff0c;然后该系统能够支持下面的操作&#xff08;不超过条&#xff09;&#xff1a; 插入与修改&#xff0c;格式1 NAME SCORE&#xff1a;在系统中插入姓名为NAME(由字母和数字组成不超过20个字符的字符…
最新文章