詳解微信小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸
正文
最近指導(dǎo)群里小兄弟技術(shù)問題,發(fā)現(xiàn)一個讓我也棘手的難題。于是激發(fā)了我潛意識精神力-干到底。 由于最近沉浸在chatgpt中,很久不用google和百度搜索東西了,正如我所料一般,他們也沒有這方面的解決方案。于是,記錄一下探索研究的過程,給各位水友一個分享擴(kuò)展。
小程序上實(shí)現(xiàn)流失傳輸
模擬ChatGPT的效果,實(shí)現(xiàn)流式傳輸,通過處理流數(shù)據(jù),實(shí)現(xiàn)打字機(jī)的效果。
什么是流式傳輸?
在解決問題之前,我們需要了解什么是流式傳輸。流式傳輸指的是將數(shù)據(jù)分成多個數(shù)據(jù)流,通過網(wǎng)絡(luò)傳輸,以減少網(wǎng)絡(luò)延遲和提高性能。在某些情況下,流式傳輸也可以用于將視頻流和音頻流傳輸?shù)娇蛻舳?。流式傳輸是一種高效的數(shù)據(jù)傳輸方式,常用于大文件下載和在線視頻播放等場景。
為什么小程序不支持流式傳輸?
盡管流式傳輸在某些情況下非常有用,但小程序目前不支持流式傳輸。主要原因是小程序的架構(gòu)和限制。
小程序的開發(fā)框架提供了一個小程序的開發(fā)和調(diào)試環(huán)境。在這個環(huán)境中,小程序的代碼和資源都是打包在一個文件中的。這意味著小程序依賴此框架的環(huán)境,無法調(diào)用瀏覽器標(biāo)準(zhǔn)的API,需要框架的整體升級和支持。
此外,小程序?qū)W(wǎng)絡(luò)請求的限制也是一個因素。小程序中的網(wǎng)絡(luò)請求必須使用微信提供的API,這些API通常只支持完整的請求和響應(yīng)。這就使得小程序無法使用流式傳輸。
我的解決方案
- 使用WebSocket協(xié)議 , WebSocket是一種網(wǎng)絡(luò)協(xié)議,它提供了雙向通信的功能,并且支持流式傳輸。在小程序中,我們可以使用WebSocket協(xié)議來實(shí)現(xiàn)流式傳輸?shù)墓δ堋?/li>
- 調(diào)整數(shù)據(jù)格式 , 如果您的應(yīng)用程序需要傳輸大量數(shù)據(jù),可以將數(shù)據(jù)分成更小的塊,以便小程序可以處理它們。這樣可以避免一次性傳輸過多數(shù)據(jù)而導(dǎo)致的問題。
- 使用分段下載 , 分段下載是一種在下載大文件時很常用的技術(shù)。在小程序中,我們也可以使用這種技術(shù)來避免一次性下載大量數(shù)據(jù)。我們可以將數(shù)據(jù)分成多個部分,每次下載一個部分,并在所有部分下載完畢后將它們合并起來。
但這些都是常規(guī)方案,實(shí)現(xiàn)也比較麻煩,把正常的請求復(fù)雜化了。拋棄~
常規(guī)方案Axios
基礎(chǔ)html模式就不列舉了,axios更便捷,我很自信這個方案可行性。
重點(diǎn):
- headers 設(shè)置為流失請求
- responseType:stream
request({ url: '/api/prompt', //請求頭需要改為stream模式 headers: { Accept: 'text/event-stream', }, //響應(yīng)類型設(shè)置stream responseType: 'stream', method: 'POST', data: { prompt: prompt, }, }).then(res => { console.log(res) }).catch(err => { console.log(err) })
他們又問我要打字機(jī)效果,我的方案:接收到ArrayBuffer
以后解碼數(shù)據(jù)。
.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,瀏覽器沒問題,小程序調(diào)試工具沒問題,我依舊自信我的方案
但是,小程序報錯了,無法打印流數(shù)據(jù),無法支持TextDecoder
方法。完犢子,顧問成瞎指揮了。
另辟蹊徑:onChunkReceived方案
微信官方文檔中提到, wx.request
中支持onChunkReceived
分段式傳輸
重點(diǎn):
- 小程序
wx.request
中開啟enableChunked
; text或stream - 當(dāng)然,OpenAI接口,也要開啟
stream
; - 解碼分段內(nèi)容為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) })
這樣,我們就發(fā)起了流式傳輸請求,當(dāng)然后端也要支持的,后面我會舉例子。
當(dāng)他們執(zhí)行時,又出問題了,搞不定TextDecoder
替代方案。我查了一下,好像有個方案,小不自信了。 使用"TextDecoder"替代庫,然后給出建議:
import {TextEncoder, TextDecoder} from "fastestsmallesttextencoderdecoder"; const encode = (new TextEncoder).encode; const decode = (new TextDecoder).decode;
等了一天沒找我,哼哼,小菜一碟,完工。
這邊又來了,大佬你的方法不好使,引入執(zhí)行又又報錯了。
穩(wěn)住別慌... 試試手寫ArrayBuffer轉(zhuǎn)string方案:text-encoding 然后親自測試,搞不定就把chatgpt-plus關(guān)掉。
最終版:
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 })
其實(shí),第二個方案是可行的,只是我也沒時間具體看報了什么錯誤。最終使用了fromCharCode的方法,恰好可以處理,當(dāng)然還一些過濾和解碼,根據(jù)業(yè)務(wù)需要寫了。
后端接口配置
后端配置教程比較多,主要是添加請求頭,支持分段傳輸?shù)姆绞健?/p>
public static function prompt($message) { $openAi = self::getOpenAI(); header('Access-Control-Allow-Credentials: true'); // 設(shè)置響應(yīng)頭信息 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) { //閉包函數(shù)處理流 $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); }); }
至此,整個瀏覽已完成,相信有技術(shù)嗅覺的小伙伴一定會大有所用。目前,還沒有看到太多小程序支持流的平替方案,至少md格式,代碼高亮,打字效果處理成和官網(wǎng)一樣的交互,還是比較棘手的。不過可以試試這個,我用著還挺好,起碼交互上。后面還會發(fā)一個整合所有平替的分享,大家可以嫖到老。
以上就是詳解小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸?shù)脑敿?xì)內(nèi)容,更多關(guān)于小程序ChatGPT流式傳輸?shù)馁Y料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Web?Animations?API實(shí)現(xiàn)一個精確計時的時鐘示例
這篇文章主要為大家介紹了Web?Animations?API實(shí)現(xiàn)一個精確計時的時鐘示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07微信小程序 (三)tabBar底部導(dǎo)航詳細(xì)介紹
這篇文章主要介紹了微信小程序 (三)tabBar底部導(dǎo)航詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-09-09微信小程序 加載 app-service.js 錯誤解決方法
這篇文章主要介紹了微信小程序 加載 app-service.js 錯誤詳解的相關(guān)資料,在開發(fā)微信小程序過程中出現(xiàn)了app-services.js的錯誤,并解決此問題,需要的朋友可以參考下2016-10-10前端JavaScript算法找出只出現(xiàn)一次的數(shù)字
這篇文章主要為大家介紹了前端JavaScript算法找出只出現(xiàn)一次的數(shù)字的算法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07JavaScript實(shí)現(xiàn)一鍵復(fù)制內(nèi)容剪貼板
這篇文章主要為大家介紹了JavaScript實(shí)現(xiàn)一鍵復(fù)制內(nèi)容,document.execCommand原生JS設(shè)置剪貼板的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07一文詳解typeScript的extends關(guān)鍵字
這篇文章主要為大家介紹了typeScript的extends關(guān)鍵字使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03微信小程序 ecshop地址三級聯(lián)動實(shí)現(xiàn)實(shí)例代碼
這篇文章主要介紹了微信小程序 ecshop地址3級聯(lián)動實(shí)現(xiàn)實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02