首頁 > 軟體

詳解微信小程式如何實現類似ChatGPT的流式傳輸

2023-03-29 06:01:56

正文

最近指導群裡小兄弟技術問題,發現一個讓我也棘手的難題。於是激發了我潛意識精神力-幹到底。 由於最近沉浸在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 "0rnrn";
                } else {
                    $message = json_decode($message, true);
                    $input = $message['choices'][0]['delta']['content'] ?? '';
                    $msg .= $input;
                    echo dechex(strlen($msg)) . "rn" . $msg . "rn";
                }
            }
            ob_flush();
            flush();
            return strlen($response);
        });
    }

至此,整個瀏覽已完成,相信有技術嗅覺的小夥伴一定會大有所用。目前,還沒有看到太多小程式支援流的平替方案,至少md格式,程式碼高亮,打字效果處理成和官網一樣的互動,還是比較棘手的。不過可以試試這個,我用著還挺好,起碼互動上。後面還會發一個整合所有平替的分享,大家可以嫖到老。

以上就是詳解小程式如何實現類似ChatGPT的流式傳輸的詳細內容,更多關於小程式ChatGPT流式傳輸的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com