干货:DeepSeek+SpringAI实现流式对话!

news/2025/2/13 8:56:25/文章来源:https://www.cnblogs.com/vipstone/p/18712440

前面一篇文章我们实现了《炸裂:SpringAI内置DeepSeek啦!》,但是大模型的响应速度通常是很慢的,为了避免用户用户能够耐心等待输出的结果,我们通常会使用流式输出一点点将结果输出给用户。

那么问题来了,想要实现流式结果输出,后端和前端要如何配合?后端要使用什么技术实现流式输出呢?接下来本文给出具体的实现代码,先看最终实现效果:

解决方案

在 Spring Boot 中实现流式输出可以使用 Sse(Server-Sent Events,服务器发送事件)技术来实现,它是一种服务器推送技术,适合单向实时数据流,我们使用 Spring MVC(基于 Servlet)中的 SseEmitter 对象来实现流式输出

具体实现如下。

1.后端代码

Spring Boot 程序使用 SseEmitter 对象提供的 send 方法发送数据,具体实现代码如下:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;@RestController
public class StreamController {@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter streamData() {// 创建 SSE 发射器,设置超时时间(例如 1 分钟)SseEmitter emitter = new SseEmitter(60_000L);// 创建新线程,防止主程序阻塞new Thread(() -> {try {for (int i = 1; i <= 5; i++) {Thread.sleep(1000); // 模拟延迟// 发送数据emitter.send("time=" + System.currentTimeMillis());}// 发送完毕emitter.complete();} catch (Exception e) {emitter.completeWithError(e);}}).start();return emitter;}
}

2.前端代码

前端接受数据流也比较简单,不需要在使用传统 Ajax 技术了,只需要创建一个 EventSource 对象,监听后端 SSE 接口,然后将接收到的数据流展示出来即可,如下代码所示:

<!DOCTYPE html>
<html><head><title>流式输出示例</title></head><body><h2>流式数据接收演示</h2><button onclick="startStream()">开始接收数据</button><div id="output" style="margin-top: 20px; border: 1px solid #ccc; padding: 10px;"></div><script>function startStream() {const output = document.getElementById('output');output.innerHTML = ''; // 清空之前的内容const eventSource = new EventSource('/stream');eventSource.onmessage = function(e) {const newElement = document.createElement('div');newElement.textContent = "print -> " + e.data;output.appendChild(newElement);};eventSource.onerror = function(e) {console.error('EventSource 错误:', e);eventSource.close();const newElement = document.createElement('div');newElement.textContent = "连接关闭";output.appendChild(newElement);};}</script></body>
</html>

3.运行项目

运行项目测试结果:

  • 启动 Spring Boot 项目。
  • 在浏览器中访问地址 http://localhost:8080/index.html,即可看到流式输出的内容逐渐显示在页面上。

4.最终版:流式输出

后端代码如下:

import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;import java.util.Map;@RestController
public class ChatController {private final OpenAiChatModel chatModel;@Autowiredpublic ChatController(OpenAiChatModel chatModel) {this.chatModel = chatModel;}@GetMapping("/ai/generate")public Map generate(@RequestParam(value = "message", defaultValue = "你是谁?") String message) {return Map.of("generation", this.chatModel.call(message));}@GetMapping("/ai/generateStream")public SseEmitter streamChat(@RequestParam String message) {// 创建 SSE 发射器,设置超时时间(例如 1 分钟)SseEmitter emitter = new SseEmitter(60_000L);// 创建 Prompt 对象Prompt prompt = new Prompt(new UserMessage(message));// 订阅流式响应chatModel.stream(prompt).subscribe(response -> {try {String content = response.getResult().getOutput().getContent();System.out.print(content);// 发送 SSE 事件emitter.send(SseEmitter.event().data(content).id(String.valueOf(System.currentTimeMillis())).build());} catch (Exception e) {emitter.completeWithError(e);}},error -> { // 异常处理emitter.completeWithError(error);},() -> { // 完成处理emitter.complete();});// 处理客户端断开连接emitter.onCompletion(() -> {// 可在此处释放资源System.out.println("SSE connection completed");});emitter.onTimeout(() -> {emitter.complete();System.out.println("SSE connection timed out");});return emitter;}
}

前端核心 JS 代码如下:

$('#send-button').click(function () {const message = $('#chat-input').val();const eventSource = new EventSource(`/ai/generateStream?message=` + message);// 构建动态结果var chatMessages = $('#chat-messages');var newMessage = $('<div class="message user"></div>');newMessage.append('<img class="avatar" src="/imgs/user.png" alt="用户头像">');newMessage.append(`<span class="nickname">${message}</span>`);chatMessages.prepend(newMessage);var botMessage = $('<div class="message bot"></div>');botMessage.append('<img class="avatar" src="/imgs/robot.png" alt="助手头像">');// 流式输出eventSource.onmessage = function (event) {botMessage.append(`${event.data}`);};chatMessages.prepend(botMessage);$('#chat-input').val('');eventSource.onerror = function (err) {console.error("EventSource failed:", err);eventSource.close();};
});

以上代码中的“$”代表的是 jQuery。

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:DeepSeek、场景题、并发编程、MySQL、Redis、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、JVM、设计模式、消息队列等模块。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ftrans文件安全外发系统,为企业数据保驾护航!

随着企业的不断发展,集团分公司及各部门需向外部客户、合作伙伴及海外同事外发文件。过去,主要通过邮件、FTP方式将数据进行外发,主要存在以下问题和挑战: 1.进行文件外发时需通过OA的审批,由于OA审批与FTP传输两个环节割裂,公司无法有效限制数据外发范围和管控数据外发安…

本地部署 DeepSeek:小白也能轻松搞定!

大家好,我是晓凡。 写在前面 最近DeepSeek太火了,以至于每个小伙伴都想试试。DeepSeek 的到来可谓是开启了全民AI热潮。 本以为DeepSeek本地化部署有多难,实际上验证后很简单,操作起来就像给电脑装个新软件那么简单,大约十多分钟可完成本地部署。 今天咱们来聊聊如何在自己…

Git指南-从入门到精通

代码提交和同步命令 流程图如下:第零步: 工作区与仓库保持一致 第一步: 文件增删改,变为已修改状态 第二步: git add ,变为已暂存状态$ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ git add xx/xx.py xx/xx2.py # 添加某几个…

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019

一分钟搞定!CentOS 7.9上用Ansible自动化部署SQL Server 2019不熟悉整个流程的朋友可以先看之前的部署文章,手动部署一遍 一步步教你在CentOS 7.9上安装SQL Server 2019前言 这套Ansible脚本属于红帽官方出品,是一套mssql的自动化运维脚本,能够实现mssql的单实例部署和Alwa…

【Linux】Linux如何查看JDK的安装路径

如何在一台Linux服务器上查找JDK的安装路径呢?有那些方法可以查找定位JDK的安装路径?是否有一些局限性呢?下面总结了一下如何查找JDK安装路径的方法. 1、echo $JAVA_HOME 使用$JAVA_HOME的话能定位JDK的安装路径的前提是配置了环境变量$JAVA_HOME,否则如下所示,根本定位不…

[虚拟化/Docker] Docker Desktop 安装与使用

0 序:DeepSeek 等AI大模型在Windows的私有化部署DeepSeek 等AI大模型在Windows的私有化部署,最流行的开源AI终端应用————Dify,依赖于 Docker 环境。由此,必然离不开:Docker Desktop1 概述:Docker Desktopdocker desktop 是一款Docker容器运行管理工具,用于在本地机器…

【VUE框架】渗透测试的一些技巧(实现自动化测试)

以下文章来源于渗透测试之道 ,作者kk1230 一、什么是VUEVue.js 是一个渐进式 JavaScript 框架,用于构建用户界面。而 Webpack 是一个模块打包工具,用于将项目中的各种资源(如 JavaScript 模块、CSS 样式文件、图片等)打包成浏览器可以识别的文件。 Webpack概念机制Webpack…

案例 百万数据批量插入测试

1、jpa、mybatis-plus、jdbc、load data infile测试比较2、load data infile语法测试3、相关代码 测试package com.xm;import com.xm.entity.UserP; import com.xm.task.JdbcInsert2Task; import com.xm.task.JdbcInsertTask; import com.xm.task.MybatisPlusInsert2Task; impo…

案例 百万级数据批量插入测试

1、jpa、mybatis-plus、jdbc、load data infile测试比较2、load data infile语法测试注:测试有限,仅供参考。雨淋淋过的季节

【向量嵌入】 大型语言模型的基础

一、向量是机器的语言 向量在 LLM 和生成式 AI 的功能中起着至关重要的作用。要了解它们的重要性,就需要理解什么是向量以及它们如何在 LLM 中生成和利用。 在数学和物理学中,向量是同时具有大小和方向的对象。它可以在几何上表示为有向线段,其中线的长度表示大小,箭头指向…

DeepSeekSelfTool :流量分析、JS代码审计、进程分析

# DeepSeek;# AI ;# 代码审计;# 0day;# 渗透测试;# 通用 免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,文章作者和本公众号不承担任何法律及连带责任,望周知!!! 前言 首个由DeepSe…