Browse Source

去除相关度小于0.7的上下文参考

lamphua 2 weeks ago
parent
commit
4f8e15cf23

+ 5
- 0
llm-back/ruoyi-agent/pom.xml View File

@@ -50,6 +50,11 @@
50 50
             </exclusions>
51 51
         </dependency>
52 52
 
53
+        <dependency>
54
+            <groupId>org.apache.poi</groupId>
55
+            <artifactId>poi-ooxml</artifactId>
56
+        </dependency>
57
+
53 58
     </dependencies>
54 59
 
55 60
     <dependencyManagement>

+ 1
- 2
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java View File

@@ -14,7 +14,6 @@ import org.springframework.stereotype.Service;
14 14
 import java.io.FileInputStream;
15 15
 import java.io.FileOutputStream;
16 16
 import java.io.IOException;
17
-import java.util.Arrays;
18 17
 import java.util.Collection;
19 18
 import java.util.Collections;
20 19
 
@@ -41,7 +40,7 @@ public class McpServiceImpl implements IMcpService {
41 40
     @ToolMapping(description = "补全技术方案")
42 41
     public String writeTechnicalPlan(@Param(description = "文件路径") String document, @Param(description = "技术方案内容") String resultContent) throws IOException {
43 42
         // 创建临时输出文件路径
44
-        String outputPath = document.replace(".docx", "_modified.docx");
43
+        String outputPath = document.replace(".docx", "_修改" + System.currentTimeMillis() + ".docx");
45 44
 
46 45
         try (FileInputStream fis = new FileInputStream(document);
47 46
              XWPFDocument doc = new XWPFDocument(fis);

+ 34
- 26
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java View File

@@ -1,20 +1,19 @@
1 1
 package com.ruoyi.web.llm.controller;
2 2
 
3
+import com.alibaba.fastjson2.JSONObject;
4
+import com.ruoyi.common.config.RuoYiConfig;
3 5
 import com.ruoyi.common.core.controller.BaseController;
4
-import com.ruoyi.web.llm.service.ILangChainMilvusService;
5 6
 import org.noear.solon.ai.chat.ChatModel;
6 7
 import org.noear.solon.ai.chat.ChatResponse;
7 8
 import org.noear.solon.ai.chat.message.AssistantMessage;
8 9
 import org.noear.solon.ai.chat.message.ChatMessage;
9
-import org.noear.solon.ai.chat.tool.FunctionToolDesc;
10 10
 import org.noear.solon.ai.mcp.client.McpClientProvider;
11
-import org.reactivestreams.Publisher;
12
-import org.springframework.beans.factory.annotation.Autowired;
13 11
 import org.springframework.web.bind.annotation.GetMapping;
14 12
 import org.springframework.web.bind.annotation.RequestMapping;
15 13
 import org.springframework.web.bind.annotation.RestController;
16
-import reactor.core.publisher.Flux;
14
+import org.springframework.web.multipart.MultipartFile;
17 15
 
16
+import java.io.File;
18 17
 import java.io.IOException;
19 18
 import java.util.HashMap;
20 19
 import java.util.List;
@@ -31,31 +30,34 @@ import java.util.Map;
31 30
 @RequestMapping("/llm/mcp")
32 31
 public class McpController extends BaseController
33 32
 {
34
-    @Autowired
35
-    private ILangChainMilvusService langChainMilvusService;
36
-
37 33
     /**
38
-     * 打开word文档
34
+     * 同步问答
39 35
      * @return
40 36
      */
41 37
     @GetMapping("/answer")
42
-    public Flux<AssistantMessage> answer(String question) throws IOException {
38
+    public AssistantMessage answer(String question) throws IOException {
43 39
         McpClientProvider clientProvider = McpClientProvider.builder()
44
-                    .apiUrl("http://localhost:8080/llm/mcp/sse")
45
-                    .build();
40
+                .apiUrl("http://localhost:8080/llm/mcp/sse")
41
+                .build();
46 42
 
47 43
         ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
48 44
                 .provider("openai")
49
-                .model("DeepSeek-R1-Distill-Qwen-1.5B")
45
+                .model("Qwen2.5-1.5B-Instruct")
50 46
                 .apiKey("1")
51 47
                 .defaultToolsAdd(clientProvider)
52 48
                 .build();
53
-        Publisher<ChatResponse> publisher = chatModel.prompt(question).stream();
54
-
55
-        return Flux.from(publisher)
56
-                .map(response -> {
57
-                    return response.getChoices().get(0).getMessage();
58
-                });
49
+        ChatResponse response = chatModel.prompt(question).call();
50
+        String resultContent = response.lastChoice().getMessage().getResultContent();
51
+        AssistantMessage assistantMessage = new AssistantMessage(resultContent);
52
+        if (resultContent.startsWith("<tool_call>")) {
53
+            String content = resultContent.replace("<tool_call>\n", "").replace("\n</tool_call>", "");
54
+            JSONObject jsonObject = JSONObject.parseObject(content);
55
+            String name = jsonObject.getString("name");
56
+            JSONObject arguments = jsonObject.getJSONObject("arguments");
57
+            resultContent = clientProvider.callToolAsText(name, arguments).getContent();
58
+            assistantMessage = new AssistantMessage(resultContent);
59
+        }
60
+        return assistantMessage;
59 61
     }
60 62
 
61 63
     /**
@@ -63,7 +65,7 @@ public class McpController extends BaseController
63 65
      * @return
64 66
      */
65 67
     @GetMapping("/tech")
66
-    public AssistantMessage tech(String file) throws IOException {
68
+    public AssistantMessage tech(MultipartFile file) throws IOException {
67 69
         McpClientProvider clientProvider = McpClientProvider.builder()
68 70
                     .apiUrl("http://localhost:8080/llm/mcp/sse")
69 71
                     .build();
@@ -73,19 +75,25 @@ public class McpController extends BaseController
73 75
                 .apiKey("1")
74 76
                 .defaultToolsAdd(clientProvider)
75 77
                 .build();
76
-
78
+        File profilePath = new File( RuoYiConfig.getProfile() + "/upload/knowledge");
79
+        if (!profilePath.exists())
80
+            profilePath.mkdirs();
81
+        File transferFile = new File( profilePath + File.separator + file.getOriginalFilename());
82
+        if (!transferFile.exists()) {
83
+            file.transferTo(transferFile);
84
+        }
77 85
         Map<String,Object> path = new HashMap<>();
78
-        path.put("document", file);
86
+        path.put("document", transferFile.getPath());
79 87
         String content = clientProvider.callToolAsText("openDocument", path).getContent();
80 88
         Map<String,Object> request = new HashMap<>();
81 89
         request.put("request", content);
82 90
         List<ChatMessage> messages = clientProvider.getPromptAsMessages("askQuestion", request);
83 91
         ChatResponse response = chatModel.prompt(messages).call();
84 92
         System.out.println(response.getChoices().get(0).getMessage());
85
-        Map<String,Object> write = new HashMap<>();
86
-        write.put("document", file);
87
-        write.put("resultContent", response.getChoices().get(0).getMessage().getResultContent());
88
-        clientProvider.callToolAsText("writeTechnicalPlan", write);
93
+//        Map<String,Object> write = new HashMap<>();
94
+//        write.put("document", transferFile.getPath());
95
+//        write.put("resultContent", response.getChoices().get(0).getMessage().getResultContent());
96
+//        String res = clientProvider.callToolAsText("writeTechnicalPlan", write).getContent();
89 97
         return response.getChoices().get(0).getMessage();
90 98
     }
91 99
 

+ 13
- 11
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/RagController.java View File

@@ -1,14 +1,8 @@
1
-/*
2
- * @Author: ysh
3
- * @Date: 2025-07-08 15:10:42
4
- * @LastEditors: 
5
- * @LastEditTime: 2025-07-09 11:37:02
6
- */
7 1
 package com.ruoyi.web.llm.controller;
8 2
 
3
+import com.alibaba.fastjson2.JSONObject;
9 4
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
10 5
 import com.ruoyi.common.core.controller.BaseController;
11
-import com.ruoyi.common.core.domain.AjaxResult;
12 6
 import dev.langchain4j.model.embedding.EmbeddingModel;
13 7
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
14 8
 import io.milvus.client.MilvusServiceClient;
@@ -20,7 +14,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
20 14
 import org.springframework.web.bind.annotation.RestController;
21 15
 import reactor.core.publisher.Flux;
22 16
 
23
-import java.io.IOException;
24 17
 import java.util.List;
25 18
 
26 19
 /**
@@ -48,10 +41,19 @@ public class RagController extends BaseController
48 41
      * 增强检索生成回答
49 42
      */
50 43
     @GetMapping("/answer")
51
-    public Flux<AssistantMessage> answer(String question, String collectionName) throws IOException {
52
-
53
-        List<String> contexts = langChainMilvusService.retrieveFromMilvus(milvusClient, embeddingModel, collectionName, question, 1);
44
+    public Flux<AssistantMessage> answer(String question, String collectionName)
45
+    {
46
+        List<JSONObject> contexts = langChainMilvusService.retrieveFromMilvus(milvusClient, embeddingModel, collectionName, question, 10);
54 47
         return langChainMilvusService.generateAnswerWithRag(question, contexts, "http://192.168.28.188:8000/v1/chat/completions");
55 48
     }
56 49
 
50
+    /**
51
+     * 增强检索生成上下文
52
+     */
53
+    @GetMapping("/context")
54
+    public List<JSONObject> context(String question, String collectionName)
55
+    {
56
+        return langChainMilvusService.similarityFromMilvus(milvusClient, embeddingModel, collectionName, question, 10);
57
+    }
58
+
57 59
 }

+ 0
- 3
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/SessionController.java View File

@@ -1,7 +1,6 @@
1 1
 package com.ruoyi.web.llm.controller;
2 2
 
3 3
 import com.ruoyi.common.core.controller.BaseController;
4
-import com.ruoyi.common.core.domain.AjaxResult;
5 4
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
6 5
 import org.noear.solon.ai.chat.message.AssistantMessage;
7 6
 import org.springframework.beans.factory.annotation.Autowired;
@@ -10,8 +9,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
10 9
 import org.springframework.web.bind.annotation.RestController;
11 10
 import reactor.core.publisher.Flux;
12 11
 
13
-import java.io.IOException;
14
-
15 12
 /**
16 13
  * session对话Controller
17 14
  * 

+ 10
- 2
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/ILangChainMilvusService.java View File

@@ -1,5 +1,6 @@
1 1
 package com.ruoyi.web.llm.service;
2 2
 
3
+import com.alibaba.fastjson2.JSONObject;
3 4
 import dev.langchain4j.model.embedding.EmbeddingModel;
4 5
 import io.milvus.client.MilvusClient;
5 6
 import io.milvus.grpc.MutationResult;
@@ -20,14 +21,21 @@ public interface ILangChainMilvusService {
20 21
 
21 22
     /**
22 23
      * 从Milvus检索相关文档
24
+     * @return
25
+     */
26
+    public List<JSONObject> retrieveFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK);
27
+
28
+    /**
29
+     * 从Milvus检索相关文档及相关度
30
+     * @return
23 31
      */
24
-    public List<String> retrieveFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK);
32
+    public List<JSONObject> similarityFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK);
25 33
 
26 34
     /**
27 35
      * 调用LLM+RAG生成回答
28 36
      * @return
29 37
      */
30
-    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl);
38
+    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<JSONObject> contexts, String llmServiceUrl);
31 39
 
32 40
     /**
33 41
      * 调用LLM生成回答

+ 75
- 41
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java View File

@@ -6,6 +6,7 @@
6 6
  */
7 7
 package com.ruoyi.web.llm.service.impl;
8 8
 
9
+import com.alibaba.fastjson2.JSONObject;
9 10
 import com.ruoyi.common.config.RuoYiConfig;
10 11
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
11 12
 import dev.langchain4j.data.document.Document;
@@ -74,7 +75,7 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
74 75
         else {
75 76
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
76 77
         }
77
-        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1024,0);;
78
+        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1024,100);;
78 79
         List<TextSegment> segments = splitter.split(document);
79 80
 
80 81
         // 提取文本和生成嵌入
@@ -112,9 +113,80 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
112 113
 
113 114
     /**
114 115
      * 从Milvus检索相关文档
116
+     * @return
117
+     */
118
+    @Override
119
+    public List<JSONObject> retrieveFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
120
+        SearchResultsWrapper wrapper = retrieve(milvusClient, embeddingModel, collectionName, query, topK);
121
+        return wrapper.getRowRecords(0).stream()
122
+                .map(record -> {
123
+                    JSONObject result = new JSONObject();
124
+                    result.put("file_name", record.get("file_name"));
125
+                    result.put("content", record.get("content"));
126
+                    return result;
127
+                })
128
+                .collect(Collectors.toList());
129
+    }
130
+
131
+    /**
132
+     * 从Milvus检索相关文档及相关度
133
+     * @return
134
+     */
135
+    @Override
136
+    public List<JSONObject> similarityFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
137
+        SearchResultsWrapper wrapper = retrieve(milvusClient, embeddingModel, collectionName, query, topK);
138
+        List<JSONObject> wrapperList =  wrapper.getRowRecords(0).stream()
139
+                .map(record -> {
140
+                    JSONObject result = new JSONObject();
141
+                    result.put("distance", record.get("distance"));
142
+                    result.put("file_name", record.get("file_name"));
143
+                    result.put("content", record.get("content"));
144
+                    return result;
145
+                })
146
+                .collect(Collectors.toList());
147
+        wrapperList.removeIf(jsonObject -> jsonObject.getDouble("distance") < 0.7);
148
+        return wrapperList;
149
+    }
150
+
151
+    /**
152
+     * 调用LLM+RAG生成回答
153
+     * @return
154
+     */
155
+    @Override
156
+    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<JSONObject> contexts, String llmServiceUrl) {
157
+        StringBuilder sb = new StringBuilder();
158
+        sb.append("问题: ").append(question).append("\n\n");
159
+        sb.append("根据以下文件中的上下文内容回答问题:\n\n");
160
+        for (int i = 0; i < contexts.size(); i++) {
161
+            sb.append("文件").append(i + 1).append(": ")
162
+                    .append(contexts.get(i).getString("file_name")).append("\n\n")
163
+                    .append("上下文").append(": ")
164
+                    .append(contexts.get(i).getString("content")).append("\n\n");
165
+        }
166
+        // 构建带动态参数的URL
167
+        return generateAnswer(sb.toString(), llmServiceUrl);
168
+    }
169
+
170
+    /**
171
+     * 调用LLM生成回答
172
+     * @return
115 173
      */
116 174
     @Override
117
-    public List<String> retrieveFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
175
+    public Flux<AssistantMessage> generateAnswer(String prompt, String llmServiceUrl) {
176
+        ChatModel chatModel = ChatModel.of(llmServiceUrl)
177
+                .provider("openai")
178
+                .model("Qwen2.5-1.5B-Instruct")
179
+                .apiKey("1")
180
+                .build();
181
+        Publisher<ChatResponse> publisher = chatModel.prompt(prompt).stream();
182
+
183
+        return Flux.from(publisher)
184
+                .map(response -> {
185
+                    return response.lastChoice().getMessage();
186
+                });
187
+    }
188
+
189
+    private SearchResultsWrapper retrieve(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
118 190
         List<List<Float>> queryVector = Collections.singletonList(embeddingModel.embed(query).content().vectorAsList());
119 191
 
120 192
         //  加载集合
@@ -148,44 +220,6 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
148 220
                 .build();
149 221
         milvusClient.releaseCollection(param);
150 222
 
151
-        return wrapper.getRowRecords(0).stream()
152
-                .map(record -> (String) record.get("content"))
153
-                .collect(Collectors.toList());
154
-    }
155
-
156
-    /**
157
-     * 调用LLM+RAG生成回答
158
-     * @return
159
-     */
160
-    @Override
161
-    public Flux<AssistantMessage> generateAnswerWithRag(String question, List<String> contexts, String llmServiceUrl) {
162
-        StringBuilder sb = new StringBuilder();
163
-        sb.append("根据以下上下文回答问题:\n\n");
164
-        for (int i = 0; i < contexts.size(); i++) {
165
-            sb.append("上下文").append(i+1).append(": ").append(contexts.get(i)).append("\n\n");
166
-        }
167
-        sb.append("问题: ").append(question).append("\n回答: ");
168
-        // 构建带动态参数的URL
169
-        return generateAnswer(sb.toString(),llmServiceUrl);
223
+        return wrapper;
170 224
     }
171
-
172
-    /**
173
-     * 调用LLM生成回答
174
-     * @return
175
-     */
176
-    @Override
177
-    public Flux<AssistantMessage> generateAnswer(String prompt, String llmServiceUrl) {
178
-        ChatModel chatModel = ChatModel.of(llmServiceUrl)
179
-                .provider("openai")
180
-                .model("DeepSeek-R1-Distill-Qwen-1.5B")
181
-                .apiKey("1")
182
-                .build();
183
-        Publisher<ChatResponse> publisher = chatModel.prompt(prompt).stream();
184
-
185
-        return Flux.from(publisher)
186
-                .map(response -> {
187
-                    return response.getChoices().get(0).getMessage();
188
-                });
189
-    }
190
-
191 225
 }

Loading…
Cancel
Save