SpringAI流式对话(带前端)

发布时间:2026/7/25 20:29:31

SpringAI流式对话(带前端) SpringAI流式对话(带前端)引言在现代Web应用中流式对话Streaming Chat已经成为提升用户体验的重要手段。尤其是在AI助手、实时客服等场景中用户希望看到AI“边思考边输出”而不是等待完整响应后才显示。Spring AI框架提供了对流式响应的原生支持结合前端技术可以轻松实现类似ChatGPT的打字机效果。本文将深入剖析SpringAI流式对话的原理并提供一个完整的可运行示例后端Spring Boot 前端Vue.js。## 流式对话的核心原理### 为什么需要流式响应传统的REST API请求-响应模式中客户端发送请求后服务器需要处理完整个任务才返回结果。对于大语言模型生成一段长文本可能需要数秒甚至数十秒用户在此期间只能看到加载动画体验较差。流式响应Server-Sent EventsSSE允许服务器逐步推送数据客户端可以实时渲染实现“逐字输出”的效果。### Spring AI 如何实现流式Spring AI 的ChatClient接口提供了stream()方法它返回一个FluxChatResponse基于Project Reactor的响应式流。每个ChatResponse包含一个token词元例如一个单词或一个字符。后端通过HTTP的text/event-stream格式将每个token作为事件推送。前端使用EventSource或Fetch API读取流并逐段更新DOM。### 技术栈选择-后端Spring Boot 3.2 Spring AI 1.0.0 OpenAI API或其他兼容模型-前端Vue 3 Vite使用原生EventSource接收SSE-通信格式Server-Sent Events (SSE)## 后端实现Spring AI 流式对话服务### 项目依赖xml!-- pom.xml --dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version1.0.0/version/dependencydependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId/dependency### 配置在application.properties中配置OpenAI API密钥propertiesspring.ai.openai.api-keysk-your-api-keyspring.ai.openai.chat.modelgpt-3.5-turbo### 核心控制器javaimport org.springframework.ai.chat.ChatClient;import org.springframework.ai.chat.ChatResponse;import org.springframework.ai.chat.prompt.Prompt;import org.springframework.ai.chat.prompt.SystemPromptTemplate;import org.springframework.ai.chat.prompt.UserMessage;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.*;import reactor.core.publisher.Flux;RestControllerRequestMapping(/api/chat)public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient chatClient) { this.chatClient chatClient; } /** * 流式对话端点返回text/event-stream格式 * param message 用户消息 (来自请求体) * return FluxChatResponse 流式响应 */ PostMapping(value /stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxChatResponse streamChat(RequestBody String message) { // 1. 构建系统提示可选 SystemPromptTemplate systemPrompt new SystemPromptTemplate( 你是一个友好的AI助手请用中文回答用户问题。 ); // 2. 构建用户消息 UserMessage userMessage new UserMessage(message); // 3. 创建Prompt对象 Prompt prompt new Prompt(List.of(systemPrompt.createMessage(), userMessage)); // 4. 调用stream方法获取流式响应 return chatClient.stream(prompt) .doOnNext(response - { // 可选日志记录每个token System.out.println(Token: response.getResult().getOutput().getContent()); }); }}关键点解释-produces MediaType.TEXT_EVENT_STREAM_VALUE告诉Spring返回SSE格式-chatClient.stream(prompt)返回FluxChatResponse每个ChatResponse包含一个token-doOnNext()可以对每个token进行额外处理如日志## 前端实现Vue 3 流式渲染### 搭建基础页面vuetemplate div classchat-container div classmessages refmessagesRef div v-for(msg, index) in messages :keyindex :class[message, msg.role] div classcontent v-htmlrenderMessage(msg.content)/div /div /div div classinput-area input v-modelinputText keyup.entersendMessage placeholder输入消息... :disabledisLoading/ button clicksendMessage :disabledisLoading || !inputText.trim() {{ isLoading ? 生成中... : 发送 }} /button /div /div/templatescript setupimport { ref, nextTick } from vue;const inputText ref();const messages ref([]);const isLoading ref(false);const messagesRef ref(null);// 模拟Markdown渲染简单实现function renderMessage(content) { return content .replace(/\n/g, br) .replace(/(.*?)/gs, precode$1/code/pre) .replace(/\*\*(.*?)\*\*/g, strong$1/strong);}async function sendMessage() { if (!inputText.value.trim() || isLoading.value) return; const userMessage inputText.value; inputText.value ; // 添加用户消息 messages.value.push({ role: user, content: userMessage }); // 添加一个空的AI消息占位 const aiMessageIndex messages.value.length; messages.value.push({ role: ai, content: }); isLoading.value true; try { // 使用Fetch API读取SSE流 const response await fetch(http://localhost:8080/api/chat/stream, { method: POST, headers: { Content-Type: text/plain }, body: userMessage }); const reader response.body.getReader(); const decoder new TextDecoder(); let buffer ; while (true) { const { done, value } await reader.read(); if (done) break; buffer decoder.decode(value, { stream: true }); // 解析SSE格式data: 字段 const lines buffer.split(\n); buffer lines.pop(); // 保留未完成行 for (const line of lines) { if (line.startsWith(data: )) { const data line.substring(6); try { // Spring AI 返回的ChatResponse JSON const parsed JSON.parse(data); const content parsed.result?.output?.content || ; if (content) { // 追加到AI消息末尾 messages.value[aiMessageIndex].content content; // 滚动到底部 await nextTick(); messagesRef.value?.scrollTo(0, messagesRef.value.scrollHeight); } } catch (e) { // 忽略解析错误如心跳包 } } } } } catch (error) { console.error(SSE连接失败:, error); messages.value[aiMessageIndex].content 连接失败请重试; } finally { isLoading.value false; }}/scriptstyle.chat-container { max-width: 800px; margin: 0 auto; padding: 20px; }.messages { height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; }.message { margin-bottom: 10px; padding: 8px; border-radius: 5px; }.message.user { background: #e3f2fd; text-align: right; }.message.ai { background: #f5f5f5; }.input-area { display: flex; margin-top: 10px; }.input-area input { flex: 1; padding: 8px; }/style前端关键原理1. 使用fetch的ReadableStream读取SSE数据而不是EventSource因为需要POST请求2. 逐行解析data:前缀的JSON数据3. 每次收到token立即更新Vue响应式数据messages触发DOM更新4. 通过nextTick和scrollTo实现自动滚动## 深入剖析SSE与WebSocket的对比| 特性 | SSE (Server-Sent Events) | WebSocket ||------|--------------------------|-----------|| 通信方向 | 仅服务器→客户端 | 双向 || 协议 | HTTP长连接 | 独立协议 (ws://) || 自动重连 | 原生支持 | 需手动实现 || 浏览器支持 | 所有现代浏览器 | 所有现代浏览器 || 适用场景 | 实时通知、流式数据推送 | 实时游戏、聊天应用 |对于流式对话场景SSE更轻量且Spring AI原生支持SSE格式无需额外配置。## 常见问题与优化### 1. 网络断开处理前端应实现重连逻辑可以使用retry机制或显示“重新连接”按钮。### 2. 大模型速率限制OpenAI 等API可能有速率限制后端可以添加令牌桶算法进行流控。### 3. 前端性能优化对于大量token使用requestAnimationFrame批量更新DOM避免频繁重绘。## 总结本文从原理到实践完整演示了如何使用Spring AI构建流式对话服务并配合Vue前端实现打字机效果。核心要点包括-后端利用Spring AI的FluxChatResponse和SSE格式将每个token逐步推送到客户端-前端使用ReadableStream读取流式数据逐段渲染到UI-架构SSE模式天然适合AI对话场景无需复杂的WebSocket配置通过流式响应用户无需等待完整回答体验大幅提升。你可以将此模式扩展到其他AI服务如Claude、Llama等只需更换Spring AI的模型适配器即可。希望本文能帮助你快速构建生产级的AI对话应用

相关新闻