文章目录
引言
在当今快速发展的数据驱动时代,企业越来越重视数据的价值。为了更好地理解和利用数据,许多公司开始采用先进的数据分析和搜索技术。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配置
bash
ds:
key: 填写在官网申请的key
url: https://api.deepseek.com/chat/completions
3.2 编写service
bash
/**
* 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);
}
bash
/**
* 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
*/
@Override
public 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 API
try (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
bash
/**
* DsController
* @author senfel
* @version 1.0
* @date 2025/3/13 17:21
*/
@RestController
@RequestMapping("/deepSeek")
@Slf4j
public class DsController {
@Resource
private 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
bash
<!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的强大功能。