NexLM 开源系列】让 AI 聊天更丝滑:WebSocket 实现流式对话!

在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 NexLM 的开发过程,从 架构设计代码实战,逐步搭建一个支持 多种大模型(GPT-4、DeepSeek 等)一站式大模型集成与管理平台,并集成 认证中心、微服务、流式对话 等核心功能。

系列目录规划:

  1. NexLM:从零开始打造你的专属大模型集成平台
  2. Spring Boot + OpenAI/DeepSeek:如何封装多个大模型 API 调用
  3. 支持流式对话 SSE & WebSocket:让 AI 互动更丝滑
  4. 微服务 + 认证中心:如何保障大模型 API 的安全调用
  5. 缓存与性能优化:提高 LLM API 响应速度
  6. NexLM 开源部署指南(Docker)

第四篇:让 AI 聊天更丝滑:WebSocket 实现流式对话!

在上一章中,我们使用 SSE (Server-Sent Events) 实现了 AI 生成式流式对话,但是 SSE 只能 单向推送数据,无法支持 用户在对话过程中发送控制指令(如 停止响应、调整模型、动态修改参数)。

SSE vs WebSocket 对比

对比项 SSE (Server-Sent Events) WebSocket
通信方向 单向 (Server → Client) 双向 (Server ↔ Client)
协议 HTTP/1.1 + EventSource WebSocket 协议
连接方式 基于 HTTP 长连接 需要单独 升级协议
消息格式 纯文本(也支持 JSON) 二进制 / JSON / 文本
断线重连 浏览器自动重连 需要手动实现重连逻辑
服务器推送 天然支持(适合 AI 生成式内容) 需要额外实现
服务器压力 轻量级(基于 HTTP/1.1) 服务器需要维护更多连接
浏览器支持 所有现代浏览器支持 需要 支持 WebSocket
适用场景 AI 流式输出、日志推送、股票行情 实时聊天、双向交互、多人协作

核心区别

  • SSE:适合 服务器推送数据,例如 AI 流式回复、日志推送、订阅通知。
  • WebSocket:适合 双向交互,例如 聊天、多人协作、在线游戏

本章,我们将升级方案,使用 WebSocket 实现 双向通信,使 AI 对话更智能、更可交互。(上一节回顾)

NexLM 开源系列】让 AI 聊天更丝滑:WebSocket 实现流式对话!_第1张图片

为什么要用 WebSocket?

与 SSE 相比,WebSocket 具有以下优点

  1. 支持双向通信 —— 客户端可以随时向服务器发送消息,而不仅仅是等待 AI 回复。
  2. 更低的连接开销 —— WebSocket 连接后,数据传输比 SSE 高效,适用于高并发场景。
  3. 支持二进制数据 —— 可以更灵活地发送 JSON、二进制流,适配 AI 复杂交互。
  4. 连接状态可控 —— 可以手动 断开连接、重连、主动停止 AI 生成

WebSocket 实现思路

  1. 后端:使用 Spring Boot 搭建 WebSocket 服务器,实现 AI 对话流式返回。

  2. 前端:用 WebSocket 对接服务器,实现 用户消息发送 & AI 流式回复

  3. 增强功能

    • 支持多轮对话
    • 支持手动终止 AI 响应
    • 支持 JSON 格式通信

后端实现 WebSocket 服务

1️⃣ 引入 WebSocket 依赖

pom.xml 添加 WebSocket 依赖:

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-websocketartifactId>
dependency>

2️⃣ 创建 WebSocket 服务器

WebSocketChatHandler.java 实现 双向通信逻辑

@Component
public class WebSocketChatHandler extends TextWebSocketHandler {

    private final DeepSeekClient deepSeekClient;

    public WebSocketChatHandler(DeepSeekClient deepSeekClient) {
        this.deepSeekClient = deepSeekClient;
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("WebSocket 连接成功:" + session.getId());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String userMessage = message.getPayload();
        System.out.println("用户发送消息:" + userMessage);

        try {
            // 调用 AI 大模型接口,并流式返回消息
            deepSeekClient.streamChatWs(userMessage, session);
        } catch (Exception e) {
            session.sendMessage(new TextMessage("AI 服务异常,请稍后重试。"));
            e.printStackTrace();
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        System.out.println("WebSocket 连接关闭:" + session.getId());
    }
}

3️⃣ 配置 WebSocket 路由

WebSocketConfig.java 里开启 WebSocket 并配置路由:

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired
    private DeepSeekClient deepSeekClient;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new WebSocketChatHandler(deepSeekClient), "/api/ai/chat/websocket").setAllowedOrigins("*");
    }
}

路由说明:

  • WebSocket 连接地址为 ws://localhost:8080/api/ai/chat/websocket
  • 允许 所有前端域名 访问 (setAllowedOrigins("*"))

前端实现 WebSocket 交互

1️⃣ 创建 WebSocket 连接

<script>
    let ws;
    let isStreaming = false; // 是否正在接收 AI 回复

    function initWebSocket() {
        ws = new WebSocket("ws://localhost:8080/web/api/ai/chat/websocket");

        ws.onopen = function() {
            console.log("WebSocket 连接成功!");
            document.getElementById("stopButton").disabled = false; // 允许停止
        };

        ws.onmessage = function(event) {
            if (!isStreaming) return; // 如果停止了,就不处理新消息
            appendBotMessage(event.data);
        };

        ws.onerror = function(error) {
            console.error("WebSocket 错误: ", error);
        };

        ws.onclose = function() {
            console.log("WebSocket 连接关闭");
            document.getElementById("stopButton").disabled = true; // 禁用停止按钮
        };
    }

    function sendMessage() {
        let input = document.getElementById("userInput");
        let message = input.value.trim();
        if (message === "") return;

        let modelType = document.getElementById("modelType").value;

        appendUserMessage(message);

        // 如果 WebSocket 关闭了,重新连接
        if (!ws || ws.readyState === WebSocket.CLOSED) {
            initWebSocket();
            setTimeout(() => ws.send(JSON.stringify({ modelType: modelType, message: message })), 500);
        } else {
            ws.send(JSON.stringify({ modelType: modelType, message: message }));
        }

        isStreaming = true; // 允许接收 AI 回复
        input.value = "";
        document.getElementById("stopButton").disabled = false; // 启用“停止”按钮
    }

    function stopStreaming() {
        isStreaming = false; // 停止接收数据
        if (ws) {
            ws.close(); // 关闭 WebSocket 连接
        }
        document.getElementById("stopButton").disabled = true; // 禁用“停止”按钮
    }

    function appendUserMessage(text) {
        let chatBox = document.getElementById("chatBox");
        let userMessage = document.createElement("div");
        userMessage.className = "message user-message";
        userMessage.innerText = text;
        chatBox.appendChild(userMessage);
        chatBox.scrollTop = chatBox.scrollHeight;
    }

    function appendBotMessage(text) {
        let chatBox = document.getElementById("chatBox");

        let lastMessage = chatBox.lastElementChild;
        if (lastMessage && lastMessage.classList.contains("bot-message")) {
            lastMessage.innerText += text;
        } else {
            let botMessage = document.createElement("div");
            botMessage.className = "message bot-message";
            botMessage.innerText = text;
            chatBox.appendChild(botMessage);
        }

        chatBox.scrollTop = chatBox.scrollHeight;
    }

    // 初始化 WebSocket 连接
    initWebSocket();
</script>

2️⃣ 添加 HTML 交互界面

新增一个"停止回答"按钮可随时停止。

<div class="chat-container">
    <h1>AI 双向流式聊天h1>
    <div class="chat-box" id="chatBox">div>
    <div class="input-area">
        <input type="text" id="userInput" placeholder="请输入问题...">
        <select id="modelType">
            <option value="chatgpt">ChatGPToption>
            <option value="deepseek">DeepSeekoption>
            <option value="local">本地模型option>
        select>
        <button onclick="sendMessage()">发送button>
        <button id="stopButton" onclick="stopStreaming()" disabled>停止回答button>
    div>
div>


✅ 功能测试&效果展示

  1. 启动 Spring Boot WebSocket 服务

  2. 访问 chat 页面,测试 WebSocket 交互

    • 发送消息
    • AI 回复流式返回
    • 支持手动终止连接(关闭 WebSocket)


进阶优化

支持 AI 生成式内容

  • 可以用 OpenAI API大模型 生成 流式 AI 响应
  • 结合 WebSocket 分块返回,提升体验

支持会话上下文

  • 维护 WebSocket 连接的 Session,实现多轮对话

实现重连机制

  • WebSocket 断开时 自动重连,避免用户体验中断

总结

方案 SSE (Server-Sent Events) WebSocket
通信方向 单向 (Server → Client) 双向 (Server ↔ Client)
适用场景 AI 生成式流式返回、订阅推送 实时对话、交互式 AI
断线重连 浏览器自动重连 需要手动实现
性能 适合中等并发 高并发更优

WebSocket 适用于

  • 实时 AI 聊天
  • 流式 AI 交互
  • 多轮会话管理

下一步优化

  • 结合大模型 API,让 WebSocket 支持 AI 生成式对话
  • 实现 JSON 通信协议,支持更复杂交互

你已经掌握 SSE + WebSocket 让你的 AI 聊天更智能!后续我们将探索 AI 生成内容的优化技巧!


你对这个项目感兴趣吗?欢迎 Star & 关注! GitHub 项目地址 你的支持是我持续创作的动力,欢迎点赞、收藏、分享!

你可能感兴趣的:(大模型开源项目,大模型探索,SEE,Websocket,DeepSeek,ChatGPT,大模型集成,流式对话)