WebSocket vs SSE:AI 应用实时交互的两种方案对比,实战选型指南
TL;DR:AI 应用需要实时展示生成结果(打字机效果)。方案有两个:WebSocket 和 Server-Sent Events(SSE)。本文用真实案例对比两者的适用场景,并给出 FastAPI + 前端的完整实现代码。
1. 先说结论
✅ WebSocket 适用
- AI Agent 多轮对话(双向通信)
- 需要服务器主动推送的场景
- 高频数据交换(游戏、协作工具)
✅ SSE 适用
- AI 流式输出(打字机效果)
- 服务器单向推送(通知、日志)
- 实现简单,无需 WebSocket 握手
| 对比维度 | WebSocket | SSE |
|---|---|---|
| 通信方向 | 全双工(双向) | 半双工(单向) |
| 协议复杂度 | 需要握手、心跳 | 基于 HTTP,简单 |
| 断线重连 | 需要手动处理 | 浏览器自动重连 |
| 兼容性 | 所有浏览器 | IE 不支持 |
| AI 流式输出 | 需要额外处理 | ✅ 原生支持 |
| 适用 AI 场景 | Agent 对话、工具调用 | 流式回答、进度展示 |
| 实现难度 | ⭐⭐⭐⭐ | ⭐⭐ |
2. 为什么 AI 应用选 SSE 而不是 WebSocket
2.1 AI 流式输出的原理
大模型生成回答是一个「流」的过程:
流式输出的数据格式
# 服务器逐块返回(每个 chunk 是一个 token) data: {"content": "今"} data: {"content": "天"} data: {"content": "天"} data: {"content": "气"} data: {"content": "很"} data: {"content": "好"} data: {"content": "。"} [DONE]SSE 天然适合这种场景——每个 `data:` 就是一个数据块,浏览器收到后直接渲染到页面上,实现「打字机效果」。
2.2 用 SSE 实现 AI 流式输出
Python - FastAPI SSE 后端
from fastapi import FastAPI from fastapi.responses import StreamingResponse import openai import asyncio import json app = FastAPI() client = openai.OpenAI( api_key="your-api-key", base_url="https://api.siliconflow.cn/v1" ) async def stream_openai_response(prompt: str): """将 OpenAI 的流式响应转为 SSE 格式""" stream = client.chat.completions.create( model="deepseek-ai/DeepSeek-V3", messages=[{"role": "user", "content": prompt}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: content = chunk.choices[0].delta.content # SSE 格式:data: {"content": "xxx"}\n\n yield f"data: {json.dumps({'content': content})}\n\n" # 发送结束信号 yield "data: [DONE]\n\n" @app.get("/stream-chat") async def stream_chat(prompt: str): """SSE 流式聊天接口""" return StreamingResponse( stream_openai_response(prompt), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" # 关闭 Nginx 缓冲 } )JavaScript - 前端 SSE 接收
async function streamChat(prompt) { const response = await fetch( `http://localhost:8000/stream-chat?prompt=${encodeURIComponent(prompt)}` ); const reader = response.body.getReader(); const decoder = new TextDecoder(); let answer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split('\n'); for (const line of lines) { if (line.startsWith('data: ')) { const data = line.slice(6); if (data === '[DONE]') { console.log('完成!'); return; } const parsed = JSON.parse(data); answer += parsed.content; // 打字机效果:实时更新页面 document.getElementById('output').textContent = answer; } } } } // 使用示例 streamChat('解释一下什么是 REST API');3. 用 WebSocket 实现 AI Agent 对话
SSE 只能单向推送。如果你要做 AI Agent——用户可以随时打断、发送新消息、调用工具——需要双向通信,用 WebSocket。
Python - FastAPI WebSocket 服务
from fastapi import FastAPI, WebSocket, WebSocketDisconnect from fastapi.responses import HTMLResponse import openai import json app = FastAPI() client = openai.OpenAI(api_key="your-api-key", base_url="https://api.siliconflow.cn/v1") class ConnectionManager: def __init__(self): self.active_connections: list[WebSocket] = [] async def connect(self, websocket: WebSocket): await websocket.accept() self.active_connections.append(websocket) def disconnect(self, websocket: WebSocket): self.active_connections.remove(websocket) manager = ConnectionManager() @app.websocket("/ws/chat") async def websocket_endpoint(websocket: WebSocket): await manager.connect(websocket) try: while True: # 接收用户消息 data = await websocket.receive_text() message = json.loads(data) # 处理不同类型的消息 if message["type"] == "chat": # 流式回复 stream = client.chat.completions.create( model="deepseek-ai/DeepSeek-V3", messages=[{"role": "user", "content": message["content"]}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: await websocket.send_json({ "type": "token", "content": chunk.choices[0].delta.content }) # 发送结束信号 await websocket.send_json({"type": "done"}) elif message["type"] == "tool_call": # 处理工具调用(如搜索、计算等) result = await handle_tool_call(message) await websocket.send_json({ "type": "tool_result", "result": result }) except WebSocketDisconnect: manager.disconnect(websocket)JavaScript - 前端 WebSocket 客户端
class AIChatAgent { constructor(wsUrl) { this.ws = new WebSocket(wsUrl); this.onToken = null; this.onToolResult = null; this.ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (msg.type === 'token') { this.onToken && this.onToken(msg.content); } else if (msg.type === 'done') { console.log('生成完成'); } else if (msg.type === 'tool_result') { this.onToolResult && this.onToolResult(msg.result); } }; } sendMessage(content) { this.ws.send(JSON.stringify({ type: 'chat', content })); } callTool(toolName, params) { this.ws.send(JSON.stringify({ type: 'tool_call', tool: toolName, params })); } } // 使用示例 const agent = new AIChatAgent('ws://localhost:8000/ws/chat'); let answer = ''; agent.onToken = (token) => { answer += token; document.getElementById('output').textContent = answer; }; agent.sendMessage('帮我查一下今天的天气');4. 实战场景对比
场景 1:AI 写作助手(打字机效果)
用户输入一段文字,AI 实时续写。
- 推荐方案:SSE
- 单向通信足够了(用户发一次,AI 流式输出)
- 实现简单,浏览器自动重连
实测延迟:SSE 平均延迟 80ms,WebSocket 平均延迟 75ms——对于打字机效果,两者在体感上没有区别,但 SSE 实现更简单。
场景 2:AI 客服机器人(多轮对话)
- 推荐方案:WebSocket
- 用户可以随时打断 AI(发送新问题)
- 需要双向通信
- AI 可能调用工具(搜索、查数据库)需要主动推送结果
场景 3:AI 代码生成(带进度展示)
- 推荐方案:WebSocket
- AI 生成代码时,需要展示「正在写 X 模块」
- 写完后需要自动运行测试
- 测试结果需要主动推送给前端
5. 混合方案:SSE + WebSocket 并存
实际项目中,两个方案可以并存——WebSocket 用于 Agent 主对话,SSE 用于辅助任务(如日志推送、进度更新)。
FastAPI 路由设计示例
# 主对话:用 WebSocket(双向) @app.websocket("/ws/agent") async def agent_ws(websocket: WebSocket): """AI Agent 主对话入口""" pass # 辅助任务:用 SSE(单向) @app.get("/sse/logs/{task_id}") async def task_logs(task_id: str): """任务日志流(SSE)""" async def log_generator(): while True: log = await get_task_log(task_id) yield f"data: {json.dumps(log)}\n\n" await asyncio.sleep(1) return StreamingResponse(log_generator(), media_type="text/event-stream") @app.get("/sse/progress/{task_id}") async def task_progress(task_id: str): """任务进度流(SSE)""" async def progress_generator(): while True: progress = await get_task_progress(task_id) yield f"data: {json.dumps(progress)}\n\n" await asyncio.sleep(0.5) return StreamingResponse(progress_generator(), media_type="text/event-stream")6. 总结
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| AI 流式回答(打字机效果) | ✅ SSE | 单向够用,实现简单 |
| AI Agent 多轮对话 | ✅ WebSocket | 需要双向通信 + 工具调用 |
| 任务进度 / 日志推送 | ✅ SSE | 单向推送,浏览器自动重连 |
| 代码协作(多人同时编辑) | ✅ WebSocket | 高频双向同步 |
| 简单 AI 查询(问一句答一句) | ✅ REST API | 不需要实时通信 |
选型口诀:「单向用 SSE,双向用 WebSocket,简单查询用 REST。」AI 写作助手 / 进度展示 → SSE;AI Agent / 实时协作 → WebSocket。
如果对你有帮助,欢迎在评论区聊聊你的实时通信方案选型经验。
