在Spring Boot项目中接入DeepSeek深度求索,感觉笨笨的呢

embedded/2025/3/18 10:29:33/

文章目录

    • 引言
    • 1. 什么是DeepSeek?
    • 2. 准备工作
      • 2.1 注册DeepSeek账号
    • 3.实战演示
      • 3.1 application增加DS配置
      • 3.2 编写service
      • 3.3 编写controller
      • 3.4 编写前端界面chat.html
      • 3.5 测试
    • 总结

引言

在当今快速发展的数据驱动时代,企业越来越重视数据的价值。为了更好地理解和利用数据,许多公司开始采用先进的数据分析和搜索技术。DeepSeek(深度求索)就是一款强大的深度学习驱动的搜索和推荐系统,可以帮助企业高效地处理和分析大规模数据。本文将详细介绍如何在Spring Boot项目中接入DeepSeek,帮助各位大大快速上手并利用其强大的功能。
在这里插入图片描述

1. 什么是DeepSeek?

DeepSeek 是一款基于深度学习的搜索和推荐系统,能够帮助企业从海量数据中快速提取有价值的信息。它结合了自然语言处理(NLP)、机器学习和深度学习技术,提供精准的搜索结果和个性化推荐。DeepSeek的主要特点包括:
精准搜索:通过深度学习算法,DeepSeek能够理解用户查询的意图,提供更精准的搜索结果。
个性化推荐:基于用户行为和偏好,DeepSeek能够为用户提供个性化的推荐内容。
高效处理:支持大规模数据处理,能够快速响应用户请求。
易于集成:提供丰富的API接口,方便与其他系统集成。

2. 准备工作

在开始接入DeepSeek之前,需要完成以下准备工作:

2.1 注册DeepSeek账号

首先,访问DeepSeek的API开放平台( https://platform.deepseek.com/sign_in),注册一个账号。注册完成后,登录账号并创建一个新的项目,获取项目ID和API密钥。
在这里插入图片描述

注意:生成key后需要充值后才能正常调用其API。

3.实战演示

3.1 application增加DS配置

ds:key: 填写在官网申请的keyurl: https://api.deepseek.com/chat/completions

3.2 编写service

/*** DsChatService* @author senfel* @version 1.0* @date 2025/3/13 17:30*/
public interface DsChatService {/*** chat* @param userId* @param question* @author senfel* @date 2025/3/13 17:30* @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter*/SseEmitter chat(String userId,String question);
}
/*** DsChatServiceImpl* @author senfel* @version 1.0* @date 2025/3/13 17:31*/
@Service
@Slf4j
public class DsChatServiceImpl implements DsChatService {@Value("${ds.key}")private String dsKey;@Value("${ds.url}")private String dsUrl;// 用于保存每个用户的对话历史private final Map<String, List<Map<String, String>>> sessionHistory = new ConcurrentHashMap<>();private final ExecutorService executorService = Executors.newCachedThreadPool();private final ObjectMapper objectMapper = new ObjectMapper();/*** chat* @param userId* @param question* @author senfel* @date 2025/3/13 17:36* @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter*/@Overridepublic SseEmitter chat(String userId,String question) {SseEmitter emitter = new SseEmitter(-1L);executorService.execute(() -> {try {log.info("流式回答开始, 问题: {}", question);// 获取当前用户的对话历史List<Map<String, String>> messages = sessionHistory.getOrDefault(userId, new ArrayList<>());// 添加用户的新问题到对话历史Map<String, String> userMessage = new HashMap<>();userMessage.put("role", "user");userMessage.put("content", question);Map<String, String> systemMessage = new HashMap<>();systemMessage.put("role", "system");systemMessage.put("content", "senfel的AI助手");messages.add(userMessage);messages.add(systemMessage);// 调用 DeepSeek APItry (CloseableHttpClient client = HttpClients.createDefault()) {HttpPost request = new HttpPost(dsUrl);request.setHeader("Content-Type", "application/json");request.setHeader("Authorization", "Bearer " + dsKey);Map<String, Object> requestMap = new HashMap<>();requestMap.put("model", "deepseek-chat");requestMap.put("messages", messages);requestMap.put("stream", true);String requestBody = objectMapper.writeValueAsString(requestMap);request.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8));try (CloseableHttpResponse response = client.execute(request);BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8))) {StringBuilder aiResponse = new StringBuilder();String line;while ((line = reader.readLine()) != null) {if (line.startsWith("data: ")) {System.err.println(line);String jsonData = line.substring(6);if ("[DONE]".equals(jsonData)) {break;}JsonNode node = objectMapper.readTree(jsonData);String content = node.path("choices").path(0).path("delta").path("content").asText("");if (!content.isEmpty()) {emitter.send(content);aiResponse.append(content); // 收集 AI 的回复}}}// 将 AI 的回复添加到对话历史Map<String, String> aiMessage = new HashMap<>();aiMessage.put("role", "assistant");aiMessage.put("content", aiResponse.toString());messages.add(aiMessage);// 更新会话状态sessionHistory.put(userId, messages);log.info("流式回答结束, 问题: {}", question);emitter.complete();}} catch (Exception e) {log.error("处理 DeepSeek 请求时发生错误", e);emitter.completeWithError(e);}} catch (Exception e) {log.error("处理 DeepSeek 请求时发生错误", e);emitter.completeWithError(e);}});return emitter;}}

3.3 编写controller

/*** DsController* @author senfel* @version 1.0* @date 2025/3/13 17:21*/
@RestController
@RequestMapping("/deepSeek")
@Slf4j
public class DsController {@Resourceprivate DsChatService dsChatService;/*** chat page* @param modelAndView* @author senfel* @date 2025/3/13 17:39* @return org.springframework.web.servlet.ModelAndView*/@GetMapping()public ModelAndView chat(ModelAndView modelAndView) {modelAndView.setViewName("chat");return modelAndView;}/*** chat* @param question* @author senfel* @date 2025/3/13 17:39* @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter*/@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter chat(@RequestBody String question) {//TODO 默认用户ID,实际场景从token获取String userId = "senfel";return dsChatService.chat(userId, question);}
}

3.4 编写前端界面chat.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DeepSeek Chat</title><script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script><style>:root {--primary-color: #5b8cff;--user-bg: linear-gradient(135deg, #5b8cff 0%, #3d6ef7 100%);--bot-bg: linear-gradient(135deg, #f0f8ff 0%, #e6f3ff 100%);--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);}body {font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;margin: 0;padding: 20px;display: flex;justify-content: center;min-height: 100vh;background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);}.chat-container {width: 100%;max-width: 800px;height: 90vh;background: rgba(255, 255, 255, 0.95);border-radius: 20px;box-shadow: var(--shadow);backdrop-filter: blur(10px);display: flex;flex-direction: column;overflow: hidden;}.chat-header {padding: 24px;background: var(--primary-color);color: white;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);}.chat-header h1 {margin: 0;font-size: 1.8rem;font-weight: 600;letter-spacing: -0.5px;}.chat-messages {flex: 1;padding: 20px;overflow-y: auto;display: flex;flex-direction: column;gap: 12px;}.chat-message {max-width: 75%;padding: 16px 20px;border-radius: 20px;line-height: 1.5;animation: messageAppear 0.3s ease-out;position: relative;word-break: break-word;}.chat-message.user {background: var(--user-bg);color: white;align-self: flex-end;border-bottom-right-radius: 4px;box-shadow: var(--shadow);}.chat-message.bot {background: var(--bot-bg);color: #2d3748;align-self: flex-start;border-bottom-left-radius: 4px;box-shadow: var(--shadow);}.chat-input {padding: 20px;background: rgba(255, 255, 255, 0.9);border-top: 1px solid rgba(0, 0, 0, 0.05);display: flex;gap: 12px;}.chat-input input {flex: 1;padding: 14px 20px;border: 2px solid rgba(0, 0, 0, 0.1);border-radius: 16px;font-size: 1rem;transition: all 0.2s ease;background: rgba(255, 255, 255, 0.8);}.chat-input input:focus {outline: none;border-color: var(--primary-color);box-shadow: 0 0 0 3px rgba(91, 140, 255, 0.2);}.chat-input button {padding: 12px 24px;border: none;border-radius: 16px;background: var(--primary-color);color: white;font-size: 1rem;font-weight: 500;cursor: pointer;transition: all 0.2s ease;display: flex;align-items: center;gap: 8px;}.chat-input button:hover {background: #406cff;transform: translateY(-1px);}.chat-input button:disabled {background: #c2d1ff;cursor: not-allowed;transform: none;}.chat-input button svg {width: 18px;height: 18px;fill: currentColor;}@keyframes messageAppear {from {opacity: 0;transform: translateY(10px);}to {opacity: 1;transform: translateY(0);}}.typing-indicator {display: inline-flex;gap: 6px;padding: 12px 20px;background: var(--bot-bg);border-radius: 20px;align-self: flex-start;}.typing-dot {width: 8px;height: 8px;background: rgba(0, 0, 0, 0.3);border-radius: 50%;animation: typing 1.4s infinite ease-in-out;}.typing-dot:nth-child(2) {animation-delay: 0.2s;}.typing-dot:nth-child(3) {animation-delay: 0.4s;}@keyframes typing {0%,100% {transform: translateY(0);}50% {transform: translateY(-4px);}}@media (max-width: 640px) {body {padding: 10px;}.chat-container {height: 95vh;border-radius: 16px;}.chat-message {max-width: 85%;}}</style>
</head>
<body>
<div class="chat-container"><div class="chat-header"><h1>DeepSeek Chat</h1></div><div class="chat-messages" id="chatMessages"></div><div class="chat-input"><input type="text" id="questionInput" placeholder="输入消息..." onkeydown="handleKeyDown(event)"><button id="sendButton" disabled><svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" /></svg>发送</button></div>
</div>
<script>const questionInput = document.getElementById('questionInput');const sendButton = document.getElementById('sendButton');const chatMessages = document.getElementById('chatMessages');let isBotResponding = false;// 输入验证questionInput.addEventListener('input', () => {sendButton.disabled = questionInput.value.trim().length === 0;});// 回车发送function handleKeyDown(e) {if (e.key === 'Enter' && !sendButton.disabled && !isBotResponding) {sendButton.click();}}// 修改后的消息处理逻辑let currentBotMessage = null; // 当前正在更新的AI消息async function handleBotResponse(response) {const reader = response.body.getReader();const decoder = new TextDecoder();let buffer = '';currentBotMessage = displayMessage('bot', '');try {while (true) {const { done, value } = await reader.read();if (done) {// 处理最后剩余的数据if (buffer) processLine(buffer);break;}buffer += decoder.decode(value, { stream: true });const lines = buffer.split('\n');// 保留未完成的行在缓冲区buffer = lines.pop() || '';lines.forEach(line => {if (line.startsWith('data:')) {const data = line.replace(/^data:\s*/g, '').trim();if (data === '[DONE]') return;currentBotMessage.textContent += data;}});chatMessages.scrollTop = chatMessages.scrollHeight;}} finally {currentBotMessage = null;}}// 发送逻辑sendButton.addEventListener('click', async () => {if (isBotResponding) return;const question = questionInput.value.trim();if (!question) return;questionInput.value = '';sendButton.disabled = true;isBotResponding = true;// 显示用户消息(新消息始终出现在下方)displayMessage('user', question);try {// 显示加载状态const typingIndicator = createTypingIndicator();const response = await fetch('/deepSeek/chat', {method: 'POST',headers: {'Content-Type': 'application/json','Accept': 'text/event-stream'},body: JSON.stringify({ question }),});typingIndicator.remove();await handleBotResponse(response); // 处理流式响应} catch (error) {displayMessage('bot', '暂时无法处理您的请求,请稍后再试');} finally {isBotResponding = false;questionInput.focus();}});// 创建消息元素function displayMessage(sender, content) {const messageDiv = document.createElement('div');messageDiv.className = `chat-message ${sender}`;messageDiv.textContent = content;chatMessages.appendChild(messageDiv);chatMessages.scrollTop = chatMessages.scrollHeight;return messageDiv;}// 创建输入指示器function createTypingIndicator() {const container = document.createElement('div');container.className = 'typing-indicator';container.innerHTML = `<div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div>`;chatMessages.appendChild(container);chatMessages.scrollTop = chatMessages.scrollHeight;return container;}
</script>
</body>
</html>

3.5 测试

  • 启动项目,访问 http://localhost:9999/deepSeek 即可出现页面,开始对话即可
    在这里插入图片描述

总结

通过本文,我们详细介绍了如何在Spring Boot项目中接入DeepSeek深度求索。从准备工作到实现搜索和推荐功能,再到处理异常,我们一步一步地完成了整个接入过程。希望本文能够帮助您快速上手并充分利用DeepSeek的强大功能。


http://www.ppmy.cn/embedded/173569.html

相关文章

K8S快速部署

前置虚拟机环境正式部署BUG解决 前置虚拟机环境 每个虚拟机配置一次就好 #关闭防火墙 systemctl stop firewalld systemctl disable firewalld #关闭 selinux sed -i s/enforcing/disabled/ /etc/selinux/config # 永久 setenforce 0 # 临时 #关闭 swap swapoff -a # 临时 vi…

08-单链表-单链表基本操作2

题目 来源 18. 链表的基本操作 思路 与上一份的最大区别就是要先判断一下要处理的k是否是合法的&#xff0c;也就是要先将指针能够指向k&#xff1b; 上一份的idx是一个全局的指针&#xff0c;由于链表天生就是物理位置不用连续&#xff0c;所以idx可以在任意位置&#xff…

使用OpenCV和MediaPipe库——抽烟检测(姿态监控)

目录 抽烟检测的运用 1. 安全监控 (1) 公共场所禁烟监管 (2) 工业安全 2. 智能城市与执法 (1) 城市违章吸烟检测 (2) 无人值守管理 3. 健康管理与医疗 (1) 吸烟习惯分析 (2) 远程监护 4. AI 监控与商业分析 (1) 保险行业 (2) 商场营销 5. 技术实现 (1) 计算机视…

微服务即时通信系统---(八)用户管理子服务

本章节,主要对项目中用户管理子服务模块进行分析、开发与测试。 功能设计 用户管理子服务,主要用于管理用户的数据,以及关于用户信息的各项操作,因此,在本模块中,用户管理子服务需要提供以下的功能性接口 用户注册用户输入 用户名(昵称) + 用户密码 进行注册。用户登录…

docker学习

基本结构 镜像(image)&#xff1a; docker镜像可以当作一个模板&#xff0c;通过这个模板可以创建多个容器。 例如一个tomcat镜像>运行>容器(提供服务) 容器(container)&#xff1a; docker利用容器技术&#xff0c;可以独立运行一个或一组应用(容器间相互隔离) docker…

C语言:编程设计猜数游戏

先由计算机想一个数给用户猜&#xff0c;如果猜对了&#xff0c;提示“right&#xff01;”&#xff0c;猜错了&#xff0c;提示“wrong&#xff01;及大小” 思路&#xff1a;用随机函数rand&#xff08;&#xff09;取到计算机想的数 代码&#xff1a; #include <stdio.…

JVM、MySQL常见面试题(尽力局)

JVM篇 一.谈一谈JDK、JRE、JVM分别是什么&#xff0c;有什么联系&#xff1f; 1.JDK是Java工具包&#xff0c;里面包含了JRE、Javac编译器等。 2.JRE是java运行环境&#xff0c;里面包含了JVM、JavaSE标准库类等。 3.JVM是Java虚拟机&#xff0c;运行编译后的.class的文件&am…

C/C++中应用程序调用其他dll模块,想要使用vs调试这个dll里的代码,附加进程的方式无法命中断点,但通过调试启动的方式却可以,是什么原因?

文章目录 1. 符号文件&#xff08;PDB&#xff09;未正确加载2. DLL 版本与调试代码不一致3. DLL 加载时机错过调试器附加4. 调试器类型不匹配5. 编译优化导致断点偏移6. 断点位置未被执行7. 配置属性设置错误快速诊断流程&#xff1a;终极方案&#xff1a;直接调试启动 在 Vis…