Procházet zdrojové kódy

写入章节内容

lamphua před 1 dnem
rodič
revize
2ea9dbfe2a

+ 5
- 4
llm-back/ruoyi-agent/pom.xml Zobrazit soubor

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

+ 76
- 14
llm-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java Zobrazit soubor

@@ -3,6 +3,7 @@ package com.ruoyi.agent.service.impl;
3 3
 import com.alibaba.fastjson2.JSONObject;
4 4
 import com.ruoyi.agent.service.IMcpService;
5 5
 import dev.langchain4j.data.document.Document;
6
+import dev.langchain4j.data.document.parser.TextDocumentParser;
6 7
 import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
7 8
 import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
8 9
 import dev.langchain4j.data.embedding.Embedding;
@@ -23,11 +24,12 @@ import io.milvus.param.collection.LoadCollectionParam;
23 24
 import io.milvus.param.collection.ReleaseCollectionParam;
24 25
 import io.milvus.param.dml.SearchParam;
25 26
 import io.milvus.response.SearchResultsWrapper;
27
+import org.apache.poi.hwpf.HWPFDocument;
26 28
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
27 29
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
28 30
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
31
+import org.apache.poi.xwpf.usermodel.XWPFRun;
29 32
 import org.noear.solon.Solon;
30
-import org.noear.solon.ai.annotation.PromptMapping;
31 33
 import org.noear.solon.ai.annotation.ToolMapping;
32 34
 import org.noear.solon.ai.chat.ChatModel;
33 35
 import org.noear.solon.ai.chat.ChatResponse;
@@ -37,9 +39,7 @@ import org.noear.solon.ai.chat.message.AssistantMessage;
37 39
 import org.noear.solon.ai.chat.message.ChatMessage;
38 40
 import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
39 41
 import org.noear.solon.annotation.Param;
40
-import org.reactivestreams.Publisher;
41 42
 import org.springframework.stereotype.Service;
42
-import reactor.core.publisher.Flux;
43 43
 
44 44
 import java.io.*;
45 45
 import java.util.*;
@@ -60,13 +60,14 @@ public class McpServiceImpl implements IMcpService {
60 60
      * 调用LLM+RAG(外部文件+知识库)生成回答
61 61
      */
62 62
     @ToolMapping(description = "章节撰写")
63
-    public AssistantMessage writeParagraph(@Param(description = "智能体名称") String collectionName,
64
-                                                 @Param(description = "章节名称") String title,
65
-                                                 @Param(description = "招标文件地址") String templatePath) throws IOException
63
+    public AssistantMessage writeParagraph(@Param(description = "知识库名称") String collectionName,
64
+                                           @Param(description = "智能体名称") String agentName,
65
+                                           @Param(description = "章节名称") String title,
66
+                                           @Param(description = "技术文件地址") String templatePath) throws IOException
66 67
     {
67 68
         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");
69
+        List<JSONObject> contexts = retrieveFromMilvus(milvusClient, embeddingModel, collectionName, title, 10);
70
+        return generateAnswerWithDocumentAndCollection(embeddingModel, agentName, templatePath, title, contexts, "http://192.168.28.188:8000/v1/chat/completions");
70 71
     }
71 72
 
72 73
     /**
@@ -88,9 +89,9 @@ public class McpServiceImpl implements IMcpService {
88 89
     /**
89 90
      * 调用LLM生成回答
90 91
      */
91
-    public AssistantMessage generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String templatePath, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
92
+    public AssistantMessage generateAnswerWithDocumentAndCollection(EmbeddingModel embeddingModel, String agentName, String templatePath, String question, List<JSONObject> contexts, String llmServiceUrl) throws IOException {
92 93
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
93
-        File profilePath = new File(Solon.cfg().getProperty("cmc.profile") + templatePath);
94
+        File profilePath = new File(templatePath.replace("/dev-api/profile", Solon.cfg().getProperty("cmc.profile")).replace("_" + agentName, ""));
94 95
         List<TextSegment> segments = splitDocument(profilePath);
95 96
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
96 97
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
@@ -112,14 +113,14 @@ public class McpServiceImpl implements IMcpService {
112 113
 //                    .append("段落格式").append(": ")
113 114
 //                    .append(context.getString("content")).append("\n\n");
114 115
 //        }
115
-        return generateAnswer(sb.toString(), llmServiceUrl);
116
+        return generateAnswer(sb.toString(), question, templatePath, llmServiceUrl);
116 117
     }
117 118
 
118 119
     /**
119 120
      * 调用LLM生成回答
120 121
      * @return
121 122
      */
122
-    public AssistantMessage generateAnswer(String prompt, String llmServiceUrl) throws IOException {
123
+    public AssistantMessage generateAnswer(String prompt, String question, String templatePath, String llmServiceUrl) throws IOException {
123 124
         ChatSession chatSession = new ChatSessionDefault();
124 125
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
125 126
                 .provider("openai")
@@ -129,7 +130,61 @@ public class McpServiceImpl implements IMcpService {
129 130
         chatSession.addMessage(ChatMessage.ofUser(prompt));
130 131
 
131 132
         ChatResponse response = chatModel.prompt(chatSession).call();
132
-        return response.lastChoice().getMessage();
133
+        String content = response.lastChoice().getMessage().getContent() + "\n\n" +
134
+                "招标文件分析完成,章节内容已写入【<a href='" + templatePath.replace("/dev-api", "") + "'> 技术文件" + "</a>】,请查阅";
135
+        String absolutePath = templatePath.replace("/dev-api/profile", Solon.cfg().getProperty("cmc.profile"));
136
+        writeContent(response.lastChoice().getMessage().getContent(), question, absolutePath);
137
+        return ChatMessage.ofAssistant(content);
138
+    }
139
+
140
+    /**
141
+     * 写入章节内容
142
+     * @return
143
+     */
144
+    public void writeContent(String content, String question, String absolutePath) throws IOException {
145
+        File file = new File(absolutePath);
146
+        FileInputStream fileInputStream = new FileInputStream(file);
147
+        XWPFDocument document = new XWPFDocument(fileInputStream);
148
+        String[] contentLines = content.split("\n");
149
+        Map<String, String> map = new HashMap<>();
150
+        String[] titles = question.split(",");
151
+        for (int i = 0; i < titles.length; i ++) {
152
+            int startIndex = Arrays.asList(contentLines).indexOf(titles[i]);
153
+            StringBuilder text = new StringBuilder("");
154
+            if (i < titles.length - 1) {
155
+                int endIndex = Arrays.asList(contentLines).indexOf(titles[i + 1]);
156
+                for (int c = startIndex + 1; c < endIndex; c++)
157
+                    text.append(contentLines[c]);
158
+            }
159
+            else {
160
+                if (startIndex + 1 < contentLines.length)
161
+                    for (int c = startIndex + 1; c < contentLines.length; c++)
162
+                        text.append(contentLines[c]);
163
+            }
164
+            map.put(titles[i], text.toString());
165
+        }
166
+        int count = 0;
167
+        for (int i = 0; i < document.getParagraphs().size(); i++) {
168
+            XWPFParagraph paragraph = document.getParagraphs().get(i);
169
+            for (String title : titles) {
170
+                if (paragraph.getText().equals(title)) {
171
+                    int pos = document.getBodyElements().indexOf(paragraph) + 1;
172
+                    XWPFParagraph contentParagraph = document.createParagraph();
173
+                    contentParagraph.setStyle("1");
174
+                    XWPFRun run = contentParagraph.createRun();
175
+                    run.setText(map.get(title));
176
+                    document.setParagraph(contentParagraph, pos);
177
+                    count++;
178
+                }
179
+            }
180
+            if (count == titles.length)
181
+                break;
182
+        }
183
+        FileOutputStream out = new FileOutputStream(absolutePath);
184
+        document.write(out);
185
+        // 关闭文档
186
+        out.close();
187
+        document.close();
133 188
     }
134 189
 
135 190
     /**
@@ -216,7 +271,11 @@ public class McpServiceImpl implements IMcpService {
216 271
         Document document;
217 272
         InputStream fileInputStream = new FileInputStream(transferFile);
218 273
         String filename = transferFile.getName().toLowerCase();
219
-        if (filename.endsWith(".docx")) {
274
+        if (filename.endsWith(".doc")) {
275
+            HWPFDocument doc = new HWPFDocument(fileInputStream);
276
+            document = Document.from(doc.getDocumentText());
277
+        }
278
+        else if (filename.endsWith(".docx")) {
220 279
             XWPFDocument docx = new XWPFDocument(fileInputStream);
221 280
             XWPFWordExtractor extractor = new XWPFWordExtractor(docx);
222 281
             String text = extractor.getText();
@@ -225,6 +284,9 @@ public class McpServiceImpl implements IMcpService {
225 284
         else if (filename.endsWith(".pdf")) {
226 285
             document = new ApachePdfBoxDocumentParser().parse(fileInputStream);
227 286
         }
287
+        else if (filename.endsWith(".txt")) {
288
+            document = new TextDocumentParser().parse(fileInputStream);
289
+        }
228 290
         else {
229 291
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
230 292
         }

+ 1
- 1
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/CmcTopicController.java Zobrazit soubor

@@ -121,7 +121,7 @@ public class CmcTopicController extends BaseController
121 121
                 String[] documentIds = new String[cmcDocumentList.size()];
122 122
                 for (int i = 0; i < cmcDocumentList.size(); i++)
123 123
                     documentIds[i] = cmcDocumentList.get(i).getDocumentId();
124
-                cmcChatService.deleteCmcChatByChatIds(documentIds);
124
+                cmcDocumentService.deleteCmcDocumentByDocumentIds(documentIds);
125 125
             }
126 126
         }
127 127
         return success(cmcTopicService.deleteCmcTopicByTopicIds(topicIds));

+ 12
- 2
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/McpController.java Zobrazit soubor

@@ -4,7 +4,9 @@ import com.alibaba.fastjson2.JSONObject;
4 4
 import com.ruoyi.common.config.RuoYiConfig;
5 5
 import com.ruoyi.common.core.controller.BaseController;
6 6
 import com.ruoyi.llm.domain.CmcChat;
7
+import com.ruoyi.llm.service.ICmcAgentService;
7 8
 import com.ruoyi.llm.service.ICmcChatService;
9
+import com.ruoyi.llm.service.ICmcTopicService;
8 10
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
9 11
 import dev.langchain4j.model.embedding.EmbeddingModel;
10 12
 import dev.langchain4j.model.embedding.onnx.bgesmallzhv15.BgeSmallZhV15EmbeddingModel;
@@ -17,7 +19,6 @@ import org.noear.solon.ai.chat.ChatSessionDefault;
17 19
 import org.noear.solon.ai.chat.message.AssistantMessage;
18 20
 import org.noear.solon.ai.chat.message.ChatMessage;
19 21
 import org.noear.solon.ai.mcp.client.McpClientProvider;
20
-import org.reactivestreams.Publisher;
21 22
 import org.springframework.beans.factory.annotation.Autowired;
22 23
 import org.springframework.web.bind.annotation.GetMapping;
23 24
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -45,6 +46,12 @@ public class McpController extends BaseController
45 46
     @Autowired
46 47
     private ICmcChatService cmcChatService;
47 48
 
49
+    @Autowired
50
+    private ICmcTopicService cmcTopicService;
51
+
52
+    @Autowired
53
+    private ICmcAgentService cmcAgentService;
54
+
48 55
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
49 56
 
50 57
     private static final MilvusServiceClient milvusClient = new MilvusServiceClient(
@@ -114,8 +121,11 @@ public class McpController extends BaseController
114 121
             JSONObject jsonObject = JSONObject.parseObject(content);
115 122
             String name = jsonObject.getString("name");
116 123
             JSONObject arguments = jsonObject.getJSONObject("arguments");
117
-            if (arguments.getString("templatePath").contains("招标") || arguments.getString("templatePath").contains("询价"))
124
+            if (arguments.getString("templatePath").contains("招标") || arguments.getString("templatePath").contains("询价")) {
118 125
                 arguments.put("collectionName", "technical");
126
+                String agentName = cmcAgentService.selectCmcAgentByAgentId(cmcTopicService.selectCmcTopicByTopicId(topicId).getAgentId()).getAgentName();
127
+                arguments.put("agentName", agentName);
128
+            }
119 129
             resultContent = clientProvider.callToolAsText(name, arguments).getContent();
120 130
             assistantMessage = new AssistantMessage(resultContent);
121 131
         }

+ 10
- 3
llm-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java Zobrazit soubor

@@ -4,11 +4,11 @@ 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;
8 7
 import com.ruoyi.llm.service.ICmcChatService;
9 8
 import com.ruoyi.llm.service.ICmcDocumentService;
10 9
 import com.ruoyi.web.llm.service.ILangChainMilvusService;
11 10
 import dev.langchain4j.data.document.Document;
11
+import dev.langchain4j.data.document.parser.TextDocumentParser;
12 12
 import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
13 13
 import dev.langchain4j.data.document.splitter.DocumentByParagraphSplitter;
14 14
 import dev.langchain4j.data.embedding.Embedding;
@@ -28,10 +28,10 @@ import io.milvus.param.collection.ReleaseCollectionParam;
28 28
 import io.milvus.param.dml.InsertParam;
29 29
 import io.milvus.param.dml.SearchParam;
30 30
 import io.milvus.response.SearchResultsWrapper;
31
+import org.apache.poi.hwpf.HWPFDocument;
31 32
 import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
32 33
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
33 34
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
34
-import org.checkerframework.checker.units.qual.C;
35 35
 import org.noear.solon.ai.chat.ChatModel;
36 36
 import org.noear.solon.ai.chat.ChatResponse;
37 37
 import org.noear.solon.ai.chat.ChatSession;
@@ -347,7 +347,11 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
347 347
         Document document;
348 348
         InputStream fileInputStream = new FileInputStream(transferFile);
349 349
         filename = filename.toLowerCase();
350
-        if (filename.endsWith(".docx")) {
350
+        if (filename.endsWith(".doc")) {
351
+            HWPFDocument doc = new HWPFDocument(fileInputStream);
352
+            document = Document.from(doc.getDocumentText());
353
+        }
354
+        else if (filename.endsWith(".docx")) {
351 355
             XWPFDocument docx = new XWPFDocument(fileInputStream);
352 356
             XWPFWordExtractor extractor = new XWPFWordExtractor(docx);
353 357
             String text = extractor.getText();
@@ -356,6 +360,9 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
356 360
         else if (filename.endsWith(".pdf")) {
357 361
             document = new ApachePdfBoxDocumentParser().parse(fileInputStream);
358 362
         }
363
+        else if (filename.endsWith(".txt")) {
364
+            document = new TextDocumentParser().parse(fileInputStream);
365
+        }
359 366
         else {
360 367
             throw new UnsupportedOperationException("不支持文件类型: " + filename);
361 368
         }

+ 5
- 3
llm-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java Zobrazit soubor

@@ -16,7 +16,6 @@ import com.ruoyi.llm.domain.CmcChat;
16 16
 import com.ruoyi.llm.domain.CmcDocument;
17 17
 import com.ruoyi.llm.mapper.CmcChatMapper;
18 18
 import com.ruoyi.llm.mapper.CmcDocumentMapper;
19
-import com.ruoyi.llm.service.ICmcChatService;
20 19
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
21 20
 import org.springframework.beans.factory.annotation.Autowired;
22 21
 import org.springframework.stereotype.Service;
@@ -99,7 +98,10 @@ public class CmcAgentServiceImpl implements ICmcAgentService
99 98
         JSONObject jsonObject = new JSONObject();
100 99
         jsonObject.put("chatId", chatId);
101 100
         String[] filenameSplit = file.getOriginalFilename().split("\\.");
102
-        String outputFilename = "/upload/agent/" + agentName + "/" + file.getOriginalFilename().replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName);
101
+        String outputFilename = "/upload/agent/" + agentName + "/" + file.getOriginalFilename()
102
+                .replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName);
103
+        if (file.getOriginalFilename().endsWith(".doc"))
104
+            outputFilename = outputFilename.replace(".doc", "docx");
103 105
         CmcDocument cmcDocument = new CmcDocument();
104 106
         cmcDocument.setDocumentId(new SnowFlake().generateId());
105 107
         cmcDocument.setChatId(chatId);
@@ -110,7 +112,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
110 112
             CmcChat cmcChat = new CmcChat();
111 113
             cmcChat.setChatId(jsonObject.getString("chatId"));
112 114
             cmcChat.setInputTime(new Date());
113
-            cmcChat.setInput("招标文件地址:" + outputFilename);
115
+            cmcChat.setInput("招标文件地址:" + "/upload/agent/" + agentName + "/" + file.getOriginalFilename());
114 116
             cmcChat.setUserId(SecurityUtils.getUserId());
115 117
             cmcChatMapper.insertCmcChat(cmcChat);
116 118
             XWPFDocument doc = new XWPFDocument(new FileInputStream(RuoYiConfig.getProfile() + "/upload/agent/template/technical.docx"));

Loading…
Zrušit
Uložit