Spring Boot SSE流式输出+AI消息持久化升级实践:从粗暴到优雅的跃迁

news2025/6/8 12:12:18

在 AI 应用落地过程中,我们常常需要将用户和 AI 的对话以“完整上下文”的形式持久化到数据库中。但当 AI 回复非常长,甚至接近上万字时,传统的单条消息保存机制就会出问题。

在本篇文章中,我将深入讲解一次实际项目中对 对话持久化+SSE流式响应机制 的全面升级,核心围绕 SeekController.java 控制器类改造展开,对比旧代码与新方案,解释其设计思路、实现细节和优化点。

场景背景:

假设你正在开发一个 AI 小学辅导应用,用户与 AI 进行对话,后端通过 SSE(Server-Sent Events)协议流式返回 AI 回复。同时你需要将所有对话保存在数据库中,供后续查看和继续。

原始版本的逻辑存在以下问题:

  • AI回复过长,数据库字段溢出

  • 保存逻辑未考虑拆分或异常

  • 用户消息也可能过长,直接保存风险大

  • SSE流式输出与持久化耦合度过高

  • 报错无提示,仅日志记录

改造目标:

  1. 使用分块机制安全持久化超长消息;
  2. 提供中英文断句逻辑,尽可能自然地分段;
  3. 保留流式体验,同时异步、稳健保存数据;
  4. 出错时降级处理,保留关键信息;
  5. 增强可维护性和日志追踪能力。

新旧对比:设计与核心区别一览

功能点原实现(假设)新实现(本代码)
SSE流式可能返回整段数据基于 DeepSeek 接口流式返回 JSON
持久化整体写入,一次提交分块处理、格式化保存
长文本处理可能直接截断中文标点 + 段落智能断点
错误处理仅 try-catch加入错误消息保存入库
用户体验容易失败不提示“内容被截断”“部分消息”明确反馈

核心升级一:AI回复和用户消息智能分块保存

private static final int MAX_DB_CHUNK_LENGTH = 16000;
private static final int PREFERRED_CHUNK_LENGTH = 8000;

系统限制数据库字段为 16000 字符以内,为避免过长内容保存失败,我们:

  1.     定义“首选分块长度”(8000)和“最大字段长度”;
  2.     优先尝试在中文句号(。)、感叹号、问号等自然断句处断开;
  3.     找不到就退而求其次,在段落分隔符(\n\n)断开;
  4.     保存时附加头部说明和结尾标注,如 [第1块/共3块];
  5.     超出字段限制则尾部加 "[内容被截断]" 明确提示。

分块格式如下:

[第1块/共2块] 这是一段很长很长的回复内容,适合分块保存。

[此为分块消息的一部分]

核心升级二:AI 响应内容通过 SSE 流式返回 + 动态拼接

RealEventSource realEventSource = new RealEventSource(request, new EventSourceListener() {
    @Override
    public void onEvent(EventSource eventSource, String id, String type, String data) {
        if (DONE.equals(data)) return;

        String content = getContent(data);
        if (content != null) {
            // 累积回复
            aiResponseRef.set(aiResponseRef.get() + content);

            // SSE 推送
            pw.write("data:" + JsonUtils.convertObj2Json(new ContentDto(content)) + "\n\n");
            pw.flush();
        }
    }
});

核心亮点:

  1.     DeepSeek 的接口通过 SSE 返回 delta 内容;
  2.     每次内容片段都会立即返回前端,极大提升响应速度和流畅性;
  3.     同时我们使用 AtomicReference 变量拼接全量回复,供后续入库。

核心升级三:用户消息也不再“盲目乐观”

别只考虑 AI 长文本,用户输入如果是整段阅读理解、文章、作文题目,也可能超长!

private void saveUserMessage(Integer conversationId, String userContent) {
    List<String> chunks = splitContentIntoChunks(userContent);

    for (int i = 0; i < chunks.size(); i++) {
        String chunkContent = formatChunkContent(chunks.get(i), i, chunks.size());
        if (chunkContent.length() > MAX_DB_CHUNK_LENGTH) {
            chunkContent = chunkContent.substring(0, MAX_DB_CHUNK_LENGTH - 100) + "...[内容被截断]";
        }

        Message userMsg = new Message();
        userMsg.setConversationId(conversationId);
        userMsg.setSenderType(Message.SenderType.USER);
        userMsg.setContent(chunkContent);
        messageService.add(userMsg);
    }
}

用户输入同样 优先尝试智能分段 + 分块保存,并在失败时记录错误提示。

核心升级四:AI系统消息加入“角色注入”

为了让 AI 更符合目标受众(小学生),我们默认在每次对话中插入一条系统 prompt:

systemMessage.put("content", "你是一个经验丰富的小学学习辅导 AI 助手...");

这段 prompt 会 始终被添加为上下文第一条消息,确保风格和角色固定一致。

技术细节亮点汇总

技术点用法说明
SSE协议使用 EventSourceRealEventSource 实现流式对话
OkHttp使用 OkHttp 发送带事件监听器的 POST 请求
CountDownLatch阻塞主线程直到 SSE 结束
Jackson ObjectMapper精准地处理 JsonNode 和字符串互转
分块处理断句处理中文标点、英文标点、段落符
分块标注提供块序号、块总数、尾注辅助前端识别
降级容错保存失败时 fallback 记录错误提示消息

用户体验提升细节

  1. AI语气风格:符合儿童认知水平和心理预期;
  2. 前端流畅呈现:每条回复几乎“实时可见”;
  3. 对话记录清晰分层:块头/块尾标注简洁明确;
  4. 出错时不报错白屏,保留重要提示信息。

小结与反思

本次升级看似只是对“长文本保存”的功能增强,但背后牵涉到了数据结构设计、AI接口集成、用户体验控制、系统健壮性等多个维度的系统性思考。

最关键的不是“写对代码”,而是能预判潜在问题,并留出足够冗余空间处理边界异常。

结语

如果你也在构建 AI 应用系统,强烈建议你:

  1.     使用 流式返回机制 提升响应体验;
  2.     加入 分块策略 处理高可变长度的输入/输出;
  3.     异常可见化,让错误被看到、被记录、被挽救;
  4.     提前考虑 前后端协作约定,如 chunk 标注语法。

希望本篇文章能为你带来实用灵感!

效果展示:

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

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

相关文章

Model Context Protocol (MCP) 是一个前沿框架

微软发布了 Model Context Protocol (MCP) 课程&#xff1a;mcp-for-beginners。 Model Context Protocol (MCP) 是一个前沿框架&#xff0c;涵盖 C#、Java、JavaScript、TypeScript 和 Python 等主流编程语言&#xff0c;规范 AI 模型与客户端应用之间的交互。 MCP 课程结构 …

内容力重塑品牌增长:开源AI大模型驱动下的智能名片与S2B2C商城赋能抖音生态种草范式

摘要&#xff1a;内容力已成为抖音生态中品牌差异化竞争的核心能力&#xff0c;通过有价值、强共鸣的内容实现产品"种草"与转化闭环。本文基于"开源AI大模型AI智能名片S2B2C商城小程序源码"技术架构&#xff0c;提出"技术赋能内容"的新型种草范式…

手机号在网状态查询接口如何用PHP实现调用?

一、什么是手机号在网状态查询接口 通过精准探测手机号的状态&#xff0c;帮助平台减少此类问题的发生&#xff0c;提供更个性化的服务或进行地域性营销 二、应用场景 1. 金融风控 通过运营商在网态查询接口&#xff0c;金融机构可以核验贷款申请人的手机状态&#xff0c;拦…

【Java微服务组件】分布式协调P4-一文打通Redisson:从API实战到分布式锁核心源码剖析

欢迎来到啾啾的博客&#x1f431;。 记录学习点滴。分享工作思考和实用技巧&#xff0c;偶尔也分享一些杂谈&#x1f4ac;。 有很多很多不足的地方&#xff0c;欢迎评论交流&#xff0c;感谢您的阅读和评论&#x1f604;。 目录 引言Redisson基本信息Redisson网站 Redisson应用…

一个简单的德劳内三角剖分实现

德劳内&#xff08;Delaunay&#xff09;三角剖分是一种经典的将点集进行三角网格化预处理的手段&#xff0c;在NavMesh、随机地牢生成等场景下都有应用。 具体内容百度一大堆&#xff0c;就不介绍了。 比较知名的算法是Bowyer-Watson算法&#xff0c;也就是逐点插入法。 下雨闲…

C#子线程更新主线程UI及委托回调使用示例

1.声明线程方法 2.线程中传入对象 3.声明委托与使用 声明委托对象 委托作为参数传入方法 4.在线程中传入委托 5.调用传入的委托

使用VuePress2.X构建个人知识博客,并且用个人域名部署到GitHub Pages中

使用VuePress2.X构建个人知识博客&#xff0c;并且用个人域名部署到GitHub Pages中 什么是VuePress VuePress 是一个以 Markdown 为中心的静态网站生成器。你可以使用 Markdown 来书写内容&#xff08;如文档、博客等&#xff09;&#xff0c;然后 VuePress 会帮助你生成一个…

手写Promise.all

前言 之前在看远方os大佬直播的时候看到有让手写的Promise.all的问题&#xff0c;然后心血来潮自己准备手写一个 开始 首先&#xff0c;我们需要明确原本js提供的Promise.all的特性 Promise.all返回的是一个Promise如果传入的数据中有一个reject即整个all返回的就是reject&…

2025年6月|注意力机制|面向精度与推理速度提升的YOLOv8模型结构优化研究:融合ACmix的自研改进方案

版本&#xff1a; 8.3.143(Ultralytics YOLOv8框架) ACmix模块原理 在目标检测任务中&#xff0c;小目标&#xff08;如裂缝、瑕疵、零件边缘等&#xff09;由于其尺寸较小、纹理信息稀疏&#xff0c;通常更容易受到图像中复杂背景或噪声的干扰&#xff0c;从而导致漏检或误检…

利用qcustomplot绘制曲线图

本文详细介绍了qcustomplot绘制曲线图的流程&#xff0c;一段代码一段代码运行看效果。通过阅读本文&#xff0c;读者可以了解到每一项怎么用代码进行配置&#xff0c;进而实现自己想要的图表效果。&#xff08;本文只针对曲线图&#xff09; 1 最简单的图形&#xff08;入门&…

【基础算法】枚举(普通枚举、二进制枚举)

文章目录 一、普通枚举1. 铺地毯(1) 解题思路(2) 代码实现 2. 回文日期(1) 解题思路思路一&#xff1a;暴力枚举思路二&#xff1a;枚举年份思路三&#xff1a;枚举月日 (2) 代码实现 3. 扫雷(2) 解题思路(2) 代码实现 二、二进制枚举1. 子集(1) 解题思路(2) 代码实现 2. 费解的…

智能对联网页小程序的仓颉之旅

#传统楹联遇上AI智能体&#xff1a;我的Cangjie Magic开发纪实 引言&#xff1a;一场跨越千年的数字对话 "云对雨&#xff0c;雪对风&#xff0c;晚照对晴空"。昨天晚上星空璀璨&#xff0c;当我用仓颉语言写下第一个智能对联网页小程序的Agent DSL代码时&#xff0…

Python分形几何可视化—— 复数迭代、L系统与生物分形模拟

Python分形几何可视化—— 复数迭代、L系统与生物分形模拟 本节将深入探索分形几何的奇妙世界&#xff0c;实现Mandelbrot集生成器和L系统分形树工具&#xff0c;并通过肺部血管分形案例展示分形在医学领域的应用。我们将使用Python的NumPy进行高效计算&#xff0c;结合Matplo…

【超详细】英伟达Jetson Orin NX-YOLOv8配置与TensorRT测试

文章主要内容如下&#xff1a; 1、基础运行环境配置 2、Torch-GPU安装 3、ultralytics环境配置 4、Onnx及TensorRT导出详解 5、YOLOv8推理耗时分析 基础库版本&#xff1a;jetpack5.1.3, torch-gpu2.1.0, torchvision0.16.0, ultralytics8.3.146 设备的软件开发包基础信息 需…

Go语言学习-->项目中引用第三方库方式

Go语言学习–&#xff1e;项目中引用第三方库方式 1 执行 go mod tidy 分析引入的依赖有没有正常放在go.mod里面 找到依赖的包会自动下载到本地 并添加在go.mod里面 执行结果&#xff1a; 2 执行go get XXXX&#xff08;库的名字&#xff09;

每日Prompt:云朵猫

提示词 仰视&#xff0c;城镇的天空&#xff0c;一片形似猫咪的云朵&#xff0c;用黑色的简笔画&#xff0c;勾勒出猫咪的形状&#xff0c;可爱&#xff0c;俏皮&#xff0c;极简

AI浪潮下的IT行业:威胁、转变与共生之道

目录 前言1 AI在IT行业的具体应用场景1.1 软件开发中的AI助手1.2 运维与监控的智能化1.3 测试自动化与质量保障1.4 安全防护中的智能威胁识别 2 AI对IT从业者的实际影响2.1 工作内容的结构性变化2.2 技能结构的再平衡 3 IT从业者不可替代的能力与价值3.1 复杂系统的架构与抽象能…

基于功能基团的3D分子生成扩散模型 - D3FG 评测

D3FG 是一个在口袋中基于功能团的3D分子生成扩散模型。与通常分子生成模型直接生成分子坐标和原子类型不同&#xff0c;D3FG 将分子分解为两类组成部分&#xff1a;官能团和连接体&#xff0c;然后使用扩散生成模型学习这些组成部分的类型和几何分布。 一、背景介绍 D3FG 来源…

蓝耘服务器与DeepSeek的结合:引领智能化时代的新突破

&#x1f31f; 嗨&#xff0c;我是Lethehong&#xff01;&#x1f31f; &#x1f30d; 立志在坚不欲说&#xff0c;成功在久不在速&#x1f30d; &#x1f680; 欢迎关注&#xff1a;&#x1f44d;点赞⬆️留言收藏&#x1f680; &#x1f340;欢迎使用&#xff1a;小智初学…

无人机光纤FC接口模块技术分析

运行方式 1. 信号转换&#xff1a;在遥控器端&#xff0c;模块接收来自遥控器主控板的电信号。 2.电光转换&#xff1a;模块内部的激光发射器将电信号转换成特定波长的光信号。 3.光纤传输&#xff1a;光信号通过光纤跳线传输。光纤利用全内反射原理将光信号约束在纤芯内进行…