AI对话为什么都用SSE?WebSocket其实用错了地方
做AI对话功能的时候,前后端怎么传数据是个绕不开的问题。轮询、SSE、WebSocket,到底该用哪个?
这篇文章把三种方式放在一起对比,看完你就能直接做出选型判断。
先记住三点
轮询是客户端反复问服务端要数据。SSE是服务端单向推送。WebSocket是双向通道。
AI对话用SSE就够了。单向推送满足需求,而且对服务器最友好。
轮询:最简单但最笨的办法
轮询的思路很简单。客户端写一个定时器,每隔几秒去问服务端:有新数据吗?
setInterval(async () => {
const res = await fetch('/api/check');
const data = await res.json();
if (data.hasNew) updateUI(data.content);
}, 3000);底层用的是普通HTTP。每次请求都经历建连、发请求、等响应、断开这一套流程。
但放到AI对话里,问题就来了。轮询间隔短了,大量用户同时高频请求会压垮服务器。间隔长了,流式输出又变得卡顿,打字机效果全没了。
不过轮询也不是没用。数据更新频率低、对实时性要求不高的场景完全够用。比如每30秒刷一次邮件列表,每分钟检查一下构建状态。
SSE:服务端单向推送
轮询是客户端不停问。那能不能反过来,让服务端主动推?
想象你点了一份外卖。下完单之后你不用反复刷,骑手每到一个节点,app自动通知你:已接单、已到店、配送中。
你只下了一次单,之后所有进度都是app主动推给你的。一次请求,服务端不断推,客户端只管接收。
浏览器为这种推送定义了一套标准格式。响应头必须是Content-Type: text/event-stream,每条消息以data:开头、\n\n结尾。
data: 今天
data: 天气
data: 不错
浏览器还提供了一个叫EventSource的API。只要连上,服务端推过来的消息它会自动解析好,你直接拿字符串就行:
const source = new EventSource('/api/stream');
source.onmessage = event => {
console.log(event.data);
};你不用自己读字节、自己解码,EventSource全包了。
那AI对话为什么不用EventSource?
EventSource有两个硬伤。
第一,只支持GET请求。AI对话需要用POST把对话历史发给服务端,GET做不到。
第二,不支持自定义请求头。你没法带Authorization token做鉴权。
所以AI对话场景,客户端基本都用fetch配合ReadableStream自己读数据。Vercel AI SDK的useChat底层也是这个方案。
业界说的SSE到底指什么?
这里有个容易搞混的地方。“SSE”这个词可以指两个不同层面的东西。
HTTP streaming是基础层,对格式没有要求。服务端持续写、客户端持续读,仅此而已。
标准SSE是在它上面加的规范:data前缀、event字段、空行分隔。
拿几个真实产品来看就清楚了。
DeepSeek用的是标准SSE。响应头里有Content-Type: text/event-stream,Chrome开发者工具会多出一个EventStream标签页,帮你把消息解析成表格。
ChatGPT也是标准SSE。但它几乎不用event:字段来区分消息类型,而是把type塞进每条data的JSON里,靠内部字段判断。
Google Gemini用的也是SSE的思路,但格式完全自定义。响应不是data格式,是一行一行的JSON数组。EventStream标签页也没有了。
这说明一件事:用不用标准SSE格式,是接入大模型API那层接口自己的选择,不是技术限制。
你自己实现的时候,可以按这个来定方向:
只传纯文本内容,标准SSE就够了。格式简单,浏览器有调试工具,前后端约定也直观。
需要在一个流里传多种数据(文字内容、工具调用结果、token统计、状态信息),自定义格式更实际。可以像ChatGPT那样在JSON里加type字段区分类型。
WebSocket:双向实时通道
还是点外卖的例子。轮询是你反复刷app。SSE是app主动推送骑手状态。
WebSocket呢?你不仅能实时收到骑手位置,还能在配送途中直接给骑手发消息:“放门口就行,不要打电话”。骑手也能马上回你:“好的”。
双方随时都能说话,不需要等对方先开口。这就是全双工。
代码写起来也很简洁:
const ws = new WebSocket('wss://example.com/chat');
ws.onmessage = event => {
console.log('收到:', event.data);
};
ws.send('你好');
ws.send('再来一条');和SSE最大的区别是:SSE客户端只能听,要“说话”得另发一次HTTP请求。WebSocket直接ws.send(),在同一条连接上双向通信。
WebSocket适合什么场景?
需要双向实时通信的场景:多人聊天室每个人都在发消息,协同编辑文档每个人的改动要实时同步,在线游戏里玩家操作和服务器状态需要持续互传。
共同点是客户端不只是在“听”,还需要频繁“说”,而且要低延迟。
三种方案怎么选?
低频状态查询选轮询。数据更新慢、实时性要求不高,用最简单的方案就够了。
AI对话选SSE。单向推送够用,HTTP原生支持,不用额外配置基础设施。你发出的是一条消息,然后等着看AI怎么回。整个过程里你不会再往服务端发任何数据,就是在等、在接收。单向推送完全够用。
而且SSE用的是普通HTTP。你现在用的CDN、反向代理、Serverless函数全都能直接支持,不需要任何额外配置。WebSocket就不一样了,它不走普通HTTP,CDN、Nginx、云函数这些中间层不一定默认支持,部署到线上要单独配置。
WebSocket还有一套要自己维护的东西:心跳检测、断线重连、连接状态管理。SSE不用管这些。
多人实时协作选WebSocket。双方都要频繁说话,需要真正的双向通道。
WebSocket和SSE不是谁更好的关系。它们是为不同场景设计的工具。
什么时候需要WebSocket?
一旦客户端也需要频繁往服务端发数据,SSE就不够用了。
比如你在做一个多人协同编辑加AI辅助的产品。协同编辑部分需要WebSocket,持续同步多人的操作。AI建议部分用SSE就够了,触发后单向推送回答。
同一个产品里不同的通信需求,用不同的方案,这在真实项目里很常见。
思考题
Q1:fetch + ReadableStream读流式数据和标准SSE的EventSource相比,各自适合什么场景?想想AI对话需要POST还是GET,需不需要自定义请求头。
Q2:你的项目里已经有WebSocket基础设施(比如用于实时通知),现在要加AI对话功能,你会复用WebSocket还是单独用SSE?想想AI对话的数据流向是单向还是双向,复用WebSocket多出来的维护成本换来了什么。
Q3:做一个实时协作文档产品,文档协同编辑和AI写作助手这两个功能,分别会选什么传输方案?想想这两个功能的通信方向有什么不同。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!