【大模型统一集成项目】让 AI 聊天更丝滑:WebSocket 实现流式对话!

news2025/3/13 9:37:39

🌟 在这系列文章中,我们将一起探索如何搭建一个支持大模型集成项目 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 + EventSourceWebSocket 协议
连接方式基于 HTTP 长连接需要单独 升级协议
消息格式纯文本(也支持 JSON)二进制 / JSON / 文本
断线重连浏览器自动重连需要手动实现重连逻辑
服务器推送天然支持(适合 AI 生成式内容)需要额外实现
服务器压力轻量级(基于 HTTP/1.1)服务器需要维护更多连接
浏览器支持所有现代浏览器支持需要 支持 WebSocket
适用场景AI 流式输出、日志推送、股票行情实时聊天、双向交互、多人协作

📌 核心区别

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

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

在这里插入图片描述

🌟 为什么要用 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.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</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">ChatGPT</option>
            <option value="deepseek">DeepSeek</option>
            <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 项目地址 🌟 你的支持是我持续创作的动力,欢迎点赞、收藏、分享!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314220.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2025】Electron Git Desktop 实战一(上)(架构及首页设计开发)

源代码仓库&#xff1a; Github仓库【electron_git】 Commit &#xff1a; bb40040 Github Desktop 页面分析 本节目标&#xff1a; 1、实现类似Github Desktop的「空仓库」提示页 2、添加本地仓库逻辑编写从 Github Desktop 我们看到 他的 主要页面分为三个区域 Head头部区域…

14 | fastgo 三层架构设计

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b; 在实现业务代码之前&#xff0c;还需要先设计一个合理的软件架构。一个好的软件架构不仅可以大大提高项目的迭代速度&#xff0c;还可以降低项目的阅读和维护难度。目前&#xff0c;行业中…

【机器学习-基础知识】统计和贝叶斯推断

1. 概率论基本概念回顾 1. 概率分布 定义: 概率分布(Probability Distribution)指的是随机变量所有可能取值及其对应概率的集合。它描述了一个随机变量可能取的所有值以及每个值被取到的概率。 对于离散型随机变量,使用概率质量函数来描述。对于连续型随机变量,使用概率…

面向对象Demo01

面向对象 什么是面向对象 回顾方法的定义 package oop; ​ import java.io.IOException; ​ public class Demo01 {public static void main(String[] args) {}//public String sayHello() {return "hello, world!";}public void sayHi() {return;}public int max(i…

C++设计模式-抽象工厂模式:从原理、适用场景、使用方法,常见问题和解决方案深度解析

一、模式基本概念 1.1 定义与核心思想 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是创建型设计模式的集大成者&#xff0c;它通过提供统一的接口来创建多个相互关联或依赖的对象族&#xff0c;而无需指定具体类。其核心思想体现在两个维度&#xff1a; …

solana区块链地址生成

solana官网地址&#xff1a;https://solana.com 先引入相关依赖solana/web3.js;bip39;ethereumjs/wallet 生成助记词 const mnemonic bip39.generateMnemonic(); 生成种子 const seed bip39.mnemonicToSeedSync(mnemonic); 生成密钥对 const root hdkey.EthereumHDKey.from…

基于python的升级队列加速决策

a-f大等级是3级 a-c建筑每升1级分别需要8天 d-f建筑每升1级分别需要10天 目前以下建筑队列正在从0级升至1级 建筑A升级需要7天05&#xff1a;16&#xff1a;20 建筑b升级需要06&#xff1a;06&#xff1a;54 建筑c升级需要00&#xff1a;37&#xff1a;00 建筑d升级需要…

Ragflow技术栈分析及二次开发指南

Ragflow是目前团队化部署大模型+RAG的优质方案,不过其仍不适合直接部署使用,本文将从实际使用的角度,对其进行二次开发。 1. Ragflow 存在问题 Ragflow 开源仓库地址:https://github.com/infiniflow/ragflow Ragflow 当前版本: v0.17.0 Ragflow 目前主要存在以下问题: …

1.7 双指针专题:三数之和(medium)

1.题目链接 15. 三数之和 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/3sum/submissions/609626561/ 2.题目描述 给你⼀个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满⾜ i ! j、i ! k 且 j ! k &#xff0c;同时…

【JavaEE】Spring Boot配置文件

目录 一、Spring Boot配置文件简介二、properties 配置⽂件说明2.1 properties 基本语法2.2 value("${}")读取配置⽂件 三、yml 配置文件说明3.1 yml 基本格式3.2 yml 配置数据类型 及 读取3.3 yml配置对象及读取ConfigurationProperties(prefix "")3.4 配…

行为模式---策略模式

概念 策略模式是一种行为设计摸是&#xff0c;它的核心思想是将一些列的算法封装成独立的对象&#xff0c;并使它们可以相互替换&#xff0c;通过上下文进行调用。 策略模式通过算法抽象为独立的策略类&#xff0c;客户端可以根据自身需求选择不同的策略类来完成任务、这种方…

Word 小黑第15套

对应大猫16 修改样式集 导航 -查找 第一章标题不显示 再选中文字 点击标题一 修改标题格式 格式 -段落 -换行和分页 勾选与下段同页 添加脚注 &#xff08;脚注默认位于底部 &#xff09;在脚注插入文档属性&#xff1a; -插入 -文档部件 -域 类别选择文档信息&#xff0c;域…

OSPF:虚链路

一、虚链路概念 在OSPF中&#xff0c;虚链路&#xff08;Virtual Link&#xff09; 是一种逻辑连接&#xff0c;用于解决因网络设计或扩展导致的区域无法直接连接到骨干区域&#xff08;Area 0&#xff09;的问题。它是通过中间区域&#xff08;Transit Area&#xff09;在两个…

网络安全事件响应--应急响应(windows)

应用系统日志 Windows主要有以下三类日志记录系统事件&#xff1a;应用程序日志、系统日志和安全日志。 系统和应用程序日志存储着故障排除信息&#xff0c;对于系统管理员更为有用。安全日志记录着事件审计信息&#xff0c;包括用户验证&#xff08;登录、远程访问等&#x…

DataEase:一款国产开源数据可视化分析工具

DataEase 是由飞致云开发的一款基于 Web 的数据可视化 BI 工具&#xff0c;支持丰富的数据源连接&#xff0c;能够通过拖拉拽方式快速制作图表&#xff0c;帮助用户快速分析业务数据并洞察其趋势&#xff0c;为企业的业务改进与优化提供支持。 DataEase 的优势在于&#xff1a;…

RTK与RTD基础原理

(文中的部分图片是摘自其他博主的文章&#xff0c;由于比较久&#xff0c;忘记原本链接了&#xff0c;侵删) GPS定位原理 卫星自身有自己的星历与原子钟&#xff0c;因此卫星知道自身准确的空间坐标与时间。因为每个卫星都有原子钟&#xff0c;因此每颗卫星的时间基本上都是相…

Python读取显示Latex的公式文档,Python加载显示markdown文件。

平时用LLM大语言模型去解释文献里面的公式含义直接复制的格式word看不懂&#xff0c;基于这个web可以正常加载显示。 下面是读取的效果展示&#xff1a;下面程序保存为stl_read.py然后运行下面指令。 streamlit run stl_read.pyimport streamlit as st import base64 import …

mapbox高阶,结合threejs(threebox)添加extrusion挤出几何体,并添加侧面窗户贴图和楼顶贴图

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️threebox extrusion挤出几何体二、🍀…

mock的定义和使用场景

Python自动化中使用mock的示例 在Python自动化测试中&#xff0c;mock 用于模拟对象、函数或方法的行为&#xff0c;以便在隔离的环境中测试代码。以下是一个简单的示例&#xff1a; 假设你有一个 user.py 模块&#xff0c;其中包含一个 get_user_info 函数&#xff0c;用于从…

封装Axios拦截器实现用户无感刷新AccessToken实践指南

一、背景与需求场景 1.1 单点登录体系中的Token管理 在单点登录&#xff08;SSO&#xff09;体系下&#xff0c;用户登录后系统会颁发两种令牌&#xff1a; AccessToken&#xff1a;短期有效&#xff08;2小时&#xff09;&#xff0c;用于接口鉴权 RefreshToken&#xff1a…