Browse Source

自动获取mcp工具撰写技术标书文档内容

lamphua 2 days ago
parent
commit
b57fb1986a

+ 28
- 1
llm-back/ruoyi-agent/pom.xml View File

@@ -54,7 +54,34 @@
54 54
             <groupId>org.apache.poi</groupId>
55 55
             <artifactId>poi-ooxml</artifactId>
56 56
         </dependency>
57
-
57
+        <dependency>
58
+            <groupId>com.alibaba.fastjson2</groupId>
59
+            <artifactId>fastjson2</artifactId>
60
+        </dependency>
61
+        <dependency>
62
+            <groupId>dev.langchain4j</groupId>
63
+            <artifactId>langchain4j-embeddings-bge-small-zh-v15</artifactId>
64
+            <version>0.35.0</version>
65
+            <scope>compile</scope>
66
+        </dependency>
67
+        <dependency>
68
+            <groupId>io.milvus</groupId>
69
+            <artifactId>milvus-sdk-java</artifactId>
70
+            <version>2.3.3</version>
71
+            <scope>compile</scope>
72
+        </dependency>
73
+        <dependency>
74
+            <groupId>dev.langchain4j</groupId>
75
+            <artifactId>langchain4j</artifactId>
76
+            <version>0.35.0</version>
77
+            <scope>compile</scope>
78
+        </dependency>
79
+        <dependency>
80
+            <groupId>dev.langchain4j</groupId>
81
+            <artifactId>langchain4j-document-parser-apache-pdfbox</artifactId>
82
+            <version>0.35.0</version>
83
+            <scope>compile</scope>
84
+        </dependency>
58 85
     </dependencies>
59 86
 
60 87
     <dependencyManagement>

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

@@ -24,7 +24,7 @@ import java.util.List;
24 24
 public class McpServerConfig {
25 25
     @PostConstruct
26 26
     public void start() {
27
-        Solon.start(McpServerConfig.class, new String[]{});
27
+        Solon.start(McpServerConfig.class, new String[]{"--cfg=application.yml"});
28 28
     }
29 29
 
30 30
     @PreDestroy

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

@@ -1,26 +1,74 @@
1 1
 package com.ruoyi.agent.service.impl;
2 2
 
3
+import com.alibaba.fastjson2.JSONObject;
3 4
 import com.ruoyi.agent.service.IMcpService;
5
+import dev.langchain4j.data.document.Document;
6
+import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
7
+import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
8
+import dev.langchain4j.data.embedding.Embedding;
9
+import dev.langchain4j.data.segment.TextSegment;
10
+import dev.langchain4j.model.embedding.EmbeddingModel;
11
+import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
12
+import dev.langchain4j.store.embedding.EmbeddingMatch;
13
+import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
14
+import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
15
+import io.milvus.client.MilvusClient;
16
+import io.milvus.client.MilvusServiceClient;
17
+import io.milvus.grpc.SearchResults;
18
+import io.milvus.param.ConnectParam;
19
+import io.milvus.param.MetricType;
20
+import io.milvus.param.R;
21
+import io.milvus.param.RpcStatus;
22
+import io.milvus.param.collection.LoadCollectionParam;
23
+import io.milvus.param.collection.ReleaseCollectionParam;
24
+import io.milvus.param.dml.SearchParam;
25
+import io.milvus.response.SearchResultsWrapper;
26
+import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
4 27
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
5 28
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
29
+import org.noear.solon.Solon;
6 30
 import org.noear.solon.ai.annotation.PromptMapping;
7
-import org.noear.solon.ai.annotation.ResourceMapping;
8 31
 import org.noear.solon.ai.annotation.ToolMapping;
32
+import org.noear.solon.ai.chat.ChatModel;
33
+import org.noear.solon.ai.chat.ChatResponse;
34
+import org.noear.solon.ai.chat.ChatSession;
35
+import org.noear.solon.ai.chat.ChatSessionDefault;
36
+import org.noear.solon.ai.chat.message.AssistantMessage;
9 37
 import org.noear.solon.ai.chat.message.ChatMessage;
10 38
 import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
11 39
 import org.noear.solon.annotation.Param;
40
+import org.reactivestreams.Publisher;
12 41
 import org.springframework.stereotype.Service;
42
+import reactor.core.publisher.Flux;
13 43
 
14
-import java.io.FileInputStream;
15
-import java.io.FileOutputStream;
16
-import java.io.IOException;
17
-import java.util.Collection;
18
-import java.util.Collections;
44
+import java.io.*;
45
+import java.util.*;
46
+import java.util.stream.Collectors;
19 47
 
20 48
 @Service
21 49
 @McpServerEndpoint(sseEndpoint = "/llm/mcp/sse")
22 50
 public class McpServiceImpl implements IMcpService {
23 51
 
52
+    private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
53
+
54
+    private static final MilvusServiceClient milvusClient = new MilvusServiceClient(
55
+            ConnectParam.newBuilder()
56
+                    .withHost("192.168.28.188")
57
+                    .withPort(19530)
58
+                    .build());
59
+    /**
60
+     * 调用LLM+RAG(外部文件+知识库)生成回答
61
+     */
62
+    @ToolMapping(description = "章节撰写")
63
+    public AssistantMessage writeParagraph(@Param(description = "智能体名称") String collectionName,
64
+                                                 @Param(description = "章节名称") String title,
65
+                                                 @Param(description = "招标文件地址") String templatePath) throws IOException
66
+    {
67
+        title = String.join(",", extractSubTitles( "/upload/agent/template/technical.docx", title));
68
+        List<JSONObject> requests = retrieveFromMilvus(milvusClient, embeddingModel, collectionName, title, 10);
69
+        return generateAnswerWithDocumentAndCollection(embeddingModel, templatePath, title, requests, "http://192.168.28.188:8000/v1/chat/completions");
70
+    }
71
+
24 72
     @ToolMapping(description = "梳理招标文件服务要求")
25 73
     public String getBidRequest(@Param(description = "文件路径") String document) throws IOException {
26 74
         try (XWPFDocument doc = new XWPFDocument(new FileInputStream(document))) {
@@ -66,4 +114,168 @@ public class McpServiceImpl implements IMcpService {
66 114
                         "回答以下问题:'" + part + "\n")
67 115
         );
68 116
     }
117
+
118
+    /**
119
+     * 从Milvus检索相关文档
120
+     * @return
121
+     */
122
+    public List<JSONObject> retrieveFromMilvus(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
123
+        SearchResultsWrapper wrapper = retrieve(milvusClient, embeddingModel, collectionName, query, topK);
124
+        return wrapper.getRowRecords(0).stream()
125
+                .map(record -> {
126
+                    JSONObject result = new JSONObject();
127
+                    result.put("file_name", record.get("file_name"));
128
+                    result.put("content", record.get("content"));
129
+                    return result;
130
+                })
131
+                .collect(Collectors.toList());
132
+    }
133
+
134
+    /**
135
+     * 调用LLM生成回答
136
+     */
137
+    public AssistantMessage generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String templatePath, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
138
+        StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
139
+        File profilePath = new File(Solon.cfg().getProperty("cmc.profile") + templatePath);
140
+        List<TextSegment> segments = splitDocument(profilePath);
141
+        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
142
+        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
143
+        embeddingStore.addAll(embeddings, segments);
144
+        Embedding queryEmbedding = embeddingModel.embed(question).content();
145
+        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
146
+                .queryEmbedding(queryEmbedding)
147
+                .maxResults(3)
148
+                .build();
149
+        for (EmbeddingMatch embeddingMatch : embeddingStore.search(embeddingSearchRequest).matches()) {
150
+            String requests = embeddingMatch.embedded().toString();
151
+            sb.append(requests).append("\n\n");
152
+
153
+        }
154
+        sb.append("针对本项目招标文件内容,补全以下章节部分:\n\n").append(question);
155
+//        for (JSONObject context : contexts) {
156
+//            sb.append("文件").append(": ")
157
+//                    .append(context.getString("file_name")).append("\n\n")
158
+//                    .append("段落格式").append(": ")
159
+//                    .append(context.getString("content")).append("\n\n");
160
+//        }
161
+        return generateAnswer(sb.toString(), llmServiceUrl);
162
+    }
163
+
164
+    /**
165
+     * 调用LLM生成回答
166
+     * @return
167
+     */
168
+    public AssistantMessage generateAnswer(String prompt, String llmServiceUrl) throws IOException {
169
+        ChatSession chatSession = new ChatSessionDefault();
170
+        ChatModel chatModel = ChatModel.of(llmServiceUrl)
171
+                .provider("openai")
172
+                .model("Qwen2.5-1.5B-Instruct")
173
+                .build();
174
+
175
+        chatSession.addMessage(ChatMessage.ofUser(prompt));
176
+
177
+        ChatResponse response = chatModel.prompt(chatSession).call();
178
+        return response.lastChoice().getMessage();
179
+    }
180
+
181
+    /**
182
+     * 获取二级标题下三级标题列表
183
+     */
184
+    public List<String> extractSubTitles(String filename, String question) throws IOException {
185
+        List<String> subTitles = new ArrayList<>();
186
+        boolean inTargetSection = false;
187
+        filename = Solon.cfg().getProperty("cmc.profile") + filename;
188
+        InputStream fileInputStream = new FileInputStream(filename);
189
+        XWPFDocument document = new XWPFDocument(fileInputStream);
190
+        for (XWPFParagraph paragraph : document.getParagraphs()) {
191
+            String text = paragraph.getText().trim();
192
+            if (paragraph.getStyle() != null) {
193
+                // 判断主标题
194
+                if (paragraph.getStyle().equals("3") &&
195
+                        text.contains(question)) {
196
+                    inTargetSection = true;
197
+                    continue;
198
+                }
199
+
200
+                // 如果已经在目标节中,收集标题3级别的子标题
201
+                if (inTargetSection) {
202
+                    if (paragraph.getStyle().equals("4")) {
203
+                        subTitles.add(text);
204
+                    }
205
+                    // 遇到下一个Heading1则退出
206
+                    else if (paragraph.getStyle().equals("3")) {
207
+                        break;
208
+                    }
209
+                }
210
+            }
211
+        }
212
+        if (subTitles.size() == 0)
213
+            subTitles.add(question);
214
+        return subTitles;
215
+    }
216
+
217
+    /**
218
+     * 检索知识库
219
+     */
220
+    private SearchResultsWrapper retrieve(MilvusClient milvusClient, EmbeddingModel embeddingModel, String collectionName, String query, int topK) {
221
+        List<List<Float>> queryVector = Collections.singletonList(embeddingModel.embed(query).content().vectorAsList());
222
+
223
+        //  加载集合
224
+        LoadCollectionParam loadParam = LoadCollectionParam.newBuilder()
225
+                .withCollectionName(collectionName)
226
+                .build();
227
+
228
+        R<RpcStatus> loadResponse = milvusClient.loadCollection(loadParam);
229
+        if (loadResponse.getStatus() != R.Status.Success.getCode()) {
230
+            System.err.println("加载Collection失败: " + loadResponse.getMessage());
231
+            milvusClient.close();
232
+        }
233
+
234
+        // 构建SearchParam
235
+        SearchParam searchParam = SearchParam.newBuilder()
236
+                .withCollectionName(collectionName)
237
+                .withVectors(queryVector)
238
+                .withTopK(topK)
239
+                .withOutFields(Arrays.asList("file_name", "file_type", "content"))
240
+                .withVectorFieldName("embedding")
241
+                .withMetricType(MetricType.COSINE)
242
+                .withParams("{\"nprobe\": 8}")
243
+                .build();
244
+
245
+        R<SearchResults> response = milvusClient.search(searchParam);
246
+        SearchResultsWrapper wrapper = new SearchResultsWrapper(response.getData().getResults());
247
+
248
+        // 释放集合
249
+        ReleaseCollectionParam param = ReleaseCollectionParam.newBuilder()
250
+                .withCollectionName(collectionName)
251
+                .build();
252
+        milvusClient.releaseCollection(param);
253
+
254
+        return wrapper;
255
+    }
256
+
257
+    /**
258
+     * 检索知识库
259
+     */
260
+    private List<TextSegment> splitDocument(File transferFile) throws IOException {
261
+        // 加载文档
262
+        Document document;
263
+        InputStream fileInputStream = new FileInputStream(transferFile);
264
+        String filename = transferFile.getName().toLowerCase();
265
+        if (filename.endsWith(".docx")) {
266
+            XWPFDocument docx = new XWPFDocument(fileInputStream);
267
+            XWPFWordExtractor extractor = new XWPFWordExtractor(docx);
268
+            String text = extractor.getText();
269
+            document = Document.from(text);
270
+        }
271
+        else if (filename.endsWith(".pdf")) {
272
+            document = new ApachePdfBoxDocumentParser().parse(fileInputStream);
273
+        }
274
+        else {
275
+            throw new UnsupportedOperationException("不支持文件类型: " + filename);
276
+        }
277
+        DocumentByParagraphSplitter splitter = new DocumentByParagraphSplitter(1000,200);
278
+        return splitter.split(document);
279
+    }
280
+
69 281
 }

+ 5
- 0
llm-back/ruoyi-agent/src/main/resources/application.yml View File

@@ -0,0 +1,5 @@
1
+# 项目相关配置
2
+cmc:
3
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
4
+  #  profile: /home/cmc/projects/cmc-llm
5
+  profile: E:/home/cmc/projects/cmc-llm

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

@@ -4,6 +4,10 @@ import java.util.List;
4 4
 import javax.servlet.http.HttpServletResponse;
5 5
 
6 6
 import com.ruoyi.common.utils.SnowFlake;
7
+import com.ruoyi.llm.domain.CmcChat;
8
+import com.ruoyi.llm.domain.CmcDocument;
9
+import com.ruoyi.llm.service.ICmcChatService;
10
+import com.ruoyi.llm.service.ICmcDocumentService;
7 11
 import org.springframework.beans.factory.annotation.Autowired;
8 12
 import org.springframework.web.bind.annotation.GetMapping;
9 13
 import org.springframework.web.bind.annotation.PostMapping;
@@ -35,6 +39,12 @@ public class CmcTopicController extends BaseController
35 39
     @Autowired
36 40
     private ICmcTopicService cmcTopicService;
37 41
 
42
+    @Autowired
43
+    private ICmcChatService cmcChatService;
44
+
45
+    @Autowired
46
+    private ICmcDocumentService cmcDocumentService;
47
+
38 48
     /**
39 49
      * 查询cmc聊天主题列表
40 50
      */
@@ -96,6 +106,24 @@ public class CmcTopicController extends BaseController
96 106
 	@DeleteMapping("/{topicIds}")
97 107
     public AjaxResult remove(@PathVariable String[] topicIds)
98 108
     {
109
+        for (String topicId : topicIds) {
110
+            CmcChat cmcChat = new CmcChat();
111
+            cmcChat.setTopicId(topicId);
112
+            List<CmcChat> cmcChatList = cmcChatService.selectCmcChatList(cmcChat);
113
+            String[] chatIds = new String[cmcChatList.size()];
114
+            for (int i = 0; i < cmcChatList.size(); i++)
115
+                chatIds[i] = cmcChatList.get(i).getChatId();
116
+            cmcChatService.deleteCmcChatByChatIds(chatIds);
117
+            for (String chatId : chatIds) {
118
+                CmcDocument cmcDocument = new CmcDocument();
119
+                cmcDocument.setChatId(chatId);
120
+                List<CmcDocument> cmcDocumentList = cmcDocumentService.selectCmcDocumentList(cmcDocument);
121
+                String[] documentIds = new String[cmcDocumentList.size()];
122
+                for (int i = 0; i < cmcDocumentList.size(); i++)
123
+                    documentIds[i] = cmcDocumentList.get(i).getDocumentId();
124
+                cmcChatService.deleteCmcChatByChatIds(documentIds);
125
+            }
126
+        }
99 127
         return success(cmcTopicService.deleteCmcTopicByTopicIds(topicIds));
100 128
     }
101 129
 }

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

@@ -3,6 +3,8 @@ package com.ruoyi.web.llm.controller;
3 3
 import com.alibaba.fastjson2.JSONObject;
4 4
 import com.ruoyi.common.config.RuoYiConfig;
5 5
 import com.ruoyi.common.core.controller.BaseController;
6
+import com.ruoyi.llm.domain.CmcChat;
7
+import com.ruoyi.llm.service.ICmcChatService;
6 8
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
7 9
 import dev.langchain4j.model.embedding.EmbeddingModel;
8 10
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
@@ -10,9 +12,12 @@ import io.milvus.client.MilvusServiceClient;
10 12
 import io.milvus.param.ConnectParam;
11 13
 import org.noear.solon.ai.chat.ChatModel;
12 14
 import org.noear.solon.ai.chat.ChatResponse;
15
+import org.noear.solon.ai.chat.ChatSession;
16
+import org.noear.solon.ai.chat.ChatSessionDefault;
13 17
 import org.noear.solon.ai.chat.message.AssistantMessage;
14 18
 import org.noear.solon.ai.chat.message.ChatMessage;
15 19
 import org.noear.solon.ai.mcp.client.McpClientProvider;
20
+import org.reactivestreams.Publisher;
16 21
 import org.springframework.beans.factory.annotation.Autowired;
17 22
 import org.springframework.web.bind.annotation.GetMapping;
18 23
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -37,6 +42,9 @@ public class McpController extends BaseController
37 42
     @Autowired
38 43
     private ILangChainMilvusService langChainMilvusService;
39 44
 
45
+    @Autowired
46
+    private ICmcChatService cmcChatService;
47
+
40 48
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
41 49
 
42 50
     private static final MilvusServiceClient milvusClient = new MilvusServiceClient(
@@ -44,47 +52,89 @@ public class McpController extends BaseController
44 52
                     .withHost("192.168.28.188")
45 53
                     .withPort(19530)
46 54
                     .build());
55
+//
56
+//    /**
57
+//     * 同步问答
58
+//     * @return
59
+//     */
60
+//    @GetMapping("/answer")
61
+//    public AssistantMessage answer(String question) throws IOException {
62
+//        McpClientProvider clientProvider = McpClientProvider.builder()
63
+//                .apiUrl("http://localhost:8080/llm/mcp/sse")
64
+//                .build();
65
+//        ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
66
+//                .provider("openai")
67
+//                .model("Qwen2.5-1.5B-Instruct")
68
+//                .defaultToolsAdd(clientProvider)
69
+//                .build();
70
+//        ChatResponse response = chatModel.prompt(question).call();
71
+//        String resultContent = response.lastChoice().getMessage().getResultContent();
72
+//        AssistantMessage assistantMessage = new AssistantMessage(resultContent);
73
+//        if (resultContent.startsWith("<tool_call>")) {
74
+//            String content = resultContent.replace("<tool_call>\n", "").replace("\n</tool_call>", "");
75
+//            JSONObject jsonObject = JSONObject.parseObject(content);
76
+//            String name = jsonObject.getString("name");
77
+//            JSONObject arguments = jsonObject.getJSONObject("arguments");
78
+//            resultContent = clientProvider.callToolAsText(name, arguments).getContent();
79
+//            assistantMessage = new AssistantMessage(resultContent);
80
+//        }
81
+//        return assistantMessage;
82
+//    }
47 83
 
48 84
     /**
49
-     * 同步问答
85
+     * 自动调用mcp工具问答
50 86
      * @return
51 87
      */
52 88
     @GetMapping("/answer")
53
-    public AssistantMessage answer(String question) throws IOException {
89
+    public AssistantMessage answer(String topicId, String question) throws IOException {
54 90
         McpClientProvider clientProvider = McpClientProvider.builder()
55 91
                 .apiUrl("http://localhost:8080/llm/mcp/sse")
56 92
                 .build();
93
+        ChatSession chatSession = new ChatSessionDefault(topicId);
57 94
         ChatModel chatModel = ChatModel.of("http://192.168.28.188:8000/v1/chat/completions")
58 95
                 .provider("openai")
59 96
                 .model("Qwen2.5-1.5B-Instruct")
60 97
                 .defaultToolsAdd(clientProvider)
61 98
                 .build();
62
-        ChatResponse response = chatModel.prompt(question).call();
99
+
100
+        CmcChat cmcChat = new CmcChat();
101
+        cmcChat.setTopicId(topicId);
102
+        List<CmcChat> cmcChatList = cmcChatService.selectCmcChatList(cmcChat);
103
+        for (CmcChat chat : cmcChatList) {
104
+            chatSession.addMessage(ChatMessage.ofUser(chat.getInput()));
105
+            chatSession.addMessage(ChatMessage.ofAssistant(chat.getOutput()));
106
+        }
107
+
108
+        chatSession.addMessage(ChatMessage.ofUser(question));
109
+        ChatResponse response = chatModel.prompt(chatSession).call();
63 110
         String resultContent = response.lastChoice().getMessage().getResultContent();
64
-        AssistantMessage assistantMessage = new AssistantMessage(resultContent);
111
+        AssistantMessage assistantMessage;
65 112
         if (resultContent.startsWith("<tool_call>")) {
66 113
             String content = resultContent.replace("<tool_call>\n", "").replace("\n</tool_call>", "");
67 114
             JSONObject jsonObject = JSONObject.parseObject(content);
68 115
             String name = jsonObject.getString("name");
69 116
             JSONObject arguments = jsonObject.getJSONObject("arguments");
117
+            if (arguments.getString("templatePath").contains("技术"))
118
+                arguments.put("collectionName", "technical");
70 119
             resultContent = clientProvider.callToolAsText(name, arguments).getContent();
71 120
             assistantMessage = new AssistantMessage(resultContent);
72 121
         }
122
+        else
123
+            throw new IOException("模型上下文工具未准备就绪,请重试");
73 124
         return assistantMessage;
74 125
     }
75 126
 
76 127
     /**
77
-     * 调用LLM+RAG(外部文件)生成回答
128
+     * 调用LLM+RAG(外部文件+知识库)生成回答
78 129
      */
79 130
     @GetMapping("/answerWithDocument")
80
-    public Flux<AssistantMessage> answerWithDocumentAndCollection(String collectionName, String topicId, String chatId, String question) throws IOException
131
+    public Flux<AssistantMessage> answerWithDocumentAndCollection(String collectionName, String topicId, String question) throws IOException
81 132
     {
82 133
         question = String.join(",", langChainMilvusService.extractSubTitles(RuoYiConfig.getProfile() + "/upload/agent/template/technical.docx", question));
83 134
         List<JSONObject> requests = langChainMilvusService.retrieveFromMilvus(milvusClient, embeddingModel, collectionName, question, 10);
84
-        return langChainMilvusService.generateAnswerWithDocumentAndCollection(embeddingModel, topicId, chatId, question, requests, "http://192.168.28.188:8000/v1/chat/completions");
135
+        return langChainMilvusService.generateAnswerWithDocumentAndCollection(embeddingModel, topicId, question, requests, "http://192.168.28.188:8000/v1/chat/completions");
85 136
     }
86 137
 
87
-
88 138
     /**
89 139
      * 根据招标文件要求编写投标文件技术方案
90 140
      * @return

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

@@ -53,7 +53,7 @@ public interface ILangChainMilvusService {
53 53
      * 调用LLM+RAG(外部文件+知识库)生成回答
54 54
      * @return
55 55
      */
56
-    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String topicId, String chatId, String question,  List<JSONObject> requests, String llmServiceUrl) throws IOException;
56
+    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String topicId, String question,  List<JSONObject> requests, String llmServiceUrl) throws IOException;
57 57
 
58 58
     /**
59 59
      * 获取二级标题下三级标题列表

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

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
4 4
 import com.ruoyi.common.config.RuoYiConfig;
5 5
 import com.ruoyi.llm.domain.CmcChat;
6 6
 import com.ruoyi.llm.domain.CmcDocument;
7
+import com.ruoyi.llm.domain.CmcTopic;
7 8
 import com.ruoyi.llm.service.ICmcChatService;
8 9
 import com.ruoyi.llm.service.ICmcDocumentService;
9 10
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
@@ -30,6 +31,7 @@ import io.milvus.response.SearchResultsWrapper;
30 31
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
31 32
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
32 33
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
34
+import org.checkerframework.checker.units.qual.C;
33 35
 import org.noear.solon.ai.chat.ChatModel;
34 36
 import org.noear.solon.ai.chat.ChatResponse;
35 37
 import org.noear.solon.ai.chat.ChatSession;
@@ -222,25 +224,32 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
222 224
      * 调用LLM生成回答
223 225
      */
224 226
     @Override
225
-    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String topicId, String chatId, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
227
+    public Flux<AssistantMessage> generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String topicId, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
226 228
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
227
-        CmcDocument cmcDocument = new CmcDocument();
228
-        cmcDocument.setChatId(chatId);
229
-        List<CmcDocument> documentList = cmcDocumentService.selectCmcDocumentList(cmcDocument);
230
-        for (CmcDocument document : documentList) {
231
-            File profilePath = new File(RuoYiConfig.getProfile() + document.getPath());
232
-            List<TextSegment> segments = splitDocument(document.getPath(), profilePath);
233
-            List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
234
-            InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
235
-            embeddingStore.addAll(embeddings, segments);
236
-            Embedding queryEmbedding = embeddingModel.embed(question).content();
237
-            EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
238
-                    .queryEmbedding(queryEmbedding)
239
-                    .maxResults(3)
240
-                    .build();
241
-            for (EmbeddingMatch embeddingMatch : embeddingStore.search(embeddingSearchRequest).matches()) {
242
-                String requests = embeddingMatch.embedded().toString();
243
-                sb.append(requests).append("\n\n");
229
+        CmcChat cmcChat = new CmcChat();
230
+        cmcChat.setTopicId(topicId);
231
+        for (CmcChat chat : cmcChatService.selectCmcChatList(cmcChat)) {
232
+            CmcDocument cmcDocument = new CmcDocument();
233
+            cmcDocument.setChatId(chat.getChatId());
234
+            List<CmcDocument> documentList = cmcDocumentService.selectCmcDocumentList(cmcDocument);
235
+            //技术标书撰写智能体一个话题只会上传一个招标文件
236
+            if (documentList.size() == 1) {
237
+                for (CmcDocument document : documentList) {
238
+                    File profilePath = new File(RuoYiConfig.getProfile() + document.getPath());
239
+                    List<TextSegment> segments = splitDocument(document.getPath(), profilePath);
240
+                    List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
241
+                    InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
242
+                    embeddingStore.addAll(embeddings, segments);
243
+                    Embedding queryEmbedding = embeddingModel.embed(question).content();
244
+                    EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
245
+                            .queryEmbedding(queryEmbedding)
246
+                            .maxResults(3)
247
+                            .build();
248
+                    for (EmbeddingMatch embeddingMatch : embeddingStore.search(embeddingSearchRequest).matches()) {
249
+                        String requests = embeddingMatch.embedded().toString();
250
+                        sb.append(requests).append("\n\n");
251
+                    }
252
+                }
244 253
             }
245 254
         }
246 255
         sb.append("针对本项目招标文件内容,补全以下章节部分:\n\n").append(question);

+ 14
- 0
llm-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java View File

@@ -4,14 +4,19 @@ import java.io.File;
4 4
 import java.io.FileInputStream;
5 5
 import java.io.FileOutputStream;
6 6
 import java.io.IOException;
7
+import java.util.Date;
7 8
 import java.util.List;
8 9
 
9 10
 import com.alibaba.fastjson2.JSONObject;
10 11
 import com.ruoyi.common.config.RuoYiConfig;
11 12
 import com.ruoyi.common.utils.DateUtils;
13
+import com.ruoyi.common.utils.SecurityUtils;
12 14
 import com.ruoyi.common.utils.SnowFlake;
15
+import com.ruoyi.llm.domain.CmcChat;
13 16
 import com.ruoyi.llm.domain.CmcDocument;
17
+import com.ruoyi.llm.mapper.CmcChatMapper;
14 18
 import com.ruoyi.llm.mapper.CmcDocumentMapper;
19
+import com.ruoyi.llm.service.ICmcChatService;
15 20
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
16 21
 import org.springframework.beans.factory.annotation.Autowired;
17 22
 import org.springframework.stereotype.Service;
@@ -35,6 +40,9 @@ public class CmcAgentServiceImpl implements ICmcAgentService
35 40
     @Autowired
36 41
     private CmcDocumentMapper cmcDocumentMapper;
37 42
 
43
+    @Autowired
44
+    private CmcChatMapper cmcChatMapper;
45
+
38 46
     /**
39 47
      * 查询智能体
40 48
      * 
@@ -99,6 +107,12 @@ public class CmcAgentServiceImpl implements ICmcAgentService
99 107
         cmcDocumentMapper.insertCmcDocument(cmcDocument);
100 108
         String message = "";
101 109
         if (agentName.contains("技术")) {
110
+            CmcChat cmcChat = new CmcChat();
111
+            cmcChat.setChatId(jsonObject.getString("chatId"));
112
+            cmcChat.setInputTime(new Date());
113
+            cmcChat.setInput("招标文件地址:" + outputFilename);
114
+            cmcChat.setUserId(SecurityUtils.getUserId());
115
+            cmcChatMapper.insertCmcChat(cmcChat);
102 116
             XWPFDocument doc = new XWPFDocument(new FileInputStream(RuoYiConfig.getProfile() + "/upload/agent/template/technical.docx"));
103 117
             // 保存文档到本地文件系统
104 118
             FileOutputStream out = new FileOutputStream(RuoYiConfig.getProfile() + outputFilename);

Loading…
Cancel
Save