AI聊天中使用SSE
·
2 min read
ChatGPT火了一年了,技术上的话也带动了SSE的使用。这里也总结下我对它的认识。
SSE的优势
- 使用标准的HTTP协议,并非WebSocket。相对比WebSocket全双工资源开销相对较小。
- SSE传输的文本数据,使用开销简单。
服务端实现
服务端实现相对简单,只要responseType设置为text/event-stream;
,持续发送数据到前端直到结束即可。
客户端实现
const evtSource = new EventSource("sse.php"); evtSource.onmessage = (e) => { console.log(`message: ${e.data}`); };
fetch('/api/openai/stream', { method: 'post', headers: { 'Content-Type': 'application/json;charset=utf-8', }, body: JSON.stringify({ messages }) }).then(res => { const reader = res.body.getReader(); let buffer = ''; const readChunk = () => { reader.read() .then(({ value, done }) => { if (done) { console.log('Stream finished'); return; } const chunkString = new TextDecoder().decode(value); buffer += chunkString; let position; while ((position = buffer.indexOf('\n\n')) !== -1) { const completeMessage = buffer.slice(0, position); buffer = buffer.slice(position + 2); completeMessage.split('\n').forEach(line => { if (line.startsWith('data:')) { const jsonText = line.slice(5).trim(); if (jsonText === '[DONE]') { console.log('done'); return; } try { const dataObject = JSON.parse(jsonText); console.log(dataObject); } catch (error) { console.error('JSON parse error:', error, jsonText); } } }); } readChunk(); }) .catch(error => { console.error(error); }); }; readChunk(); })
如上两个办法
对比
- 两者兼容性差不多,当然IE是都不考虑。
- EventSource的缺点是无法发送请求体,所以一般需要先建立一个请求发送消息,之后再发起EventSource。而Fetch与XHR一样可以携带请求体。所以没特殊需求fetch是更好的选择。
ChatGPT的打字机效果
ChatGPT很早就支持了流返回,用户体验到的是打字机效果。那么GPT是如何做的呢。
抓包分析ChatGPT使用的是fetch,发起请求是将message信息携带,返回结果类型为text/event-stream; charset=utf-8
,即服务端不断返回回答信息直到结束。
具体请求为network中检索https://chat.openai.com/backend-api/conversation
done。