SpringBoot快速接入DeepSeek?api(帶頁面)保姆級(jí)教程
一.前往deepseek官網(wǎng)申請(qǐng)Api key
二.java端接入
首先展示項(xiàng)目結(jié)構(gòu)
1.pom文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.chat</groupId> <artifactId>chat_demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>chat_demo</name> <description>chat_demo</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.6.13</spring-boot.version> </properties> <dependencies> <!-- 添加Thymeleaf依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.35</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.30</version> <scope>provided</scope> </dependency> <!-- HTTP客戶端 --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.9.0</version> </dependency> <dependency> <groupId>com.mashape.unirest</groupId> <artifactId>unirest-java</artifactId> <version>1.4.9</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.3.6</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> <version>4.0.2</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.3.6</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>com.chat.chat_demo.ChatDemoApplication</mainClass> <skip>true</skip> </configuration> </plugin> </plugins> </build> </project>
2.springboot配置文件 application.yaml
ai: config: deepseek: apiKey: 填寫官網(wǎng)申請(qǐng)的Key baseUrl: https://api.deepseek.com/chat/completions server: port: 8080 spring: thymeleaf: prefix: classpath:/templates/ suffix: .html
3.編寫controller接口
package com.chat.chat_demo.controller; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController @RequestMapping("deepSeek") @Slf4j public class OpenAIController { @Value("${ai.config.deepseek.apiKey}") private String API_KEY; @Value("${ai.config.deepseek.baseUrl}") private String API_URL; // 用于保存每個(gè)用戶的對(duì)話歷史 //https://api.deepseek.com/chat/completions 此接口為無狀態(tài)接口,需要上下文連貫對(duì)話需要將歷史聊天記錄一并發(fā)送至接口中 private final Map<String, List<Map<String, String>>> sessionHistory = new ConcurrentHashMap<>(); private final ExecutorService executorService = Executors.newCachedThreadPool(); private final ObjectMapper objectMapper = new ObjectMapper(); @GetMapping() public ModelAndView chat(ModelAndView modelAndView) { modelAndView.setViewName("chat"); return modelAndView; } @PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter chat( // @RequestHeader("Authorization") String token, @RequestBody String question) { // 假設(shè) token 是用戶的唯一標(biāo)識(shí) // String userId = token; // 或者從 token 中解析出用戶 ID String userId = "123"; // 或者從 token 中解析出用戶 ID SseEmitter emitter = new SseEmitter(-1L); executorService.execute(() -> { try { log.info("流式回答開始, 問題: {}", question); // 獲取當(dāng)前用戶的對(duì)話歷史 List<Map<String, String>> messages = sessionHistory.getOrDefault(userId, new ArrayList<>()); // 添加用戶的新問題到對(duì)話歷史 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", "聚城網(wǎng)絡(luò)科技有限公司的物業(yè)管理助手"); messages.add(userMessage); messages.add(systemMessage); // 調(diào)用 Deepseek API try (CloseableHttpClient client = HttpClients.createDefault()) { HttpPost request = new HttpPost(API_URL); request.setHeader("Content-Type", "application/json"); request.setHeader("Authorization", "Bearer " + API_KEY); Map<String, Object> requestMap = new HashMap<>(); requestMap.put("model", "deepseek-chat"); // requestMap.put("model", "deepseek-reasoner"); requestMap.put("messages", messages); // 包含對(duì)話歷史 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 的回復(fù) } } } // 將 AI 的回復(fù)添加到對(duì)話歷史 Map<String, String> aiMessage = new HashMap<>(); aiMessage.put("role", "assistant"); aiMessage.put("content", aiResponse.toString()); messages.add(aiMessage); // 更新會(huì)話狀態(tài) sessionHistory.put(userId, messages); log.info("流式回答結(jié)束, 問題: {}", question); emitter.complete(); } } catch (Exception e) { log.error("處理 Deepseek 請(qǐng)求時(shí)發(fā)生錯(cuò)誤", e); emitter.completeWithError(e); } } catch (Exception e) { log.error("處理 Deepseek 請(qǐng)求時(shí)發(fā)生錯(cuò)誤", e); emitter.completeWithError(e); } }); return emitter; } }
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> <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>聚城網(wǎng)絡(luò)科技有限公司 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> 發(fā)送 </button> </div> </div> <script> const questionInput = document.getElementById('questionInput'); const sendButton = document.getElementById('sendButton'); const chatMessages = document.getElementById('chatMessages'); let isBotResponding = false; // 輸入驗(yàn)證 questionInput.addEventListener('input', () => { sendButton.disabled = questionInput.value.trim().length === 0; }); // 回車發(fā)送 function handleKeyDown(e) { if (e.key === 'Enter' && !sendButton.disabled && !isBotResponding) { sendButton.click(); } } // 修改后的消息處理邏輯 let currentBotMessage = null; // 當(dāng)前正在更新的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) { // 處理最后剩余的數(shù)據(jù) if (buffer) processLine(buffer); break; } buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); // 保留未完成的行在緩沖區(qū) 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; } } // 發(fā)送邏輯 sendButton.addEventListener('click', async () => { if (isBotResponding) return; const question = questionInput.value.trim(); if (!question) return; questionInput.value = ''; sendButton.disabled = true; isBotResponding = true; // 顯示用戶消息(新消息始終出現(xiàn)在下方) displayMessage('user', question); try { // 顯示加載狀態(tài) 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); // 處理流式響應(yīng) } catch (error) { displayMessage('bot', '暫時(shí)無法處理您的請(qǐng)求,請(qǐng)稍后再試'); } finally { isBotResponding = false; questionInput.focus(); } }); // 創(chuàng)建消息元素 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; } // 創(chuàng)建輸入指示器 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; } // 更新 AI 消息 // let currentBotMessage = null; // function updateBotMessage(content) { // if (!currentBotMessage) { // currentBotMessage = displayMessage('bot', content); // } else { // currentBotMessage.textContent = content; // } // chatMessages.scrollTop = chatMessages.scrollHeight; // } </script> </body> </html>
三.測(cè)試
- 啟動(dòng)項(xiàng)目,訪問 http://localhost:8080/deepSeek 即可出現(xiàn)頁面,開始對(duì)話即可
總結(jié)
到此這篇關(guān)于SpringBoot快速接入DeepSeek api(帶頁面)的文章就介紹到這了,更多相關(guān)SpringBoot快速接入DeepSeek api內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot兩種方式接入DeepSeek的實(shí)現(xiàn)
- SpringBoot調(diào)用DeepSeek接口的實(shí)現(xiàn)
- SpringBoot或SpringAI對(duì)接DeepSeek大模型的詳細(xì)步驟
- SpringBoot接入deepseek深度求索示例代碼(jdk1.8)
- SpringBoot整合DeepSeek實(shí)現(xiàn)AI對(duì)話功能
- SpringBoot調(diào)用DeepSeek?API的完整操作指南
- springboot接入deepseek深度求索代碼示例(java版)
- springboot集成Deepseek4j的項(xiàng)目實(shí)踐
相關(guān)文章
Java中的interrupt、interrupted和isInterrupted方法區(qū)別詳解
這篇文章主要介紹了Java中的interrupt、interrupted和isInterrupted方法區(qū)別詳解,interrupt用于中斷線程,調(diào)用該方法的線程的狀態(tài)將會(huì)被設(shè)置為中斷狀態(tài),線程中斷僅僅是設(shè)置線程的中斷狀態(tài)位,并不會(huì)停止線程,需要用戶自己去監(jiān)視線程的狀態(tài)并作出處理,需要的朋友可以參考下2023-12-12JAVA中通過自定義注解進(jìn)行數(shù)據(jù)驗(yàn)證的方法
java 自定義注解驗(yàn)證可自己添加所需要的注解,下面這篇文章主要給大家介紹了關(guān)于JAVA中通過自定義注解進(jìn)行數(shù)據(jù)驗(yàn)證的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08詳解Java程序啟動(dòng)時(shí)-D指定參數(shù)是什么
java服務(wù)啟動(dòng)的時(shí)候,都會(huì)指定一些參數(shù),下面這篇文章主要給大家介紹了關(guān)于Java程序啟動(dòng)時(shí)-D指定參數(shù)是什么的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12聊聊@RequestParam,@PathParam,@PathVariable等注解的區(qū)別
這篇文章主要介紹了聊聊@RequestParam,@PathParam,@PathVariable等注解的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02Mybatis-plus出現(xiàn)數(shù)據(jù)庫id很大或者為負(fù)數(shù)的解決
本文主要介紹了Mybatis-plus出現(xiàn)數(shù)據(jù)庫id很大或者為負(fù)數(shù)的解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02java對(duì)象轉(zhuǎn)成byte數(shù)組的3種方法
這篇文章主要為大家詳細(xì)介紹了java對(duì)象轉(zhuǎn)成byte數(shù)組的3種方法,具有一定的參考價(jià)值,感興趣的朋友可以參考一下2018-06-06JAVA實(shí)現(xiàn)長(zhǎng)連接(含心跳檢測(cè)Demo)
這篇文章主要介紹了JAVA實(shí)現(xiàn)長(zhǎng)連接(含心跳檢測(cè)Demo),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10