最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue3實現(xiàn)chatgpt的流式輸出過程

 更新時間:2026年03月27日 09:18:24   作者:臨楓541  
這篇文章主要介紹了Vue3實現(xiàn)chatgpt的流式輸出過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

前言

我在使用Vue3開發(fā)一個chatgpt工具類網(wǎng)站的時候,翻閱了不少博客和github上的一些相關項目,都沒能找到適合Vue3去實現(xiàn)stream的流式數(shù)據(jù)處理。

經(jīng)過踩坑,最終實現(xiàn)了適用直接調(diào)chatgpt接口的方法以及改為調(diào)用Python后端接口的方法。

背景

默認情況下,當用戶從 OpenAI 請求完成時,會生成整個完成,然后再通過單個響應發(fā)回,這樣可能會造成等待響應時間過長。

解決

“流式傳輸”,需在調(diào)用聊天完成或完成端點時設置 stream=True,這將返回一個對象,該對象將響應作為僅數(shù)據(jù)服務器發(fā)送的事件流回。

參數(shù)說明

  • messages: 必須是對象數(shù)組
類型作用
system設置chatgpt的角色
user用戶輸入的內(nèi)容
assistantchatgpt返回的內(nèi)容
  • system 設定角色助手,使得下文對話走這條線路
  • assistant充當歷史記錄,達到多輪對話,需將用戶所問、AI所答數(shù)據(jù)存儲,實現(xiàn)上下文功能,代價是消耗更多的tokens
  • temperature
類型默認值取值范圍是否必填
浮點數(shù)10 - 2

隨著temperature的取值越大,其輸出結果更加隨機(較低能集中穩(wěn)定輸出字節(jié),但較高能有意想不到的性能創(chuàng)意)

  • top_p
類型默認值取值范圍是否必填
浮點數(shù)10 - 1

top_p用于預測可能,值越小時,其輸出結果會更加肯定,響應性能會相對快,但值越大時,輸出的結果可能會更貼近用戶需求

How to stream completions?

相信你們早就閱讀了上面的文檔,但還是很迷茫,感覺無從下手...下面說說我的踩坑經(jīng)歷:我在網(wǎng)上搜索到的信息是,需要一些流式處理庫,我就問chatgpt,它給我推薦了以下幾種

  • RxJS:是一個響應式編程庫,支持流式處理和異步操作。
  • Bacon.js:也是一個響應式編程庫,提供了一個功能強大的事件流模型,可以用來處理異步事件。
  • Highland.js:是一個基于流的函數(shù)式編程庫,提供了廣泛的流操作和管道組合功能。
  • Node.js的stream模塊:是一個流式處理庫,提供了流處理的核心功能。可以通過其定義自己的流轉換器和消費者函數(shù)。
  • lodash-fp:是一個功能強大的函數(shù)式編程庫,提供了一整套函數(shù)式的操作和工具,可以用來方便快捷地進行流處理。

我沒走這條路,我重新查詢了一波,網(wǎng)上的意思是,可以利用WebSocket方式或SSE的方式去實現(xiàn)長連接,但我都沒采納,最終使用的是fetch去實現(xiàn)請求即可,不用將問題復雜化哈哈哈

  • 適用直接調(diào)chatgpt的接口
// gpt.js
import { CHATGPT_API_URL } from '@/common/config.js'
const OPENAI_API_KEY = '你的接口'
// TODO 適用直接調(diào)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.created
        yield {
          content: jsonMessage.choices[0]?.delta?.content || '',
          role: "assistant",
          id: createdID
        };
      }
    }
  }
}

以上是利用迭代器的寫法去實現(xiàn)流式輸出,我上面的字符串其實是chatgpt響應輸出的數(shù)據(jù),例如:

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

注意:

我利用迭代器需要將每一句created相同的流數(shù)據(jù)存儲到一起,才能形成一個消息的閉環(huán),否則頁面的效果會是一個字就占一個段落,你們可以去試一試

// 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.id
    list.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 ,到這一步就可以實現(xiàn)chatgpt的流式輸出啦,響應速度是非常快的?。。?/p>

補充:

1. list 是用戶角色和AI角色的對話數(shù)組,可以傳遞給子組件去遍歷渲染不同角色的聊天,在文章尾部將展示實現(xiàn)Markdown代碼塊的步驟

2. message 是將user以及assistant的所有歷史記錄push進去,是實現(xiàn)多輪對話的關鍵

  • 改為調(diào)用后端Python的接口,先看看后端哥哥@ToTensor寫給前端的文檔
# ChatGPT流式輸出接口

## 接口路徑

```bash
https://后端提供的url
```

## 請求方式

**POST**

## 請求參數(shù)


```bash
{
    "messages": [
        {
            "role": "user",
            "content": "你好"
        }
    ]
}
```

## 請求參數(shù)說明

```bash
messages: 消息體
```


## curl

```bash
curl --location 'https://后端提供的url' \
--header 'Content-Type: application/json' \
--data '{
    "messages": [
        {
            "role": "user",
            "content": "你好"
        }
    ]
}'
```


## 返回數(shù)據(jù)

```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"
        }
    ]
}
```

## 返回參數(shù)說明

```bash
role = assistant, 開始輸出
finish_reason = stop, 輸出結束
finish_reason = null, 正在輸出
content 輸出內(nèi)容
```

根據(jù)文檔,我們只需要小小改動代碼

// 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 = false

  while (!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.content
            if (content) {
              yield { id: json.created, content }
            }
          }
          if (json.choices && json.choices[0].finish_reason === 'stop') {
            done = true
            onStreamDone()
            break
          }
        } catch (e) {
          console.error(e)
        }
      }
    }
    if (streamDone) {
      done = true;
    }
  }
}

上面代碼多了個onStreamDone參數(shù),是我需要利用它處理響應完成的邏輯,沒有這個需求的伙伴可以適當刪改,接下來再看看父組件如何獲取數(shù)據(jù)吧

// 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 創(chuàng)建了一個名為 sortedList 的計算屬性,該屬性返回一個已排序的 list 數(shù)組副本。

在排序過程中,使用了 slice 方法創(chuàng)建了一個數(shù)組副本,以避免直接修改原始數(shù)組。排序方式為按照每個數(shù)組元素的 timestamp 屬性升序排序。

在模板中遍歷循環(huán)sortedList的內(nèi)容就能實現(xiàn)用戶和ai對話啦

  •  選擇合適的Markdown 編輯器組件庫

介紹一下md-editor-v3 

 它提供了一些基礎的 Markdown 編輯功能,如加粗、斜體、標題、無序列表、有序列表、引用、代碼塊等。除此之外,它還支持上傳圖片、撤銷/重做、全屏等功能。

md-editor-v3 的優(yōu)點是易于使用、易于擴展,并且提供了一些定制化的選項。但是我只是想實現(xiàn)代碼塊,故解構出MdPreview

使用:

// 模板中
<MdPreview
   :showCodeRowNumber="true"            // 顯示行號
   :modelValue="item.content"
/>


import { MdPreview } from 'md-editor-v3'
import 'md-editor-v3/lib/style.css'

效果圖:

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • 解決element?ui?cascader?動態(tài)加載回顯問題

    解決element?ui?cascader?動態(tài)加載回顯問題

    這篇文章主要介紹了element?ui?cascader?動態(tài)加載回顯問題解決方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • 詳解elementui之el-image-viewer(圖片查看器)

    詳解elementui之el-image-viewer(圖片查看器)

    這篇文章主要介紹了詳解elementui之el-image-viewer(圖片查看器),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-08-08
  • 使用Vue Router路由懶加載引發(fā)的生產(chǎn)頁面白屏問題的解決方法

    使用Vue Router路由懶加載引發(fā)的生產(chǎn)頁面白屏問題的解決方法

    這篇文章主要介紹了使用Vue Router 路由懶加載引發(fā)的生產(chǎn)頁面白屏問題的原因和解決方法,并有相關的代碼示例和圖文供大家參考,需要的朋友可以參考下
    2025-09-09
  • 詳解vue 實例方法和數(shù)據(jù)

    詳解vue 實例方法和數(shù)據(jù)

    這篇文章主要介紹了vue 實例方法和數(shù)據(jù)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2017-10-10
  • Vue的生命周期一起來看看

    Vue的生命周期一起來看看

    這篇文章主要為大家詳細介紹了Vue的生命周期,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • vue使用exif獲取圖片旋轉,壓縮的示例代碼

    vue使用exif獲取圖片旋轉,壓縮的示例代碼

    這篇文章主要介紹了vue使用exif獲取圖片旋轉,壓縮的示例代碼,幫助大家更好的利用python處理圖片,感興趣的朋友可以了解下
    2020-12-12
  • vue2.0 獲取從http接口中獲取數(shù)據(jù),組件開發(fā),路由配置方式

    vue2.0 獲取從http接口中獲取數(shù)據(jù),組件開發(fā),路由配置方式

    今天小編就為大家分享一篇vue2.0 獲取從http接口中獲取數(shù)據(jù),組件開發(fā),路由配置方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-11-11
  • 關于vue跳轉后頁面置頂?shù)膯栴}

    關于vue跳轉后頁面置頂?shù)膯栴}

    這篇文章主要介紹了關于vue跳轉后頁面置頂?shù)膯栴},具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • uniapp+vue3路由跳轉傳參的實現(xiàn)

    uniapp+vue3路由跳轉傳參的實現(xiàn)

    本文主要介紹了uniapp+vue3路由跳轉傳參的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-11-11
  • vue實現(xiàn)滾動加載的表格

    vue實現(xiàn)滾動加載的表格

    在系統(tǒng)開發(fā)中遇到了這么一個問題:后端一次性返回上百條的數(shù)據(jù),我需要把返回的數(shù)據(jù)全部顯示在表格里,而且甲方爸爸明確指定了表格是不允許使用分頁的??墒钱斒褂胊-table裝載上百條數(shù)據(jù)時,頁面出現(xiàn)了明顯的卡頓現(xiàn)象。只能使用滾動加載的方案來代替了。
    2021-06-06

最新評論

昌宁县| 隆安县| 淳安县| 禹城市| 菏泽市| 宁陕县| 渝北区| 陆川县| 浏阳市| 马公市| 黔西县| 肃南| 民权县| 彰化县| 万全县| 东至县| 巴马| 静海县| 个旧市| 华宁县| 略阳县| 策勒县| 竹北市| 汉源县| 衡水市| 荆州市| 二连浩特市| 大悟县| 湟源县| 博罗县| 方城县| 仁布县| 鄂州市| 临湘市| 厦门市| 宜兰市| 全州县| 峡江县| 临武县| 阿瓦提县| 南部县|