Spring AI+DeepSeek快速构建AI智能机器人

article/2025/8/15 1:23:35

引言

在AI技术蓬勃发展的当下,Spring生态推出了Spring AI项目,为Java开发者提供了便捷的AI集成方案。本文将演示如何用Spring AI+DeepSeek V3 快速搭建一个具备自然语言处理能力的智能对话机器人。

一、环境准备

JDK 17+

Maven/Gradle构建工具

DeepSeek API Key

由于各种原因官网的开放平台目前关闭了,我这里使用的是硅基流动+华为云推出的DeepSeek-V3/R1服务。

Spring Boot 3.2+

关注公众号 每天懂点AI ,回复 机器人 获取源码。

二、项目创建

快速创建Spring AI项目可以参考:使用Spring Boot&Spring AI快速构建AI应用程序
本项目使用的环境是:本文使用的开发工具IDEA+Spring Boot 3.4+Spring AI 1.0.0-SNAPSHOT+Maven+硅基流动提供的DeepSeek API服务,接口需要付费,默认注册送14块钱,官网。注册成功后创建密钥,如下图所示:
在这里插入图片描述
application.properties添加如下配置:


spring.ai.openai.base-url=https://api.siliconflow.cn/
spring.ai.openai.api-key=你自己的密钥
spring.ai.openai.chat.options.model=deepseek-ai/DeepSeek-V3
logging.level.org.springframework.ai.chat.client.advisor=DEBUG

pom.xml添加openai starter,因为接口是兼容openai api规范的。

		<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency>

三、核心实现

1.人设设定

新建Config配置类,设置智能体的人设。

@Configuration
class Config {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人").build();}
}

2.流式对话

新建ChatbotController类,因为需要流式传输,后端需要支持流式响应,前端要能逐步接收并显示数据。可能需要使用Server-Sent Events(SSE)或者WebSocket。不过考虑到简单性,SSE可能更容易在Spring中实现,因为它是基于HTTP的,不需要额外的协议。

@RestController
@CrossOrigin("*")
@Slf4j
public class ChatbotController {private final ChatClient chatClient;public ChatbotController(ChatClient chatClient {this.chatClient = chatClient;}@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<ServerSentEvent<String>> streamChat(@RequestBody ChatRequest request) {//用户idString userId = request.userId();return chatClient.prompt(request.message()).stream().content().map(content -> ServerSentEvent.builder(content).event("message").build())//问题回答结速标识,以便前端消息展示处理.concatWithValues(ServerSentEvent.builder("[DONE]").build()).onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));}record ChatRequest(String userId, String message) {}
}

主要的技术细节:

  1. 使用标准 ServerSentEvent 构建响应
  2. 增加结束标识 [DONE] 事件
  3. 支持事件类型区分(message/error

接口测试:

 curl -X POST -H "Content-Type: application/json" -d '{"userId":"testuserid", "message":"你好"}' http://localhost:8080/chat/stream 
event:message
data:你好
event:message
data:!
event:message
data:我是
event:message
data:Spring
event:message
data: AI
event:message
data:智能
event:message
data:机器人
event:message
data:,
event:message
data:很高兴
event:message
data:在这里
event:message
data:与你
event:message
data:交流
event:message
data:。
event:message
data:有什么
event:message
data:可以帮助
event:message
data:你的
event:message
data:吗
event:message
data:?
data:[DONE]

3.前端实现

前端技术栈 Vite+Vue3+TS

Vite 是一个现代化的前端构建工具,支持 Vue 3TypeScript

运行以下命令创建一个新项目:

npm create vite@latest

按照提示操作:

输入项目名称(chatbot)。

选择框架:Vue

选择语言:TypeScript
添加依赖

{"@microsoft/fetch-event-source": "^2.0.1"}

核心代码,完整代码地址:

<script setup lang="ts">
import {ref, reactive, onMounted, onBeforeUnmount, nextTick} from 'vue'
import {fetchEventSource} from '@microsoft/fetch-event-source'// 生成随机用户ID(示例:8位字母数字组合)
const generateUserId = () => {return Math.random().toString(36).substr(2, 8);
};// 持久化存储用户ID
const userId = ref('');enum MessageStatus {Streaming = 'streaming',Complete = 'complete',Error = 'error',
}interface Message {id: stringcontent: stringisBot: booleanstatus?: MessageStatustimestamp: numberretry?: () => Promise<void>
}const messages = ref<Message[]>([])
const inputMessage = ref('')
const isLoading = ref(false)
const controller = ref<AbortController>()
const messageContainer = ref<HTMLElement>()
const inputRef = ref<HTMLInputElement>()// 自动滚动控制
let autoScroll = true
let lastCharType: 'chinese' | 'english' | 'other' = 'other'const scrollToBottom = () => {nextTick(() => {if (messageContainer.value && autoScroll) {messageContainer.value.scrollTop = messageContainer.value.scrollHeight}})
}const handleScroll = () => {if (!messageContainer.value) returnconst {scrollTop, scrollHeight, clientHeight} = messageContainer.valueautoScroll = scrollHeight - (scrollTop + clientHeight) < 50
}// 字符类型检测
const getCharType = (char: string): 'chinese' | 'english' | 'other' => {if (/[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]/.test(char)) {return 'chinese'}if (/[a-zA-Z]/.test(char)) {return 'english'}return 'other'
}// 智能空格处理核心逻辑
const processContent = (prev: string, newData: string): string => {if (prev.length === 0) return newDataconst lastChar = prev.slice(-1)const newFirstChar = newData[0] || ''const prevType = getCharType(lastChar)const newType = getCharType(newFirstChar)let processed = newData// 需要添加空格的情况const shouldAddSpace =(prevType === 'english' && newType === 'english') || // 英文接英文(prevType === 'chinese' && newType === 'english') || // 中文接英文(prevType === 'english' && newType === 'chinese' && !/[!?,.]$/.test(lastChar)) // 英文接中文(非标点结尾)// 需要删除空格的情况const shouldRemoveSpace =(prevType === 'chinese' && newType === 'chinese') || // 中文接中文(prevType === 'other' && /^[\u4e00-\u9fa5]/.test(newData)) // 特殊符号接中文if (shouldAddSpace && !lastChar.match(/\s/) && !newFirstChar.match(/\s/)) {processed = ' ' + processed} else if (shouldRemoveSpace) {processed = processed.replace(/^\s+/, '')}return processed
}const sendChatRequest = async (content: string, botMessage: Message) => {controller.value = new AbortController()await fetchEventSource('http://localhost:8080/chat/stream', {method: 'POST',headers: {'Content-Type': 'application/json','Accept': 'text/event-stream','X-Content-Lang': 'zh-CN'},body: JSON.stringify({message: content,userId:userId.value}),signal: controller.value?.signal,openWhenHidden: true,onopen: async response => {if (!response.ok) throw new Error(`HTTP error ${response.status}`)},onmessage: event => {if (event.data === '[DONE]') {botMessage.status = MessageStatus.Completereturn}const processedData = processContent(botMessage.content, event.data)botMessage.content += processedDatabotMessage.timestamp = Date.now()// 更新最后字符类型const lastChar = processedData.slice(-1)lastCharType = getCharType(lastChar)scrollToBottom()},onerror: err => {throw err}})
}// 错误处理
const handleRequestError = (botMessage: Message, error: unknown) => {const errorMessage = error instanceof Error? navigator.onLine? error.message: '网络连接不可用': '请求发生未知错误'botMessage.status = MessageStatus.ErrorbotMessage.content = errorMessagebotMessage.retry = createRetryHandler(botMessage.content)
}// 主发送逻辑
const sendMessage = async () => {if (!inputMessage.value.trim() || isLoading.value) returnconst userContent = inputMessage.value.trim()inputMessage.value = ''// 创建用户消息const userMessage = reactive<Message>({id: `user-${Date.now()}`,content: userContent,isBot: false,timestamp: Date.now()})messages.value.push(userMessage)// 创建机器人消息const botMessage = reactive<Message>({id: `bot-${Date.now()}`,content: '',isBot: true,status: MessageStatus.Streaming,timestamp: Date.now()})messages.value.push(botMessage)isLoading.value = truetry {await sendChatRequest(userContent, botMessage)} catch (err) {handleRequestError(botMessage, err)} finally {isLoading.value = falsenextTick(() => inputRef.value?.focus())}
}// 停止生成
const stopGeneration = () => {controller.value?.abort()isLoading.value = false
}// 生命周期
onMounted(() => {userId.value = localStorage.getItem('chatUserId') || generateUserId();localStorage.setItem('chatUserId', userId.value);messageContainer.value?.addEventListener('scroll', handleScroll)inputRef.value?.focus()
})onBeforeUnmount(() => {messageContainer.value?.removeEventListener('scroll', handleScroll)controller.value?.abort()
})
</script>

前端主要实现打字效果,然后针对中英文空格问题渲染问题。实现效果如下:
在这里插入图片描述

4.对话记忆(多轮对话)

到目前为止对话实现,其实存在一个大问题,用户问问题每次都是新的一次对话,无法做到多轮次,就是常说的对话记忆,如下图所示问题所在:
在这里插入图片描述
如上图所示,大模型两次回复是独立的,没有形成对话记忆,要实现这个功能,Spring AI提供了Advisors API,MessageChatMemoryAdvisor主要实现对话记忆,本文基于内存的方式,首先Config类新增内存记忆的Bean

@Configuration
class Config {@BeanChatClient chatClient(ChatClient.Builder builder) {return builder.defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人").build();}@BeanInMemoryChatMemory inMemoryChatMemory() {return new InMemoryChatMemory();}}

对话接口修改如下:

 @PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<ServerSentEvent<String>> streamChat(@RequestBody ChatRequest request) {//用户idString userId = request.userId();return chatClient.prompt(request.message()).advisors(new MessageChatMemoryAdvisor(inMemoryChatMemory, userId, 10), new SimpleLoggerAdvisor()).stream().content().map(content -> ServerSentEvent.builder(content).event("message").build())//问题回答结速标识,以便前端消息展示处理.concatWithValues(ServerSentEvent.builder("[DONE]").build()).onErrorResume(e -> Flux.just(ServerSentEvent.builder("Error: " + e.getMessage()).event("error").build()));}

核心代码分析:

new MessageChatMemoryAdvisor(inMemoryChatMemory, userId, 10)

对话添加一个上下文记忆增强,每个用户数据是隔离的,10表示历史对话数据最多取10条,每次向大模型发送消息,实际上会把用户前面的问题一起组装到Prompt中。
修改后实现的支持对话记忆的功能:
在这里插入图片描述
这样就实现了上下文记忆。

四、总结

本文详细介绍了如何使用 Spring AI 快速搭建一个具备自然语言处理能力的智能对话机器人,并结合 Vue 3 + TypeScript 实现前后端交互。

关注公众号 每天懂点AI ,回复 机器人 获取源码。
在这里插入图片描述


http://www.hkcw.cn/article/PItmHvTnTx.shtml

相关文章

【大模型科普】大模型:人工智能的前沿(一文读懂大模型)

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈人工智能与大模型应用 ⌋ ⌋ ⌋ 人工智能&#xff08;AI&#xff09;通过算法模拟人类智能&#xff0c;利用机器学习、深度学习等技术驱动医疗、金融等领域的智能化。大模型是千亿参数的深度神经网络&#xff08;如ChatGPT&…

借用AI工具(cursor/vscode) 调试matlab代码(2025.4最新实测)

本文实测环境&#xff1a;MATLAB 2025a Windows 11 本文亮点&#xff1a;无需重写Python&#xff01;用AI直接优化现有MATLAB工程 一、AI调试MATLAB的紧迫性 因为matlab无法内置ai 工具 &#xff0c;别人都有的不能out了 另外说一声matlba2025a已经很改版很多了&#xff0c;与…

5 分钟用满血 DeepSeek R1 搭建个人 AI 知识库(含本地部署)

最近很多朋友都在问:怎么本地部署 DeepSeek 搭建个人知识库。 老实说,如果你不是为了研究技术,或者确实需要保护涉密数据,我真不建议去折腾本地部署。 为什么呢? 目前 Ollama 从 1.5B 到 70B 都只是把 R1 的推理能力提炼到 Qwen 和 Llama 的蒸馏版本上。 虽说性能是提升…

灰狼优化算法(GWO)(含ai创作)

GWO简介 灰狼优化算法&#xff08;Grey Wolf Optimizer&#xff0c;GWO&#xff09;是一种模仿灰狼狩猎行为的群体智能优化算法&#xff0c;由Seyedali Mirjalili等人在2014年提出。这种算法主要模拟了灰狼的社会等级结构和狩猎策略&#xff0c;用于解决各种优化问题。 在灰狼…

5步掌握MCP HTTP模式部署:从零开始搭建你的AI“邮局”!

&#x1f525;关注墨瑾轩&#xff0c;带你探索编程的奥秘&#xff01;&#x1f680; &#x1f525;超萌技术攻略&#xff0c;轻松晋级编程高手&#x1f680; &#x1f525;技术宝库已备好&#xff0c;就等你来挖掘&#x1f680; &#x1f525;订阅墨瑾轩&#xff0c;智趣学习不…

基于DeepSeek-Coder,实现Microi吾码低代码平台AI代码辅助生成的思路设想【辅助代码生成】

文章目录 引言一、整体架构设计二、实现流程与关键技术1. 构建领域数据集2. 模型训练与微调3. 生成代码的安全控制4. 平台集成与交互设计 三、效果优化与评估1. 效果展示2. 评估指标 四、未来优化方向结语 引言 低代码开发平台&#xff08;Microi吾码&#xff09;通过可视化交…

【DeepSeek+即梦AI:零基础生成专业级AI图片全流程指南(2025实战版)】

第一部分:工具认知篇——认识你的数字画笔 1.1 工具定位与核心价值 (讲师开场白)各位同学好,今天我们要解锁的是AI创作领域最具生产力的组合工具——DeepSeek+即梦AI。就像画家需要画笔与颜料,这对组合就是你的数字创作套装: • DeepSeek:国内顶尖的智能提示词工程师 …

【Ai学习】利用扣子(Coze)简单搭建图像生成工作流(小白初学版)

开始之前我们先了解一下我们准备使用的工具。 什么是扣子&#xff08;Coze&#xff09;&#xff1f; 官网链接&#xff1a;扣子 扣子&#xff08;Coze&#xff09;是一个开源的AI工具开发平台&#xff0c;提供了丰富的API和简单易用的界面&#xff0c;帮助用户快速搭建各种A…

AIGC时代——语义化AI驱动器:提示词的未来图景与技术深潜

文章目录 一、技术范式重构&#xff1a;从指令集到语义认知网络1.1 多模态语义解析器的进化路径1.2 提示词工程的认知分层 二、交互革命&#xff1a;从提示词到意图理解2.1 自然语言交互的认知进化2.2 专业领域的认知增强 三、未来技术图谱&#xff1a;2025-2030演进路线3.1 20…

【AI 大模型】LlamaIndex 大模型开发框架 ② ( LlamaIndex 可配置的 LLM 类型 | LlamaIndex 可配置的 文本向量模型 类型 )

文章目录 一、LlamaIndex 可配置的 LLM 类型1、云端 API 类型 LLM2、本地部署 类型 LLM3、混合部署 LLM4、错误示例 - 设置 云端 DeepSeek 大模型 二、LlamaIndex 可配置的 文本向量模型 类型1、云端 文本向量模型2、本地部署 文本向量模型3、适配器微调模型 AdapterEmbeddingM…

云端微光,AI启航:低代码开发的智造未来

文章目录 前言一、引言&#xff1a;技术浪潮中的个人视角初次体验腾讯云开发 Copilot1.1 低代码的时代机遇1.1.1 为什么低代码如此重要&#xff1f; 1.2 AI 的引入&#xff1a;革新的力量1.1.2 Copilot 的亮点 1.3 初学者的视角1.3.1 Copilot 带来的改变 二、体验记录&#xff…

OCR+AI双引擎驱动:手把手教学构建智能财报分析系统

在金融行业中&#xff0c;财报分析是帮助企业和投资者做出决策的关键环节。随着科技的快速发展&#xff0c;自动化、智能化的财报分析变得越来越重要。传统的人工财报分析不仅费时费力&#xff0c;而且容易受到人为错误的影响&#xff0c;因此企业急需借助先进的技术来提高效率…

秒杀系统—4.第二版升级优化的技术文档二

大纲 7.秒杀系统的秒杀活动服务实现 (1)数据库表设计 (2)秒杀活动状态机 (3)添加秒杀活动 (4)删除秒杀活动 (5)修改秒杀活动 (6)后台查询秒杀活动 (7)前台查询秒杀活动 (8)查询秒杀商品的销售进度 (9)秒杀活动添加秒杀商品 (10)秒杀活动删除秒杀商品 (11)触发渲染秒…

“苏超”10元门票被炒到500元 散装江苏的足球狂热

端午假期,当全球球迷的目光聚焦在欧冠决赛时,江苏人正为自己的“苏超”联赛沸腾。尽管没有大牌外援和全网转播,但场均上座率接近万人,比赛门票一票难求,这场江苏省内的业余联赛迅速走红。“苏超”有多火?10元的门票被炒到500元,上座率甚至超过了一些职业联赛。实际上,“…

【Leetcode】vector刷题

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;Leetcode刷题 目录 1.只出现一次的数字2.杨辉三角3.删除有序数组中的重复项4.只出现一次的数字II5.只出现一次的数字III6.电话号码的字母组合 1.只出现一次的数字 题目链接&#xff1a;136.只出现一…

深入解析yolov5,为什么算法都是基于yolov5做改进的?(一)

YOLOv5简介 YOLOv5是一种单阶段目标检测算法&#xff0c;它在YOLOv4的基础上引入了多项改进&#xff0c;显著提升了检测的速度和精度。YOLOv5的设计哲学是简洁高效&#xff0c;它有四个版本&#xff1a;YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x&#xff0c;分别对应不同的模型大小…

【数据结构】手撕AVL树(万字详解)

目录 AVL树的概念为啥要有AVL树&#xff1f;概念 AVL树节点的定义AVL树的插入AVL树的旋转左单旋右单旋左右双旋右左双旋 AVL树的查找AVL树的验证end AVL树的概念 为啥要有AVL树&#xff1f; 在上一章节的二叉搜索树中&#xff0c;我们在插入节点的操作中。有可能一直往一边插…

2024年信息素养大赛 C++小学组初赛 算法创意实践挑战赛 真题详细解析

2024年信息素养大赛初赛C真题解析 选择题&#xff08;共15题&#xff0c;每题5分&#xff0c;共75分&#xff09; 1、运行下列程序段&#xff0c;输出的结果是( ) int n572765; cout <<n/10%10; A、5 B、6 C、4 D、1 答案&#xff1a;B 考点分析&#xff1a;考察…

GPIO子系统层次与数据结构详解

往期内容 本专栏往期内容&#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析&#xff1a;imx_pinctrl_soc_info结构体Pinctrl子系统中c…

深度解析算法之模拟

39.替换所有的问号 题目链接 给你一个仅包含小写英文字母和 ? 字符的字符串 s&#xff0c;请你将所有的 ? 转换为若干小写字母&#xff0c;使最终的字符串不包含任何 连续重复 的字符。 注意&#xff1a;你 不能 修改非 ? 字符。 题目测试用例保证 除 ? 字符 之外&#…