对比ChatGPT接受数据的方法是逐字逐字地传输,它得到第一个字就开始输出,这让用户体验更佳。相比,得到全部结果再一次输出,虽然时间都一样,但等待还是太煎熬了!一边输出一边看,这才是最佳打开方式嘛。
这种边看边输的方式其实就是现在的推流(streaming)模式,像现在的视频站、直播等,肯定都是用的数据流,都是边看边播的。ChatGPT API也是可以实现同样的功能的。
服务器端实现
//nodejs
try {
res.setHeader('Cache-Control', 'no-cache')
res.setHeader('Content-Type', 'text/event-stream')
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Connection', 'keep-alive')
res.flushHeaders() // flush the headers to establish SSE with client
const response = openai.createChatCompletion({
model: "gpt-3.5-turbo",
messages: query,
max_tokens: 3000,
temperature: 0.2,
stream: true, //推流模式打开
}, { responseType: 'stream' })
response.then(resp => {
resp.data.on('data', data => {
const lines = data.toString().split('\n').filter(line => line.trim() !== '')
for (const line of lines) {
const message = line.replace(/^data: /, '')
if (message === '[DONE]') {
// console.log(996, "done")
res.end()
return
}
let strTemp = getReg(message)
if(strTemp != null){
// console.log(1188, "strTemp", strTemp)
res.write(strTemp)
}
}
})
})
}
用户端
//浏览器
let query = [{role: "user", content: data.get('prompt')}]
let dataObj = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: query,
})
}
const response = await fetch(url, dataObj)
let that = this
messageDiv.innerHTML = " "
if (response.ok) {
let i = 0
let getStream = function (reader) {
return reader.read().then(function (result) {
// 如果数据已经读取完毕,直接返回
if (result.done) {
console.log(889, "result done")
that.clickFlag = false
clearInterval(loadInterval)
loading.textContent = ''
return
}
// 取出本段数据(二进制格式)
let chunk = result.value
let text = that.utf8ArrayToStr(chunk)
if(i === 0){
text = text.replace(/\\n/g,'') //去除首段换行
} else{
text = text.replace(/\\n/g,'
')
}
// console.log(5667, "i", i, text)
// 将本段数据追加到网页之中
messageDiv.innerHTML += text
i ++
// 递归处理下一段数据
return getStream(reader)
})
}
getStream(response.body.getReader())
用户端方面是折腾了许久,用axios尝试了半天 ,也是没搞定。用fetch也是尝试了半天,才发现可行。然后是慢慢完善,几天时间竟然就这样过去了!
折腾的结果还不赖,现在AI·Joe流畅了不少,再接再励,慢慢完善。