Quellcode durchsuchen

智能体页面修改

lamphua vor 16 Stunden
Ursprung
Commit
e837ca4aea

+ 3
- 0
oa-back/ruoyi-admin/src/main/java/com/ruoyi/web/controller/oa/CmcDeviceApprovalController.java Datei anzeigen

145
             cmcDeviceApproval.setDispatchTime(new Date());
145
             cmcDeviceApproval.setDispatchTime(new Date());
146
         }
146
         }
147
         if (formDataJson.getJSONArray("modifyDevices").size() > 0 && formDataJson.getString("managerComment") == null) {
147
         if (formDataJson.getJSONArray("modifyDevices").size() > 0 && formDataJson.getString("managerComment") == null) {
148
+            cmcDeviceApproval.setDispatcher(getLoginUser().getUserId());
149
+            cmcDeviceApproval.setDispatchComment(formDataJson.getString("dispatchComment"));
150
+            cmcDeviceApproval.setDispatchTime(new Date());
148
             String deviceString = formDataJson.getString("modifyDevices").substring(1, formDataJson.getString("modifyDevices").length() - 1);
151
             String deviceString = formDataJson.getString("modifyDevices").substring(1, formDataJson.getString("modifyDevices").length() - 1);
149
             cmcDeviceApproval.setModifyDevices(deviceString);
152
             cmcDeviceApproval.setModifyDevices(deviceString);
150
             if (!formDataJson.getString("projectId").equals("")) {
153
             if (!formDataJson.getString("projectId").equals("")) {

+ 18
- 2
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/controller/CmcAgentController.java Datei anzeigen

15
 import org.springframework.web.bind.annotation.RequestBody;
15
 import org.springframework.web.bind.annotation.RequestBody;
16
 import org.springframework.web.bind.annotation.RequestMapping;
16
 import org.springframework.web.bind.annotation.RequestMapping;
17
 import org.springframework.web.bind.annotation.RestController;
17
 import org.springframework.web.bind.annotation.RestController;
18
+
19
+import com.alibaba.fastjson2.JSONObject;
18
 import com.ruoyi.common.annotation.Log;
20
 import com.ruoyi.common.annotation.Log;
19
 import com.ruoyi.common.core.controller.BaseController;
21
 import com.ruoyi.common.core.controller.BaseController;
20
 import com.ruoyi.common.core.domain.AjaxResult;
22
 import com.ruoyi.common.core.domain.AjaxResult;
27
 
29
 
28
 /**
30
 /**
29
  * 智能体Controller
31
  * 智能体Controller
30
- * 
32
+ *
31
  * @author ruoyi
33
  * @author ruoyi
32
  * @date 2025-07-17
34
  * @date 2025-07-17
33
  */
35
  */
79
         return success(ChatMessage.ofAssistant(cmcAgentService.getOpening(agentName)));
81
         return success(ChatMessage.ofAssistant(cmcAgentService.getOpening(agentName)));
80
     }
82
     }
81
 
83
 
84
+    /**
85
+     * 保存目录到Word文件
86
+     */
87
+    @PostMapping("/writeTitles")
88
+    public AjaxResult writeTitles(@RequestBody JSONObject data) throws IOException
89
+    {
90
+        JSONObject result = cmcAgentService.writeTitles(data);
91
+        if (result.getIntValue("code") == 200) {
92
+            return AjaxResult.success(result.getString("message"));
93
+        } else {
94
+            return AjaxResult.error(result.getString("message"));
95
+        }
96
+    }
97
+
82
     /**
98
     /**
83
      * 上传单文件
99
      * 上传单文件
84
      * @return
100
      * @return
143
      * 删除智能体
159
      * 删除智能体
144
      */
160
      */
145
     @Log(title = "智能体", businessType = BusinessType.DELETE)
161
     @Log(title = "智能体", businessType = BusinessType.DELETE)
146
-	@DeleteMapping("/{agentIds}")
162
+    @DeleteMapping("/{agentIds}")
147
     public AjaxResult remove(@PathVariable Integer[] agentIds)
163
     public AjaxResult remove(@PathVariable Integer[] agentIds)
148
     {
164
     {
149
         return success(cmcAgentService.deleteCmcAgentByAgentIds(agentIds));
165
         return success(cmcAgentService.deleteCmcAgentByAgentIds(agentIds));

+ 16
- 8
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/ICmcAgentService.java Datei anzeigen

9
 
9
 
10
 /**
10
 /**
11
  * 智能体Service接口
11
  * 智能体Service接口
12
- * 
12
+ *
13
  * @author ruoyi
13
  * @author ruoyi
14
  * @date 2025-07-17
14
  * @date 2025-07-17
15
  */
15
  */
16
-public interface ICmcAgentService 
16
+public interface ICmcAgentService
17
 {
17
 {
18
     /**
18
     /**
19
      * 查询智能体
19
      * 查询智能体
20
-     * 
20
+     *
21
      * @param agentId 智能体主键
21
      * @param agentId 智能体主键
22
      * @return 智能体
22
      * @return 智能体
23
      */
23
      */
25
 
25
 
26
     /**
26
     /**
27
      * 查询智能体列表
27
      * 查询智能体列表
28
-     * 
28
+     *
29
      * @param cmcAgent 智能体
29
      * @param cmcAgent 智能体
30
      * @return 智能体集合
30
      * @return 智能体集合
31
      */
31
      */
72
 
72
 
73
     /**
73
     /**
74
      * 新增智能体
74
      * 新增智能体
75
-     * 
75
+     *
76
      * @param cmcAgent 智能体
76
      * @param cmcAgent 智能体
77
      * @return 结果
77
      * @return 结果
78
      */
78
      */
80
 
80
 
81
     /**
81
     /**
82
      * 修改智能体
82
      * 修改智能体
83
-     * 
83
+     *
84
      * @param cmcAgent 智能体
84
      * @param cmcAgent 智能体
85
      * @return 结果
85
      * @return 结果
86
      */
86
      */
88
 
88
 
89
     /**
89
     /**
90
      * 批量删除智能体
90
      * 批量删除智能体
91
-     * 
91
+     *
92
      * @param agentIds 需要删除的智能体主键集合
92
      * @param agentIds 需要删除的智能体主键集合
93
      * @return 结果
93
      * @return 结果
94
      */
94
      */
96
 
96
 
97
     /**
97
     /**
98
      * 删除智能体信息
98
      * 删除智能体信息
99
-     * 
99
+     *
100
      * @param agentId 智能体主键
100
      * @param agentId 智能体主键
101
      * @return 结果
101
      * @return 结果
102
      */
102
      */
103
     public int deleteCmcAgentByAgentId(Integer agentId);
103
     public int deleteCmcAgentByAgentId(Integer agentId);
104
+
105
+    /**
106
+     * 保存目录到Word文件
107
+     *
108
+     * @param data 目录数据
109
+     * @return 结果
110
+     */
111
+    public JSONObject writeTitles(JSONObject data) throws IOException;
104
 }
112
 }

+ 360
- 142
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java Datei anzeigen

1
 package com.ruoyi.llm.service.impl;
1
 package com.ruoyi.llm.service.impl;
2
 
2
 
3
 import com.alibaba.fastjson2.JSONObject;
3
 import com.alibaba.fastjson2.JSONObject;
4
+import com.alibaba.fastjson2.JSONArray;
4
 import com.ruoyi.common.config.RuoYiConfig;
5
 import com.ruoyi.common.config.RuoYiConfig;
5
 import com.ruoyi.common.utils.DateUtils;
6
 import com.ruoyi.common.utils.DateUtils;
6
 import com.ruoyi.common.utils.SecurityUtils;
7
 import com.ruoyi.common.utils.SecurityUtils;
7
 import com.ruoyi.common.utils.SnowFlake;
8
 import com.ruoyi.common.utils.SnowFlake;
8
 import com.ruoyi.llm.domain.CmcAgent;
9
 import com.ruoyi.llm.domain.CmcAgent;
9
 import com.ruoyi.llm.domain.CmcChat;
10
 import com.ruoyi.llm.domain.CmcChat;
11
+import com.ruoyi.llm.domain.CmcTopic;
10
 import com.ruoyi.llm.domain.CmcDocument;
12
 import com.ruoyi.llm.domain.CmcDocument;
11
 import com.ruoyi.llm.mapper.CmcAgentMapper;
13
 import com.ruoyi.llm.mapper.CmcAgentMapper;
12
 import com.ruoyi.llm.mapper.CmcChatMapper;
14
 import com.ruoyi.llm.mapper.CmcChatMapper;
13
 import com.ruoyi.llm.mapper.CmcDocumentMapper;
15
 import com.ruoyi.llm.mapper.CmcDocumentMapper;
14
 import com.ruoyi.llm.service.ICmcAgentService;
16
 import com.ruoyi.llm.service.ICmcAgentService;
17
+import com.ruoyi.llm.service.ICmcTopicService;
15
 import dev.langchain4j.data.document.Document;
18
 import dev.langchain4j.data.document.Document;
16
 import dev.langchain4j.data.document.parser.TextDocumentParser;
19
 import dev.langchain4j.data.document.parser.TextDocumentParser;
17
 import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
20
 import dev.langchain4j.data.document.parser.apache.pdfbox.ApachePdfBoxDocumentParser;
41
 import org.noear.solon.ai.chat.ChatResponse;
44
 import org.noear.solon.ai.chat.ChatResponse;
42
 import org.noear.solon.ai.chat.ChatSession;
45
 import org.noear.solon.ai.chat.ChatSession;
43
 import org.noear.solon.ai.chat.message.ChatMessage;
46
 import org.noear.solon.ai.chat.message.ChatMessage;
47
+import org.noear.solon.ai.chat.prompt.Prompt;
44
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
48
 import org.noear.solon.ai.chat.session.InMemoryChatSession;
45
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber;
46
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP;
47
-import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
48
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPrGeneral;
49
 import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPrGeneral;
49
 import org.springframework.beans.factory.annotation.Autowired;
50
 import org.springframework.beans.factory.annotation.Autowired;
50
 import org.springframework.beans.factory.annotation.Value;
51
 import org.springframework.beans.factory.annotation.Value;
78
     @Autowired
79
     @Autowired
79
     private CmcChatMapper cmcChatMapper;
80
     private CmcChatMapper cmcChatMapper;
80
 
81
 
82
+    @Autowired
83
+    private ICmcTopicService cmcTopicService;
84
+
81
     private String processValue = "";
85
     private String processValue = "";
82
 
86
 
83
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
87
     private static final EmbeddingModel embeddingModel = new BgeSmallZhV15EmbeddingModel();
95
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
99
         if (milvusServiceUrl == null || milvusServiceUrl.isEmpty()) {
96
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
100
             throw new IllegalArgumentException("milvusServiceUrl 配置不能为空");
97
         }
101
         }
98
-//        milvusClient = new MilvusClientV2(
99
-//                ConnectConfig.builder()
100
-//                        .uri(milvusServiceUrl)
101
-//                        .build());
102
+        milvusClient = new MilvusClientV2(
103
+                ConnectConfig.builder()
104
+                        .uri(milvusServiceUrl)
105
+                        .build());
102
     }
106
     }
103
 
107
 
104
     @PreDestroy
108
     @PreDestroy
189
         cmcChat.setInput("招标文件地址:" + prefixPath + "/" + file.getOriginalFilename());
193
         cmcChat.setInput("招标文件地址:" + prefixPath + "/" + file.getOriginalFilename());
190
         cmcChat.setUserId(SecurityUtils.getUserId());
194
         cmcChat.setUserId(SecurityUtils.getUserId());
191
         cmcChatMapper.insertCmcChat(cmcChat);
195
         cmcChatMapper.insertCmcChat(cmcChat);
192
-        if (agentName.contains("技术")) {
196
+        if (agentName.contains("技术标书")) {
193
             String[] filenameSplit = file.getOriginalFilename().split("\\.");
197
             String[] filenameSplit = file.getOriginalFilename().split("\\.");
194
             String outputFilename = prefixPath + "/" + file.getOriginalFilename()
198
             String outputFilename = prefixPath + "/" + file.getOriginalFilename()
195
                     .replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName);
199
                     .replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName);
197
                 outputFilename = outputFilename.replace(".doc", ".docx");
201
                 outputFilename = outputFilename.replace(".doc", ".docx");
198
             if (file.getOriginalFilename().endsWith(".pdf"))
202
             if (file.getOriginalFilename().endsWith(".pdf"))
199
                 outputFilename = outputFilename.replace(".pdf", ".docx");
203
                 outputFilename = outputFilename.replace(".pdf", ".docx");
200
-            Path outputFilePath = Paths.get(RuoYiConfig.getProfile() + outputFilename);
201
-            Files.deleteIfExists(outputFilePath);
202
-            InputStream fileInputStream = new FileInputStream(RuoYiConfig.getProfile() + "/upload/agent/template/technical.docx");
203
-            try (XWPFDocument doc = new XWPFDocument(fileInputStream)) {
204
-                // 保存文档到本地文件系统
205
-                try (FileOutputStream out = new FileOutputStream(RuoYiConfig.getProfile() + outputFilename)) {
206
-                    doc.write(out);
207
-                }
208
-            }
209
-            String question = "工作大纲/工作范围/招标范围/服务范围";
210
-            String chapters = generateAnswerWithDocument(profilePath + "/" + file.getOriginalFilename(),
211
-                    RuoYiConfig.getProfile() + outputFilename, question);
212
-
213
-            // 生成详细目录结构(包含二三级标题)
214
-            String detailedDirectory = generateDetailedDirectory(profilePath + "/" + file.getOriginalFilename());
215
-
204
+            
205
+            // 分割文档
206
+            List<TextSegment> segments = splitDocument(transferFile, 300, 50);
207
+            
216
             // 分析项目概况
208
             // 分析项目概况
217
-            String projectOverview = analyzeProjectOverview(profilePath + "/" + file.getOriginalFilename());
209
+            String projectOverview = analyzeProjectOverview(profilePath + "/" + file.getOriginalFilename(), segments);
210
+            jsonObject.put("projectOverview", projectOverview);
218
 
211
 
219
             // 分析评分要求
212
             // 分析评分要求
220
-            String scoringRequirements = analyzeScoringRequirements(profilePath + "/" + file.getOriginalFilename());
213
+            String scoringRequirements = analyzeScoringRequirements(profilePath + "/" + file.getOriginalFilename(), segments, agentName);
214
+            jsonObject.put("scoringRequirements", scoringRequirements);
215
+
216
+            // 生成详细目录结构(包含二三级标题)并写入文件
217
+            JSONArray detailedDirectoryTree = generateDetailedDirectory(file.getOriginalFilename(), agentName);
218
+            String detailedDirectory = buildDirectoryText(detailedDirectoryTree);
219
+            jsonObject.put("detailedDirectory", detailedDirectoryTree);
220
+            jsonObject.put("directoryText", detailedDirectory);
221
 
221
 
222
-            message = "好的,我已经收到您上传的招标文件。以下为根据招标文件生成的章节大纲:\n\n"+ chapters + "\n\n" +
222
+            message = "好的,我已经收到您上传的招标文件。以下为根据招标文件生成的章节大纲:\n\n"+ detailedDirectory + "\n\n" +
223
                     "若您对章节标题有异议,请打开" + "【<a href='/profile" + outputFilename + "'> 技术文件 " + "</a>】" + "进行修改,后续将根据修改后的章节标题,帮您生成对应章节内容。\n\n" +
223
                     "若您对章节标题有异议,请打开" + "【<a href='/profile" + outputFilename + "'> 技术文件 " + "</a>】" + "进行修改,后续将根据修改后的章节标题,帮您生成对应章节内容。\n\n" +
224
                     "思考时间可能较长,请耐心等待!\n";
224
                     "思考时间可能较长,请耐心等待!\n";
225
 
225
 
226
-            // 将分析结果添加到返回对象中
227
-            jsonObject.put("detailedDirectory", detailedDirectory);
228
-            jsonObject.put("projectOverview", projectOverview);
229
-            jsonObject.put("scoringRequirements", scoringRequirements);
226
+            // 返回输出文件名,以便前端在保存目录时使用
227
+            jsonObject.put("filename", file.getOriginalFilename()
228
+                    .replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName));
229
+            
230
+            // 根据 agentName 查询 agentId
231
+            CmcAgent queryAgent = new CmcAgent();
232
+            queryAgent.setAgentName(agentName);
233
+            List<CmcAgent> agentList = cmcAgentMapper.selectCmcAgentList(queryAgent);
234
+            Integer agentId = null;
235
+            if (!agentList.isEmpty()) {
236
+                agentId = agentList.get(0).getAgentId();
237
+            }
238
+            
239
+            // 去除文件名后缀作为 topic 名称
240
+            String originalFilename = file.getOriginalFilename();
241
+            String topic = originalFilename.substring(0, originalFilename.lastIndexOf('.'));
242
+            
243
+            // 创建 topic
244
+            CmcTopic cmcTopic = new CmcTopic();
245
+            cmcTopic.setTopicId(new SnowFlake().generateId());
246
+            cmcTopic.setAgentId(agentId);
247
+            cmcTopic.setTopic(topic);
248
+            cmcTopicService.insertCmcTopic(cmcTopic);
249
+            String topicId = cmcTopic.getTopicId();
250
+            
251
+            // 合并 projectOverview、scoringRequirements、detailedDirectory 作为 output
252
+            StringBuilder output = new StringBuilder();
253
+            output.append("【项目概况】\n").append(projectOverview).append("\n\n");
254
+            output.append("【评分要求】\n").append(scoringRequirements).append("\n\n");
255
+            output.append("【详细目录】\n").append(detailedDirectory);
256
+            
257
+            // 更新 cmc_chat 表
258
+            cmcChat.setTopicId(topicId);
259
+            cmcChat.setOutput(output.toString());
260
+            cmcChat.setOutputTime(new Date());
261
+            cmcChatMapper.updateCmcChat(cmcChat);
262
+            jsonObject.put("topicId", topicId);
230
         }
263
         }
231
         else if (agentName.contains("检查")) {
264
         else if (agentName.contains("检查")) {
232
             message = generateAnswerWithDocumentContent(profilePath + "/" + file.getOriginalFilename());
265
             message = generateAnswerWithDocumentContent(profilePath + "/" + file.getOriginalFilename());
472
             EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
505
             EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
473
                     .queryEmbedding(queryEmbedding)
506
                     .queryEmbedding(queryEmbedding)
474
                     .minScore(0.7) // 降低阈值以获取更多相关内容
507
                     .minScore(0.7) // 降低阈值以获取更多相关内容
475
-                    .maxResults(5) // 限制结果数量
476
                     .build();
508
                     .build();
477
 
509
 
478
             List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
510
             List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
585
     }
617
     }
586
 
618
 
587
     /**
619
     /**
588
-     * 调用LLM生成回答
620
+     * 调用LLM生成目录结构
589
      */
621
      */
590
-    public String generateAnswerWithDocument(String uploadFilePath, String templatePath, String question) throws IOException {
622
+    public JSONArray generateDetailedDirectory(String fileName, String agentName) throws IOException {
623
+        String prefixPath = "/upload/agent/" + agentName;
591
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
624
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
592
-        File profilePath = new File(uploadFilePath);
625
+        File profilePath = new File(new File( RuoYiConfig.getProfile() + prefixPath) + "/" + fileName);
593
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
626
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
594
         List<TextSegment> segments = splitDocument(profilePath, 300, 50);
627
         List<TextSegment> segments = splitDocument(profilePath, 300, 50);
595
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
628
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
596
         embeddingStore.addAll(embeddings, segments);
629
         embeddingStore.addAll(embeddings, segments);
597
-        Embedding queryEmbedding = embeddingModel.embed(question).content();
630
+        Embedding queryEmbedding = embeddingModel.embed("工作大纲/工作范围/招标范围/服务范围").content();
598
         EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
631
         EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
599
                 .queryEmbedding(queryEmbedding)
632
                 .queryEmbedding(queryEmbedding)
633
+                .maxResults(5)
600
                 .minScore(0.7)
634
                 .minScore(0.7)
601
                 .build();
635
                 .build();
602
         List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
636
         List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
607
         }
641
         }
608
         //根据招标文件工作大纲要求生成二级标题
642
         //根据招标文件工作大纲要求生成二级标题
609
         sb.append("请基于上述招标文件中提到的")
643
         sb.append("请基于上述招标文件中提到的")
610
-                .append(question)
644
+                .append("工作大纲/工作范围/招标范围/服务范围")
611
                 .append(",先列出二级章节标题,严格按以下格式,仅输出标题列表:\n")
645
                 .append(",先列出二级章节标题,严格按以下格式,仅输出标题列表:\n")
612
                 .append("6.1 XX\n" +
646
                 .append("6.1 XX\n" +
613
                         "6.2 XX\n" +
647
                         "6.2 XX\n" +
615
                         "......\n" +
649
                         "......\n" +
616
                         "6.n-1 XX\n" +
650
                         "6.n-1 XX\n" +
617
                         "6.n XX\n");
651
                         "6.n XX\n");
618
-        return writeChapters(sb.toString(), templatePath);
652
+        String directoryContent = writeChapters(sb.toString(), fileName, agentName);
653
+        return parseDirectoryToTree(directoryContent);
619
     }
654
     }
620
 
655
 
621
     /**
656
     /**
622
-     * 生成详细目录结构(包含二三级标题)
657
+     * 将目录文本解析为树结构
623
      */
658
      */
624
-    public String generateDetailedDirectory(String uploadFilePath) throws IOException {
625
-        processValue = "生成详细目录结构中: 0%";
626
-        StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
627
-        File profilePath = new File(uploadFilePath);
628
-        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
629
-        List<TextSegment> segments = splitDocument(profilePath, 300, 50);
630
-        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
631
-        embeddingStore.addAll(embeddings, segments);
659
+    private JSONArray parseDirectoryToTree(String directoryContent) {
660
+        JSONArray tree = new JSONArray();
661
+        Map<String, JSONObject> level1Map = new LinkedHashMap<>();
662
+        Map<String, JSONObject> level2Map = new LinkedHashMap<>();
663
+        Map<String, JSONObject> level3Map = new LinkedHashMap<>();
664
+
665
+        String[] lines = directoryContent.split("\n");
666
+        for (String line : lines) {
667
+            line = line.trim().replace("*", "").replace("#", "").replace("-", "");
668
+            if (!line.contains("6.") || line.isEmpty()) {
669
+                continue;
670
+            }
632
 
671
 
633
-        // 搜索与目录相关的内容
634
-        String directoryQuery = "目录结构、章节划分、文件结构";
635
-        Embedding queryEmbedding = embeddingModel.embed(directoryQuery).content();
636
-        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
637
-                .queryEmbedding(queryEmbedding)
638
-                .minScore(0.7)
639
-                .build();
640
-        List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
641
-        results.sort(Comparator.comparingDouble(EmbeddingMatch<TextSegment>::score).reversed());
672
+            String[] parts = line.split(" ", 2);
673
+            if (parts.length < 2) {
674
+                continue;
675
+            }
642
 
676
 
643
-        for (EmbeddingMatch<TextSegment> embeddingMatch : results) {
644
-            String content = embeddingMatch.embedded().toString();
645
-            sb.append(content).append("\n\n");
677
+            String number = parts[0].trim();
678
+            String title = parts[1].trim();
679
+            int dotCount = number.split("\\.").length - 1;
680
+
681
+            if (dotCount == 1) {
682
+                JSONObject level1 = new JSONObject();
683
+                level1.put("title", number + " " + title);
684
+                level1.put("children", new JSONArray());
685
+                level1Map.put(number, level1);
686
+            } else if (dotCount == 2) {
687
+                JSONObject level2 = new JSONObject();
688
+                level2.put("title", number + " " + title);
689
+                level2.put("children", new JSONArray());
690
+                level2Map.put(number, level2);
691
+
692
+                String parentNumber = number.substring(0, number.lastIndexOf("."));
693
+                if (level1Map.containsKey(parentNumber)) {
694
+                    level1Map.get(parentNumber).getJSONArray("children").add(level2);
695
+                }
696
+            } else if (dotCount == 3) {
697
+                JSONObject level3 = new JSONObject();
698
+                level3.put("title", number + " " + title);
699
+                level3Map.put(number, level3);
700
+
701
+                String parentNumber = number.substring(0, number.lastIndexOf("."));
702
+                if (level2Map.containsKey(parentNumber)) {
703
+                    level2Map.get(parentNumber).getJSONArray("children").add(level3);
704
+                }
705
+            }
706
+        }
707
+
708
+        for (Map.Entry<String, JSONObject> entry : level1Map.entrySet()) {
709
+            tree.add(entry.getValue());
646
         }
710
         }
647
 
711
 
648
-        // 生成详细目录结构
649
-        sb.append("请基于上述招标文件内容,生成详细的目录结构,包含一级、二级和三级标题,严格按以下格式输出:\n")
650
-                .append("1. 一级标题\n")
651
-                .append("  1.1 二级标题\n")
652
-                .append("    1.1.1 三级标题\n")
653
-                .append("    1.1.2 三级标题\n")
654
-                .append("  1.2 二级标题\n")
655
-                .append("2. 一级标题\n")
656
-                .append("  2.1 二级标题\n")
657
-                .append("    2.1.1 三级标题\n")
658
-                .append("......\n");
659
-
660
-        processValue = "生成详细目录结构中: 50%";
661
-        String directory = generateAnswer(sb.toString());
662
-        processValue = "生成详细目录结构中: 100%";
663
-        return directory;
712
+        return tree;
664
     }
713
     }
665
 
714
 
666
     /**
715
     /**
667
-     * 分析项目概况
716
+     * 将目录树结构转换为文本格式
668
      */
717
      */
669
-    public String analyzeProjectOverview(String uploadFilePath) throws IOException {
670
-        processValue = "分析项目概况中: 0%";
671
-        StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
672
-        File profilePath = new File(uploadFilePath);
673
-        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
674
-        List<TextSegment> segments = splitDocument(profilePath, 300, 50);
675
-        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
676
-        embeddingStore.addAll(embeddings, segments);
677
-
678
-        // 搜索与项目概况相关的内容
679
-        String overviewQuery = "项目概况、项目基本信息、项目背景、项目目标、项目范围";
680
-        Embedding queryEmbedding = embeddingModel.embed(overviewQuery).content();
681
-        EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
682
-                .queryEmbedding(queryEmbedding)
683
-                .minScore(0.7)
684
-                .build();
685
-        List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
686
-        results.sort(Comparator.comparingDouble(EmbeddingMatch<TextSegment>::score).reversed());
687
-
688
-        for (EmbeddingMatch<TextSegment> embeddingMatch : results) {
689
-            String content = embeddingMatch.embedded().toString();
690
-            sb.append(content).append("\n\n");
718
+    private String buildDirectoryText(JSONArray tree) {
719
+        StringBuilder sb = new StringBuilder();
720
+        for (int i = 0; i < tree.size(); i++) {
721
+            JSONObject level1 = tree.getJSONObject(i);
722
+            sb.append(level1.getString("title")).append("\n");
723
+            if (level1.containsKey("children")) {
724
+                JSONArray children = level1.getJSONArray("children");
725
+                for (int j = 0; j < children.size(); j++) {
726
+                    JSONObject level2 = children.getJSONObject(j);
727
+                    sb.append(level2.getString("title")).append("\n");
728
+                    if (level2.containsKey("children")) {
729
+                        JSONArray grandchildren = level2.getJSONArray("children");
730
+                        for (int k = 0; k < grandchildren.size(); k++) {
731
+                            JSONObject level3 = grandchildren.getJSONObject(k);
732
+                            sb.append(level3.getString("title")).append("\n");
733
+                        }
734
+                    }
735
+                }
736
+            }
691
         }
737
         }
692
-
693
-        // 分析项目概况
694
-        sb.append("请基于上述招标文件内容,分析项目概况,包括但不限于以下内容:\n")
695
-                .append("1. 项目名称\n")
696
-                .append("2. 项目类型\n")
697
-                .append("3. 项目预算\n")
698
-                .append("4. 项目周期\n")
699
-                .append("5. 项目地点\n")
700
-                .append("6. 招标人\n")
701
-                .append("7. 项目背景\n")
702
-                .append("8. 项目目标\n")
703
-                .append("9. 项目范围\n")
704
-                .append("请以清晰的结构输出分析结果。\n");
705
-
706
-        processValue = "分析项目概况中: 50%";
707
-        String overview = generateAnswer(sb.toString());
708
-        processValue = "分析项目概况中: 100%";
709
-        return overview;
738
+        return sb.toString();
710
     }
739
     }
711
 
740
 
712
     /**
741
     /**
713
-     * 分析评分要求
742
+     * 分析招标文件内容(公共方法)
743
+     * @param uploadFilePath 文件路径
744
+     * @param query 查询关键词
745
+     * @param analysisPrompt 分析提示语
746
+     * @param progressStartMsg 进度开始消息
747
+     * @param progressFoundMsg 进度找到消息
748
+     * @param progressEndMsg 进度结束消息
749
+     * @return 分析结果
714
      */
750
      */
715
-    public String analyzeScoringRequirements(String uploadFilePath) throws IOException {
716
-        processValue = "分析评分要求中: 0%";
751
+    private String analyzeDocumentContent(List<TextSegment> segments, String query, String analysisPrompt,
752
+                                         String progressStartMsg, String progressFoundMsg, String progressEndMsg) throws IOException {
753
+        processValue = progressStartMsg;
717
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
754
         StringBuilder sb = new StringBuilder("招标文件内容:\n\n");
718
-        File profilePath = new File(uploadFilePath);
719
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
755
         InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
720
-        List<TextSegment> segments = splitDocument(profilePath, 300, 50);
721
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
756
         List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
722
         embeddingStore.addAll(embeddings, segments);
757
         embeddingStore.addAll(embeddings, segments);
723
 
758
 
724
-        // 搜索与评分要求相关的内容
725
-        String scoringQuery = "评分要求、评分标准、评标办法、打分规则";
726
-        Embedding queryEmbedding = embeddingModel.embed(scoringQuery).content();
759
+        // 搜索相关内容
760
+        Embedding queryEmbedding = embeddingModel.embed(query).content();
727
         EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
761
         EmbeddingSearchRequest embeddingSearchRequest = EmbeddingSearchRequest.builder()
728
                 .queryEmbedding(queryEmbedding)
762
                 .queryEmbedding(queryEmbedding)
763
+                .maxResults(5)
729
                 .minScore(0.7)
764
                 .minScore(0.7)
730
                 .build();
765
                 .build();
731
         List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
766
         List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(embeddingSearchRequest).matches();
736
             sb.append(content).append("\n\n");
771
             sb.append(content).append("\n\n");
737
         }
772
         }
738
 
773
 
739
-        // 分析评分要求
740
-        sb.append("请基于上述招标文件内容,分析评分要求,包括但不限于以下内容:\n")
741
-                .append("1. 评分项及权重\n")
742
-                .append("2. 技术方案评分标准\n")
743
-                .append("3. 商务方案评分标准\n")
744
-                .append("4. 服务方案评分标准\n")
745
-                .append("5. 其他评分项\n")
746
-                .append("请以清晰的结构输出分析结果,包括各项的具体分值和评分细则。\n");
747
-
748
-        processValue = "分析评分要求中: 50%";
749
-        String scoring = generateAnswer(sb.toString());
750
-        processValue = "分析评分要求中: 100%";
751
-        return scoring;
774
+        // 添加分析提示
775
+        sb.append(analysisPrompt);
776
+
777
+        processValue = progressFoundMsg;
778
+        String result = generateAnswer(sb.toString());
779
+        processValue = progressEndMsg;
780
+        return result;
781
+    }
782
+
783
+    /**
784
+     * 分析项目概况
785
+     */
786
+    public String analyzeProjectOverview(String uploadFilePath, List<TextSegment> segments) throws IOException {
787
+        String query = uploadFilePath.split("/")[uploadFilePath.split("/").length - 1] + "项目概况";
788
+        String analysisPrompt = "请基于上述招标文件内容,分析项目概况。\n" +
789
+                "请以清晰的结构输出分析结果。\n";
790
+        return analyzeDocumentContent(segments, query, analysisPrompt,
791
+                "分析项目概况中: 0%",
792
+                "已查到项目概况: 50%",
793
+                "分析项目概况中: 100%");
794
+    }
795
+
796
+    /**
797
+     * 分析评分要求
798
+     */
799
+    public String analyzeScoringRequirements(String uploadFilePath, List<TextSegment> segments, String agentName) throws IOException {
800
+        String query = "评分要求、评分标准、评标办法、打分规则";
801
+        if (agentName.contains("技术"))
802
+            query = "技术部分评分要求、评分标准、评标办法、打分规则";
803
+        else if (agentName.contains("商务"))
804
+            query = "商务部分评分要求、评分标准、评标办法、打分规则";
805
+        String analysisPrompt = "请基于上述招标文件内容,分析评分要求。\n" +
806
+                "请以清晰的结构输出分析结果,包括各项的具体分值和评分细则。\n";
807
+        return analyzeDocumentContent(segments, query, analysisPrompt,
808
+                "分析评分要求中: 0%",
809
+                "已查到评分要求: 50%",
810
+                "分析评分要求中: 100%");
752
     }
811
     }
753
 
812
 
754
     /**
813
     /**
755
      * 编排章节
814
      * 编排章节
756
      * @return
815
      * @return
757
      */
816
      */
758
-    public String writeChapters(String prompt, String templatePath) throws IOException {
817
+    public String writeChapters(String prompt, String fileName, String agentName) throws IOException {
759
         String chapters2 = generateAnswer(prompt);
818
         String chapters2 = generateAnswer(prompt);
760
 
819
 
761
         //根据技术文档知识库生成二级标题下三级标题
820
         //根据技术文档知识库生成二级标题下三级标题
801
                 "6.n.1 XX\n" +
860
                 "6.n.1 XX\n" +
802
                 "6.n 2 XX\n";
861
                 "6.n 2 XX\n";
803
         String content = generateAnswer(sb);
862
         String content = generateAnswer(sb);
804
-        writeTitles(content, templatePath);
863
+        writeTitles(content, fileName, agentName);
805
         return content;
864
         return content;
806
     }
865
     }
807
 
866
 
812
     public String generateAnswer(String prompt) throws IOException {
871
     public String generateAnswer(String prompt) throws IOException {
813
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
872
         ChatModel chatModel = ChatModel.of(llmServiceUrl)
814
                 .model("Qwen")
873
                 .model("Qwen")
874
+                .timeout(java.time.Duration.ofSeconds(120))
815
                 .build();
875
                 .build();
816
 
876
 
817
         List<ChatMessage> messages = new ArrayList<>();
877
         List<ChatMessage> messages = new ArrayList<>();
818
         messages.add(ChatMessage.ofUser(prompt));
878
         messages.add(ChatMessage.ofUser(prompt));
819
         ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
879
         ChatSession chatSession =  InMemoryChatSession.builder().messages(messages).build();
820
-        ChatResponse response = chatModel.prompt(chatSession).call();
821
 
880
 
822
-        return response.lastChoice().getMessage().getContent();
881
+        Prompt prompt1 = Prompt.of(prompt).attrPut("session", chatSession);
882
+        ChatResponse response = chatModel.prompt(prompt1).call();
883
+        String content = response.lastChoice().getMessage().getContent();
884
+        return content;
823
     }
885
     }
824
 
886
 
825
     /**
887
     /**
893
      * 写入章节大纲
955
      * 写入章节大纲
894
      * @return
956
      * @return
895
      */
957
      */
896
-    public void writeTitles(String content, String templatePath) throws IOException {
958
+    public void writeTitles(String content, String fileName, String agentName) throws IOException {
897
         List<String> chapters = new ArrayList<>();
959
         List<String> chapters = new ArrayList<>();
898
         String[] contentLines = content.split("\n");
960
         String[] contentLines = content.split("\n");
899
         for (String line : contentLines) {
961
         for (String line : contentLines) {
900
             if (line.contains("6."))
962
             if (line.contains("6."))
901
                 chapters.add(line.replace("*", "").replace("#", "").replace("-", ""));
963
                 chapters.add(line.replace("*", "").replace("#", "").replace("-", ""));
902
         }
964
         }
965
+        String prefixPath = "/upload/agent/" + agentName;
966
+        String[] filenameSplit = fileName.split("\\.");
967
+        String outputFilename = prefixPath + "/" + fileName
968
+                .replace(filenameSplit[filenameSplit.length - 2], filenameSplit[filenameSplit.length - 2] + "_" + agentName);
969
+        String templatePath = RuoYiConfig.getProfile() + outputFilename;
970
+        Path outputFilePath = Paths.get(templatePath);
971
+        Files.deleteIfExists(outputFilePath);
972
+        InputStream inputStream = new FileInputStream(RuoYiConfig.getProfile() + "/upload/agent/template/technical.docx");
973
+        try (XWPFDocument doc = new XWPFDocument(inputStream)) {
974
+            // 保存文档到本地文件系统
975
+            try (FileOutputStream out = new FileOutputStream(templatePath)) {
976
+                doc.write(out);
977
+            }
978
+        }
903
         File file = new File(templatePath);
979
         File file = new File(templatePath);
904
         FileInputStream fileInputStream = new FileInputStream(file);
980
         FileInputStream fileInputStream = new FileInputStream(file);
905
         try (XWPFDocument document = new XWPFDocument(fileInputStream)) {
981
         try (XWPFDocument document = new XWPFDocument(fileInputStream)) {
1010
     /**
1086
     /**
1011
      * 获取最低级别子标题列表
1087
      * 获取最低级别子标题列表
1012
      */
1088
      */
1089
+    /**
1090
+     * 保存目录到Word文件
1091
+     */
1092
+    @Override
1093
+    public JSONObject writeTitles(JSONObject data) throws IOException {
1094
+        JSONObject result = new JSONObject();
1095
+        try {
1096
+            String filename = data.getString("filename");
1097
+            String agentName = data.getString("agentName");
1098
+            JSONArray titlesArray = data.getJSONArray("titles");
1099
+
1100
+            // 构建目录字符串
1101
+            StringBuilder titlesContent = new StringBuilder();
1102
+            buildTitlesContent(titlesArray, titlesContent);
1103
+
1104
+            // 调用现有的writeTitles方法写入Word文件
1105
+            writeTitles(titlesContent.toString(), filename, agentName);
1106
+
1107
+            result.put("code", 200);
1108
+            result.put("message", "目录保存成功");
1109
+        } catch (Exception e) {
1110
+            e.printStackTrace();
1111
+            result.put("code", 500);
1112
+            result.put("message", "目录保存失败: " + e.getMessage());
1113
+        }
1114
+        return result;
1115
+    }
1116
+
1117
+    /**
1118
+     * 构建目录内容字符串
1119
+     */
1120
+    private void buildTitlesContent(JSONArray titlesArray, StringBuilder content) {
1121
+        for (int i = 0; i < titlesArray.size(); i++) {
1122
+            JSONObject titleObj = titlesArray.getJSONObject(i);
1123
+            String title = titleObj.getString("title");
1124
+            content.append(title).append("\n");
1125
+
1126
+            if (titleObj.containsKey("children")) {
1127
+                JSONArray children = titleObj.getJSONArray("children");
1128
+                buildTitlesContent(children, content);
1129
+            }
1130
+        }
1131
+    }
1132
+
1013
     public List<String> extractSubTitles(String filename, String question) throws IOException {
1133
     public List<String> extractSubTitles(String filename, String question) throws IOException {
1014
         List<String> subTitles = new ArrayList<>();
1134
         List<String> subTitles = new ArrayList<>();
1015
         InputStream fileInputStream = new FileInputStream(filename);
1135
         InputStream fileInputStream = new FileInputStream(filename);
1152
         }
1272
         }
1153
     }
1273
     }
1154
 
1274
 
1275
+    /**
1276
+     * 从 DOCX 文档中提取表格内容
1277
+     */
1278
+    private List<TextSegment> extractTablesFromDocx(XWPFDocument document) {
1279
+        List<TextSegment> tableSegments = new ArrayList<>();
1280
+        List<XWPFTable> tables = document.getTables();
1281
+
1282
+        for (int i = 0; i < tables.size(); i++) {
1283
+            XWPFTable table = tables.get(i);
1284
+            StringBuilder tableContent = new StringBuilder();
1285
+
1286
+            // 添加表格标识
1287
+            tableContent.append("[表格 ").append(i + 1).append("]\n");
1288
+
1289
+            // 提取表格内容
1290
+            for (XWPFTableRow row : table.getRows()) {
1291
+                StringBuilder rowContent = new StringBuilder();
1292
+                for (XWPFTableCell cell : row.getTableCells()) {
1293
+                    String cellText = cell.getText().trim();
1294
+                    if (!cellText.isEmpty()) {
1295
+                        rowContent.append(cellText).append(" | ");
1296
+                    }
1297
+                }
1298
+                if (rowContent.length() > 0) {
1299
+                    // 移除末尾的分隔符
1300
+                    if (rowContent.length() >= 3) {
1301
+                        rowContent.setLength(rowContent.length() - 3);
1302
+                    }
1303
+                    tableContent.append(rowContent).append("\n");
1304
+                }
1305
+            }
1306
+
1307
+            // 只有当表格内容有实际内容时才添加
1308
+            if (tableContent.length() > 10) {
1309
+                TextSegment segment = TextSegment.from(tableContent.toString());
1310
+                tableSegments.add(segment);
1311
+            }
1312
+        }
1313
+
1314
+        return tableSegments;
1315
+    }
1316
+
1317
+    /**
1318
+     * 从 DOC 文档中提取表格内容
1319
+     */
1320
+    private List<TextSegment> extractTablesFromDoc(HWPFDocument document) {
1321
+        List<TextSegment> tableSegments = new ArrayList<>();
1322
+        Range range = document.getRange();
1323
+
1324
+        int tableCount = 0;
1325
+        StringBuilder tableContent = new StringBuilder();
1326
+        boolean inTable = false;
1327
+
1328
+        for (int i = 0; i < range.numParagraphs(); i++) {
1329
+            Paragraph paragraph = range.getParagraph(i);
1330
+            String text = paragraph.text().trim();
1331
+
1332
+            // 检测表格段落
1333
+            if (paragraph.isInTable()) {
1334
+                if (!inTable) {
1335
+                    // 开始新表格
1336
+                    tableCount++;
1337
+                    tableContent = new StringBuilder();
1338
+                    tableContent.append("[表格 ").append(tableCount).append("]\n");
1339
+                    inTable = true;
1340
+                }
1341
+
1342
+                // 添加表格行内容
1343
+                if (!text.isEmpty()) {
1344
+                    tableContent.append(text).append("\n");
1345
+                }
1346
+            } else if (inTable) {
1347
+                // 表格结束
1348
+                inTable = false;
1349
+                if (tableContent.length() > 10) {
1350
+                    TextSegment segment = TextSegment.from(tableContent.toString());
1351
+                    tableSegments.add(segment);
1352
+                }
1353
+            }
1354
+        }
1355
+
1356
+        // 处理最后一个表格
1357
+        if (inTable && tableContent.length() > 10) {
1358
+            TextSegment segment = TextSegment.from(tableContent.toString());
1359
+            tableSegments.add(segment);
1360
+        }
1361
+
1362
+        return tableSegments;
1363
+    }
1364
+
1155
     /**
1365
     /**
1156
      * 按三级标题分割DOC内容
1366
      * 按三级标题分割DOC内容
1157
      */
1367
      */
1243
                         splitDocxByLevel3(currentLevel2Content.toString(), xwpfDocument, segments);
1453
                         splitDocxByLevel3(currentLevel2Content.toString(), xwpfDocument, segments);
1244
                     }
1454
                     }
1245
                 }
1455
                 }
1456
+
1457
+                // 提取文档中的所有表格
1458
+                List<TextSegment> tableSegments = extractTablesFromDocx(xwpfDocument);
1459
+                segments.addAll(tableSegments);
1246
             }
1460
             }
1247
             return segments;
1461
             return segments;
1248
         }
1462
         }
1289
                         splitDocByLevel3(currentLevel2Content.toString(), hwpfDocument, segments);
1503
                         splitDocByLevel3(currentLevel2Content.toString(), hwpfDocument, segments);
1290
                     }
1504
                     }
1291
                 }
1505
                 }
1506
+
1507
+                // 提取文档中的所有表格
1508
+                List<TextSegment> tableSegments = extractTablesFromDoc(hwpfDocument);
1509
+                segments.addAll(tableSegments);
1292
             }
1510
             }
1293
             return segments;
1511
             return segments;
1294
         }
1512
         }

+ 14
- 2
oa-ui/src/api/llm/agent.js Datei anzeigen

2
  * @Author: wrh
2
  * @Author: wrh
3
  * @Date: 2025-07-17 18:06:24
3
  * @Date: 2025-07-17 18:06:24
4
  * @LastEditors: wrh
4
  * @LastEditors: wrh
5
- * @LastEditTime: 2025-09-02 16:43:25
5
+ * @LastEditTime: 2026-04-24 13:52:03
6
  */
6
  */
7
 import request from '@/utils/request'
7
 import request from '@/utils/request'
8
 
8
 
65
 }
65
 }
66
 
66
 
67
 // 上传单文件
67
 // 上传单文件
68
-export function uploadModifyFile(topicId, file, agentName) {
68
+export function uploadModifyFile(topicId, file, agentName, chapterNumber) {
69
   const formData = new FormData()
69
   const formData = new FormData()
70
   formData.append('topicId', topicId)
70
   formData.append('topicId', topicId)
71
   formData.append('file', file)
71
   formData.append('file', file)
72
   formData.append('agentName', agentName)
72
   formData.append('agentName', agentName)
73
+  if (chapterNumber) {
74
+    formData.append('chapterNumber', chapterNumber)
75
+  }
73
   return request({
76
   return request({
74
     url: '/llm/agent/modifyFile',
77
     url: '/llm/agent/modifyFile',
75
     method: 'post',
78
     method: 'post',
113
     params: { agentName }
116
     params: { agentName }
114
   })
117
   })
115
 }
118
 }
119
+
120
+// 保存目录到Word文件
121
+export function writeTitles(data) {
122
+  return request({
123
+    url: '/llm/agent/writeTitles',
124
+    method: 'post',
125
+    data: data
126
+  })
127
+}

+ 861
- 156
oa-ui/src/views/llm/agent/AgentDetail.vue
Datei-Diff unterdrückt, da er zu groß ist
Datei anzeigen


+ 3
- 1
oa-ui/src/views/llm/agent/index.vue Datei anzeigen

310
 }
310
 }
311
 
311
 
312
 .left-panel {
312
 .left-panel {
313
-  width: 400px;
313
+  width: 25%;
314
+  min-width: 260px;
315
+  max-width: 320px;
314
   background: white;
316
   background: white;
315
   border-radius: 8px;
317
   border-radius: 8px;
316
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
318
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

+ 1
- 1
oa-ui/src/views/llm/knowledge/index.vue Datei anzeigen

1277
 }
1277
 }
1278
 
1278
 
1279
 .left-panel {
1279
 .left-panel {
1280
-  width: 400px;
1280
+  width: 250px;
1281
   background: white;
1281
   background: white;
1282
   border-radius: 8px;
1282
   border-radius: 8px;
1283
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
1283
   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

Laden…
Abbrechen
Speichern