Browse Source

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

lamphua 2 weeks ago
parent
commit
4f8e15cf23

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

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

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

14
 import java.io.FileInputStream;
14
 import java.io.FileInputStream;
15
 import java.io.FileOutputStream;
15
 import java.io.FileOutputStream;
16
 import java.io.IOException;
16
 import java.io.IOException;
17
-import java.util.Arrays;
18
 import java.util.Collection;
17
 import java.util.Collection;
19
 import java.util.Collections;
18
 import java.util.Collections;
20
 
19
 
41
     @ToolMapping(description = "补全技术方案")
40
     @ToolMapping(description = "补全技术方案")
42
     public String writeTechnicalPlan(@Param(description = "文件路径") String document, @Param(description = "技术方案内容") String resultContent) throws IOException {
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
         try (FileInputStream fis = new FileInputStream(document);
45
         try (FileInputStream fis = new FileInputStream(document);
47
              XWPFDocument doc = new XWPFDocument(fis);
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
 package com.ruoyi.web.llm.controller;
1
 package com.ruoyi.web.llm.controller;
2
 
2
 
3
+import com.alibaba.fastjson2.JSONObject;
4
+import com.ruoyi.common.config.RuoYiConfig;
3
 import com.ruoyi.common.core.controller.BaseController;
5
 import com.ruoyi.common.core.controller.BaseController;
4
-import com.ruoyi.web.llm.service.ILangChainMilvusService;
5
 import org.noear.solon.ai.chat.ChatModel;
6
 import org.noear.solon.ai.chat.ChatModel;
6
 import org.noear.solon.ai.chat.ChatResponse;
7
 import org.noear.solon.ai.chat.ChatResponse;
7
 import org.noear.solon.ai.chat.message.AssistantMessage;
8
 import org.noear.solon.ai.chat.message.AssistantMessage;
8
 import org.noear.solon.ai.chat.message.ChatMessage;
9
 import org.noear.solon.ai.chat.message.ChatMessage;
9
-import org.noear.solon.ai.chat.tool.FunctionToolDesc;
10
 import org.noear.solon.ai.mcp.client.McpClientProvider;
10
 import org.noear.solon.ai.mcp.client.McpClientProvider;
11
-import org.reactivestreams.Publisher;
12
-import org.springframework.beans.factory.annotation.Autowired;
13
 import org.springframework.web.bind.annotation.GetMapping;
11
 import org.springframework.web.bind.annotation.GetMapping;
14
 import org.springframework.web.bind.annotation.RequestMapping;
12
 import org.springframework.web.bind.annotation.RequestMapping;
15
 import org.springframework.web.bind.annotation.RestController;
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
 import java.io.IOException;
17
 import java.io.IOException;
19
 import java.util.HashMap;
18
 import java.util.HashMap;
20
 import java.util.List;
19
 import java.util.List;
31
 @RequestMapping("/llm/mcp")
30
 @RequestMapping("/llm/mcp")
32
 public class McpController extends BaseController
31
 public class McpController extends BaseController
33
 {
32
 {
34
-    @Autowired
35
-    private ILangChainMilvusService langChainMilvusService;
36
-
37
     /**
33
     /**
38
-     * 打开word文档
34
+     * 同步问答
39
      * @return
35
      * @return
40
      */
36
      */
41
     @GetMapping("/answer")
37
     @GetMapping("/answer")
42
-    public Flux<AssistantMessage> answer(String question) throws IOException {
38
+    public AssistantMessage answer(String question) throws IOException {
43
         McpClientProvider clientProvider = McpClientProvider.builder()
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
         ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
43
         ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
48
                 .provider("openai")
44
                 .provider("openai")
49
-                .model("DeepSeek-R1-Distill-Qwen-1.5B")
45
+                .model("Qwen2.5-1.5B-Instruct")
50
                 .apiKey("1")
46
                 .apiKey("1")
51
                 .defaultToolsAdd(clientProvider)
47
                 .defaultToolsAdd(clientProvider)
52
                 .build();
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
      * @return
65
      * @return
64
      */
66
      */
65
     @GetMapping("/tech")
67
     @GetMapping("/tech")
66
-    public AssistantMessage tech(String file) throws IOException {
68
+    public AssistantMessage tech(MultipartFile file) throws IOException {
67
         McpClientProvider clientProvider = McpClientProvider.builder()
69
         McpClientProvider clientProvider = McpClientProvider.builder()
68
                     .apiUrl("http://localhost:8080/llm/mcp/sse")
70
                     .apiUrl("http://localhost:8080/llm/mcp/sse")
69
                     .build();
71
                     .build();
73
                 .apiKey("1")
75
                 .apiKey("1")
74
                 .defaultToolsAdd(clientProvider)
76
                 .defaultToolsAdd(clientProvider)
75
                 .build();
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
         Map<String,Object> path = new HashMap<>();
85
         Map<String,Object> path = new HashMap<>();
78
-        path.put("document", file);
86
+        path.put("document", transferFile.getPath());
79
         String content = clientProvider.callToolAsText("openDocument", path).getContent();
87
         String content = clientProvider.callToolAsText("openDocument", path).getContent();
80
         Map<String,Object> request = new HashMap<>();
88
         Map<String,Object> request = new HashMap<>();
81
         request.put("request", content);
89
         request.put("request", content);
82
         List<ChatMessage> messages = clientProvider.getPromptAsMessages("askQuestion", request);
90
         List<ChatMessage> messages = clientProvider.getPromptAsMessages("askQuestion", request);
83
         ChatResponse response = chatModel.prompt(messages).call();
91
         ChatResponse response = chatModel.prompt(messages).call();
84
         System.out.println(response.getChoices().get(0).getMessage());
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
         return response.getChoices().get(0).getMessage();
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
-/*
2
- * @Author: ysh
3
- * @Date: 2025-07-08 15:10:42
4
- * @LastEditors: 
5
- * @LastEditTime: 2025-07-09 11:37:02
6
- */
7
 package com.ruoyi.web.llm.controller;
1
 package com.ruoyi.web.llm.controller;
8
 
2
 
3
+import com.alibaba.fastjson2.JSONObject;
9
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
4
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
10
 import com.ruoyi.common.core.controller.BaseController;
5
 import com.ruoyi.common.core.controller.BaseController;
11
-import com.ruoyi.common.core.domain.AjaxResult;
12
 import dev.langchain4j.model.embedding.EmbeddingModel;
6
 import dev.langchain4j.model.embedding.EmbeddingModel;
13
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
7
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
14
 import io.milvus.client.MilvusServiceClient;
8
 import io.milvus.client.MilvusServiceClient;
20
 import org.springframework.web.bind.annotation.RestController;
14
 import org.springframework.web.bind.annotation.RestController;
21
 import reactor.core.publisher.Flux;
15
 import reactor.core.publisher.Flux;
22
 
16
 
23
-import java.io.IOException;
24
 import java.util.List;
17
 import java.util.List;
25
 
18
 
26
 /**
19
 /**
48
      * 增强检索生成回答
41
      * 增强检索生成回答
49
      */
42
      */
50
     @GetMapping("/answer")
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
         return langChainMilvusService.generateAnswerWithRag(question, contexts, "http://192.168.28.188:8000/v1/chat/completions");
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
 package com.ruoyi.web.llm.controller;
1
 package com.ruoyi.web.llm.controller;
2
 
2
 
3
 import com.ruoyi.common.core.controller.BaseController;
3
 import com.ruoyi.common.core.controller.BaseController;
4
-import com.ruoyi.common.core.domain.AjaxResult;
5
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
4
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
6
 import org.noear.solon.ai.chat.message.AssistantMessage;
5
 import org.noear.solon.ai.chat.message.AssistantMessage;
7
 import org.springframework.beans.factory.annotation.Autowired;
6
 import org.springframework.beans.factory.annotation.Autowired;
10
 import org.springframework.web.bind.annotation.RestController;
9
 import org.springframework.web.bind.annotation.RestController;
11
 import reactor.core.publisher.Flux;
10
 import reactor.core.publisher.Flux;
12
 
11
 
13
-import java.io.IOException;
14
-
15
 /**
12
 /**
16
  * session对话Controller
13
  * session对话Controller
17
  * 
14
  * 

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

1
 package com.ruoyi.web.llm.service;
1
 package com.ruoyi.web.llm.service;
2
 
2
 
3
+import com.alibaba.fastjson2.JSONObject;
3
 import dev.langchain4j.model.embedding.EmbeddingModel;
4
 import dev.langchain4j.model.embedding.EmbeddingModel;
4
 import io.milvus.client.MilvusClient;
5
 import io.milvus.client.MilvusClient;
5
 import io.milvus.grpc.MutationResult;
6
 import io.milvus.grpc.MutationResult;
20
 
21
 
21
     /**
22
     /**
22
      * 从Milvus检索相关文档
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
      * 调用LLM+RAG生成回答
35
      * 调用LLM+RAG生成回答
28
      * @return
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
      * 调用LLM生成回答
41
      * 调用LLM生成回答

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

6
  */
6
  */
7
 package com.ruoyi.web.llm.service.impl;
7
 package com.ruoyi.web.llm.service.impl;
8
 
8
 
9
+import com.alibaba.fastjson2.JSONObject;
9
 import com.ruoyi.common.config.RuoYiConfig;
10
 import com.ruoyi.common.config.RuoYiConfig;
10
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
11
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
11
 import dev.langchain4j.data.document.Document;
12
 import dev.langchain4j.data.document.Document;
74
         else {
75
         else {
75
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
76
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
76
         }
77
         }
77
-        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1024,0);;
78
+        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1024,100);;
78
         List<TextSegment> segments = splitter.split(document);
79
         List<TextSegment> segments = splitter.split(document);
79
 
80
 
80
         // 提取文本和生成嵌入
81
         // 提取文本和生成嵌入
112
 
113
 
113
     /**
114
     /**
114
      * 从Milvus检索相关文档
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
     @Override
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
         List<List<Float>> queryVector = Collections.singletonList(embeddingModel.embed(query).content().vectorAsList());
190
         List<List<Float>> queryVector = Collections.singletonList(embeddingModel.embed(query).content().vectorAsList());
119
 
191
 
120
         //  加载集合
192
         //  加载集合
148
                 .build();
220
                 .build();
149
         milvusClient.releaseCollection(param);
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