From 584212c5690802dd488924b94a3735e7be34bc6b Mon Sep 17 00:00:00 2001 From: zhouweiyi Date: 星期三, 14 五月 2025 16:13:07 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/main' --- ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/QueryVectorBo.java | 9 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java | 10 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java | 500 ++++++++++++++----------------------- script/sql/update/20250514.sql | 6 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java | 78 +++-- ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/StoreEmbeddingBo.java | 9 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java | 14 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeInfoVo.java | 16 README.md | 17 + ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoBo.java | 14 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java | 69 ++-- 11 files changed, 349 insertions(+), 393 deletions(-) diff --git a/README.md b/README.md index 4aff8d3..cfb13aa 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,23 @@ <a href="https://github.com/ageerle/ruoyi-ai/issues">鎻愬嚭鏂扮壒鎬�</a> </p> +## 蹇�熷惎鍔� + +1. **鍏嬮殕椤圭洰** + ```bash + git clone https://github.com/alanpeng/ruoyi-ai-docker-deploy + cd ruoyi-ai-docker-deploy + ``` + +2. **鍚姩鍏ㄥ搴旂敤** + ```bash + docker-compose up -d + ``` + +3. **璁块棶搴旂敤鐣岄潰** + - 鐢ㄦ埛鐣岄潰锛歚http://your-server-ip:8081` + - 绠$悊鍛樼晫闈細`http://your-server-ip:8082` + ## 鐩綍 - [绯荤粺浣撻獙](#绯荤粺浣撻獙) diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java index 230a1d6..34b1c2d 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeInfo.java @@ -1,5 +1,6 @@ package org.ruoyi.domain; +import com.alibaba.excel.annotation.ExcelProperty; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; @@ -78,14 +79,19 @@ private Long textBlockSize; /** - * 鍚戦噺搴� + * 鍚戦噺搴撴ā鍨嬪悕绉� */ - private String vector; + private String vectorModelName; /** - * 鍚戦噺妯″瀷 + * 鍚戦噺鍖栨ā鍨嬪悕绉� */ - private String vectorModel; + private String embeddingModelName; + + /** + * 绯荤粺鎻愮ず璇� + */ + private String systemPrompt; /** * 澶囨敞 diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoBo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoBo.java index 00a22b7..9a510a6 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoBo.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoBo.java @@ -83,16 +83,22 @@ private Long textBlockSize; /** - * 鍚戦噺搴� + * 鍚戦噺搴撴ā鍨嬪悕绉� */ @NotBlank(message = "鍚戦噺搴撲笉鑳戒负绌�", groups = { AddGroup.class, EditGroup.class }) - private String vector; + private String vectorModelName; /** - * 鍚戦噺妯″瀷 + * 鍚戦噺鍖栨ā鍨嬪悕绉� */ @NotBlank(message = "鍚戦噺妯″瀷涓嶈兘涓虹┖", groups = { AddGroup.class, EditGroup.class }) - private String vectorModel; + private String embeddingModelName; + + + /** + * 绯荤粺鎻愮ず璇� + */ + private String systemPrompt; /** * 澶囨敞 diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/QueryVectorBo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/QueryVectorBo.java index 33e8204..ff3a26e 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/QueryVectorBo.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/QueryVectorBo.java @@ -26,9 +26,14 @@ private Integer maxResults; /** - * 妯″瀷鍚嶇О + * 鍚戦噺搴撴ā鍨嬪悕绉� */ - private String modelName; + private String vectorModelName; + + /** + * 鍚戦噺鍖栨ā鍨嬪悕绉� + */ + private String embeddingModelName; /** * 璇锋眰key diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/StoreEmbeddingBo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/StoreEmbeddingBo.java index 9510403..e4d8c38 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/StoreEmbeddingBo.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/StoreEmbeddingBo.java @@ -32,9 +32,14 @@ private List<String> fids; /** - * 妯″瀷鍚嶇О + * 鍚戦噺搴撴ā鍨嬪悕绉� */ - private String modelName; + private String vectorModelName; + + /** + * 鍚戦噺鍖栨ā鍨嬪悕绉� + */ + private String embeddingModelName; /** * 璇锋眰key diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeInfoVo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeInfoVo.java index 6a5fdbf..ed4f6aa 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeInfoVo.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeInfoVo.java @@ -98,16 +98,20 @@ private Integer textBlockSize; /** - * 鍚戦噺搴� + * 鍚戦噺搴撴ā鍨嬪悕绉� */ - @ExcelProperty(value = "鍚戦噺搴�") - private String vector; + private String vectorModelName; /** - * 鍚戦噺妯″瀷 + * 鍚戦噺鍖栨ā鍨嬪悕绉� */ - @ExcelProperty(value = "鍚戦噺妯″瀷") - private String vectorModel; + private String embeddingModelName; + + + /** + * 绯荤粺鎻愮ず璇� + */ + private String systemPrompt; /** * 澶囨敞 diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java index e27c94c..be445b9 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java @@ -13,14 +13,14 @@ void storeEmbeddings(StoreEmbeddingBo storeEmbeddingBo); - void removeByDocId(String kid,String docId); - - void removeByKid(String kid); - List<String> getQueryVector(QueryVectorBo queryVectorBo); void createSchema(String kid,String modelName); - void removeByKidAndFid(String kid, String fid); + void removeByKid(String kid,String modelName); + + void removeByDocId(String kid,String docId,String modelName); + + void removeByKidAndFid(String kid, String fid,String modelName); } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java index d74176a..b5f7962 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java @@ -1,5 +1,7 @@ package org.ruoyi.service.impl; +import cn.hutool.core.util.RandomUtil; +import com.google.protobuf.ServiceException; import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.EmbeddingModel; @@ -16,6 +18,7 @@ import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore; import dev.langchain4j.store.embedding.weaviate.WeaviateEmbeddingStore; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.ruoyi.common.core.service.ConfigService; import org.ruoyi.domain.bo.QueryVectorBo; @@ -40,11 +43,10 @@ private final ConfigService configService; - Map<String,EmbeddingStore<TextSegment>> storeMap = new HashMap<>(); + private EmbeddingStore<TextSegment> embeddingStore; @Override public void createSchema(String kid,String modelName) { - EmbeddingStore<TextSegment> embeddingStore; switch (modelName) { case "weaviate" -> { String protocol = configService.getConfigValue("weaviate", "protocol"); @@ -84,88 +86,83 @@ embeddingStore = new InMemoryEmbeddingStore<>(); } } - storeMap.put(kid,embeddingStore); } @Override public void storeEmbeddings(StoreEmbeddingBo storeEmbeddingBo) { - EmbeddingStore<TextSegment> store = storeMap.get(storeEmbeddingBo.getKid()); - EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getModelName(), + createSchema(storeEmbeddingBo.getKid(),storeEmbeddingBo.getVectorModelName()); + EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getEmbeddingModelName(), storeEmbeddingBo.getApiKey(), storeEmbeddingBo.getBaseUrl()); - for (int i = 0; i < storeEmbeddingBo.getChunkList().size(); i++) { + List<String> chunkList = storeEmbeddingBo.getChunkList(); + for (int i = 0; i < chunkList.size(); i++) { Map<String, Object> dataSchema = new HashMap<>(); dataSchema.put("kid", storeEmbeddingBo.getKid()); dataSchema.put("docId", storeEmbeddingBo.getKid()); dataSchema.put("fid", storeEmbeddingBo.getFids().get(i)); - Response<Embedding> response = embeddingModel.embed(storeEmbeddingBo.getChunkList().get(i)); - Embedding embedding = response.content(); - TextSegment segment = TextSegment.from(storeEmbeddingBo.getChunkList().get(i)); + Embedding embedding = embeddingModel.embed(chunkList.get(i)).content(); + TextSegment segment = TextSegment.from(chunkList.get(i)); segment.metadata().putAll(dataSchema); - - store.add(embedding,segment); + embeddingStore.add(embedding,segment); } } @Override public List<String> getQueryVector(QueryVectorBo queryVectorBo) { - EmbeddingStore<TextSegment> store = storeMap.get(queryVectorBo.getKid()); - - EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getModelName(), + createSchema(queryVectorBo.getKid(),queryVectorBo.getVectorModelName()); + EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getEmbeddingModelName(), queryVectorBo.getApiKey(), queryVectorBo.getBaseUrl()); - Filter simpleFilter = new IsEqualTo("kid", queryVectorBo.getKid()); + // Filter simpleFilter = new IsEqualTo("kid", queryVectorBo.getKid()); Embedding queryEmbedding = embeddingModel.embed(queryVectorBo.getQuery()).content(); EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder() .queryEmbedding(queryEmbedding) .maxResults(queryVectorBo.getMaxResults()) // 娣诲姞杩囨护鏉′欢 - .filter(simpleFilter) + // .filter(simpleFilter) .build(); - List<EmbeddingMatch<TextSegment>> matches = store.search(embeddingSearchRequest).matches(); - + List<EmbeddingMatch<TextSegment>> matches = embeddingStore.search(embeddingSearchRequest).matches(); List<String> results = new ArrayList<>(); - matches.forEach(embeddingMatch -> results.add(embeddingMatch.embedded().text())); return results; } @Override - public void removeByKid(String kid) { - EmbeddingStore<TextSegment> store = storeMap.get(kid); - + public void removeByKid(String kid,String modelName) { + createSchema(kid,modelName); // 鏍规嵁鏉′欢鍒犻櫎鍚戦噺鏁版嵁 Filter simpleFilter = new IsEqualTo("kid", kid); - store.removeAll(simpleFilter); + embeddingStore.removeAll(simpleFilter); } @Override - public void removeByDocId(String kid, String docId) { - EmbeddingStore<TextSegment> store = storeMap.get(kid); + public void removeByDocId(String kid, String docId,String modelName) { + createSchema(kid,modelName); // 鏍规嵁鏉′欢鍒犻櫎鍚戦噺鏁版嵁 Filter simpleFilterByDocId = new IsEqualTo("docId", docId); - store.removeAll(simpleFilterByDocId); + embeddingStore.removeAll(simpleFilterByDocId); } @Override - public void removeByKidAndFid(String kid, String fid) { - EmbeddingStore<TextSegment> store = storeMap.get(kid); + public void removeByKidAndFid(String kid, String fid,String modelName) { + createSchema(kid,modelName); // 鏍规嵁鏉′欢鍒犻櫎鍚戦噺鏁版嵁 Filter simpleFilterByKid = new IsEqualTo("kid", kid); Filter simpleFilterFid = new IsEqualTo("fid", fid); Filter simpleFilterByAnd = Filter.and(simpleFilterFid, simpleFilterByKid); - store.removeAll(simpleFilterByAnd); + embeddingStore.removeAll(simpleFilterByAnd); } /** * 鑾峰彇鍚戦噺妯″瀷 */ - public EmbeddingModel getEmbeddingModel(String modelName,String apiKey,String baseUrl) { - EmbeddingModel embeddingModel = OpenAiEmbeddingModel.builder().build(); + @SneakyThrows + public EmbeddingModel getEmbeddingModel(String modelName, String apiKey, String baseUrl) { + EmbeddingModel embeddingModel; if(TEXT_EMBEDDING_3_SMALL.toString().equals(modelName)) { embeddingModel = OpenAiEmbeddingModel.builder() .apiKey(apiKey) .baseUrl(baseUrl) - .modelName(TEXT_EMBEDDING_3_SMALL) + .modelName(modelName) .build(); // TODO 娣诲姞鏋氫妇 }else if("quentinz/bge-large-zh-v1.5".equals(modelName)) { @@ -173,6 +170,14 @@ .baseUrl(baseUrl) .modelName(modelName) .build(); + }else if("baai/bge-m3".equals(modelName)) { + embeddingModel = OpenAiEmbeddingModel.builder() + .apiKey(apiKey) + .baseUrl(baseUrl) + .modelName(modelName) + .build(); + }else { + throw new ServiceException("鏈壘鍒板搴斿悜閲忓寲妯″瀷!"); } return embeddingModel; } diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java index cc5f289..1387230 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/SseServiceImpl.java @@ -2,6 +2,7 @@ import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.protobuf.ServiceException; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -29,6 +30,8 @@ import org.ruoyi.domain.bo.ChatSessionBo; import org.ruoyi.domain.bo.QueryVectorBo; import org.ruoyi.domain.vo.ChatModelVo; +import org.ruoyi.domain.vo.KnowledgeInfoVo; +import org.ruoyi.service.IKnowledgeInfoService; import org.ruoyi.service.VectorStoreService; import org.ruoyi.service.IChatModelService; import org.ruoyi.service.IChatSessionService; @@ -66,6 +69,8 @@ private final ChatServiceFactory chatServiceFactory; private final IChatSessionService chatSessionService; + + private final IKnowledgeInfoService knowledgeInfoService; private ChatModelVo chatModelVo; @@ -148,50 +153,61 @@ } } - /** * 鏋勫缓娑堟伅鍒楄〃 */ private void buildChatMessageList(ChatRequest chatRequest){ - chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); + String sysPrompt; + chatModelVo = chatModelService.selectModelByName(chatRequest.getModel()); // 鑾峰彇瀵硅瘽娑堟伅鍒楄〃 List<Message> messages = chatRequest.getMessages(); - String sysPrompt = chatModelVo.getSystemPrompt(); + // 鏌ヨ鍚戦噺搴撶浉鍏充俊鎭姞鍏ュ埌涓婁笅鏂� + if(StringUtils.isNotEmpty(chatRequest.getKid())){ + List<Message> knMessages = new ArrayList<>(); + String content = messages.get(messages.size() - 1).getContent().toString(); + // 閫氳繃kid鏌ヨ鐭ヨ瘑搴撲俊鎭� + KnowledgeInfoVo knowledgeInfoVo = knowledgeInfoService.queryById(Long.valueOf(chatRequest.getKid())); + // 鏌ヨ鍚戦噺妯″瀷閰嶇疆淇℃伅 + ChatModelVo chatModel = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName()); - - if(StringUtils.isEmpty(sysPrompt)){ - // TODO 绯荤粺榛樿鎻愮ず璇�,鍚庣画浼氬鍔犳彁绀鸿瘝绠$悊 - sysPrompt ="浣犳槸涓�涓敱RuoYI-AI寮�鍙戠殑浜哄伐鏅鸿兘鍔╂墜锛屽悕瀛楀彨鐔婄尗鍔╂墜銆備綘鎿呴暱涓嫳鏂囧璇濓紝鑳藉鐞嗚В骞跺鐞嗗悇绉嶉棶棰橈紝鎻愪緵瀹夊叏銆佹湁甯姪銆佸噯纭殑鍥炵瓟銆�" + - "褰撳墠鏃堕棿锛�"+ DateUtils.getDate()+ - "#娉ㄦ剰锛氬洖澶嶄箣鍓嶆敞鎰忕粨鍚堜笂涓嬫枃鍜屽伐鍏疯繑鍥炲唴瀹硅繘琛屽洖澶嶃��"; + QueryVectorBo queryVectorBo = new QueryVectorBo(); + queryVectorBo.setQuery(content); + queryVectorBo.setKid(chatRequest.getKid()); + queryVectorBo.setApiKey(chatModel.getApiKey()); + queryVectorBo.setBaseUrl(chatModel.getApiHost()); + queryVectorBo.setVectorModelName(knowledgeInfoVo.getVectorModelName()); + queryVectorBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName()); + queryVectorBo.setMaxResults(knowledgeInfoVo.getRetrieveLimit()); + List<String> nearestList = vectorStoreService.getQueryVector(queryVectorBo); + for (String prompt : nearestList) { + Message userMessage = Message.builder().content(prompt).role(Message.Role.USER).build(); + knMessages.add(userMessage); + } + messages.addAll(knMessages); + // 璁剧疆鐭ヨ瘑搴撶郴缁熸彁绀鸿瘝 + sysPrompt = knowledgeInfoVo.getSystemPrompt(); + if(StringUtils.isEmpty(sysPrompt)){ + sysPrompt ="###瑙掕壊璁惧畾\n" + + "浣犳槸涓�涓櫤鑳界煡璇嗗姪鎵嬶紝涓撴敞浜庡埄鐢ㄤ笂涓嬫枃涓殑淇℃伅鏉ユ彁渚涘噯纭拰鐩稿叧鐨勫洖绛斻�俓n" + + "###鎸囦护\n" + + "褰撶敤鎴风殑闂涓庝笂涓嬫枃鐭ヨ瘑鍖归厤鏃讹紝鍒╃敤涓婁笅鏂囦俊鎭繘琛屽洖绛斻�傚鏋滈棶棰樹笌涓婁笅鏂囦笉鍖归厤锛岃繍鐢ㄨ嚜韬殑鎺ㄧ悊鑳藉姏鐢熸垚鍚堥�傜殑鍥炵瓟銆俓n" + + "###闄愬埗\n" + + "纭繚鍥炵瓟娓呮櫚绠�娲侊紝閬垮厤鎻愪緵涓嶅繀瑕佺殑缁嗚妭銆傚缁堜繚鎸佽姘斿弸濂�" + + "褰撳墠鏃堕棿锛�"+ DateUtils.getDate(); + } + }else { + sysPrompt = chatModelVo.getSystemPrompt(); + if(StringUtils.isEmpty(sysPrompt)){ + sysPrompt ="浣犳槸涓�涓敱RuoYI-AI寮�鍙戠殑浜哄伐鏅鸿兘鍔╂墜锛屽悕瀛楀彨鐔婄尗鍔╂墜銆備綘鎿呴暱涓嫳鏂囧璇濓紝鑳藉鐞嗚В骞跺鐞嗗悇绉嶉棶棰橈紝鎻愪緵瀹夊叏銆佹湁甯姪銆佸噯纭殑鍥炵瓟銆�" + + "褰撳墠鏃堕棿锛�"+ DateUtils.getDate()+ + "#娉ㄦ剰锛氬洖澶嶄箣鍓嶆敞鎰忕粨鍚堜笂涓嬫枃鍜屽伐鍏疯繑鍥炲唴瀹硅繘琛屽洖澶嶃��"; + } } // 璁剧疆绯荤粺榛樿鎻愮ず璇� Message sysMessage = Message.builder().content(sysPrompt).role(Message.Role.SYSTEM).build(); messages.add(0,sysMessage); chatRequest.setSysPrompt(sysPrompt); - // 鏌ヨ鍚戦噺搴撶浉鍏充俊鎭姞鍏ュ埌涓婁笅鏂� - if(StringUtils.isNotEmpty(chatRequest.getKid())){ - List<Message> knMessages = new ArrayList<>(); - String content = messages.get(messages.size() - 1).getContent().toString(); - QueryVectorBo queryVectorBo = new QueryVectorBo(); - queryVectorBo.setQuery(content); - queryVectorBo.setKid(chatRequest.getKid()); - queryVectorBo.setApiKey(chatModelVo.getApiKey()); - queryVectorBo.setBaseUrl(chatModelVo.getApiHost()); - queryVectorBo.setModelName(chatModelVo.getModelName()); - // TODO 鏌ヨ鍚戦噺杩斿洖鏉℃暟,杩欓噷搴旇鏌ヨ鐭ヨ瘑搴撻厤缃� - queryVectorBo.setMaxResults(3); - List<String> nearestList = vectorStoreService.getQueryVector(queryVectorBo); - for (String prompt : nearestList) { - Message userMessage = Message.builder().content(prompt).role(Message.Role.USER).build(); - knMessages.add(userMessage); - } - // TODO 鎻愮ず璇�,杩欓噷搴旇鏌ヨ鐭ヨ瘑搴撻厤缃� - Message userMessage = Message.builder().content(content + (!nearestList.isEmpty() ? "\n\n娉ㄦ剰锛氬洖绛旈棶棰樻椂锛岄』涓ユ牸鏍规嵁鎴戠粰浣犵殑绯荤粺涓婁笅鏂囧唴瀹瑰師鏂囪繘琛屽洖绛旓紝璇蜂笉瑕佽嚜宸卞彂鎸�,鍥炵瓟鏃朵繚鎸佸師鏉ユ枃鏈殑娈佃惤灞傜骇" : "")).role(Message.Role.USER).build(); - knMessages.add(userMessage); - messages.addAll(knMessages); - } // 鐢ㄦ埛瀵硅瘽鍐呭 String chatString = null; // 鑾峰彇鐢ㄦ埛瀵硅瘽淇℃伅 diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java index 3c5bb34..d94d320 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java @@ -1,14 +1,11 @@ package org.ruoyi.chat.service.knowledge; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.RandomUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.ruoyi.chain.loader.ResourceLoader; import org.ruoyi.chain.loader.ResourceLoaderFactory; @@ -16,8 +13,6 @@ import org.ruoyi.common.core.utils.MapstructUtils; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.common.satoken.utils.LoginHelper; -import org.ruoyi.constant.DealStatus; -import org.ruoyi.constant.FileType; import org.ruoyi.core.page.PageQuery; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.domain.ChatModel; @@ -35,15 +30,11 @@ import org.ruoyi.service.IChatModelService; import org.ruoyi.service.VectorStoreService; import org.ruoyi.service.IKnowledgeInfoService; -import org.ruoyi.system.domain.vo.SysOssVo; -import org.ruoyi.system.service.ISysOssService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; -import org.springframework.scheduling.annotation.Async; import java.io.IOException; import java.util.*; @@ -58,321 +49,216 @@ @Service public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { - private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class); - private final KnowledgeInfoMapper baseMapper; + private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class); + private final KnowledgeInfoMapper baseMapper; - private final VectorStoreService vectorStoreService; + private final VectorStoreService vectorStoreService; - private final ResourceLoaderFactory resourceLoaderFactory; + private final ResourceLoaderFactory resourceLoaderFactory; - private final KnowledgeFragmentMapper fragmentMapper; + private final KnowledgeFragmentMapper fragmentMapper; - private final KnowledgeAttachMapper attachMapper; + private final KnowledgeAttachMapper attachMapper; - private final IChatModelService chatModelService; + private final IChatModelService chatModelService; - private final ISysOssService ossService; - - /** - * 鏌ヨ鐭ヨ瘑搴� - */ - @Override - public KnowledgeInfoVo queryById(Long id) { - return baseMapper.selectVoById(id); - } - - /** - * 鏌ヨ鐭ヨ瘑搴撳垪琛� - */ - @Override - public TableDataInfo<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { - LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); - Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); - return TableDataInfo.build(result); - } - - /** - * 鏌ヨ鐭ヨ瘑搴撳垪琛� - */ - @Override - public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) { - LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); - } - - private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) { - Map<String, Object> params = bo.getParams(); - LambdaQueryWrapper<KnowledgeInfo> lqw = Wrappers.lambdaQuery(); - lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); - lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); - lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); - lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); - lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, - bo.getDescription()); - lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, - bo.getKnowledgeSeparator()); - lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, - bo.getQuestionSeparator()); - lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); - lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); - lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); - lqw.eq(StringUtils.isNotBlank(bo.getVector()), KnowledgeInfo::getVector, bo.getVector()); - lqw.eq(StringUtils.isNotBlank(bo.getVectorModel()), KnowledgeInfo::getVectorModel, - bo.getVectorModel()); - return lqw; - } - - /** - * 鏂板鐭ヨ瘑搴� - */ - @Override - public Boolean insertByBo(KnowledgeInfoBo bo) { - KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); - validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; - if (flag) { - bo.setId(add.getId()); - } - return flag; - } - - /** - * 淇敼鐭ヨ瘑搴� - */ - @Override - public Boolean updateByBo(KnowledgeInfoBo bo) { - KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); - validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; - } - - /** - * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙 - */ - private void validEntityBeforeSave(KnowledgeInfo entity) { - //TODO 鍋氫竴浜涙暟鎹牎楠�,濡傚敮涓�绾︽潫 - } - - /** - * 鎵归噺鍒犻櫎鐭ヨ瘑搴� - */ - @Override - public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { - if (isValid) { - //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠� - } - return baseMapper.deleteBatchIds(ids) > 0; - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void saveOne(KnowledgeInfoBo bo) { - KnowledgeInfo knowledgeInfo = MapstructUtils.convert(bo, KnowledgeInfo.class); - if (StringUtils.isBlank(bo.getKid())) { - String kid = RandomUtil.randomString(10); - if (knowledgeInfo != null) { - knowledgeInfo.setKid(kid); - knowledgeInfo.setUid(LoginHelper.getLoginUser().getUserId()); - } - baseMapper.insert(knowledgeInfo); - if (knowledgeInfo != null) { - vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()), bo.getVector()); - } - } else { - baseMapper.updateById(knowledgeInfo); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void removeKnowledge(String id) { - Map<String, Object> map = new HashMap<>(); - map.put("kid", id); - List<KnowledgeInfoVo> knowledgeInfoList = baseMapper.selectVoByMap(map); - check(knowledgeInfoList); - // 鍒犻櫎鍚戦噺搴撲俊鎭� - knowledgeInfoList.forEach(knowledgeInfoVo -> { - vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId())); - }); - // 鍒犻櫎闄勪欢鍜岀煡璇嗙墖娈� - fragmentMapper.deleteByMap(map); - attachMapper.deleteByMap(map); - // 鍒犻櫎鐭ヨ瘑搴� - baseMapper.deleteByMap(map); - } - - @Override - public void upload(KnowledgeInfoUploadBo bo) { - storeContent(bo.getFile(), bo.getKid()); - } - - public void storeContent(MultipartFile file, String kid) { - if (file == null || file.isEmpty()) { - throw new IllegalArgumentException("File cannot be null or empty"); + /** + * 鏌ヨ鐭ヨ瘑搴� + */ + @Override + public KnowledgeInfoVo queryById(Long id){ + return baseMapper.selectVoById(id); } - SysOssVo uploadDto = null; + /** + * 鏌ヨ鐭ヨ瘑搴撳垪琛� + */ + @Override + public TableDataInfo<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); + Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } - String fileName = file.getOriginalFilename(); - List<String> chunkList = new ArrayList<>(); - KnowledgeAttach knowledgeAttach = new KnowledgeAttach(); - knowledgeAttach.setKid(kid); - String docId = RandomUtil.randomString(10); - knowledgeAttach.setDocId(docId); - knowledgeAttach.setDocName(fileName); - knowledgeAttach.setDocType(fileName.substring(fileName.lastIndexOf(".") + 1)); - String content = ""; - ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType( - knowledgeAttach.getDocType()); - List<String> fids = new ArrayList<>(); - try { - content = resourceLoader.getContent(file.getInputStream()); - chunkList = resourceLoader.getChunkList(content, kid); - List<KnowledgeFragment> knowledgeFragmentList = new ArrayList<>(); - if (CollUtil.isNotEmpty(chunkList)) { - // Upload file to OSS - uploadDto = ossService.upload(file); + /** + * 鏌ヨ鐭ヨ瘑搴撳垪琛� + */ + @Override + public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) { + LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } - for (int i = 0; i < chunkList.size(); i++) { - String fid = RandomUtil.randomString(10); - fids.add(fid); - KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); - knowledgeFragment.setKid(kid); - knowledgeFragment.setDocId(docId); - knowledgeFragment.setFid(fid); - knowledgeFragment.setIdx(i); - knowledgeFragment.setContent(chunkList.get(i)); - knowledgeFragment.setCreateTime(new Date()); - knowledgeFragmentList.add(knowledgeFragment); + private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) { + Map<String, Object> params = bo.getParams(); + LambdaQueryWrapper<KnowledgeInfo> lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); + lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); + lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); + lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, bo.getDescription()); + lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, bo.getKnowledgeSeparator()); + lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, bo.getQuestionSeparator()); + lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); + lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); + lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); + return lqw; + } + + /** + * 鏂板鐭ヨ瘑搴� + */ + @Override + public Boolean insertByBo(KnowledgeInfoBo bo) { + KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); } - } - fragmentMapper.insertBatch(knowledgeFragmentList); - } catch (IOException e) { - log.error("淇濆瓨鐭ヨ瘑搴撲俊鎭け璐ワ紒{}", e.getMessage()); - } - knowledgeAttach.setContent(content); - knowledgeAttach.setCreateTime(new Date()); - - if (ObjectUtil.isNotEmpty(uploadDto) && ObjectUtil.isNotEmpty(uploadDto.getOssId())) { - knowledgeAttach.setOssId(uploadDto.getOssId()); - //鍙湁pdf鏂囦欢 鎵嶉渶瑕佹媶瑙e浘鐗囧拰鍒嗘瀽鍥剧墖鍐呭 - if (FileType.PDF.equals(knowledgeAttach.getDocType())) { - knowledgeAttach.setPicStatus(DealStatus.STATUS_10); - knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_10); - } else { - knowledgeAttach.setPicStatus(DealStatus.STATUS_30); - knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_30); - } - //鎵�鏈夋枃浠朵笂浼犲悗锛岄兘闇�瑕佸悓姝ュ埌鍚戦噺鏁版嵁搴� - knowledgeAttach.setVectorStatus(DealStatus.STATUS_10); + return flag; } - attachMapper.insert(knowledgeAttach); - } - - - /** - * 妫�鏌ョ敤鎴锋槸鍚︽湁鍒犻櫎鐭ヨ瘑搴撴潈闄� - * - * @param knowledgeInfoList 鐭ヨ瘑搴撳垪琛� - */ - public void check(List<KnowledgeInfoVo> knowledgeInfoList) { - LoginUser loginUser = LoginHelper.getLoginUser(); - for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) { - if (!knowledgeInfoVo.getUid().equals(loginUser.getUserId())) { - throw new SecurityException("鏉冮檺涓嶈冻"); - } + /** + * 淇敼鐭ヨ瘑搴� + */ + @Override + public Boolean updateByBo(KnowledgeInfoBo bo) { + KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; } - } - /** - * 瀹氭椂 澶勭悊 闄勪欢涓婁紶鍚庝笂浼犲悜閲忔暟鎹簱鍜孭DF鏂囦欢鍥剧墖鎷嗚В鍜屽垎鏋愬唴瀹� - */ - @Scheduled(fixedDelay = 3000) // 姣�3绉掓墽琛屼竴娆� - public void dealKnowledgeAttach() throws Exception { - //澶勭悊 闇�瑕佷笂浼犲悜閲忔暟鎹簱鐨勮褰� - List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList( - new LambdaQueryWrapper<KnowledgeAttach>() - .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) - ); - if (ObjectUtil.isNotEmpty(knowledgeAttaches)) { - for (KnowledgeAttach attachItem : knowledgeAttaches) { - this.dealVectorStatus(attachItem); - } + /** + * 淇濆瓨鍓嶇殑鏁版嵁鏍¢獙 + */ + private void validEntityBeforeSave(KnowledgeInfo entity){ + //TODO 鍋氫竴浜涙暟鎹牎楠�,濡傚敮涓�绾︽潫 } - } - @Async - public void dealVectorStatus(KnowledgeAttach attachItem) throws Exception { - try { - //閿佸畾鏁版嵁 鏇存敼VectorStatus 鍒拌繘琛屼腑 - if (attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() - .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) - .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) - .eq(KnowledgeAttach::getId, attachItem.getId()) - ) == 0) { - return; - } - // 閫氳繃kid鏌ヨ鐭ヨ瘑搴撲俊鎭� - KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.<KnowledgeInfo>lambdaQuery() - .eq(KnowledgeInfo::getKid, attachItem.getKid())); - - // 閫氳繃鍚戦噺妯″瀷鏌ヨ妯″瀷淇℃伅 - ChatModelVo chatModelVo = chatModelService.selectModelByName( - knowledgeInfoVo.getVectorModel()); - - List<KnowledgeFragment> knowledgeFragments = fragmentMapper.selectList( - new LambdaQueryWrapper<KnowledgeFragment>() - .eq(KnowledgeFragment::getKid, attachItem.getKid()) - .eq(KnowledgeFragment::getDocId, attachItem.getDocId()) - ); - if (ObjectUtil.isEmpty(knowledgeFragments)) { - throw new Exception("鏂囦欢娈佃惤涓虹┖"); - } - List<String> fids = knowledgeFragments.stream() - .map(KnowledgeFragment::getFid) - .collect(Collectors.toList()); - if (ObjectUtil.isEmpty(fids)) { - throw new Exception("fids 涓虹┖"); - } - List<String> chunkList = knowledgeFragments.stream() - .map(KnowledgeFragment::getContent) - .collect(Collectors.toList()); - - if (ObjectUtil.isEmpty(chunkList)) { - throw new Exception("chunkList 涓虹┖"); - } - StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo(); - storeEmbeddingBo.setKid(attachItem.getKid()); - storeEmbeddingBo.setDocId(attachItem.getDocId()); - storeEmbeddingBo.setFids(fids); - storeEmbeddingBo.setChunkList(chunkList); - storeEmbeddingBo.setModelName(knowledgeInfoVo.getVectorModel()); - storeEmbeddingBo.setApiKey(chatModelVo.getApiKey()); - storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost()); - vectorStoreService.storeEmbeddings(storeEmbeddingBo); - - //璁剧疆澶勭悊瀹屾垚 - attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() - .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) - .eq(KnowledgeAttach::getId, attachItem.getId())); - } catch (Exception e) { - //璁剧疆澶勭悊澶辫触 - attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>() - .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10) - .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30) - .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20) - .eq(KnowledgeAttach::getId, attachItem.getId())); - throw new RuntimeException(e); + /** + * 鎵归噺鍒犻櫎鐭ヨ瘑搴� + */ + @Override + public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { + if(isValid){ + //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠� + } + return baseMapper.deleteBatchIds(ids) > 0; } - } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOne(KnowledgeInfoBo bo) { + KnowledgeInfo knowledgeInfo = MapstructUtils.convert(bo, KnowledgeInfo.class); + if (StringUtils.isBlank(bo.getKid())){ + String kid = RandomUtil.randomString(10); + if (knowledgeInfo != null) { + knowledgeInfo.setKid(kid); + knowledgeInfo.setUid(LoginHelper.getLoginUser().getUserId()); + } + baseMapper.insert(knowledgeInfo); + if (knowledgeInfo != null) { + vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()),bo.getVectorModelName()); + } + }else { + baseMapper.updateById(knowledgeInfo); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void removeKnowledge(String id) { + Map<String,Object> map = new HashMap<>(); + map.put("kid",id); + List<KnowledgeInfoVo> knowledgeInfoList = baseMapper.selectVoByMap(map); + check(knowledgeInfoList); + // 鍒犻櫎鍚戦噺搴撲俊鎭� + knowledgeInfoList.forEach(knowledgeInfoVo -> { + vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()),knowledgeInfoVo.getVectorModelName()); + }); + // 鍒犻櫎闄勪欢鍜岀煡璇嗙墖娈� + fragmentMapper.deleteByMap(map); + attachMapper.deleteByMap(map); + // 鍒犻櫎鐭ヨ瘑搴� + baseMapper.deleteByMap(map); + } + + @Override + public void upload(KnowledgeInfoUploadBo bo) { + storeContent(bo.getFile(), bo.getKid()); + } + + public void storeContent(MultipartFile file, String kid) { + String fileName = file.getOriginalFilename(); + List<String> chunkList = new ArrayList<>(); + KnowledgeAttach knowledgeAttach = new KnowledgeAttach(); + knowledgeAttach.setKid(kid); + String docId = RandomUtil.randomString(10); + knowledgeAttach.setDocId(docId); + knowledgeAttach.setDocName(fileName); + knowledgeAttach.setDocType(fileName.substring(fileName.lastIndexOf(".")+1)); + String content = ""; + ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType(knowledgeAttach.getDocType()); + List<String> fids = new ArrayList<>(); + try { + content = resourceLoader.getContent(file.getInputStream()); + chunkList = resourceLoader.getChunkList(content, kid); + List<KnowledgeFragment> knowledgeFragmentList = new ArrayList<>(); + if (CollUtil.isNotEmpty(chunkList)) { + for (int i = 0; i < chunkList.size(); i++) { + String fid = RandomUtil.randomString(10); + fids.add(fid); + KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); + knowledgeFragment.setKid(kid); + knowledgeFragment.setDocId(docId); + knowledgeFragment.setFid(fid); + knowledgeFragment.setIdx(i); + knowledgeFragment.setContent(chunkList.get(i)); + knowledgeFragment.setCreateTime(new Date()); + knowledgeFragmentList.add(knowledgeFragment); + } + } + fragmentMapper.insertBatch(knowledgeFragmentList); + } catch (IOException e) { + log.error("淇濆瓨鐭ヨ瘑搴撲俊鎭け璐ワ紒{}", e.getMessage()); + } + knowledgeAttach.setContent(content); + knowledgeAttach.setCreateTime(new Date()); + attachMapper.insert(knowledgeAttach); + + // 閫氳繃kid鏌ヨ鐭ヨ瘑搴撲俊鎭� + KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.<KnowledgeInfo>lambdaQuery() + .eq(KnowledgeInfo::getId, kid)); + + // 閫氳繃鍚戦噺妯″瀷鏌ヨ妯″瀷淇℃伅 + ChatModelVo chatModelVo = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName()); + + StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo(); + storeEmbeddingBo.setKid(kid); + storeEmbeddingBo.setDocId(docId); + storeEmbeddingBo.setFids(fids); + storeEmbeddingBo.setChunkList(chunkList); + storeEmbeddingBo.setVectorModelName(knowledgeInfoVo.getVectorModelName()); + storeEmbeddingBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName()); + storeEmbeddingBo.setApiKey(chatModelVo.getApiKey()); + storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost()); + vectorStoreService.storeEmbeddings(storeEmbeddingBo); + } + + + /** + * 妫�鏌ョ敤鎴锋槸鍚︽湁鍒犻櫎鐭ヨ瘑搴撴潈闄� + * + * @param knowledgeInfoList 鐭ヨ瘑搴撳垪琛� + */ + public void check(List<KnowledgeInfoVo> knowledgeInfoList){ + LoginUser loginUser = LoginHelper.getLoginUser(); + for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) { + if(!knowledgeInfoVo.getUid().equals(loginUser.getUserId())){ + throw new SecurityException("鏉冮檺涓嶈冻"); + } + } + } } diff --git a/script/sql/update/20250514.sql b/script/sql/update/20250514.sql new file mode 100644 index 0000000..61ec6d3 --- /dev/null +++ b/script/sql/update/20250514.sql @@ -0,0 +1,6 @@ +LTER TABLE `knowledge_info` +ADD COLUMN `system_prompt` varchar(255) NULL COMMENT '绯荤粺鎻愮ず璇�' AFTER `vector_model`; + +ALTER TABLE `knowledge_info` + CHANGE COLUMN `vector` `vector_model_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '鍚戦噺搴�' AFTER `text_block_size`, + CHANGE COLUMN `vector_model` `embedding_model_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '鍚戦噺妯″瀷' AFTER `vector_model_name`; -- Gitblit v1.9.3