AI聊天中使用SSE

· 2 min read

ChatGPT火了一年了,技术上的话也带动了SSE的使用。这里也总结下我对它的认识。

https://static.1991421.cn/2024/2024-04-13-153102.jpeg

SSE的优势

  1. 使用标准的HTTP协议,并非WebSocket。相对比WebSocket全双工资源开销相对较小。
  2. SSE传输的文本数据,使用开销简单。

服务端实现

服务端实现相对简单,只要responseType设置为text/event-stream;,持续发送数据到前端直到结束即可。

客户端实现

  1. EventSource

    const evtSource = new EventSource("sse.php");
    evtSource.onmessage = (e) => {
      console.log(`message: ${e.data}`);
    };
    
  2. Fetch

    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();
        })
    

如上两个办法

对比

  1. 两者兼容性差不多,当然IE是都不考虑。
  2. 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。