利用 SSE 实现流式 AI 聊天交互(三)
在 AI 赋能的时代,即时交互式对话体验成为众多应用的核心功能之一。本文将介绍如何使用 流式 SSE (Server-Sent Events) 技术,实现高效的 AI 聊天交互,提供更加丝滑的用户体验。
一、SSE 介绍
SSE (Server-Sent Events) 是一种基于 HTTP 的服务器推送技术,适用于需要实时更新但数据流量较小的场景,例如 AI 对话、股票行情、新闻推送等。它的主要特点包括:
-
单向通信:服务器可以主动向客户端推送消息,而客户端仅能接收。
-
轻量级:相比 WebSocket,SSE 使用 HTTP 连接,无需额外协议支持。
-
自动重连:连接断开时,浏览器会自动尝试重新建立连接。
相比 WebSocket,SSE 更适合 AI 聊天等对实时性要求较高但数据方向单一的场景。
二、实现思路
传统的 AI 聊天一般是用户发送请求后,后端计算完成后一次性返回结果。但对于长文本生成,这种方式可能导致较长的响应延迟。
相比之下,流式响应 (Streaming) 可以让 AI 逐步输出生成内容,从而提升用户体验。
我们的实现基于 Odoo 18 + OpenAI API,通过 SSE (Server-Sent Events) 让前端实时接收 AI 生成的内容,达到流畅的交互效果。
三、后端实现
在 Odoo 的 @http.route 中,我们定义了一个流式接口,负责与 AI 进行通信,并通过 SSE 将生成内容逐步推送到前端。
后端代码:请求 AI 生成内容
@http.route('/ai/stream_chat', type='http', auth='public', cors='*') def ai_stream_chat(self, **kwargs): user_id = request.session.uid user_message = kwargs.get('user_message') file_content = kwargs.get('file_content', '') api_url = request.env['ir.config_parameter'].sudo().get_param('ai_chat_url') api_key = request.env['ir.config_parameter'].sudo().get_param('ai_chat_api_key') ai_model = request.env['ir.config_parameter'].sudo().get_param('ai_chat_model') # ✅ 初始化 OpenAI 客户端 client = OpenAI(base_url=api_url, api_key=api_key) messages = [ {"role": "system", "content": "我是一个AI助手,我的名字叫小加!"}, {"role": "user", "content": file_content[:5000]}, {"role": "user", "content": user_message} ] def event_stream(): try: completion = client.chat.completions.create( model=ai_model, messages=messages, stream=True, extra_headers={ "HTTP-Referer": "DeepSeek R1", "X-Title": "Odoo AIChat" } ) response_text = "" for chunk in completion: delta = chunk.choices[0].delta if delta and delta.content: response_text += delta.content yield f"data: {chunk.model_dump_json()}\n\n" yield "data: [DONE]\n\n" except Exception as e: yield f"data: {{\"error\": \"{str(e)}\"}}\n\n" return Response(event_stream(), content_type='text/event-stream')
实现细节:
-
参数获取:前端传递
user_message和file_content。 -
AI 请求:使用 OpenAI API 发起流式请求。
-
SSE 事件流:遍历 AI 生成结果并逐步推送。
-
异常处理:防止请求失败导致 SSE 断开。
四、前端实现
前端使用 EventSource 监听 SSE 事件流,逐步渲染 AI 回复内容。
前端代码:接收 AI 数据并实时渲染
function startAIStream(userMessage, fileContent, base64File, fileName) { const chatBox = document.getElementById('chat-box'); const botMessageDiv = document.createElement('div'); botMessageDiv.classList.add('message', 'bot'); botMessageDiv.innerHTML = ` <img src="./image/icon1.gif" alt="Bot"> <span id="bot-response">AI 正在思考...</span> <span id="bot-time" style="font-size: 10px; color: #888; margin-left: 10px;"></span>`; chatBox.appendChild(botMessageDiv); chatBox.scrollTop = chatBox.scrollHeight; const botResponseSpan = botMessageDiv.querySelector("#bot-response"); const botTimeSpan = botMessageDiv.querySelector("#bot-time"); const params = new URLSearchParams({ user_message: userMessage, file_content: fileContent }); const eventSource = new EventSource(`/ai/stream_chat?${params.toString()}`); let fullBotMessage = ""; botResponseSpan.innerText = ""; eventSource.onmessage = function (event) { if (event.data === "[DONE]") { eventSource.close(); botTimeSpan.innerText = new Date().toLocaleTimeString(); createHistoryRecord(userMessage, fullBotMessage, base64File, fileName); return; } try { const jsonData = JSON.parse(event.data); const deltaContent = jsonData.choices[0]?.delta?.content; if (deltaContent) { fullBotMessage += deltaContent; requestAnimationFrame(() => { botResponseSpan.innerHTML = marked.parse(fullBotMessage); chatBox.scrollTop = chatBox.scrollHeight; }); } } catch (e) { console.error("解析数据出错:", e); } }; eventSource.onerror = function (err) { console.error("SSE 连接错误:", err); eventSource.close(); botResponseSpan.innerText += "\n[连接已断开]"; }; }
五、效果展示
当用户发送消息后,前端立即显示 AI 正在思考...,随后 AI 逐步返回内容,并以 Markdown 解析进行美化。如下图所示:

六、项目特点
-
流式传输:AI 逐步生成内容,提升用户体验。
-
轻量级实现:基于 SSE,避免复杂的 WebSocket 连接管理。
-
Markdown 支持:使用
marked.js美化 AI 回复。 -
自动重连:确保长时间使用时连接稳定。
如果你也在开发 AI 聊天功能,不妨尝试这种流式优化方案!

浙公网安备 33010602011771号