仿DeepSeek AI問答系統完整版(帶RAG本地知識庫+聯網搜索+深度思考) +springboot+vue3

今天教大家如何設計一個企業級的?deepseek問答?一樣的系統?, 基于目前主流的技術:前端vue3,后端springboot。同時還帶來的項目的部署教程

系統的核心功能

1. 支持本地上傳文檔知識庫,RAG技術。 支持的文檔有txt,doc,docx,pdf 。

2. 支持聯網搜索。

3. 支持深度思考。

4. 支持歷史上下文消息。

5.支持websocket流式。

6. 支持用戶登錄,注冊。

7. 支持會話管理。

系統需要的組件

ElasticSearch8 : 存儲知識庫文檔向量。

redis: 存儲系統用的消息緩存。

mysql8: 存儲關系型表。

技術棧

JDK11 + SpringBoot + VUE3

視頻演示

仿DeepSeek AI問答系統完整版

圖片演示

系統實現

RAG技術實現

RAG是一種結合?信息檢索(Retrieval)?和?文本生成(Generation)?的 AI 技術,主要用于提升大語言模型(LLM)生成內容的準確性和時效性。

下面來介紹下RAG實現的核心步驟。

文本提取分塊

用戶上傳文檔時, 首選需要將文檔解析成很多文本塊, 系統通過?DocmentChunkParser 接口的?textChunks 方法 將傳入 的文檔(txt,docx,pdf)解析成文本塊,代碼:

public interface DocmentChunkParser {// 每個文本塊的最大字符數public static final int CHUNK_SIZE = 1000;// 文本塊之間的重疊字符數public static final int CHUNK_OVERLAP = 200;List<String> textChunks();
}

對應的TXT實現:

?對應的WORD實現:

??對應的PDF實現:

embeddings階段,文本轉向量

拿到文本塊后, 需要將文本轉換成向量, 也就是?embeddings階段。系統采用的是 “阿里云百煉” 平臺的 向量模型:

用戶需要自己申請api的key。 到阿里百煉平臺申請就行了。

向量數據庫存儲+檢索

將文本轉成向量后,需要存儲到向量數據庫里面,這里我選擇的是elasticsearch8。

為什么選擇elasticsearch , 支持數據量大,能水平分片, 支持?dense_vector?字段,直接支持向量存儲和相似性搜索,無需插件。支持?cosine(余弦相似度)、dot_product(點積)、l2_norm(歐式距離)等計算方式。

支持 近似最近鄰搜索(ANN)。如果企業用也可以選擇。

整個向量數據操作都是在?DocumentChunkRepository 接口中實現:

public interface DocumentChunkRepository {/*** 通過文檔id查詢文本塊* @param documentId* @return*/List<DocumentChunk> findByDocumentId(String documentId);/*** 刪除文檔* @param documentId*/void deleteByDocumentId(String documentId);/*** 存儲文本向量* @param documentChunk* @return*/DocumentChunk save(DocumentChunk documentChunk);/*** 向量關鍵詞搜索,基于KNN算法* @param documentId* @param queryVector* @param k* @return*/List<DocumentChunk> findTopKSimilarChunks(String documentId, List<Float> queryVector, int k);
}

聯網搜索實現

我們直到deepseek模型是不能搜索到今天的天氣,新聞等信息的。如果有這樣的需求,就需要開啟聯網搜索功能。

首先 需要尋找一個聯網搜索的插件或者api接口。目前有很多這樣的接口,比如:

searchapi (國外),?duckduckgo(國外),必應搜索API 等。

國內的我隨便找了一個叫 “博查搜索” ,提供了api搜索。

下面是對接博查搜索的代碼:

/*** 博查AI搜索服務實現* 基于博查AI開放平臺的Web Search API* 支持實時網頁搜索,適用于AI應用*/
@Slf4j
@Service
public class BochaWebSearchService implements WebSearchService {// 博查API配置private static final String BOCHA_API_URL = "https://api.bochaai.com/v1/web-search";private static final String BACKUP_API_URL = "https://api.bochaai.com/v1/search";@Value("${bocha.api.key}")private String apiKey;private final HttpClient httpClient;private final ObjectMapper objectMapper;private final Random random = new Random();// 用戶代理池private static final String[] USER_AGENTS = {"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0"};public BochaWebSearchService() {this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();this.objectMapper = new ObjectMapper();}@Overridepublic List<String> search(String query, int maxResults) {if (query == null || query.trim().isEmpty()) {log.warn("搜索查詢為空");return Collections.emptyList();}try {// 首先嘗試主APIList<String> results = performSearch(BOCHA_API_URL, query, maxResults);if (!results.isEmpty()) {return results;}// 主API失敗,嘗試備用APIlog.warn("主API返回空結果,嘗試備用API");results = performSearch(BACKUP_API_URL, query, maxResults);if (!results.isEmpty()) {return results;}log.warn("所有API都返回空結果,返回模擬結果");return getMockResults(query, maxResults);} catch (Exception e) {log.error("博查搜索服務異常: {}", e.getMessage(), e);return getMockResults(query, maxResults);}}/*** 執行搜索請求*/private List<String> performSearch(String apiUrl, String query, int maxResults) throws Exception {String requestBody = buildRequestBody(query, maxResults);HttpRequest request = HttpRequest.newBuilder().uri(URI.create(apiUrl)).header("Authorization", "Bearer " + apiKey).header("Content-Type", "application/json").header("Accept", "application/json").header("User-Agent", getRandomUserAgent()).POST(HttpRequest.BodyPublishers.ofString(requestBody, StandardCharsets.UTF_8)).timeout(Duration.ofSeconds(30)).build();log.info("發送博查搜索請求: {}", query);CompletableFuture<HttpResponse<String>> future = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));HttpResponse<String> response = future.get(30, TimeUnit.SECONDS);if (response.statusCode() == 200) {return parseSearchResults(response.body(), maxResults);} else {log.warn("博查API請求失敗,狀態碼: {}, 響應: {}", response.statusCode(), response.body());throw new RuntimeException("API請求失敗: " + response.statusCode());}}/*** 構建請求體*/private String buildRequestBody(String query, int maxResults) {try {Map<String, Object> requestData = new HashMap<>();requestData.put("query", query);requestData.put("count", Math.min(maxResults, 20)); // 博查API最大支持20個結果requestData.put("freshness", "oneYear"); // 搜索一年內的內容requestData.put("summary", false); // 不需要摘要requestData.put("safeSearch", "moderate"); // 中等安全搜索return objectMapper.writeValueAsString(requestData);} catch (Exception e) {log.error("構建請求體失敗: {}", e.getMessage());throw new RuntimeException("構建請求體失敗", e);}}/*** 解析搜索結果*/private List<String> parseSearchResults(String responseBody, int maxResults) {try {JsonNode root = objectMapper.readTree(responseBody);List<String> results = new ArrayList<>();// 解析博查API響應格式JsonNode data = root.path("data") ;JsonNode webPages = data.path("webPages");JsonNode valueArray = webPages.path("value");if (valueArray.isArray()) {for (JsonNode item : valueArray) {if (results.size() >= maxResults) {break;}String title = getJsonValue(item, "name");String url = getJsonValue(item, "url");String snippet = getJsonValue(item, "snippet");String siteName = getJsonValue(item, "siteName");if (!title.isEmpty() && !url.isEmpty()) {StringBuilder result = new StringBuilder();result.append("標題: ").append(title);if (!siteName.isEmpty()) {result.append(" (來源: ").append(siteName).append(")");}result.append("\n鏈接: ").append(url);if (!snippet.isEmpty()) {result.append("\n摘要: ").append(snippet);}results.add(result.toString());}}}log.info("博查搜索成功,返回{}個結果", results.size());return results;} catch (Exception e) {log.error("解析博查搜索結果失敗: {}", e.getMessage(), e);return Collections.emptyList();}}/*** 安全獲取JSON值*/private String getJsonValue(JsonNode node, String fieldName) {JsonNode field = node.path(fieldName);return field.isMissingNode() ? "" : field.asText("").trim();}/*** 獲取隨機User-Agent*/private String getRandomUserAgent() {return USER_AGENTS[random.nextInt(USER_AGENTS.length)];}/*** 獲取模擬搜索結果(當API不可用時)*/private List<String> getMockResults(String query, int maxResults) {List<String> mockResults = new ArrayList<>();int count = Math.min(maxResults, 3);for (int i = 1; i <= count; i++) {mockResults.add(String.format("標題: 關于'%s'的搜索結果 %d (模擬數據)\n" +"鏈接: https://example.com/search-result-%d\n" +"摘要: 這是關于'%s'的模擬搜索結果,實際使用時請配置博查API Key。",query, i, i, query));}log.info("返回{}個模擬搜索結果", mockResults.size());return mockResults;}
}

將聯網搜索的結果轉變成一個List<String>的字符串集合,然后傳到deepseek ,deepseek就會按照聯網的結果進行總結輸出。

深度思考實現

這個其實很簡單,我們打開deepseek官網 , 找到推理模型,?deepseek-reasoner。

當設置為?deepseek-reasoner 模型,然后問答時,? 就會先輸出推理的內容, 然后才輸出有用結果。

部署教程

前端部署

安裝node , 版本:v22.15.0 , 安裝完成后。

?進入到項目?chatgpt-web 目錄下,這個項目是vue前端, 右鍵,運行cmd,運行下面命令:

npm run dev

由于我已經跟你npm install好了,所以你無需執行,直接run就可以了!!

運行項目

執行sql

自己安裝好數據庫,注意,必須是mysql8 ,否則代碼運行會出錯。新建一個?wxhadluo-deepseek?數據庫, 然后執行? “wxhadluo-deepseek.sql”

Redis安裝

項目需要安裝redis,直接下載一個windows版本的redis即可,沒有的聯系我。

ElasticSearch8安裝

es的安裝可以自行百度

幾個api key的申請

1. deepseek官網的api key申請,這個自己申請就好。

2. 博查搜索的api key 申請, 這個自己到官網申請就好。

3. 向量模型的api key 申請,這個到阿里的百煉大模型平臺申請就好。前面講解原理我也提到過。

啟動后端項目

然后部署后端 , 打開idea, 導入maven工程 。

打開resources目錄, 修改 application.yml 配置文件,主要修改下面幾個信息:

?郵件服務器是用來注冊 用戶的, 因為是通過郵箱注冊的。

然后就是幾個api key:

然后啟動? main 啟動類 :DeepSeekApiApplication.class

訪問項目

必須登錄成功后,才可以使用。

前端:

http://localhost:3010/

賬號可以自己注冊用戶。需要配置好郵箱服務器, 如果沒有的,直接往數據庫表?user 插入數據也行, 密碼直接是明文的就可以。using_doc_id不用填。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/84802.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/84802.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/84802.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

27、請求處理-【源碼分析】-怎么改變默認的_method

27、請求處理-【源碼分析】-怎么改變默認的_method 要改變 Spring Boot 中默認的 _method 參數&#xff0c;可以通過以下步驟實現&#xff1a; #### 原理分析 Spring Boot 中默認的 HiddenHttpMethodFilter 用于將表單中的 _method 參數值映射為實際的 HTTP 方法&#xff08;如…

歐拉角轉為旋轉矩陣

外旋是固定坐標系&#xff0c;內旋是動態坐標系。外旋和內旋具有等價性。 固定坐標系依次繞xyz軸旋轉&#xff0c;旋轉矩陣 動態坐標系依次繞zyx軸旋轉&#xff0c;旋轉矩陣 numpy和scipy計算對比 import numpy as np from numpy import sin, cos, pi # 抑制科學計數法&#…

【AI學習筆記】Coze平臺實現生成小紅書熱門多圖筆記

背景前搖&原視頻教程&#xff1a; 最近總是在小紅書上刷到多圖組成的養生小妙招、效率提升小tips、退休奶奶療愈語錄等等這樣的圖文筆記&#xff0c;而且人物圖像一眼就是AI畫的。 當時我以為這個排版和文字是人工的&#xff0c;就讓AI保持角色一致性畫了下圖&#xff0c;…

如何選擇自動化編程平臺

從事自動化行業的工作者都知道&#xff0c;做PLC編程需要PLC編程軟件&#xff0c;做HMI可視化需要HMI編程軟件&#xff0c;做SCADA需要SCADA編程軟件&#xff0c;做DCS需要DCS軟件&#xff0c;做仿真調試需要仿真軟件。這些軟件有國外的、國內的&#xff0c;有傳統自動化廠商開…

Bug 背后的隱藏劇情

Bug 背后的隱藏劇情 flyfish 1. 「bug」&#xff1a;70多年前那只被拍進史書的飛蛾 故事原型&#xff1a;1947年哈佛實驗室的「昆蟲命案」 1947年的計算機長啥樣&#xff1f;像一間教室那么大&#xff0c;塞滿了幾萬根繼電器&#xff08;類似老式開關&#xff09;&#xff…

如何將通話記錄從Android傳輸到Android

“如何將通話記錄從 Android 轉移到 Android&#xff1f;我換了一部新的 Android 手機&#xff0c;想要將通話記錄復制到其中。”您需要將通話記錄從 Android 傳輸到 Android 是一種常見的情況&#xff0c;因為通話記錄是手機上最重要的數據之一。幸運的是&#xff0c;如果您從…

Android 云手機橫屏模式下真機鍵盤遮擋輸入框問題處理

一、背景 打開橫屏應用,點擊云機EditText輸入框,輸入框被鍵盤遮擋,如下圖&#xff1a; 未打開鍵盤狀態: 點擊第二個輸入框,鍵盤遮擋了輸入框&#xff1a; 二、解決方案&#xff08;推薦第三中方案,博主采用的也是第三種方案&#xff09; 博主這里整理了三種方案&#xff1a;…

進程IO之 進程

一、進程相關概念 1.什么是進程 程序&#xff1a;靜態的&#xff0c;編譯好的可執行文件&#xff0c;存放在磁盤中的指令和數據的集合 進程&#xff1a;動態的&#xff0c;是程序的一次執行過程&#xff0c;是獨立的可調度的任務 2.進程的特點 &#xff08;1&#xff09;對…

Condition源碼解讀(二)

本章我們繼續將Condition的最后一個方法signal方法&#xff0c;如果前面沒有看過的可以點擊LockSupport與Condition解析來看看Condition解讀的前半部分。 signal方法&#xff1a; public final void signal() {if (!AbstractQueuedLongSynchronizer.this.isHeldExclusively())…

股票收益率的計算

首先&#xff0c;需要從 Tushare.pro 注冊一個賬號并調用其API獲取股票日線數據&#xff08;具體操作請查看官網&#xff09;。 以通過調用tushare獲取股票000001(平安銀行)的股票數據為例&#xff0c;這里不設置日期&#xff0c;那么默認獲取Tushare提供的所有歷史數據。也可…

《算法筆記》13.2小節——專題擴展->樹狀數組(BIT) 問題 D: 數列-訓練套題T10T3

數列(sequence.pas/c/cpp) - 問題描述 一個簡單的數列問題&#xff1a;給定一個長度為n的數列&#xff0c;求這樣的三個元素ai, aj, ak的個數&#xff0c;滿足ai < aj > ak&#xff0c;且i < j < k。 - 輸入數據 第一行是一個整數n(n < 50000)。 第二行n個整…

C# Windows Forms應用程序-001

目錄 項目概述 主要組件及功能 類定義 控件聲明 構造函數 Dispose 方法 InitializeComponents 方法 控件配置詳解 Button 控件 (button1) TextBox 控件 (textBox1) GroupBox 控件 (groupBox1) Label 控件 (label1 至 label5) OpenFileDialog 控件 (openFileDialog1…

2025.5.28總結

今日工作&#xff1a;最近進入了項目的關鍵節點&#xff0c;要求每人每天提兩單&#xff0c;今天周三&#xff0c;下班前只提了一個單。下午開了一場需求服務驗收會&#xff0c;我演示了自己驗收的那個需求&#xff0c;然后講的不是很好。當初再構造數據時請教了一個人&#xf…

Transformer核心技術解析LCPO方法:精準控制推理長度的新突破

原創文章1FFN前饋網絡與激活函數技術解析&#xff1a;Transformer模型中的關鍵模塊2Transformer掩碼技術全解析&#xff1a;分類、原理與應用場景3【大模型技術】Attention注意力機制詳解一4Transformer模型中位置編碼&#xff08;Positional Embedding&#xff09;技術全解析(…

在 WSL 中安裝 JetBrains Toolbox:完整指南

JetBrains Toolbox 是一個非常實用的工具&#xff0c;它可以幫助開發者輕松管理 JetBrains 的各種開發工具&#xff0c;如 IntelliJ IDEA、PyCharm、WebStorm 等。通過它&#xff0c;你可以快速安裝、更新和管理這些工具&#xff0c;極大地提高了開發效率。而在 WSL 環境中安裝…

ZooKeeper 命令操作

文章目錄 Zookeeper 數據模型Zookeeper 服務端常用命令Zookeeper 客戶端常用命令 Zookeeper 數據模型 ZooKeeper 是一個樹形目錄服務,其數據模型和Unix的文件系統目錄樹很類似&#xff0c;擁有一個層次化結構。這里面的每一個節點都被稱為&#xff1a; ZNode&#xff0c;每個節…

Turf.js:前端地理空間分析的瑞士軍刀

在Web開發中,地理空間數據處理已成為許多應用的核心需求。從地圖可視化到位置服務,再到復雜的數據分析,前端開發者需要強大的工具來處理這些任務。Turf.js 作為一款輕量級、模塊化的地理空間分析庫,憑借其豐富的功能和易用性,成為前端開發者的得力助手。本文將深入探討 Tu…

大模型微調

使用 Ollama 微調大語言模型&#xff08;如 LLaMA、Mistral、Gemma 等&#xff09;主要是圍繞 LoRA&#xff08;Low-Rank Adaptation&#xff09;或者 QLoRA 等輕量級微調技術進行的。Ollama 本身是一個部署和運行本地大語言模型的平臺&#xff0c;但其微調能力有限&#xff0c…

《自動駕駛軌跡規劃實戰:Lattice Planner實現避障路徑生成(附可運行Python代碼)》—— 零基礎實現基于離散優化的避障路徑規劃

《自動駕駛軌跡規劃實戰&#xff1a;Lattice Planner實現避障路徑生成&#xff08;附可運行Python代碼&#xff09;》 —— 零基礎實現基于離散優化的避障路徑規劃 一、為什么Lattice Planner成為自動駕駛的核心算法&#xff1f; 在自動駕駛的路徑規劃領域&#xff0c;Lattice…

切換到舊提交,同時保證當前修改不丟失

在 Git 中&#xff0c;可以通過以下幾種方式切換到之前的提交&#xff0c;同時保留當前的提交&#xff08;即不丟失工作進度&#xff09;&#xff1a; 1. 使用 git checkout 創建臨時分離頭指針&#xff08;推薦用于查看&#xff09; git checkout <commit-hash>這會讓…