大模型应用开发:为产品创建一个AI客服/智能助手

欢迎阅读本系列文章!我将带你一起探索如何使用OpenAI API来开发GPT大模型应用。无论你是编程新手还是资深开发者,都能在这里获得灵感和收获。

本文将继续展示AI助手的开发方式,在OpenAPI中它的名字是Assistants。

什么是Assistants?

在之前的文章中我演示了插件的使用方法,比如查询实时天气、进行数学运算等,这些都是大模型自身做不到的事情,因此可以说插件的主要作用是扩展了大模型的处理能力。那么Assistants能干什么呢?

Assistants的主要作用是强化大模型在某方面的应用能力,比如目前已经大范围使用的AI客服和知识库助手,它们可以准确的理解用户的问题,并在限定的知识范围内进行精准回答。另外借助Assistants的能力,我们还可以做更多有趣的事情,比如让它按照指定的规范对代码进行Review,按照某种指定的风格或者模式来进行文学创作,等等。

本文我们将通过一个AI客服来演示Assistants的使用方法。先看效果:

这里我开发了一个空气净化器的AI客服,然后用户向AI客服提了四个问题,前三个问题AI都理解准确并回答正确,回答内容全部来源于产品手册,最后一个问题脱离了产品手册的内容范围,AI只能拒绝回答。

Assistants的运行原理

工欲善其事,必先知其理。在编写Assistants的代码之前,我们先要搞清楚它是怎么运行的,然后写代码的时候才能有的放矢、逻辑清晰。

请看下边这张图:

1、创建智能助手(Assistant):这一步我们要给智能助手下个定义,包括起个名字、声明它的能力、使用的大模型版本、增强能力的方式(执行代码、从知识库检索、调用外部函数)等。

2、创建用户会话(Thread):会话就是用户和智能助手之间的一次聊天,GPT可以通过会话方便的管理聊天上下文。

3、添加用户消息到会话(Message):就是用户向智能助手说的话,必须添加到会话中。

4、在会话中运行智能助手(Run):将会话和智能助手进行绑定,运行智能助手来处理用户的消息。这一步实际上会创建一个智能助手的执行对象,然后把这个执行对象添加到一个处理队列中,最终处理状态会更新到运行对象中。

5、获取GPT响应的消息(Response):通过不断检查运行对象的状态,获取智能助手的响应结果。

实现AI客服

我们这里就按照Assistant的运行原理来实现一个AI客服。

产品手册

首先我们要准备一个产品手册,随便写点什么都行,为了方便大家,可以直接下载我这个:

https://github.com/bosima/openai-api-demo/blob/main/niubiclean-book.txt

然后我们需要将这个文件上传到OpenAI,注意把文件放到程序能够访问到的地方。

niubiclean_book = client.files.create(file=open("niubiclean-book.txt", "rb"),purpose="assistants"
)

purpose 可选的值有两个:fine-tune 和 assistants。

创建助手

这里使用的是 client.beta.assistants.create 来创建客服,因为assistants还没有正式发布,所以这里的包空间名称中包含了一个beta,正式发布时会去掉。具体代码如下:

waiter = client.beta.assistants.create(name="牛逼净化器智能客服",description="24小时为您服务",instructions="你是牛逼净化器公司的智能客服,请引用文件中的内容回答问题,表达要通俗易懂、尽量简短;若问题超出文件内容,请委婉拒绝。",model="gpt-3.5-turbo-1106",tools=[{"type": "retrieval",}],# 知识文件,通过File接口上传的file_ids=[niubiclean_book.id]
)

简单说下这几个参数:

name:智能助手的名字,随便起。

description:智能助手的简介描述,最长 512 字符。

instructions:给智能助手的指令,也就是提示词,让智能助手按照这里的提示词提供服务。这里我用了一个常见的提示词套路,让它扮演一个角色,有什么样的能力,如何回答用户的问题等。最长 32768 字符。

model:使用的GPT大模型,这里用便宜的3.5,你也可以换成GPT-4。

tools:assistants开启的工具,共有三种类型:code_interpreter、retrieval、function。

  • code_interpreter:是代码解释器,能让GPT在一个沙盒环境中执行python代码,能从文件读取数据,也能生成文件,需要通过instructions提示assistant可以执行代码。
  • retrieval:从文件检索内容,这里我们的AI客服只能根据产品手册回答问题,所以这里只开启了retrieval的能力。
  • function:和聊天插件的使用方法一样,调用执行函数,根据执行结果向用户返回内容。

file_ids:指定GPT要检索的文件Id,可以设置多个。这里设置为我们上一步上传的手册。

创建用户会话

使用 client.beta.threads.create 创建用户会话,具体代码如下。

thread_userjia = client.beta.threads.create(metadata={"姓名": "用户甲","年龄": 36,"性别": "男"}
)

metadata是可选的,可以设置一些附加信息,无固定属性,key-value格式即可。

添加用户消息到会话

我们其实可以在创建 thread 时初始化一些消息,不过既然要对话,演示下如何添加消息更有意义。

使用 client.beta.threads.messages.create 来创建一条用户消息,并绑定到某个会话,代码如下:

message = client.beta.threads.messages.create(thread_id=thread_userjia.id,  role="user",          content="净化器有什么功能?",)

这里有三个参数:

  • thread_id:消息绑定到的会话Id。
  • role:消息的角色,目前只支持 user,只能向其中添加用户消息。至于完整的聊天上下文,GPT内部会自动维护。
  • content:消息内容,这个很好理解。

在会话中运行智能助手

使用 client.beta.threads.runs.create 来运行智能助手,代码如下:

run = client.beta.threads.runs.create(thread_id=thread_userjia.id,assistant_id=waiter.id,)

这里有两个关键的参数:

  • thread_id:要在哪个会话中运行智能助手。
  • assistant_id:要运行哪个智能助手。

这里还有一些其它的参数,比如model、instructions、tools等,使用它们会覆盖我们在创建 assistant 设置的参数。

获取智能助手的回应

运行智能助手后得到的返回值 run 是一个对象,代表运行在会话中的一个执行,这个执行是通过队列异步处理的,我们不能立即得到执行结果,需要定期检查 run 的状态,处理完毕了才能获取到GPT的回应消息。

先看检查状态的处理:

while run.status == "queued" or run.status == "in_progress":time.sleep(1)run = client.beta.threads.runs.retrieve(thread_id=thread_userjia.id,run_id=run.id,)

run 有多个状态: queued, in_progress, requires_action, cancelling, cancelled, failed, completed, expired,这个例子中如果不是 queued 或者 in_progress 状态就代表已经有结果了。requires_action 是智能助手使用 function 工具时才会存在的状态,这个例子不涉及。

状态

含义

queued

创建run之后 或者 使用function时确定了要调用的function及其参数 之后,就会进入这个状态,这个状态很短,马上会进入 in_progress状态。

in_progress

使用模型或者tools处理消息。

completed

本次运行成功完成,可以读取GPT响应的消息了。

requires_action

使用function时,一旦模型确定要调用的function及其参数,run将进入这个状态。

expired

function执行的时间太长或者整个run运行的时间太长,达到了过期阈值(大约10分钟)。

cancelling

可以在queued和in_progress状态时发起取消,将进入这个状态。

cancelled

已成功取消。

failed

您运行失败了,可以在 run.last_error 中获得失败原因。

使用 client.beta.threads.messages.list 获取GPT响应消息,代码如下:

 if run.status=="failed":print(run.last_error.message)
else:messages = client.beta.threads.messages.list(thread_id=thread_userjia.id, order="asc", after=message.id)print("牛逼智能客服:",extract_message_content(messages.data[0]),'\n')

获取响应消息时用到了3个参数:

  • thread_id:会话Id。
  • order:消息排序,asc代表正序,也就是先产生的消息在前边。
  • after:指定消息的起始位置,因为我们要获取GPT针对某条用户消息的响应,所以这里通过after指定获取某条用户消息之后的消息,也就是GPT的响应消息。

最后我们还使用了一个函数来提取消息内容:extract_message_content,代码如下:

def extract_message_content(message):# Extract the message contentmessage_content = message.content[0].textannotations = message_content.annotations# Iterate over the annotations and add footnotesfor index, annotation in enumerate(annotations):# Replace the text with a footnote# print(annotation.text)message_content.value = message_content.value.replace(annotation.text, ' ')return  message_content.value

注意这里有一个annotation的概念,中文就是注解的意思。因为AI客服生成的内容可能来自多个产品文档,有了注解,用户就可以通过它跳转到相关的文档进行详细阅读。这个和论文中的引用注解是同一种方式。

不过我们这里的产品手册比较简单,所以就把注解都替换成空字符串了。完整的处理方法可以参考下边这个:

# Extract the message content
message_content = message.content[0].text
annotations = message_content.annotations
citations = []# Iterate over the annotations and add footnotes
for index, annotation in enumerate(annotations):# Replace the text with a footnotemessage_content.value = message_content.value.replace(annotation.text, f' [{index}]')# Gather citations based on annotation attributesif (file_citation := getattr(annotation, 'file_citation', None)):cited_file = client.files.retrieve(file_citation.file_id)citations.append(f'[{index}] {file_citation.quote} from {cited_file.filename}')elif (file_path := getattr(annotation, 'file_path', None)):cited_file = client.files.retrieve(file_path.file_id)citations.append(f'[{index}] Click <here> to download {cited_file.filename}')# Note: File download functionality not implemented above for brevity# Add footnotes to the end of the message before displaying to user
message_content.value += '\n' + '\n'.join(citations)

完整示例

我在完整的示例程序中向智能助手循环提出了四个问题,每个问题都需要重新创建一个run,然后再检查状态,获取响应结果。

需要完整代码的同学请访问Github:

https://github.com/bosima/openai-api-demo/blob/main/assistants_demo.ipynb


以上就是本文的主要内容,有兴趣的同学快去试试吧,效果绝对震惊你的小伙伴!

如需GPT账号、学习陪伴群、AI编程训练营,推荐关注小册:大模型应用开发 | API 实操

关注萤火架构,加速技术提升!

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

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

相关文章

Python网络爬虫实战——实验4:Python爬虫代理的使用

【实验内容】 本实验主要介绍在爬虫采集数据的过程中代理的使用。 【实验目的】 1、掌握代理使用的基本场景&#xff1b; 2、解决IP封锁问题&#xff1b; 3、提高爬虫访问效率&#xff1b; 【实验步骤】 步骤1选择代理服务提供商 步骤2配置爬虫使用代理 步骤3 采集数据生成…

priority_queue模拟

文章目录 模拟priority_queue性质&#xff1a;成员函数&#xff1a;向上调整、向下调整push/pop/empty/top/size/empty成员变量 仿函数struct和class练习题堆的一些性质 模拟priority_queue 性质&#xff1a; 是容器适配器&#xff1b; 底层逻辑是堆&#xff0c;适配器是vect…

【遥感专题系列】影像信息提取之——基于专家知识的决策树分类

可以将多源数据用于影像分类当中&#xff0c;这就是专家知识的决策树分类器&#xff0c;本专题以ENVI中Decision Tree为例来叙述这一分类器。 本专题包括以下内容&#xff1a; 专家知识分类器概述知识&#xff08;规则&#xff09;定义ENVI中Decision Tree的使用 概述 基于知…

数据结构与算法——队列

概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。添加的一端称为尾&#xff0c;移除的一端称为头。 功能 插入offer(value : E) : boolean  取值并移除poll() : E  取值peek() : E  判断…

LeetCode 40.组合总和 II

组合总和 II 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用 一次 。 注意&#xff1a;解集不能包含重复的组合。 方法一、回溯 由于题目要求解集…

实体识别与分类方法综述

目录 前言1 实体识别简介2 基于模板和规则的方法3 基于序列标注的方法3.1 常见序列标注模型3.2 模型参数估计和学习问题3.3 常见序列预测模型 4. 基于深度学习的实体识别方法5 基于预训练语言模型的实体识别5.1 BERT、GPT等预训练语言模型5.2 解码策略 6 特殊问题与挑战6.1 标签…

如何提高记忆力?

许多学员经常问我&#xff1a;为什么您的记忆力那么好&#xff1f;有没有什么方法&#xff0c;可以提高记忆力&#xff1f; 今天&#xff0c;我想好好聊聊这个问题。 当然&#xff0c;学习和记忆&#xff0c;是一个巨大的话题。这篇文章只是一个初探。希望能帮你打开一些视野&a…

深入理解Redis:如何设置缓存数据的过期时间及其背后的机制

目录 Redis 给缓存数据设置过期时间 Redis是如何判断数据是否过期的呢&#xff1f; 过期的数据的删除策略 Redis 内存淘汰机制 Redis 给缓存数据设置过期时间 一般情况下&#xff0c;我们设置保存的缓存数据的时候都会设置一个过期时间。为什么呢&#xff1f; 因为内存是有…

电流检测电路设计方案汇总

电流检测电路设计方案&#xff08;一&#xff09; 低端检流电路的检流电阻串联到地&#xff08;图1&#xff09;&#xff0c;而高端检流电路的检流电阻是串联到高电压端&#xff08;图2&#xff09;。两种方法各有特点&#xff1a;低端检流方式在地线回路中增加了额外的线绕电…

​ElasticSearch

目录 简介 基本概念 倒排索引 FST 简介 ES是一个基于lucene构建的&#xff0c;分布式的&#xff0c;RESTful的开源全文搜索引擎。支持对各种类型的数据的索引&#xff1b;搜索速度快&#xff0c;可以提供实时的搜索服务&#xff1b;便于水平扩展&#xff0c;每秒可以处理 …

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例5-1事件处理

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>事件处理</title> </head><body> <input id"btn" type"button" name"btn" value"提交" /> <…

深入理解ZooKeeper分布式锁

第1章&#xff1a;引言 分布式系统&#xff0c;简单来说&#xff0c;就是由多台计算机通过网络相连&#xff0c;共同完成任务的系统。想象一下&#xff0c;咱们平时上网浏览网页、看视频&#xff0c;背后其实都是一大堆服务器在协同工作。这些服务器之间需要协调一致&#xff…

小游戏选型(二):第三方社交小游戏厂家对比,即构/声网/融云/云信等

前言&#xff1a; 上一篇文章我们主要介绍社交游戏化趋势&#xff0c;并分析了直播平台面临的买量贵、变现难等问题&#xff0c;探讨了小游戏作为新的运营变现玩法的优势。同时还列举了各大直播平台TOP5的小游戏。今天我们继续介绍小游戏系列内容&#xff0c;本文是该系列的第…

浪花 - 添加队伍业务开发

一、接口设计 1. 请求参数&#xff1a;封装添加队伍参数 TeamAddRequest package com.example.usercenter.model.request;import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.ann…

勤学苦练“prompts“,如沐春风“CodeArts Snap“

前言 CodeArts Snap 上手一段时间了&#xff0c;对编程很有帮助。但是&#xff0c;感觉代码编写的不尽人意。 我因此也感到困惑&#xff0c;想要一份完整的 CodeArts Snap 手册看看。 就在我感觉仿佛"独自彷徨在这条悠长、悠长又寂寥的雨巷"时&#xff0c;我听了大…

【数据库】聊聊explain如何优化sql以及索引最佳实践

在实际的开发中&#xff0c;我们难免会遇到一些SQL优化的场景&#xff0c;虽然之前也看过周阳的课程&#xff0c;但是一直没有进行细心的整理&#xff0c;所以本篇会进行详细列举explain的相关使用&#xff0c;以及常见的索引最佳实践&#xff0c;并通过案例进行讲解。 数据准…

Java复习系列之阶段三:框架原理

1. Spring 1.1 核心功能 1. IOC容器 IOC&#xff0c;全称为控制反转&#xff08;Inversion of Control&#xff09;&#xff0c;是一种软件设计原则&#xff0c;用于减少计算机代码之间的耦合度。控制反转的核心思想是将传统程序中对象的创建和绑定由程序代码直接控制转移到…

阿里云幻兽帕鲁服务器4核16G配置报价

自建幻兽帕鲁服务器租用价格表&#xff0c;2024阿里云推出专属幻兽帕鲁Palworld游戏优惠服务器&#xff0c;配置分为4核16G和4核32G服务器&#xff0c;4核16G配置32.25元/1个月、10M带宽66.30元/1个月、4核32G配置113.24元/1个月&#xff0c;4核32G配置3个月339.72元。ECS云服务…

C++(搜索二叉树)

目录 前言&#xff1a; 1.二叉搜索树 1.1二叉搜索树的定义 1.2二叉搜索树的特点 2.二叉搜索树的实现 2.1框架 2.2查找 2.3插入 2.4删除 1.右子树为空 2.左子树为空 3.左右都不为空 3.递归版本 3.1前序遍历 3.2中序遍历 3.3后续遍历 3.4查找&#xff08;递…

【日常学习笔记】gflags

https://mp.weixin.qq.com/s/FFdAUuQavhD5jCCY9aHBRg gflags定义的是全局变量&#xff0c;在main函数后&#xff0c;添加::gflags::ParseCommandLineFlags函数&#xff0c;就能解析命令行&#xff0c;在命令行传递定义的参数。 在程序中使用DEFINE_XXX函数定义的变量时&#x…