Vue3实现chatgpt的流式输出

前言:

我在使用Vue3开发一个chatgpt工具类网站的时候,翻阅了不少博客和github上的一些相关项目,都没能找到适合Vue3去实现stream的流式数据处理。经过踩坑,最终实现了适用直接调chatgpt接口的方法以及改为调用Python后端接口的方法。

背景:

默认情况下,当用户从 OpenAI 请求完成时,会生成整个完成,然后再通过单个响应发回,这样可能会造成等待响应时间过长。

解决:

“流式传输”,需在调用聊天完成或完成端点时设置 stream=True,这将返回一个对象,该对象将响应作为仅数据服务器发送的事件流回。

参数说明:

  • messages: 必须是对象数组

    类型作用
    system设置chatgpt的角色
    user用户输入的内容
    assistantchatgpt返回的内容
  • system 设定角色助手,使得下文对话走这条线路
  • assistant充当历史记录,达到多轮对话,需将用户所问、AI所答数据存储,实现上下文功能,代价是消耗更多的tokens
  • temperature
类型默认值取值范围是否必填
浮点数10 - 2
  • 随着temperature的取值越大,其输出结果更加随机(较低能集中稳定输出字节,但较高能有意想不到的性能创意)
  • top_p
类型默认值取值范围是否必填
浮点数10 - 1
  • top_p用于预测可能,值越小时,其输出结果会更加肯定,响应性能会相对快,但值越大时,输出的结果可能会更贴近用户需求

How to stream completions?

API文档:https://platform.openai.com/docs/api-reference/chat/create

有关实例代码:openai-cookbook/How_to_stream_completions.ipynb at main · openai/openai-cookbook · GitHub

相信你们早就阅读了上面的文档,但还是很迷茫,感觉无从下手...下面说说我的踩坑经历:
我在网上搜索到的信息是,需要一些流式处理库,我就问chatgpt,它给我推荐了以下几种
 

  1. RxJS:是一个响应式编程库,支持流式处理和异步操作。

  2. Bacon.js:也是一个响应式编程库,提供了一个功能强大的事件流模型,可以用来处理异步事件。

  3. Highland.js:是一个基于流的函数式编程库,提供了广泛的流操作和管道组合功能。

  4. Node.js的stream模块:是一个流式处理库,提供了流处理的核心功能。可以通过其定义自己的流转换器和消费者函数。

  5. lodash-fp:是一个功能强大的函数式编程库,提供了一整套函数式的操作和工具,可以用来方便快捷地进行流处理。

我没走这条路,我重新查询了一波,网上的意思是,可以利用WebSocket方式或SSE的方式去实现长连接,但我都没采纳,最终使用的是fetch去实现请求即可,不用将问题复杂化哈哈哈

  • 适用直接调chatgpt的接口
// gpt.js
import { CHATGPT_API_URL } from '@/common/config.js'
const OPENAI_API_KEY = '你的接口'
// TODO 适用直接调chatgpt接口
export async function* getChatgpt_Multurn_qa(messages) {const response = await fetch(CHATGPT_API_URL + '你的url', {method: 'POST',headers: {'Content-Type': 'application/json','Authorization': `Bearer ${ OPENAI_API_KEY }`},body: JSON.stringify({model: 'gpt-3.5-turbo',stream: true,messages: messages})});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const reader = response.body.getReader();let decoder = new TextDecoder();let resultData = '';while (true) {const { done, value } = await reader.read();if (done) break;resultData += decoder.decode(value);while (resultData.includes('\n')) {const messageIndex = resultData.indexOf('\n');const message = resultData.slice(0, messageIndex);resultData = resultData.slice(messageIndex + 1);if (message.startsWith('data: ')) {const jsonMessage = JSON.parse(message.substring(5));if (resultData.includes('[DONE]')) {break}const createdID = jsonMessage.createdyield {content: jsonMessage.choices[0]?.delta?.content || '',role: "assistant",id: createdID};}}}
}

以上是利用迭代器的写法去实现流式输出,我上面的字符串其实是chatgpt响应输出的数据,例如:

{"id":"chatcmpl-7B48ttLhb1iR4JoaCzElQTvxyAgsw","object":"chat.completion.chunk","created":1682871887,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"."},"index":0,"finish_reason":null}]}

 注意:我利用迭代器需要将每一句 created 相同的流数据存储到一起,才能形成一个消息的闭环,否则页面的效果会是一个字就占一个段落,你们可以去试一试

// vue组件部分代码  const currentDialogId = ref(null)const dialogId = uniqueId()
currentDialogId.value = dialogId   // 获取聊天机器人的回复
for await (const result of getChatgpt_Multurn_qa(messages.value)) {// 如果返回的结果 ID 与当前对话 ID 相同,则将聊天机器人的回复拼接到当前对话中if (result.id === currentDialogId.value) {const index = list.value.findIndex(item => item.id === currentDialogId.value)const dialog = list.value[index]dialog.content += result.content} else {currentDialogId.value = result.idlist.value.push({content: result.content,role: "assistant",id: result.id,timestamp: Date.now()})messages.value.push({role: "assistant",content: result.content})}
}

上面代码比较关键的点就是条件的判断 ---  result.id === currentDialogId.value ,到这一步就可以实现chatgpt的流式输出啦,响应速度是非常快的!!!

补充:

1. list 是用户角色和AI角色的对话数组,可以传递给子组件去遍历渲染不同角色的聊天,在文章尾部将展示实现Markdown代码块的步骤

2. message 是将user以及assistant的所有历史记录push进去,是实现多轮对话的关键

  •  改为调用后端Python的接口,先看看后端哥哥@ToTensor写给前端的文档
# ChatGPT流式输出接口## 接口路径```bash
https://后端提供的url
```## 请求方式**POST**## 请求参数```bash
{"messages": [{"role": "user","content": "你好"}]
}
```## 请求参数说明```bash
messages: 消息体
```## curl```bash
curl --location 'https://后端提供的url' \
--header 'Content-Type: application/json' \
--data '{"messages": [{"role": "user","content": "你好"}]
}'
```## 返回数据```bash
{"id": "chatcmpl-7GpjNUPkhPZF0MtJBqTMvW2bbWPPG","object": "chat.completion.chunk","created": 1684246457,"model": "gpt-3.5-turbo-0301","choices": [{"delta": {"role": "assistant"},"index": 0,"finish_reason": null}]
}
{"id": "chatcmpl-7GpjNUPkhPZF0MtJBqTMvW2bbWPPG","object": "chat.completion.chunk","created": 1684246457,"model": "gpt-3.5-turbo-0301","choices": [{"delta": {"content": "你"},"index": 0,"finish_reason": null}]
}
{"id": "chatcmpl-7GpjNUPkhPZF0MtJBqTMvW2bbWPPG","object": "chat.completion.chunk","created": 1684246457,"model": "gpt-3.5-turbo-0301","choices": [{"delta": {"content": "好"},"index": 0,"finish_reason": null}]
}
{"id": "chatcmpl-7GpjNUPkhPZF0MtJBqTMvW2bbWPPG","object": "chat.completion.chunk","created": 1684246457,"model": "gpt-3.5-turbo-0301","choices": [{"delta": {"content": "!"},"index": 0,"finish_reason": null}]
}
{"id": "chatcmpl-7GpjNUPkhPZF0MtJBqTMvW2bbWPPG","object": "chat.completion.chunk","created": 1684246457,"model": "gpt-3.5-turbo-0301","choices": [{"delta": {},"index": 0,"finish_reason": "stop"}]
}
```## 返回参数说明```bash
role = assistant, 开始输出
finish_reason = stop, 输出结束
finish_reason = null, 正在输出
content 输出内容
```

根据文档,我们只需要小小改动代码

// TODO 改用chatgpt接口
import { _BASE_API_URL } from '@/common/config.js'// 流式输出接口
export async function* getChatgpt_Multurn_qa(messages, onStreamDone) {const response = await fetch(_BASE_API_URL + `你的url`, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({messages: messages})})if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`)}const reader = response.body.getReader()let result = ''let done = falsewhile (!done) {const { value, done: streamDone } = await reader.read()if (value) {const decoder = new TextDecoder()result += decoder.decode(value)const lines = result.split('\n')result = lines.pop()for (const line of lines) {try {const json = JSON.parse(line)if (json.choices && json.choices.length > 0) {const content = json.choices[0].delta.contentif (content) {yield { id: json.created, content }}}if (json.choices && json.choices[0].finish_reason === 'stop') {done = trueonStreamDone()break}} catch (e) {console.error(e)}}}if (streamDone) {done = true;}}
}

上面代码多了个onStreamDone参数,是我需要利用它处理响应完成的逻辑,没有这个需求的伙伴可以适当删改,接下来再看看父组件如何获取数据吧

// vue父组件 for await (const result of getChatgpt_Multurn_qa(messages.value, onStreamDone)) {if (currentConversationId.value === null) {currentConversationId.value = result.id;}if (result.id === currentConversationId.value) {const index = list.value.findIndex(item => item.id === currentConversationId.value);const dialog = list.value[index];dialog.content += result.content;} else {currentConversationId.value = result.id;list.value.push({content: result.content || '',role: "assistant",id: result.id,timestamp: Date.now()});messages.value.push({role: "assistant",content: result.content || ''});}}
  • 父子组件是如何通信的呢?
// 父组件<session-box :list="list" @sent="handleSent"></session-box>

 

// 子组件
const props = defineProps({list: {type: Array,default: []}
})const { list } = toRefs(props)const sessionList = ref(null)const sortedList = computed(() => {return list.value.slice().sort((a, b) => a.timestamp - b.timestamp)
})

说明:

通过 computed 创建了一个名为 sortedList 的计算属性,该属性返回一个已排序的 list 数组副本。在排序过程中,使用了 slice 方法创建了一个数组副本,以避免直接修改原始数组。排序方式为按照每个数组元素的 timestamp 属性升序排序。

在模板中遍历循环sortedList的内容就能实现用户和ai对话啦

  •  选择合适的Markdown 编辑器组件库

介绍一下md-editor-v3 

官网:MdEditorV3 Documentation (imzbf.github.io)

github地址:imzbf/md-editor-v3: Markdown editor for vue3, developed in jsx and typescript, dark theme、beautify content by prettier、render articles directly、paste or clip the picture and upload it... (github.com)

文档说明:MdEditorV3 Documentation (imzbf.github.io)

 它提供了一些基础的 Markdown 编辑功能,如加粗、斜体、标题、无序列表、有序列表、引用、代码块等。除此之外,它还支持上传图片、撤销/重做、全屏等功能。md-editor-v3 的优点是易于使用、易于扩展,并且提供了一些定制化的选项。但是我只是想实现代码块,故解构出MdPreview

 使用:

// 模板中
<MdPreview:showCodeRowNumber="true"            // 显示行号:modelValue="item.content"
/>import { MdPreview } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'

效果图:

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

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

相关文章

ChatGPT基础知识系列之大型语言模型(LLM)初识

ChatGPT基础知识系列之大型语言模型(LLM)初识 ChatGPT本质是一个对话模型,它可以回答日常问题、挑战不正确的前提,甚至会拒绝不适当的请求,在去除偏见和安全性上不同于以往的语言模型。ChatGPT从闲聊、回答日常问题,到文本改写、诗歌小说生成、视频脚本生成,以及编写和调…

特朗普、马斯克和比尔·盖茨贫民窟AI画“让人尖叫”

点击上方“AI遇见机器学习”&#xff0c;选择“星标”公众号 重磅干货&#xff0c;第一时间送 深度学习与NLP编辑 一组名为“贫民窟的亿万富豪”的人工智能&#xff08;AI&#xff09;画作在网上发布后&#xff0c;引起了全球关注。这组画作的作者是印度数字艺术家戈库尔皮莱&a…

华为开发者大会2023官宣,华为云在憋什么大招?

文丨智能相对论 作者丨沈浪 华为云也坐不住了。 在此之前&#xff0c;百度、阿里、商汤、科大讯飞等国内科技厂商以及微软、谷歌等国际巨头都已经发布了自家的大模型新品以及AIGC等相关应用。而华为云手握盘古大模型&#xff0c;却始终按兵不动&#xff0c;迟迟没有正式进场…

又一家顶级的大模型开源商用了!Meta(Facebook)的 Llama 2 搅动大模型混战的格局...

“ 百模大战&#xff0c;花落谁家&#xff1f;” 01 — 开源、免费‍ 今年2月24日&#xff0c;Meta推出大语言模型Llama&#xff08;羊驼&#xff09;&#xff0c;按参数量分为7B、13B、33B和65B四个版本。它凭借一己之力&#xff0c;引导了开源大模型的发展&#xff0c;由其演…

深度测评全新大模型「天工」,这些AI体验太香了

ChatGPT火了后&#xff0c;很多人都在关注“国产ChatGPT”的名号究竟花落谁家。 事实上&#xff0c;名号不重要&#xff0c;体验才是王道。ChatGPT能够火成“史上增长最快的消费者应用”&#xff0c;关键在于把体验提升到了新层次。毕竟对于用户来说&#xff0c;并不清楚产品背…

BLEXBot是什么蜘蛛,需要屏蔽这个爬虫吗

BLEXBot这个蜘蛛也是最近爬的比较厉害的一个&#xff0c;属于一家美国的反向链接查询网站&#xff08;WebMeUp&#xff09;的蜘蛛程序&#xff0c;它会大量的抓取我们的网站链接&#xff0c;所以一旦我们发现有他的抓取的踪迹&#xff0c;就会发现他真的是大量的抓取你的链接。…

孔乙己终结者!GPT-4拿100美元自创业,还要让马斯克下岗

【导读】GPT-4引发的新一波革命&#xff0c;把打工人推上了「断头台」。孔乙己的未来在哪里&#xff1f; GPT-4才诞生4天&#xff0c;人类就要失业了&#xff01; 不仅要取代马斯克&#xff0c;还当上了大Boss&#xff0c;「孔乙己」的未来该怎么办&#xff1f; 就连Sam Altman…

文字转绘画的AI绘画效果不好?用ChatGPT辅助下立竿见影

对于那些喜欢宅在家里度过时光的女孩们来说&#xff0c;这种略带“莫测高深”的生活方式已经成为她们的日常习惯。不需要拘束的服装&#xff0c;只需舒适的衣服&#xff0c;蜷缩在舒适的沙发上与电脑、电视作伴&#xff0c;身边还要放置各种零食和饮料。最重要的是要有一只可爱…

申请百度语音识别API 接口-免费

1、浏览器打开&#xff1a;语音识别_语音识别技术_百度语音识别-百度AI开放平台 2、右上角-控制台&#xff0c;先登录上账号&#xff0c; 3、然后去点立即使用&#xff0c;进入后台&#xff0c; 4、点击-去领取&#xff0c;领取免费的额度 5、进去之后先实名认证&#xff0c;可…

百度API调用(三)——语音识别

python 调用百度语音识别API 一、开通百度语音技术接口服务二、python实现百度语音识别1、实现功能2、代码&#xff08;已加注释&#xff09; 最后 一、开通百度语音技术接口服务 基本过程&#xff1a; 1、打开百度ai开放平台 https://ai.baidu.com/ 2、打开控制台 3、选择…

百度语音SDK使用

百度语音SDK提供: 语音识别&#xff1a;将声音转成文字语音合成&#xff1a;将文字转成语音文件&#xff0c;然后播放语音文件&#xff0c;即文字变声音。语音唤醒&#xff1a;语音唤醒&#xff0c;激活运用程序 在这里&#xff0c;本篇介绍百度语音合成的使用。 百度语音介…

python 语音识别(百度api)

文章目录 前言准备下载库代码实现以及编写我的key语音的录入&#xff1a;使用百度语音作为STT引擎并通过pyttsx3函数将返回的内容读出来形成简单的对话完整代码运行结果 前言 现在语音助手已经成为我们生活中ai的代表&#xff0c;下面让我们看一下语音助手的语音输入功能 准备…

Python调用百度API实现语音识别(一)

咪哥杂谈 本篇阅读时间约为 2 分钟。 1 前言 上篇文章介绍了下如何用 Python 剪辑视频&#xff0c;想回顾的同学可以拉到文章最下面&#xff0c;有历史链接。 有了上篇文章野狼disco的音频&#xff0c;今天就来带大家玩一下百度的API&#xff0c;如何借用百度 API 的语音识别功…

百度语音识别服务 —— 语音识别 REST API 开发笔记

在以前的项目中用到了百度语音识别服务&#xff0c;在这里做一个笔记。这里还是要和大家强调一下&#xff0c;最好的学习资料就是官网网站。我这里只是一个笔记&#xff0c;一方面整理了思路&#xff0c;另一方面方便以后我再次用到的时候可以快速回忆起来。 百度语音识别服务…

树莓派实现语音识别与语音合成——百度云语音识别API

本文采用百度云语音识别API接口&#xff0c;实现低于60s音频的语音识别&#xff0c;也可以用于合成文本长度小于1024字节的音频&#xff0c;此外采用snowboy离线语音唤醒引擎可实现离线语音唤醒&#xff0c;实现语音交互。基于本内容可实现语音控制小车&#xff0c;语音控制音箱…

项目:基于百度API智能语音家居控制系统

目录 开发平台/开发环境&#xff1a; windows 10、Linux、arm cortex A9(Exynos 4412)、ubuntu20.04、zigebee通信、摄像头外设、cortex-M0&#xff1b; 项目模块&#xff1a; 摄像头模块&#xff1b;qt界面及语音识别模块&#xff1b;qt客户端模块&#xff1b;服务器模块&am…

Python调用百度API进行语音识别

目录 1.作者介绍2.基于百度API的普通话识别2.1语音识别2.2百度API调用方法 3&#xff0e;实验3.1实验准备3.2实验结果 4.实验代码 1.作者介绍 高志翔&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2021级研究生 研究方向&#xff1a;机器视觉与人工智能 电…

百度语音识别API的简单应用

1. prepare 根据百度开发文档的提示可以知道&#xff0c;API仅能处理特定格式的语音文件。 由于底层识别使用的是pcm&#xff0c;因此推荐直接上传pcm文件。如果上传其它格式&#xff0c;会在服务器端转码成pcm&#xff0c;调用接口的耗时会增加。 语音识别仅支持以下格式 &am…

什么是CatGPT-使用效果如何-

个人使用效果&#xff0c;评分优&#xff0c;足以满足教学和填表。 程序媛借助CatGPT&#xff08;ChatGPT更佳&#xff09;&#xff0c;基本上可以秒杀不用此类工具的程序猿&#xff08;男&#xff09;&#xff01;&#xff01;&#xff01; 问&#xff1a;为什么使用AIGC能大幅…

OpenAI ChatGPT3.5 completion API 入门指南

官方介绍 ChatGPT 由 OpenAI 最先进的语言模型 gpt-3.5-turbo 提供支持。 使用 OpenAI API&#xff0c;您可以使用 GPT-3.5-turbo 构建自己的程序来做一些如下的事情&#xff1a; 起草电子邮件或其他书面文件编写 Python 代码回答关于一组文档的问题创建对话代理程序为你的软件…