详解微信小程序如何实现类似ChatGPT的流式传输

目录
  • 正文
  • 小程序上实现流失传输
  • 什么是流式传输?
  • 为什么小程序不支持流式传输?
  • 我的解决方案
    • 常规方案Axios
    • 另辟蹊径:onChunkReceived方案
  • 后端接口配置

正文

最近指导群里小兄弟技术问题,发现一个让我也棘手的难题。于是激发了我潜意识精神力-干到底。 由于最近沉浸在chatgpt中,很久不用google和百度搜索东西了,正如我所料一般,他们也没有这方面的解决方案。于是,记录一下探索研究的过程,给各位水友一个分享扩展。

小程序上实现流失传输

模拟ChatGPT的效果,实现流式传输,通过处理流数据,实现打字机的效果。

什么是流式传输?

在解决问题之前,我们需要了解什么是流式传输。流式传输指的是将数据分成多个数据流,通过网络传输,以减少网络延迟和提高性能。在某些情况下,流式传输也可以用于将视频流和音频流传输到客户端。流式传输是一种高效的数据传输方式,常用于大文件下载和在线视频播放等场景。

为什么小程序不支持流式传输?

尽管流式传输在某些情况下非常有用,但小程序目前不支持流式传输。主要原因是小程序的架构和限制。

小程序的开发框架提供了一个小程序的开发和调试环境。在这个环境中,小程序的代码和资源都是打包在一个文件中的。这意味着小程序依赖此框架的环境,无法调用浏览器标准的API,需要框架的整体升级和支持。

此外,小程序对网络请求的限制也是一个因素。小程序中的网络请求必须使用微信提供的API,这些API通常只支持完整的请求和响应。这就使得小程序无法使用流式传输。

我的解决方案

  • 使用WebSocket协议 , WebSocket是一种网络协议,它提供了双向通信的功能,并且支持流式传输。在小程序中,我们可以使用WebSocket协议来实现流式传输的功能。
  • 调整数据格式 , 如果您的应用程序需要传输大量数据,可以将数据分成更小的块,以便小程序可以处理它们。这样可以避免一次性传输过多数据而导致的问题。
  • 使用分段下载 , 分段下载是一种在下载大文件时很常用的技术。在小程序中,我们也可以使用这种技术来避免一次性下载大量数据。我们可以将数据分成多个部分,每次下载一个部分,并在所有部分下载完毕后将它们合并起来。

但这些都是常规方案,实现也比较麻烦,把正常的请求复杂化了。抛弃~

常规方案Axios

基础html模式就不列举了,axios更便捷,我很自信这个方案可行性。

重点:

  • headers 设置为流失请求
  • responseType:stream
request({
    url: '/api/prompt',
    //请求头需要改为stream模式
    headers: {
      Accept: 'text/event-stream',
    },
    //响应类型设置stream
    responseType: 'stream',
    method: 'POST',
    data: {
      prompt: prompt,
    },
  }).then(res => {
      console.log(res)
  }).catch(err => {
    console.log(err)
  })

他们又问我要打字机效果,我的方案:接收到ArrayBuffer以后解码数据。

.then((res) => {
  const arrayBuffer = res.data;
  const uint8Array = new Uint8Array(arrayBuffer);
  const textDecoder = new TextDecoder('utf-8');
  const text = textDecoder.decode(uint8Array);
  for (let i = 0; i < text.length; i++) {
    setTimeout(() => {
      resultText += text[i];
      console.log(resultText);
    }, i * 100);
  }
})

ok,浏览器没问题,小程序调试工具没问题,我依旧自信我的方案

但是,小程序报错了,无法打印流数据,无法支持TextDecoder方法。完犊子,顾问成瞎指挥了。

另辟蹊径:onChunkReceived方案

微信官方文档中提到, wx.request中支持onChunkReceived分段式传输

重点:

  • 小程序 wx.request 中开启 enableChunked; text或stream
  • 当然,OpenAI接口,也要开启 stream;
  • 解码分段内容为string,使用其他方案代替TextDecoder
const requestTask = wx.request({
    url: '/api/prompt',
    //请求头需要改为stream模式
    header: {
      "Transfer-Encoding": 'chunked'
    },
    timeout: 15000,
    responseType: 'text',
    method: 'POST',
    enableChunked: true,
    data: {
      prompt: prompt,
    },
  }).then(res => {
      console.log(res)
  }).catch(err => {
    console.log(err)
  })

这样,我们就发起了流式传输请求,当然后端也要支持的,后面我会举例子。

当他们执行时,又出问题了,搞不定TextDecoder替代方案。我查了一下,好像有个方案,小不自信了。 使用"TextDecoder"替代库,然后给出建议:

import {TextEncoder, TextDecoder} from "fastestsmallesttextencoderdecoder";
const encode = (new TextEncoder).encode;
const decode = (new TextDecoder).decode;

等了一天没找我,哼哼,小菜一碟,完工。

这边又来了,大佬你的方法不好使,引入执行又又报错了。

稳住别慌... 试试手写ArrayBuffer转string方案:text-encoding 然后亲自测试,搞不定就把chatgpt-plus关掉。

最终版:

let buffer=''
requestTask.onChunkReceived(function (response) {
    const arrayBuffer = response.data;
    const uint8Array = new Uint8Array(arrayBuffer);
    let text = String.fromCharCode.apply(null, uint8Array);
    buffer += text;
    full_command.value = buffer
  })

其实,第二个方案是可行的,只是我也没时间具体看报了什么错误。最终使用了fromCharCode的方法,恰好可以处理,当然还一些过滤和解码,根据业务需要写了。

后端接口配置

后端配置教程比较多,主要是添加请求头,支持分段传输的方式。

public static function prompt($message)
    {
        $openAi = self::getOpenAI();
        header('Access-Control-Allow-Credentials: true');
        // 设置响应头信息
        header('Transfer-Encoding: chunked');
        header('Content-Type: text/plain');
        header('Cache-Control: no-cache');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');
        header('Connection: keep-alive');
        $msg = "";
        $openAi->prompt([
            'messages' => $message,
            'model' => 'gpt-3.5-turbo',
            "stream" => true,
        ], function ($curl_info, $response) {
        //闭包函数处理流
            $data = [];
            $lines = explode("\n", $response);
            foreach ($lines as $line) {
                if (!str_contains($line, ':')) {
                    continue;
                }
                [$name, $value] = explode(':', $line, 2);
                if ($name == 'data') {
                    $data[] = trim($value);
                }
            }
            foreach ($data as $message) {
                if ('[DONE]' === $message) {
                    echo "0\r\n\r\n";
                } else {
                    $message = json_decode($message, true);
                    $input = $message['choices'][0]['delta']['content'] ?? '';
                    $msg .= $input;
                    echo dechex(strlen($msg)) . "\r\n" . $msg . "\r\n";
                }
            }
            ob_flush();
            flush();
            return strlen($response);
        });
    }

至此,整个浏览已完成,相信有技术嗅觉的小伙伴一定会大有所用。目前,还没有看到太多小程序支持流的平替方案,至少md格式,代码高亮,打字效果处理成和官网一样的交互,还是比较棘手的。不过可以试试这个,我用着还挺好,起码交互上。后面还会发一个整合所有平替的分享,大家可以嫖到老。

以上就是详解小程序如何实现类似ChatGPT的流式传输的详细内容,更多关于小程序ChatGPT流式传输的资料请关注我们其它相关文章!

(0)

相关推荐

  • ChatGPT编程秀之跨越认知边界

    目录 作者说 碰到了认知边界 跨越认知边界 总结一下 作者说 最近要忙了,日更的日子要到头了.后面每一篇讲的点就小一点吧,大的点等后面有空了再写.大家见谅. 碰到了认知边界 我的有的朋友跟我说,用ChatGPT编程需要你至少要跟他对等水平,因为现阶段我们还不能做到完全不需要关心它写出来的代码,当你要读懂它写的代码的时候,就必须能力对等.还有的朋友跟我说,ChatGPT的不能超过你的认知水平,你的认知水平的上限决定了它的表现,比如你认知水平不行,导致自己不能分解任务的时候,那么你用ChatGPT也

  • python借助ChatGPT读取.env实现文件配置隔离保障私有数据安全

    目录 正文 Python怎么读取.env配置文件,实现一个代码封装 Python怎么读取.env配置文件,获取所有项,实现一个代码封装 Python怎么读取.env配置文件,获取所有项,只读取.env中的项,实现一个代码封装 正文 今天借助ChatGPT完成我们这步骤,主要涉及三个问题: 1. Python怎么读取.env配置文件,实现一个代码封装 2. Python怎么读取.env配置文件,获取所有项,实现一个代码封装 3. Python怎么读取.env配置文件,获取所有项,只读取.env中的

  • SpringBoot整合chatGPT的项目实践

    目录 1 添加依赖 2 创建相关文件 2.1 实体类:OpenAi.java 2.2 配置类:OpenAiProperties.java 2.3 核心业务逻辑OpenAiUtils.java 2.4 自动配置类OpenAiAutoConfiguration.java 2.5 在resources文件夹下的META-INF/spring.factories文件中增加配置 2.6 在yml文件上配置token 3 编写测试类 4 补充 4.1 添加依赖 4.2 添加代码 5 总结 1 添加依赖 <!

  • ChatGPT用于OA聊天助手导致访问量服务宕机

    目录 闲谈 开搞 面临的问题 聊天UI 服务端接口 上线宕机 优化问题处理 流式传输 MD格式 看看效果 闲谈 最近,火到不行的明星团队产品 ChatGPT,热度一度非常高,付费用户都开始通过邀请制,专属登陆链接来限制流量了.开了Plus以后返回内容和速度真是10倍速啊~ 但对于小白或普通用户(也可能非技术行业的大佬),想要访问和体验还是挺麻烦的.除了准备梯子.接码.账号以外还可能遇到节点或网络,多次连接失败的问题. 所以,本着能折腾绝对不休息的原则,2天搞了一个聊天助手,凭借其语义的理解,关联

  • ChatGPT前端编程秀之别拿编程语言不当语言

    目录 TDD第一步就卡住了 破门而入,针对性反馈 总结一下 TDD第一步就卡住了 写完小工具,这一篇回来我们接着写我们的程序.再看一眼我们的程序运行视图: 带着TDD思路,我进入了 ejs_and_yaml_dsl_loader 这个模块,这块因为我切的不是很好,所以这代码有点难写,不过没关系,正好我们实际工作大部分的场景都是这样的.看看我们在这里能玩出点什么来. 那么这次的需求呢是这个样子的,我们需要把ejs模版引擎渲染出的yaml转换为json,那么我们这个功能会非常复杂,所以我们没有以上来

  • 详解微信小程序如何实现类似ChatGPT的流式传输

    目录 正文 小程序上实现流失传输 什么是流式传输? 为什么小程序不支持流式传输? 我的解决方案 常规方案Axios 另辟蹊径:onChunkReceived方案 后端接口配置 正文 最近指导群里小兄弟技术问题,发现一个让我也棘手的难题.于是激发了我潜意识精神力-干到底. 由于最近沉浸在chatgpt中,很久不用google和百度搜索东西了,正如我所料一般,他们也没有这方面的解决方案.于是,记录一下探索研究的过程,给各位水友一个分享扩展. 小程序上实现流失传输 模拟ChatGPT的效果,实现流式传

  • 详解微信小程序(Taro)手动埋点和自动埋点的实现

    每一个公司要想用户增长,都要收集和分析用户操作数据,因此埋点是必不可少的事情. 而对于前端职业发展来说,传统的手动埋点,无疑是繁琐又无聊的事情,能简化就简化. 一.手动埋点 手动埋点就是在每一处需要的地方,都加一段上报埋点的代码.影响代码的阅读体验,且散落的埋点代码不方便管理. 以页面 pv 为例,我们此前是在每一个页面中上报 pv: // src/manual/home/index.tsx import tracking from "./tracking"; // pageSn 是前

  • 详解微信小程序 同步异步解决办法

    详解微信小程序 同步异步解决办法 小程序中函数体还没有完成,下一个函数就开始执行了,而且两个函数之间需要传参.那是因为微信小程序函数是异步执行的.但微信小程序增加了ES6的promise特性支持,微信小程序新版本中移除了promise的支持,需要自己使用第三方库来自行实现ES6的promise特性. WxService.js import Tools from 'Tools' import es6 from '../assets/plugins/es6-promise' class Servic

  • 详解微信小程序Radio选中样式切换

    详解微信小程序Radio选中样式切换 本篇文章主要讲解在微信小程序中如何根据Radio选中来切换样式.效果如下: 原理主要是通过判断一个radio-group中哪个被选中,就让它加上一个"active"的样式. 代码如下: <!--index.wxml--> <view class="container"> <radio-group bindchange="radioCheckedChange"> <vi

  • 详解微信小程序 登录获取unionid

    详解微信小程序 登录获取unionid 首先公司开发了小程序, 公众号网页和app等, 之前都是用的openid来区分用户, 但openid只能标识用户在当前小程序或公众号里唯一, 我们希望用户可以在公司各个产品(比如公众号, 小程序, app里的微信登录)之间, 可以保持用户的唯一性, 还好微信给出了unionid. 下面分两步介绍一下 微信小程序 获取unionid的过程. 1. 首先 在微信公众平台注册小程序 , 然后在小程序上模拟登录流程. 注 : 这里只是简单登录流程, 实际中需要维护

  • 详解微信小程序 通过控制CSS实现view隐藏与显示

    详解微信小程序 通过控制CSS实现view隐藏与显示 实现效果图: 视图代码,使用变量控制隐藏类名 <scroll-view scroll-y="true" > <view class="user_freeback"> <view class="txt"> <text> 为了更好地帮助您解决问题,请准确填写您的邮箱地址和电话号码,以便管理员给你答复.</text> </view&g

  • 详解微信小程序 template添加绑定事件

    详解微信小程序 template添加绑定事件 对于模板的使用,我是想将模板的事件单独出来,其他引用模板的页面中不再掺杂模板事件,比较方便管理,如果还有其他好的解决办法, 请赐教. template.wxml <view bindtap="clickView" class="tempClass">temp模板</view> template.js var temp = { clickView: function () { console.log

  • 详解微信小程序设置底部导航栏目方法

    详解微信小程序设置底部导航栏目方法 小程序底部想要有一个漂亮的导航栏目,不知道怎么制作,于是百度找到了本篇文章,分享给大家. 好了 小程序的头部标题 设置好了,我们来说说底部导航栏是如何实现的. 我们先来看个效果图 这里,我们添加了三个导航图标,因为我们有三个页面,小程序最多能加5个. 那他们是怎么出现怎么着色的呢?两步就搞定! 1. 图标准备 阿里图标库  http://www.iconfont.cn/collections/show/29 我们进入该网站,鼠标滑到一个喜欢的图标上面  点击下

  • 详解微信小程序中的页面代码中的模板的封装

    详解微信小程序中的页面代码中的模板的封装 最近在进行微信小程序中的页面开发,其实在c++或者说是js中都会出现这种情况,就是相同的代码会反复出现,这就是进行一定的封装,封装的好处就是可以是程序中在于减少一定的代码量,并且可是使代码结构更加清晰.那今天所要记录的就是关于微信小程序中的页面的模板封装. 在微信小程序中的文件名都带有wxml等样式,在wxml中提供了模板,即可以在模板中定义代码片段,然后可以在页面中的不同位置进行调用,模板的定义: <templatename="products&

  • 详解微信小程序 相对定位和绝对定位

    详解微信小程序 相对定位和绝对定位 相对定位:元素是相对自身进行定位,参照物是自己. 绝对定位:元素是相对离它最近的一个已定位的父级元素进行定位 相对定位: position:relative;    /*启用相对定位*/         left:150rpx;    /*相对于自己往右偏离150*/         top:50rpx;     /*相对于自己往下偏离150*/ 绝对定位: position: absolute;           left: 50rpx;         

随机推荐