什么是流式输出
使用过官方chatGPT的都知道,chatGPT的回复并不是全量返回的,简单来说,就像模拟了一个思考的过程,把一整段要表达的话像溪流流水一样一点一点的返回(node里面的stream流)。
如何实现
或许有人会说,使用websocket啊,但chatGPT属于简单的对话模式,也就是说你问一句它答一句,并不需要额外的交互。SSE对于这种简单的对话模式就起到了关键性作用,SSE(Server-Sent Events,服务器推送事件)是一种浏览器与服务器之间实现单向实时通信的技术。(具体文档:developer.mozilla.org/zh-CN/docs/…
后端实现
此处采用express的路由的实现方式,具体实现请根据实际业务修改
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer openAi的密钥`
};
// 创建新的 Axios 实例
const axiosStream = axios.create({
baseURL: 'https://api.openai.com/v1',
headers
});
router.get('/chat', async (req, res, next)=> {
// 获取前端传的参数
const params = req.query
try {
// 设置返回的请求头为事件流
res.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
});
// 设置返回状态码
res.status(200);
// 请求chatGPT接口
axiosStream.post(`/chat/completions`,
{
model: 'gpt-3.5-turbo',
messages: params.content,
stream: true
},
{
responseType: 'stream' // 流式返回
}
).then(response => {
const chatResponseStream = response.data;
let str = ''
// 监听有数据返回,res.write一点一点写入返回给前端
chatResponseStream.on('data', (chunk) => {
const chunkRes = chunk.toString()
str += chunkRes
res.write(chunkRes)
});
// 监听数据返回完成,关闭
chatResponseStream.on('end', () => {
res.end()
})
}).catch(e => {
res.end()
})
}catch (e) {
res.end()
}
})
前端实现
前端实现其实非常简单,创建一个SSE的实例然后监听数据的状态即可
let reply = ''
const evtSource = new EventSource(`${baseUrl}/chat?content=你好`);
evtSource.onmessage = (event) => {
// 流式数据最后一条的状态为[DONE]代表结束返回
if(event.data === '[DONE]') {
console.log(`回答完毕!:$${reply}`)
} else {
const res = JSON.parse(event.data)
reply += res.choices[0].delta.content || ''
}
}