JAVA集成本地部署的DeepSeek的圖文教程
一、下載部署DeepSeek
1.下載ollama
前往 Ollama 主頁。
點擊下載,選擇對應操作系統(tǒng),點擊下載。
下載完成后打開直接無腦下一步就行。
安裝完成之后打開CMD頁面,輸入ollama,如果有輸出就代表安裝成功,如果顯示"不是內(nèi)部命令"就需要配置一下環(huán)境變量。
###配置環(huán)境變量以及更改ollama模型路徑###
win10系統(tǒng)為例,右鍵此電腦>高級系統(tǒng)設(shè)置>高級>環(huán)境變量
ollama 默認會安裝到 C:\Users\XX\AppData\Local\Programs\ollama 下,將此文件全部移到其他盤。
選擇系統(tǒng)變量Path>編輯>新建>輸入移動后的ollama地址
在系統(tǒng)變量中點擊新建,輸入變量名OLLAMA_MODELS,變量值D:\ollama\models(希望ollama模型放入哪個位置)
配置完成后重啟電腦,打開CMD頁面再次輸入ollama,查看是否有輸出
2.下載DeepSeek-R1模型并啟動
打開 Ollama 首頁,點擊 DeepSeek-R1
在這里有模型各種參數(shù)大小
DeepSeek-R1模型有1.5b、7b、8b、14b、32b、70b和671b的參數(shù)量區(qū)別,B代表十億的意思,1.5b代表15億參數(shù)量的意思。671B是基礎(chǔ)大模型,1.5B、7B、8B、14B、32B、70B是蒸餾后的小模型,它們的區(qū)別主要體現(xiàn)在參數(shù)規(guī)模、模型容量、性能表現(xiàn)、準確性、訓練成本、推理成本和不同使用場景:
DeepSeek模型版本 | 參數(shù)量 | 特點 | 適用場景 | 硬件配置 |
DeepSeek-R1-1.5B | 1.5B | 輕量級模型,參數(shù)量少,模型規(guī)模小 | 適用于輕量級任務(wù),如短文本生成、基礎(chǔ)問答等 | 4核處理器、8G內(nèi)存,無需顯卡 |
DeepSeek-R1-7B | 7B | 平衡型模型,性能較好,硬件需求適中 | 適合中等復雜度任務(wù),如文案撰寫、表格處理、統(tǒng)計分析等 | 8核處理器、16G內(nèi)存,Ryzen7或更高,RTX 3060(12GB)或更高 |
DeepSeek-R1-8B | 8B | 性能略強于7B模型,適合更高精度需求 | 適合需要更高精度的輕量級任務(wù),比如代碼生成、邏輯推理等 | 8核處理器、16G內(nèi)存,Ryzen7或更高,RTX 3060(12GB)或4060 |
DeepSeek-R1-14B | 14B | 高性能模型,擅長復雜的任務(wù),如數(shù)學推理、代碼生成 | 可處理復雜任務(wù),如長文本生成、數(shù)據(jù)分析等 | i9-13900K或更高、32G內(nèi)存,RTX 4090(24GB)或A5000 |
DeepSeek-R1-32B | 32B | 專業(yè)級模型,性能強大,適合高精度任務(wù) | 適合超大規(guī)模任務(wù),如語言建模、大規(guī)模訓練、金融預測等 | Xeon 8核、128GB內(nèi)存或更高,2-4張A100(80GB)或更高 |
DeepSeek-R1-70B | 70B | 頂級模型,性能最強,適合大規(guī)模計算和高復雜任務(wù) | 適合高精度專業(yè)領(lǐng)域任務(wù),比如多模態(tài)任務(wù)預處理。這些任務(wù)對硬件要求非常高,需要高端的 CPU 和顯卡,適合預算充足的企業(yè)或研究機構(gòu)使用 | Xeon 8核、128GB內(nèi)存或更高,8張A100/H100(80GB)或更高 |
DeepSeek-R1-671B | 671B | 超大規(guī)模模型,性能卓越,推理速度快,適合極高精度需求 | 適合國家級 / 超大規(guī)模 AI 研究,如氣候建模、基因組分析等,以及通用人工智能探索 | 64核、512GB或更高,8張A100/H100 |
根據(jù)自己電腦的配置選擇對應大小的模型,復制安裝命令,在cmd中執(zhí)行命令
等待安裝完成后會自己啟動。
退出 DeepSeek 輸入 /bye 或者 Ctrl+D
ollama 常用命令
ollama list :查看已安裝的模型
ollama ps:正在運行的模型
ollama run 模型名稱:啟動模型
ollama rm 模型名稱:卸載模型
下次啟動 DeepSeek 只需要先 ollama list 查看已安裝的模型,ollama run 模型名稱啟動即可。
二、JAVA項目接入DeepSeek
創(chuàng)建一個是 Springboot 項目引入 okhttp,lombok 等依賴
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.10.0</version> </dependency>
配置文件中配置好本地 DeepSeek 的訪問URL,ollama啟動的 DeepSeek 端口默認為 11434
deepseek: api: url: "http://127.0.0.1:11434/api/generate" model: "deepseek-r1:1.5b"
編寫 DeepSeekController
@RestController @RequestMapping("/deepSeek") public class DeepSeekController { @Autowired private DeepSeekService deepSeekService; @GetMapping @RequestMapping("/chat") public String chat(String question) { try { return deepSeekService.chat(question); } catch (IOException e) { return "服務(wù)器繁忙,請稍后重試!"; } } }
編寫DeepSeekRequest實體類
@Data @Builder public class DeepSeekRequest { /** * 消息列表,包含對話中的消息對象 */ private String prompt; /** * 模型名稱,指定要使用的模型 */ private String model; }
請求參數(shù)有很多,只列舉最主要的兩個參數(shù)
編寫DeepSeekResponse實體類
@Data @NoArgsConstructor @AllArgsConstructor @TableName("deepseek_response") public class DeepSeekResponse { private String id; private String questionId; //模型名稱 private String model; //創(chuàng)建時間 @JSONField(name = "created_at") private String createdAt; //響應內(nèi)容 private String response; // private boolean done; // @JSONField(name = "done_reason") private String doneReason; // @TableField(typeHandler = JacksonTypeHandler.class) private Integer[] context; // @JSONField(name = "total_duration") private Long totalDuration; // @JSONField(name = "load_duration") private Long loadDuration; // @JSONField(name = "prompt_eval_count") private Long promptEvalCount; // @JSONField(name = "prompt_eval_duration") private Long promptEvalDuration; // @JSONField(name = "eval_count") private Long evalCount; // @JSONField(name = "eval_duration") private Long evalDuration; }
編寫DeepSeekService
@Service public class DeepSeekService { @Value("${deepseek.api.url}") private String url; @Value("${deepseek.api.model}") private String model; private final OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .build(); public String chat(String question) throws IOException { DeepSeekRequest requestBody = DeepSeekRequest.builder() .model(model) .prompt(question) .build(); // 創(chuàng)建HTTP請求 Request request = new Request.Builder() .url(url) .post(RequestBody.create(JSON.toJSONString(requestBody), MediaType.get("application/json"))) .build(); // 發(fā)送請求并處理響應 try { Response response = client.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } DeepSeekResponse deepSeekResponse = JSON.parseObject(response.body().string(), DeepSeekResponse.class); return deepSeekResponse.getResponse(); } catch (IOException e) { e.printStackTrace(); } return "服務(wù)器繁忙!"; }
隨便寫一個前端頁面使用axios訪問剛才暴露出來的接口就可以啦
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>智能 AI</title> <link rel="stylesheet" rel="external nofollow" > <link rel="stylesheet" href="/css/self.css" rel="external nofollow" > </head> <body> <div id="app"> <div class="chat-container"> <div v-if="showTitle" class="welcome-title">你好!我是CloseAI,有什么可以幫助你的嗎?</div> <div class="chat-messages"> <transition-group name="list"> <div v-for="message in messages" :key="message.id" class="message-wrapper" :class="{'user-message-wrapper': message.sender === 'user', 'ai-message-wrapper': message.sender === 'ai'}"> <img :src="message.sender === 'user' ? '/images/user.png' : '/images/ai.png'" class="avatar"/> <div class="message" :class="{'user-message': message.sender === 'user', 'ai-message': message.sender === 'ai'}"> <span v-if="message.sender === 'user'">{{ message.text }}</span> <div v-else v-html="message.formattedText || message.text"></div> <div v-if="message.sender === 'ai' && message.isLoading" class="loading-icon"></div> <!-- 調(diào)整停止按鈕容器位置 --> <div v-if="(isThinking || animationFrameId) && message.sender === 'ai' && message.isActive" class="stop-container"> <button @click="stopThinking" class="stop-btn" title="停止響應"> <svg width="24" height="24" viewBox="0 0 24 24"> <rect x="6" y="4" width="4" height="16" rx="1"/> <rect x="14" y="4" width="4" height="16" rx="1"/> </svg> </button> </div> </div> </div> </transition-group> </div> <div class="chat-input"> <input type="text" v-model="userInput" @keyup.enter="sendMessage" placeholder="給 CloseAI 發(fā)送消息..."/> <button @click="sendMessage">Send</button> </div> </div> </div> <script src="/js/vue.js"></script> <script src="/js/axios.min.js"></script> <script src="/js/method.js"></script> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.0.5/purify.min.js"></script> <script> function debounce(func, wait) { let timeout; return function (...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } new Vue({ el: '#app', data: { userInput: '', messages: [], showTitle: true, // 添加一個新屬性來控制標題的顯示 performanceConfig: { maxMessageLength: 1000, // 超過該長度啟用優(yōu)化 chunkSize: window.innerWidth < 768 ? 3 : 5 // 響應式分段 }, // 新增控制變量 isThinking: false, currentController: null, // 請求中止控制器 animationFrameId: null // 動畫幀ID }, methods: { sendMessage() { if (this.userInput.trim() !== '') { this.showTitle = false; // 用戶開始提問時隱藏標題 const userMessage = { id: Date.now(), sender: 'user', text: this.userInput }; this.messages.push(userMessage); this.userInput = ''; // 停用所有舊消息 this.messages.forEach(msg => { if (msg.sender === 'ai') { this.$set(msg, 'isActive', false); } }); const aiMessage = { id: Date.now() + 1, sender: 'ai', text: '思考中 ', isLoading: true, isThinking: true, // 新增狀態(tài)字段 isActive: true // 新增激活狀態(tài)字段 }; this.messages.push(aiMessage); // 在請求前重置狀態(tài) this.isThinking = true; // 使用 axios 的取消令牌 const controller = new AbortController(); this.currentController = controller; axios.get('/deepSeek/chat?question=' + userMessage.text, { signal: controller.signal }).then(response => { aiMessage.isLoading = false; aiMessage.text = '' aiMessage.rawContent = response.data; // 新增原始內(nèi)容存儲 let index = 0; let chunk = ''; const chunkSize = this.performanceConfig.chunkSize; let lastUpdate = 0; let lastFullRender = 0; const processChunk = () => { // 分塊處理邏輯 const remaining = response.data.length - index; const step = remaining > chunkSize * 5 ? chunkSize * 5 : 1; chunk += response.data.slice(index, index + step); index += step; // 增量更新文本 aiMessage.text = chunk; this.safeParseMarkdown(aiMessage, true); // 實時解析 this.scrollToBottom(); }; const animate = (timestamp) => { // 時間片控制邏輯 if (timestamp - lastUpdate > 16) { // 約60fps processChunk(); lastUpdate = timestamp; } // 完整渲染控制(每100ms強制更新一次) if (timestamp - lastFullRender > 100) { this.$forceUpdate(); lastFullRender = timestamp; } if (timestamp - lastUpdate > 20) { const remainingChars = response.data.length - index; const step = remainingChars > chunkSize ? chunkSize : 1; aiMessage.text += response.data.substr(index, step); index += step; this.safeParseMarkdown(aiMessage); this.scrollToBottom(); lastUpdate = timestamp; } if (index < response.data.length) { this.animationFrameId = requestAnimationFrame(animate); } else { this.$set(aiMessage, 'isActive', false); // 關(guān)閉激活狀態(tài) this.$set(aiMessage, 'formattedText', DOMPurify.sanitize(marked.parse(aiMessage.text)) ); this.animationFrameId = null; // 新增重置 this.isThinking = false; // 在此處更新狀態(tài) this.finalizeMessage(aiMessage); } }; // 修改動畫啟動方式 this.animationFrameId = requestAnimationFrame(animate); }).catch(error => { if (error.name !== 'CanceledError') { console.error('Error:', error); } }).finally(() => { this.animationFrameId = null; // 新增消息狀態(tài)更新 this.messages.forEach(msg => { if (msg.sender === 'ai') { msg.isLoading = false; msg.isThinking = false; } }); }); } }, // 新增滾動方法 scrollToBottom() { const container = this.$el.querySelector('.chat-messages'); container.scrollTop = container.scrollHeight; }, // 優(yōu)化的Markdown解析方法 safeParseMarkdown: debounce(function (aiMessage, immediate = false) { if (immediate) { // // 快速解析基礎(chǔ)格式(粗體、斜體等) // const quickFormatted = aiMessage.text // .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>') // .replace(/\*(.*?)\*/g, '<em>$1</em>'); // 完整Markdown解析 this.$set(aiMessage, 'formattedText', DOMPurify.sanitize(marked.parse(aiMessage.text)) ); } else { // 完整Markdown解析 this.$set(aiMessage, 'formattedText', DOMPurify.sanitize(marked.parse(aiMessage.text)) ); } }, 50), // 完整解析保持50ms防抖 stopThinking() { // 只停止當前活動消息 const activeMessage = this.messages.find(msg => msg.sender === 'ai' && msg.isActive ); if (activeMessage) { // 更新消息狀態(tài) this.$set(activeMessage, 'text', activeMessage.text + '\n(響應已停止)'); this.$set(activeMessage, 'isLoading', false); this.$set(activeMessage, 'isActive', false); this.safeParseMarkdown(activeMessage); } // 取消網(wǎng)絡(luò)請求 if (this.currentController) { this.currentController.abort() } // 停止打印渲染 if (this.animationFrameId) { cancelAnimationFrame(this.animationFrameId); this.animationFrameId = null; } // 更新所有相關(guān)消息狀態(tài) this.messages.forEach(msg => { if (msg.sender === 'ai' && (msg.isLoading || msg.isThinking)) { msg.text += '\n(響應已停止)'; msg.isLoading = false; msg.isThinking = false; this.safeParseMarkdown(msg); } }); this.isThinking = false; this.animationFrameId = null; // 確保重置 }, // 新增最終處理函數(shù) finalizeMessage(aiMessage) { this.$set(aiMessage, 'formattedText', DOMPurify.sanitize(marked.parse(aiMessage.text)) ); this.$set(aiMessage, 'isActive', false); this.animationFrameId = null; this.isThinking = false; this.$forceUpdate(); }, } }); </script> </body> </html>
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; padding: 0; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f0f0; } .chat-container { width: 100%; /*max-width: 800px;*/ background-color: #fff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); overflow: hidden; display: flex; flex-direction: column; } .welcome-title { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 100%; text-align: center; font-size: 24px; font-weight: bold; } /* 新增停止按鈕樣式 */ .stop-btn { background-color: #ff4d4f; color: white; margin-left: 8px; transition: all 0.3s; } .stop-btn:hover { background-color: #f5f7fa; } .avatar { width: 40px; height: 40px; border-radius: 50%; margin-right: 10px; vertical-align: middle; } .chat-messages { /*flex: 1;*/ overflow-y: auto; padding: 20px; /*display: flex;*/ flex-direction: column-reverse; height: 70vh; width: 500px; scrollbar-width: none; scroll-behavior: smooth; /* 平滑滾動效果 */ contain: strict; /* 限制瀏覽器重排范圍 */ transform: translateZ(0); } .message { margin-bottom: 15px; padding: 12px; border-radius: 12px; max-width: 70%; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; /*align-self: flex-end; !* 默認消息靠右對齊 *!*/ display: table; } .message-wrapper { display: flex; max-width: 80%; margin: 10px; align-items: flex-start; will-change: transform, opacity; /* 啟用GPU加速 */ } /*.user-message-wrapper {*/ /* justify-content: flex-end;*/ /*}*/ .ai-message-wrapper { justify-content: flex-start; position: relative; /* 為絕對定位的停止按鈕提供參考系 */ margin-bottom: 50px; /* 給停止按鈕留出空間 */ } .avatar { width: 40px; height: 40px; border-radius: 50%; margin-right: 10px; vertical-align: middle; } .message { margin-bottom: 0; } .user-message { background-color: #007bff; color: #fff; margin-left: auto; /* 用戶消息靠右對齊 */ } .ai-message { background-color: #f0f2f5; /* AI消息背景色 */ margin-right: auto; /* 消息內(nèi)容靠左 */ position: relative; /* 添加相對定位 */ padding-bottom: 40px; /* 為按鈕預留空間 */ } /* 在self.css中添加 */ .user-message-wrapper { flex-direction: row-reverse; /* 反轉(zhuǎn)排列方向 */ margin-left: auto; /* 讓用戶消息靠右 */ } .user-message-wrapper .avatar { margin: 0 0 0 15px; /* 調(diào)整頭像右邊距 */ } .chat-input { display: flex; align-items: center; padding: 15px; background-color: #f8f9fa; } .chat-input input { flex: 1; padding: 12px; border: 1px solid #ccc; border-radius: 6px; margin-right: 15px; transition: border-color 0.3s ease; } .chat-input input:focus { border-color: #007bff; } .chat-input button { padding: 12px 24px; border: none; border-radius: 6px; background-color: #007bff; color: #fff; cursor: pointer; transition: background-color 0.3s ease; } .message { display: flex; align-items: center; } /* 調(diào)整停止按鈕定位 */ .stop-container { position: absolute; bottom: 10px; right: 10px; z-index: 10; } .stop-btn { background: #ff4757; border: none; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: transform 0.2s; } .stop-btn:hover { transform: scale(1.1); } .stop-btn svg { fill: white; width: 16px; height: 16px; } .ai-message pre { background: #f5f7fa; padding: 15px; border-radius: 6px; overflow-x: auto; } .ai-message code { font-family: 'Courier New', monospace; font-size: 14px; } .ai-message table { border-collapse: collapse; margin: 10px 0; } .ai-message td, .ai-message th { border: 1px solid #ddd; padding: 8px; } .loading-icon { width: 20px; height: 20px; border: 2px solid #ccc; border-top-color: #007bff; border-radius: 50%; /*animation: spin 1s linear infinite;*/ margin-left: 10px; /* 替換為CSS動畫實現(xiàn)的加載效果 */ /*background: linear-gradient(90deg, #eee 25%, #ddd 50%, #eee 75%);*/ animation: spin 1s infinite linear; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .chat-input button:hover { background-color: #0056b3; } .list-enter-active, .list-leave-active { transition: all 0.5s ease; } .list-enter, .list-leave-to /* .list-leave-active in <2.1.8 */ { opacity: 0; transform: translateY(30px); }
我的這個頁面有bug,只供參考!??!
到此這篇關(guān)于JAVA集成本地部署的DeepSeek的圖文教程的文章就介紹到這了,更多相關(guān)JAVA集成本地部署DeepSeek內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot用JdbcTemplates操作Mysql實例代碼詳解
JdbcTemplate是Spring框架自帶的對JDBC操作的封裝,目的是提供統(tǒng)一的模板方法使對數(shù)據(jù)庫的操作更加方便、友好,效率也不錯,這篇文章主要介紹了SpringBoot用JdbcTemplates操作Mysql2022-10-10java.sql.SQLException:?connection?holder?is?null錯誤解決辦法
這篇文章主要給大家介紹了關(guān)于java.sql.SQLException:?connection?holder?is?null錯誤的解決辦法,這個錯誤通常是由于連接對象為空或未正確初始化導致的,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-02-02Java反射獲取所有Controller和RestController類的方法
這篇文章給大家分享了Java反射獲取所有Controller和RestController類的方法,文中有詳細的代碼示例講解,具有一定的參考價值,需要的朋友可以參考下2023-08-08Springboot整合RabbitMQ實現(xiàn)發(fā)送驗證碼的示例代碼
這篇文章主要介紹了Springboot整合RabbitMQ實現(xiàn)發(fā)送驗證碼的功能,基于AMQP協(xié)議實現(xiàn)的消息隊列,它是一種應用程序之間的通信方法,消息隊列在分布式系統(tǒng)開 發(fā)中應用非常廣泛,需要的朋友可以參考下2022-02-02SpringBoot集成Redis實現(xiàn)消息隊列的方法
這篇文章主要介紹了SpringBoot集成Redis實現(xiàn)消息隊列的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02