AI聊天中使用SSE
ChatGPT火了一年了,技术上的话也带动了SSE的使用。这里也总结下我对它的认识。
SSE的优势
- 使用标准的HTTP协议,并非WebSocket。相对比WebSocket全双工资源开销相对较小。
- SSE传输的文本数据,使用开销简单。
服务端实现
服务端实现相对简单,只要responseType设置为text/event-stream;
,持续发送数据到前端直到结束即可。
客户端实现
-
1
2
3
4const evtSource = new EventSource("sse.php");
evtSource.onmessage = (e) => {
console.log(`message: ${e.data}`);
}; -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53fetch('/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。