소스 검색

上传知识库文件,修改判断二三级标题

lamphua 4 일 전
부모
커밋
2fa6a78c59

+ 141
- 89
oa-back/ruoyi-agent/src/main/java/com/ruoyi/agent/service/impl/McpServiceImpl.java 파일 보기

@@ -79,7 +79,7 @@ public class McpServiceImpl implements IMcpService {
79 79
 //                ConnectConfig.builder()
80 80
 //                        .uri(milvusServiceUrl)
81 81
 //                        .build());
82
-    }    
82
+    }
83 83
 
84 84
     @PreDestroy
85 85
     public void destroyMilvusClient() {
@@ -104,16 +104,16 @@ public class McpServiceImpl implements IMcpService {
104 104
                                            @Param(description = "章节名称") String title,
105 105
                                            @Param(description = "技术文件地址") String templatePath) throws IOException
106 106
     {
107
-            try {
108
-                if (templatePath.startsWith("/dev-api"))
109
-                    templatePath = templatePath.replace("/dev-api/profile", profile);
110
-                templatePath =profile + templatePath;
111
-                List<String> subTitles = extractSubTitles(templatePath, title);
112
-                List<JSONObject> contexts = retrieveFromMilvus(collectionName, title, 10);
113
-                return generateAnswerWithDocumentAndCollection(agentName, templatePath, subTitles, contexts);
114
-            } catch (IOException e) {
115
-                throw new RuntimeException(e);
116
-            }
107
+        try {
108
+            if (templatePath.startsWith("/dev-api"))
109
+                templatePath = templatePath.replace("/dev-api/profile", profile);
110
+            templatePath =profile + templatePath;
111
+            List<String> subTitles = extractSubTitles(templatePath, title);
112
+            List<JSONObject> contexts = retrieveFromMilvus(collectionName, title, 10);
113
+            return generateAnswerWithDocumentAndCollection(agentName, templatePath, subTitles, contexts);
114
+        } catch (IOException e) {
115
+            throw new RuntimeException(e);
116
+        }
117 117
     }
118 118
 
119 119
     /**
@@ -122,27 +122,27 @@ public class McpServiceImpl implements IMcpService {
122 122
     @ToolMapping(description = "执行SQL查询")
123 123
     public JSONArray SQLQuery(@Param(description = "sql语句") String sqlString)
124 124
     {
125
-            sqlString = sqlString.replace("[","").replace("]","");
126
-            return executeQuery(sqlString);
125
+        sqlString = sqlString.replace("[","").replace("]","");
126
+        return executeQuery(sqlString);
127 127
     }
128 128
 
129 129
     /**
130
-      * 获取所有表名列表
131
-      */
132
-     @ToolMapping(description = "获取数据库中所有表名列表")
133
-     public JSONArray GetAllTableNames()
134
-     {
135
-             return getAllTableNames();
136
-     }
137
-
138
-     /**
139
-      * 获取指定表的结构信息
140
-      */
141
-     @ToolMapping(description = "获取指定表的结构信息")
142
-     public JSONObject GetTableStructure(@Param(description = "表名") String tableName)
143
-     {
144
-             return getTableStructure(tableName);
145
-     }
130
+     * 获取所有表名列表
131
+     */
132
+    @ToolMapping(description = "获取数据库中所有表名列表")
133
+    public JSONArray GetAllTableNames()
134
+    {
135
+        return getAllTableNames();
136
+    }
137
+
138
+    /**
139
+     * 获取指定表的结构信息
140
+     */
141
+    @ToolMapping(description = "获取指定表的结构信息")
142
+    public JSONObject GetTableStructure(@Param(description = "表名") String tableName)
143
+    {
144
+        return getTableStructure(tableName);
145
+    }
146 146
 
147 147
     public JSONArray executeQuery(String sql) {
148 148
         JSONArray result = new JSONArray();
@@ -337,25 +337,48 @@ public class McpServiceImpl implements IMcpService {
337 337
 
338 338
             for (XWPFParagraph  paragraph : document.getParagraphs()) {
339 339
                 String text = paragraph.getText();
340
-                int level = Integer.parseInt(paragraph.getStyle());
341
-
342
-                // 维护当前路径
343
-                while (!currentPath.isEmpty() && Integer.parseInt(currentPath.get(currentPath.size() - 1).getStyle()) >= level) {
344
-                    currentPath.remove(currentPath.size() - 1);
345
-                }
346
-                currentPath.add(paragraph);
347
-                if (foundParent) {
348
-                    if (level > parentLevel) { // 是子标题
349
-                        if (isLeafNode(document, paragraph, level)) {
350
-                            subTitles.add(text);
340
+                if (paragraph.getStyle() != null) {
341
+                    String styleId = paragraph.getStyle();
342
+                    XWPFStyle style = document.getStyles().getStyle(styleId);
343
+                    String styleName = style.getName();
344
+                    int level = 0;
345
+                    if (styleName.equals("heading 2")) level = 2;
346
+                    else if (styleName.equals("heading 3")) level = 3;
347
+                    else if (styleName.equals("heading 4")) level = 4;
348
+                    else continue;
349
+
350
+                    // 维护当前路径
351
+                    while (!currentPath.isEmpty()) {
352
+                        XWPFParagraph lastPara = currentPath.get(currentPath.size() - 1);
353
+                        String lastStyleId = lastPara.getStyle();
354
+                        XWPFStyle lastStyle = document.getStyles().getStyle(lastStyleId);
355
+                        String lastStyleName = lastStyle.getName();
356
+                        int lastLevel = 0;
357
+                        if (lastStyleName.equals("heading 2")) lastLevel = 2;
358
+                        else if (lastStyleName.equals("heading 3")) lastLevel = 3;
359
+                        else if (lastStyleName.equals("heading 4")) lastLevel = 4;
360
+                        else break;
361
+
362
+                        if (lastLevel >= level) {
363
+                            currentPath.remove(currentPath.size() - 1);
364
+                        } else {
365
+                            break;
351 366
                         }
352
-                    } else {
353
-                        // 遇到同级或更高级别的标题,停止搜索
354
-                        break;
355 367
                     }
356
-                } else if (text.equals(question)) {
357
-                    foundParent = true;
358
-                    parentLevel = level;
368
+                    currentPath.add(paragraph);
369
+                    if (foundParent) {
370
+                        if (level > parentLevel) { // 是子标题
371
+                            if (isLeafNode(document, paragraph, level)) {
372
+                                subTitles.add(text);
373
+                            }
374
+                        } else {
375
+                            // 遇到同级或更高级别的标题,停止搜索
376
+                            break;
377
+                        }
378
+                    } else if (text.equals(question)) {
379
+                        foundParent = true;
380
+                        parentLevel = level;
381
+                    }
359 382
                 }
360 383
             }
361 384
 
@@ -374,10 +397,19 @@ public class McpServiceImpl implements IMcpService {
374 397
 
375 398
         // 检查后续段落
376 399
         XWPFParagraph nextP = doc.getParagraphs().get(index + 1);
377
-        int nextLevel = Integer.parseInt(nextP.getStyle());
378
-        // 遇到同级或更高级别标题,是叶子节点
379
-        return nextLevel <= level; // 存在更低级别的标题,不是叶子节点
380
-
400
+        if (nextP.getStyle() != null) {
401
+            String styleId = nextP.getStyle();
402
+            XWPFStyle style = doc.getStyles().getStyle(styleId);
403
+            String styleName = style.getName();
404
+            int nextLevel = 0;
405
+            if (styleName.equals("heading 2")) nextLevel = 2;
406
+            else if (styleName.equals("heading 3")) nextLevel = 3;
407
+            else if (styleName.equals("heading 4")) nextLevel = 4;
408
+            else return true;
409
+            // 遇到同级或更高级别标题,是叶子节点
410
+            return nextLevel <= level; // 存在更低级别的标题,不是叶子节点
411
+        }
412
+        return true;
381 413
     }
382 414
 
383 415
     /**
@@ -390,8 +422,11 @@ public class McpServiceImpl implements IMcpService {
390 422
             for (XWPFParagraph paragraph : document.getParagraphs()) {
391 423
                 String text = paragraph.getText().trim();
392 424
                 if (paragraph.getStyle() != null) {
425
+                    String styleId = paragraph.getStyle();
426
+                    XWPFStyle style = document.getStyles().getStyle(styleId);
427
+                    String styleName = style.getName();
393 428
                     // 判断主标题
394
-                    if (paragraph.getStyle().equals("3") || paragraph.getStyle().equals("4") ) {
429
+                    if (styleName.equals("heading 3") || styleName.equals("heading 4")) {
395 430
                         subTitles.append(text).append("\n");
396 431
                     }
397 432
                 }
@@ -456,9 +491,12 @@ public class McpServiceImpl implements IMcpService {
456 491
                     String text = paragraph.getText().trim();
457 492
                     if (text.isEmpty()) continue;
458 493
 
459
-                    if (paragraph.getStyle() != null) {
460
-                        // 二级标题(样式2)开始新的分段
461
-                        if (paragraph.getStyle().equals("2")) {
494
+                    if (paragraph.getStyleID() != null) {
495
+                        String styleId = paragraph.getStyleID();
496
+                        XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
497
+                        String styleName = style.getName();
498
+                        // 二级标题开始新的分段
499
+                        if (styleName.equals("heading 2")) {
462 500
                             // 保存当前二级标题下的内容
463 501
                             if (currentLevel2Content.length() != 0) {
464 502
                                 // 检查当前二级标题内容的字节数
@@ -474,16 +512,24 @@ public class McpServiceImpl implements IMcpService {
474 512
                                     String[] paragraphs = currentLevel2Content.toString().split("\n");
475 513
                                     for (String para : paragraphs) {
476 514
                                         if (para.trim().isEmpty()) continue;
477
-                                        
478
-                                        // 检查是否是三级标题
515
+
516
+                                        // 检查是否是三级标题(简单判断:如果是三级标题,样式应该是3,但这里需要重新解析)
517
+                                        // 由于已经将内容转为字符串,这里采用另一种方式:假设三级标题以数字+点+空格开头(如"1.1.")
518
+                                        // 或者可以重新遍历段落,但为了效率,这里采用简单的判断方式
479 519
                                         boolean isLevel3Title = false;
480 520
                                         for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
481
-                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("3")) {
482
-                                                isLevel3Title = true;
483
-                                                break;
521
+                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
522
+                                                styleId = p.getStyle();
523
+                                                style = xwpfDocument.getStyles().getStyle(styleId);
524
+                                                styleName = style.getName();
525
+                                                // 二级标题(样式2)开始新的分段
526
+                                                if (styleName.equals("heading 3")) {
527
+                                                    isLevel3Title = true;
528
+                                                    break;
529
+                                                }
484 530
                                             }
485 531
                                         }
486
-                                        
532
+
487 533
                                         if (isLevel3Title) {
488 534
                                             // 保存当前三级标题内容
489 535
                                             if (currentLevel3Content.length() != 0) {
@@ -541,15 +587,21 @@ public class McpServiceImpl implements IMcpService {
541 587
                         String[] paragraphs = currentLevel2Content.toString().split("\n");
542 588
                         for (String para : paragraphs) {
543 589
                             if (para.trim().isEmpty()) continue;
544
-                            
590
+
545 591
                             boolean isLevel3Title = false;
546 592
                             for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
547
-                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("3")) {
548
-                                    isLevel3Title = true;
549
-                                    break;
593
+                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
594
+                                    String styleId = p.getStyle();
595
+                                    XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
596
+                                    String styleName = style.getName();
597
+                                    // 二级标题(样式2)开始新的分段
598
+                                    if (styleName.equals("heading 3")) {
599
+                                        isLevel3Title = true;
600
+                                        break;
601
+                                    }
550 602
                                 }
551 603
                             }
552
-                            
604
+
553 605
                             if (isLevel3Title) {
554 606
                                 // 保存当前三级标题内容
555 607
                                 if (currentLevel3Content.length() != 0) {
@@ -608,20 +660,20 @@ public class McpServiceImpl implements IMcpService {
608 660
                                 StringBuilder currentLevel3Content = new StringBuilder();
609 661
                                 Range level2Range = hwpfDocument.getRange();
610 662
                                 boolean foundCurrentLevel2 = false;
611
-                                
663
+
612 664
                                 for (int j = 0; j < level2Range.numParagraphs(); j++) {
613 665
                                     Paragraph p = level2Range.getParagraph(j);
614 666
                                     String paraText = p.text().trim();
615 667
                                     if (paraText.isEmpty()) continue;
616
-                                    
668
+
617 669
                                     // 找到当前二级标题的开始
618 670
                                     if (p.getStyleIndex() == 2 && paraText.equals(currentLevel2Content.toString().split("\n")[0].trim())) {
619 671
                                         foundCurrentLevel2 = true;
620 672
                                     }
621
-                                    
673
+
622 674
                                     if (foundCurrentLevel2) {
623 675
                                         short paraStyleIndex = p.getStyleIndex();
624
-                                        
676
+
625 677
                                         if (paraStyleIndex == 3) {
626 678
                                             // 三级标题
627 679
                                             // 保存当前三级标题内容
@@ -643,7 +695,7 @@ public class McpServiceImpl implements IMcpService {
643 695
                                         }
644 696
                                     }
645 697
                                 }
646
-                                
698
+
647 699
                                 // 保存最后一个三级标题内容
648 700
                                 if (currentLevel3Content.length() != 0) {
649 701
                                     TextSegment segment = TextSegment.from(currentLevel3Content.toString());
@@ -676,20 +728,20 @@ public class McpServiceImpl implements IMcpService {
676 728
                         StringBuilder currentLevel3Content = new StringBuilder();
677 729
                         Range level2Range = hwpfDocument.getRange();
678 730
                         boolean foundCurrentLevel2 = false;
679
-                        
731
+
680 732
                         for (int j = 0; j < level2Range.numParagraphs(); j++) {
681 733
                             Paragraph p = level2Range.getParagraph(j);
682 734
                             String paraText = p.text().trim();
683 735
                             if (paraText.isEmpty()) continue;
684
-                            
736
+
685 737
                             // 找到当前二级标题的开始
686 738
                             if (p.getStyleIndex() == 2 && paraText.equals(currentLevel2Content.toString().split("\n")[0].trim())) {
687 739
                                 foundCurrentLevel2 = true;
688 740
                             }
689
-                            
741
+
690 742
                             if (foundCurrentLevel2) {
691 743
                                 short paraStyleIndex = p.getStyleIndex();
692
-                                
744
+
693 745
                                 if (paraStyleIndex == 3) {
694 746
                                     // 三级标题
695 747
                                     // 保存当前三级标题内容
@@ -711,7 +763,7 @@ public class McpServiceImpl implements IMcpService {
711 763
                                 }
712 764
                             }
713 765
                         }
714
-                        
766
+
715 767
                         // 保存最后一个三级标题内容
716 768
                         if (currentLevel3Content.length() != 0) {
717 769
                             TextSegment segment = TextSegment.from(currentLevel3Content.toString());
@@ -743,7 +795,7 @@ public class McpServiceImpl implements IMcpService {
743 795
      */
744 796
     private JSONObject getTableStructure(String tableName) {
745 797
         JSONObject result = new JSONObject();
746
-        
798
+
747 799
         try {
748 800
             Class.forName("com.mysql.cj.jdbc.Driver");
749 801
         } catch (ClassNotFoundException e) {
@@ -751,12 +803,12 @@ public class McpServiceImpl implements IMcpService {
751 803
         }
752 804
 
753 805
         try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),Solon.cfg().getProperty("cmc.mysqlService.username"), Solon.cfg().getProperty("cmc.mysqlService.password"))) {
754
-            
806
+
755 807
             DatabaseMetaData metaData = conn.getMetaData();
756
-            
808
+
757 809
             ResultSet columns = metaData.getColumns(null, null, tableName, null);
758 810
             JSONArray columnsInfo = new JSONArray();
759
-            
811
+
760 812
             while (columns.next()) {
761 813
                 JSONObject column = new JSONObject();
762 814
                 column.put("columnName", columns.getString("COLUMN_NAME"));
@@ -767,23 +819,23 @@ public class McpServiceImpl implements IMcpService {
767 819
                 columnsInfo.add(column);
768 820
             }
769 821
             columns.close();
770
-            
822
+
771 823
             ResultSet primaryKeys = metaData.getPrimaryKeys(null, null, tableName);
772 824
             JSONArray primaryKeyColumns = new JSONArray();
773
-            
825
+
774 826
             while (primaryKeys.next()) {
775 827
                 primaryKeyColumns.add(primaryKeys.getString("COLUMN_NAME"));
776 828
             }
777 829
             primaryKeys.close();
778
-            
830
+
779 831
             result.put("tableName", tableName);
780 832
             result.put("columns", columnsInfo);
781 833
             result.put("primaryKey", primaryKeyColumns);
782
-            
834
+
783 835
         } catch (SQLException e) {
784 836
             throw new RuntimeException("获取表结构失败: " + tableName, e);
785 837
         }
786
-        
838
+
787 839
         return result;
788 840
     }
789 841
 
@@ -792,7 +844,7 @@ public class McpServiceImpl implements IMcpService {
792 844
      */
793 845
     private JSONArray getAllTableNames() {
794 846
         JSONArray tableNames = new JSONArray();
795
-        
847
+
796 848
         try {
797 849
             Class.forName("com.mysql.cj.jdbc.Driver");
798 850
         } catch (ClassNotFoundException e) {
@@ -800,10 +852,10 @@ public class McpServiceImpl implements IMcpService {
800 852
         }
801 853
 
802 854
         try (Connection conn = DriverManager.getConnection(Solon.cfg().getProperty("cmc.mysqlService.jdbcUrl"),Solon.cfg().getProperty("cmc.mysqlService.username"), Solon.cfg().getProperty("cmc.mysqlService.password"))) {
803
-            
855
+
804 856
             DatabaseMetaData metaData = conn.getMetaData();
805 857
             ResultSet tables = metaData.getTables(null, null, null, new String[]{"TABLE"});
806
-            
858
+
807 859
             while (tables.next()) {
808 860
                 String tableName = tables.getString("TABLE_NAME");
809 861
                 String tableComment = tables.getString("REMARKS");
@@ -816,11 +868,11 @@ public class McpServiceImpl implements IMcpService {
816 868
                 }
817 869
             }
818 870
             tables.close();
819
-            
871
+
820 872
         } catch (SQLException e) {
821 873
             throw new RuntimeException("获取表名列表失败", e);
822 874
         }
823
-        
875
+
824 876
         return tableNames;
825 877
     }
826 878
 

+ 39
- 16
oa-back/ruoyi-llm/src/main/java/com/ruoyi/web/llm/service/impl/LangChainMilvusServiceImpl.java 파일 보기

@@ -37,6 +37,7 @@ import org.apache.poi.hwpf.usermodel.Paragraph;
37 37
 import org.apache.poi.hwpf.usermodel.Range;
38 38
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
39 39
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
40
+import org.apache.poi.xwpf.usermodel.XWPFStyle;
40 41
 import org.noear.solon.ai.chat.ChatModel;
41 42
 import org.noear.solon.ai.chat.ChatResponse;
42 43
 import org.noear.solon.ai.chat.ChatSession;
@@ -421,9 +422,12 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
421 422
         try (XWPFDocument document = new XWPFDocument(fileInputStream)) {
422 423
             for (XWPFParagraph paragraph : document.getParagraphs()) {
423 424
                 String text = paragraph.getText().trim();
424
-                if (paragraph.getStyle() != null) {
425
+                if (paragraph.getStyleID() != null) {
426
+                    String styleId = paragraph.getStyleID();
427
+                    XWPFStyle style = document.getStyles().getStyle(styleId);
428
+                    String styleName = style.getName();
425 429
                     // 判断主标题
426
-                    if (paragraph.getStyle().equals("3") &&
430
+                    if (styleName.equals("heading 3") &&
427 431
                             text.contains(question)) {
428 432
                         inTargetSection = true;
429 433
                         continue;
@@ -431,11 +435,11 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
431 435
 
432 436
                     // 如果已经在目标节中,收集标题3级别的子标题
433 437
                     if (inTargetSection) {
434
-                        if (paragraph.getStyle().equals("4")) {
438
+                        if (styleName.equals("heading 4")) {
435 439
                             subTitles.add(text);
436 440
                         }
437
-                        // 遇到下一个Heading1则退出
438
-                        else if (paragraph.getStyle().equals("3")) {
441
+                        // 遇到下一个Heading2则退出
442
+                        else if (styleName.equals("heading 3")) {
439 443
                             break;
440 444
                         }
441 445
                     }
@@ -497,8 +501,12 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
497 501
                 for (XWPFParagraph paragraph : document.getParagraphs()) {
498 502
                     String text = paragraph.getText().trim();
499 503
                     if (text.isEmpty()) continue;
500
-                    if (paragraph.getStyle() != null) {
501
-                        if (paragraph.getStyle().equals("2") || paragraph.getStyle().equals("3")) {
504
+                    if (paragraph.getStyleID() != null) {
505
+                        String styleId = paragraph.getStyleID();
506
+                        XWPFStyle style = document.getStyles().getStyle(styleId);
507
+                        String styleName = style.getName();
508
+                        // 二级标题(样式2)开始新的分段
509
+                        if (styleName.equals("heading 2") || styleName.equals("heading 3")) {
502 510
                             titlesBuilder.append(text).append("\n");
503 511
                         }
504 512
                     }
@@ -540,9 +548,12 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
540 548
                     String text = paragraph.getText().trim();
541 549
                     if (text.isEmpty()) continue;
542 550
 
543
-                    if (paragraph.getStyle() != null) {
544
-                        // 二级标题(样式2)开始新的分段
545
-                        if (paragraph.getStyle().equals("2")) {
551
+                    if (paragraph.getStyleID() != null) {
552
+                        String styleId = paragraph.getStyleID();
553
+                        XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
554
+                        String styleName = style.getName();
555
+                        // 二级标题开始新的分段
556
+                        if (styleName.equals("heading 2")) {
546 557
                             // 保存当前二级标题下的内容
547 558
                             if (currentLevel2Content.length() != 0) {
548 559
                                 // 检查当前二级标题内容的字节数
@@ -564,9 +575,15 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
564 575
                                         // 或者可以重新遍历段落,但为了效率,这里采用简单的判断方式
565 576
                                         boolean isLevel3Title = false;
566 577
                                         for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
567
-                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("3")) {
568
-                                                isLevel3Title = true;
569
-                                                break;
578
+                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
579
+                                                styleId = p.getStyle();
580
+                                                style = xwpfDocument.getStyles().getStyle(styleId);
581
+                                                styleName = style.getName();
582
+                                                // 二级标题(样式2)开始新的分段
583
+                                                if (styleName.equals("heading 3")) {
584
+                                                    isLevel3Title = true;
585
+                                                    break;
586
+                                                }
570 587
                                             }
571 588
                                         }
572 589
 
@@ -630,9 +647,15 @@ public class LangChainMilvusServiceImpl implements ILangChainMilvusService
630 647
 
631 648
                             boolean isLevel3Title = false;
632 649
                             for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
633
-                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("3")) {
634
-                                    isLevel3Title = true;
635
-                                    break;
650
+                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
651
+                                    String styleId = p.getStyle();
652
+                                    XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
653
+                                    String styleName = style.getName();
654
+                                    // 二级标题(样式2)开始新的分段
655
+                                    if (styleName.equals("heading 3")) {
656
+                                        isLevel3Title = true;
657
+                                        break;
658
+                                    }
636 659
                                 }
637 660
                             }
638 661
 

+ 109
- 57
oa-back/ruoyi-system/src/main/java/com/ruoyi/llm/service/impl/CmcAgentServiceImpl.java 파일 보기

@@ -96,7 +96,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
96 96
 //                        .uri(milvusServiceUrl)
97 97
 //                        .build());
98 98
     }
99
-    
99
+
100 100
     @PreDestroy
101 101
     public void destroyMilvusClient() {
102 102
         if (milvusClient != null) {
@@ -436,7 +436,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
436 436
         }
437 437
         message.append("\n技术文件已更新,请查看:\n");
438 438
         message.append("【<a href='/profile").append(templatePath.replace("\\", "/").replace(RuoYiConfig.getProfile(), ""))
439
-               .append("'>点击查看技术文件</a>】\n\n");
439
+                .append("'>点击查看技术文件</a>】\n\n");
440 440
         message.append("如需修改特定章节,请输入章节标题名称。\n").append("若提示未检测到工具,麻烦重新发送,谢谢");
441 441
 
442 442
         return message.toString();
@@ -591,11 +591,11 @@ public class CmcAgentServiceImpl implements ICmcAgentService
591 591
                 .append(question)
592 592
                 .append(",先列出二级章节标题,严格按以下格式,仅输出标题列表:\n")
593 593
                 .append("6.1 XX\n" +
594
-                "6.2 XX\n" +
595
-                "6.3 XX\n" +
596
-                "......\n" +
597
-                "6.n-1 XX\n" +
598
-                "6.n XX\n");
594
+                        "6.2 XX\n" +
595
+                        "6.3 XX\n" +
596
+                        "......\n" +
597
+                        "6.n-1 XX\n" +
598
+                        "6.n XX\n");
599 599
         return writeChapters(sb.toString(), templatePath);
600 600
     }
601 601
 
@@ -711,7 +711,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
711 711
                 .metricType(IndexParam.MetricType.COSINE)
712 712
                 .searchParams(searchParams)
713 713
                 .build();
714
-        
714
+
715 715
         if (filter != null && !filter.isEmpty()) {
716 716
             searchReq = SearchReq.builder()
717 717
                     .collectionName(collectionName)
@@ -853,25 +853,48 @@ public class CmcAgentServiceImpl implements ICmcAgentService
853 853
 
854 854
             for (XWPFParagraph  paragraph : document.getParagraphs()) {
855 855
                 String text = paragraph.getText();
856
-                int level = Integer.parseInt(paragraph.getStyle());
857
-
858
-                // 维护当前路径
859
-                while (!currentPath.isEmpty() && Integer.parseInt(currentPath.get(currentPath.size() - 1).getStyle()) >= level) {
860
-                    currentPath.remove(currentPath.size() - 1);
861
-                }
862
-                currentPath.add(paragraph);
863
-                if (foundParent) {
864
-                    if (level > parentLevel) { // 是子标题
865
-                        if (isLeafNode(document, paragraph, level)) {
866
-                            subTitles.add(text.replace("\n", ""));
856
+                if (paragraph.getStyle() != null) {
857
+                    String styleId = paragraph.getStyle();
858
+                    XWPFStyle style = document.getStyles().getStyle(styleId);
859
+                    String styleName = style.getName();
860
+                    int level = 0;
861
+                    if (styleName.equals("heading 2")) level = 2;
862
+                    else if (styleName.equals("heading 3")) level = 3;
863
+                    else if (styleName.equals("heading 4")) level = 4;
864
+                    else continue;
865
+
866
+                    // 维护当前路径
867
+                    while (!currentPath.isEmpty()) {
868
+                        XWPFParagraph lastPara = currentPath.get(currentPath.size() - 1);
869
+                        String lastStyleId = lastPara.getStyle();
870
+                        XWPFStyle lastStyle = document.getStyles().getStyle(lastStyleId);
871
+                        String lastStyleName = lastStyle.getName();
872
+                        int lastLevel = 0;
873
+                        if (lastStyleName.equals("heading 2")) lastLevel = 2;
874
+                        else if (lastStyleName.equals("heading 3")) lastLevel = 3;
875
+                        else if (lastStyleName.equals("heading 4")) lastLevel = 4;
876
+                        else break;
877
+
878
+                        if (lastLevel >= level) {
879
+                            currentPath.remove(currentPath.size() - 1);
880
+                        } else {
881
+                            break;
867 882
                         }
868
-                    } else {
869
-                        // 遇到同级或更高级别的标题,停止搜索
870
-                        break;
871 883
                     }
872
-                } else if (text.equals(question)) {
873
-                    foundParent = true;
874
-                    parentLevel = level;
884
+                    currentPath.add(paragraph);
885
+                    if (foundParent) {
886
+                        if (level > parentLevel) { // 是子标题
887
+                            if (isLeafNode(document, paragraph, level)) {
888
+                                subTitles.add(text.replace("\n", ""));
889
+                            }
890
+                        } else {
891
+                            // 遇到同级或更高级别的标题,停止搜索
892
+                            break;
893
+                        }
894
+                    } else if (text.equals(question)) {
895
+                        foundParent = true;
896
+                        parentLevel = level;
897
+                    }
875 898
                 }
876 899
             }
877 900
 
@@ -892,10 +915,19 @@ public class CmcAgentServiceImpl implements ICmcAgentService
892 915
 
893 916
         // 检查后续段落
894 917
         XWPFParagraph nextP = doc.getParagraphs().get(index + 1);
895
-        int nextLevel = Integer.parseInt(nextP.getStyle());
896
-        // 遇到同级或更高级别标题,是叶子节点
897
-        return nextLevel <= level; // 存在更低级别的标题,不是叶子节点
898
-
918
+        if (nextP.getStyle() != null) {
919
+            String styleId = nextP.getStyle();
920
+            XWPFStyle style = doc.getStyles().getStyle(styleId);
921
+            String styleName = style.getName();
922
+            int nextLevel = 0;
923
+            if (styleName.equals("heading 2")) nextLevel = 2;
924
+            else if (styleName.equals("heading 3")) nextLevel = 3;
925
+            else if (styleName.equals("heading 4")) nextLevel = 4;
926
+            else return true;
927
+            // 遇到同级或更高级别标题,是叶子节点
928
+            return nextLevel <= level; // 存在更低级别的标题,不是叶子节点
929
+        }
930
+        return true;
899 931
     }
900 932
 
901 933
     /**
@@ -908,8 +940,11 @@ public class CmcAgentServiceImpl implements ICmcAgentService
908 940
             for (XWPFParagraph paragraph : document.getParagraphs()) {
909 941
                 String text = paragraph.getText().trim();
910 942
                 if (paragraph.getStyle() != null) {
943
+                    String styleId = paragraph.getStyle();
944
+                    XWPFStyle style = document.getStyles().getStyle(styleId);
945
+                    String styleName = style.getName();
911 946
                     // 判断主标题
912
-                    if (paragraph.getStyle().equals("3") || paragraph.getStyle().equals("4") ) {
947
+                    if (styleName.equals("heading 3") || styleName.equals("heading 4")) {
913 948
                         subTitles.append(text).append("\n");
914 949
                     }
915 950
                 }
@@ -936,9 +971,12 @@ public class CmcAgentServiceImpl implements ICmcAgentService
936 971
                     String text = paragraph.getText().trim();
937 972
                     if (text.isEmpty()) continue;
938 973
 
939
-                    if (paragraph.getStyle() != null) {
940
-                        // 二级标题(样式2)开始新的分段
941
-                        if (paragraph.getStyle().equals("3")) {
974
+                    if (paragraph.getStyleID() != null) {
975
+                        String styleId = paragraph.getStyleID();
976
+                        XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
977
+                        String styleName = style.getName();
978
+                        // 二级标题开始新的分段
979
+                        if (styleName.equals("heading 2")) {
942 980
                             // 保存当前二级标题下的内容
943 981
                             if (currentLevel2Content.length() != 0) {
944 982
                                 // 检查当前二级标题内容的字节数
@@ -954,16 +992,24 @@ public class CmcAgentServiceImpl implements ICmcAgentService
954 992
                                     String[] paragraphs = currentLevel2Content.toString().split("\n");
955 993
                                     for (String para : paragraphs) {
956 994
                                         if (para.trim().isEmpty()) continue;
957
-                                        
958
-                                        // 检查是否是三级标题
995
+
996
+                                        // 检查是否是三级标题(简单判断:如果是三级标题,样式应该是3,但这里需要重新解析)
997
+                                        // 由于已经将内容转为字符串,这里采用另一种方式:假设三级标题以数字+点+空格开头(如"1.1.")
998
+                                        // 或者可以重新遍历段落,但为了效率,这里采用简单的判断方式
959 999
                                         boolean isLevel3Title = false;
960 1000
                                         for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
961
-                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("4")) {
962
-                                                isLevel3Title = true;
963
-                                                break;
1001
+                                            if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
1002
+                                                styleId = p.getStyle();
1003
+                                                style = xwpfDocument.getStyles().getStyle(styleId);
1004
+                                                styleName = style.getName();
1005
+                                                // 二级标题(样式2)开始新的分段
1006
+                                                if (styleName.equals("heading 3")) {
1007
+                                                    isLevel3Title = true;
1008
+                                                    break;
1009
+                                                }
964 1010
                                             }
965 1011
                                         }
966
-                                        
1012
+
967 1013
                                         if (isLevel3Title) {
968 1014
                                             // 保存当前三级标题内容
969 1015
                                             if (currentLevel3Content.length() != 0) {
@@ -1021,15 +1067,21 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1021 1067
                         String[] paragraphs = currentLevel2Content.toString().split("\n");
1022 1068
                         for (String para : paragraphs) {
1023 1069
                             if (para.trim().isEmpty()) continue;
1024
-                            
1070
+
1025 1071
                             boolean isLevel3Title = false;
1026 1072
                             for (XWPFParagraph p : xwpfDocument.getParagraphs()) {
1027
-                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null && p.getStyle().equals("4")) {
1028
-                                    isLevel3Title = true;
1029
-                                    break;
1073
+                                if (p.getText().trim().equals(para.trim()) && p.getStyle() != null) {
1074
+                                    String styleId = p.getStyle();
1075
+                                    XWPFStyle style = xwpfDocument.getStyles().getStyle(styleId);
1076
+                                    String styleName = style.getName();
1077
+                                    // 二级标题(样式2)开始新的分段
1078
+                                    if (styleName.equals("heading 3")) {
1079
+                                        isLevel3Title = true;
1080
+                                        break;
1081
+                                    }
1030 1082
                                 }
1031 1083
                             }
1032
-                            
1084
+
1033 1085
                             if (isLevel3Title) {
1034 1086
                                 // 保存当前三级标题内容
1035 1087
                                 if (currentLevel3Content.length() != 0) {
@@ -1088,20 +1140,20 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1088 1140
                                 StringBuilder currentLevel3Content = new StringBuilder();
1089 1141
                                 Range level2Range = hwpfDocument.getRange();
1090 1142
                                 boolean foundCurrentLevel2 = false;
1091
-                                
1143
+
1092 1144
                                 for (int j = 0; j < level2Range.numParagraphs(); j++) {
1093 1145
                                     Paragraph p = level2Range.getParagraph(j);
1094 1146
                                     String paraText = p.text().trim();
1095 1147
                                     if (paraText.isEmpty()) continue;
1096
-                                    
1148
+
1097 1149
                                     // 找到当前二级标题的开始
1098 1150
                                     if (p.getStyleIndex() == 2 && paraText.equals(currentLevel2Content.toString().split("\n")[0].trim())) {
1099 1151
                                         foundCurrentLevel2 = true;
1100 1152
                                     }
1101
-                                    
1153
+
1102 1154
                                     if (foundCurrentLevel2) {
1103 1155
                                         short paraStyleIndex = p.getStyleIndex();
1104
-                                        
1156
+
1105 1157
                                         if (paraStyleIndex == 3) {
1106 1158
                                             // 三级标题
1107 1159
                                             // 保存当前三级标题内容
@@ -1123,7 +1175,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1123 1175
                                         }
1124 1176
                                     }
1125 1177
                                 }
1126
-                                
1178
+
1127 1179
                                 // 保存最后一个三级标题内容
1128 1180
                                 if (currentLevel3Content.length() != 0) {
1129 1181
                                     TextSegment segment = TextSegment.from(currentLevel3Content.toString());
@@ -1156,20 +1208,20 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1156 1208
                         StringBuilder currentLevel3Content = new StringBuilder();
1157 1209
                         Range level2Range = hwpfDocument.getRange();
1158 1210
                         boolean foundCurrentLevel2 = false;
1159
-                        
1211
+
1160 1212
                         for (int j = 0; j < level2Range.numParagraphs(); j++) {
1161 1213
                             Paragraph p = level2Range.getParagraph(j);
1162 1214
                             String paraText = p.text().trim();
1163 1215
                             if (paraText.isEmpty()) continue;
1164
-                            
1216
+
1165 1217
                             // 找到当前二级标题的开始
1166 1218
                             if (p.getStyleIndex() == 2 && paraText.equals(currentLevel2Content.toString().split("\n")[0].trim())) {
1167 1219
                                 foundCurrentLevel2 = true;
1168 1220
                             }
1169
-                            
1221
+
1170 1222
                             if (foundCurrentLevel2) {
1171 1223
                                 short paraStyleIndex = p.getStyleIndex();
1172
-                                
1224
+
1173 1225
                                 if (paraStyleIndex == 3) {
1174 1226
                                     // 三级标题
1175 1227
                                     // 保存当前三级标题内容
@@ -1191,7 +1243,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1191 1243
                                 }
1192 1244
                             }
1193 1245
                         }
1194
-                        
1246
+
1195 1247
                         // 保存最后一个三级标题内容
1196 1248
                         if (currentLevel3Content.length() != 0) {
1197 1249
                             TextSegment segment = TextSegment.from(currentLevel3Content.toString());
@@ -1367,7 +1419,7 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1367 1419
                         blocks.add(new Block("para", line));
1368 1420
                     }
1369 1421
                 }
1370
-                
1422
+
1371 1423
                 // 更新上一行记录
1372 1424
                 previousLine = line;
1373 1425
             }
@@ -1439,13 +1491,13 @@ public class CmcAgentServiceImpl implements ICmcAgentService
1439 1491
                     case "boldOnly": {
1440 1492
                         // 处理格式:**文本** 或 **文本**: 转换为 Word 中的部分加粗
1441 1493
                         XWPFParagraph p = document.insertNewParagraph(cursor);
1442
-                        
1494
+
1443 1495
                         // 加粗文本部分
1444 1496
                         XWPFRun run1 = p.createRun();
1445 1497
                         run1.setText(b.boldText);
1446 1498
                         run1.setFontSize(12);
1447 1499
                         run1.setBold(true);
1448
-                        
1500
+
1449 1501
                         // 后缀部分(不加粗,如冒号及后续内容,如果有的话)
1450 1502
                         if (b.suffixText != null && !b.suffixText.isEmpty()) {
1451 1503
                             XWPFRun run2 = p.createRun();

oa-ui/src/assets/icons/svg/Branding.svg → oa-ui/src/assets/icons/svg/branding.svg 파일 보기


+ 16
- 16
oa-ui/src/views/llm/knowledge/index.vue 파일 보기

@@ -71,31 +71,31 @@
71 71
       <!-- 右侧面板 -->
72 72
       <div class="right-panel">
73 73
         <div class="panel-header">
74
-          <h3>{{ isChatMode ? '知识库对话' : (isTitleMode ? '标题列表' : '文件列表') }}</h3>
74
+          <h3>{{ isChatMode ? '知识库对话' : (isScanMode ? '内容列表' : '文件列表') }}</h3>
75 75
           <div class="header-actions">
76
-            <el-button v-if="!isChatMode && !isTitleMode" type="primary" icon="el-icon-chat-round" @click="handleChat(selectedKnowledge)"
76
+            <el-button v-if="!isChatMode && !isScanMode" type="primary" icon="el-icon-chat-round" @click="handleChat(selectedKnowledge)"
77 77
               :disabled="!selectedKnowledge" v-hasPermi="['llm:knowledge:chat']">
78 78
               开始对话
79 79
             </el-button>
80
-            <el-button v-if="!isChatMode && !isTitleMode" type="primary" icon="el-icon-upload" @click="handleUpload(selectedKnowledge)"
80
+            <el-button v-if="!isChatMode && !isScanMode" type="primary" icon="el-icon-upload" @click="handleUpload(selectedKnowledge)"
81 81
               :disabled="!selectedKnowledge" v-hasPermi="['llm:knowledge:upload']">
82 82
               上传文件
83 83
             </el-button>
84
-            <el-button v-if="!isChatMode && !isTitleMode" type="default" icon="el-icon-notebook-2" @click="switchToTitleMode"
84
+            <el-button v-if="!isChatMode && !isScanMode" type="default" icon="el-icon-notebook-2" @click="switchToTitleMode"
85 85
               :disabled="!selectedKnowledge">
86 86
               查看内容
87 87
             </el-button>
88 88
             <el-button v-if="isChatMode" type="default" icon="el-icon-document" @click="switchToFileMode">
89 89
               返回文件列表
90 90
             </el-button>
91
-            <el-button v-if="isTitleMode" type="default" icon="el-icon-document" @click="switchToFileMode">
91
+            <el-button v-if="isScanMode" type="default" icon="el-icon-document" @click="switchToFileMode">
92 92
               返回文件列表
93 93
             </el-button>
94 94
           </div>
95 95
         </div>
96 96
 
97 97
         <!-- 文件列表模式 -->
98
-        <div v-if="!isChatMode && !isTitleMode" class="file-content" v-loading="fileLoading">
98
+        <div v-if="!isChatMode && !isScanMode" class="file-content" v-loading="fileLoading">
99 99
           <div v-if="selectedKnowledge" class="selected-knowledge">
100 100
             <i class="el-icon-folder folder-icon">
101 101
             </i>
@@ -140,13 +140,13 @@
140 140
             @pagination="handleFilePagination" :autoScroll="false" />
141 141
         </div>
142 142
 
143
-        <!-- 标题列表模式 -->
144
-        <div v-if="!isChatMode && isTitleMode" class="title-content" v-loading="titleLoading">
143
+        <!-- 内容列表模式 -->
144
+        <div v-if="!isChatMode && isScanMode" class="title-content" v-loading="titleLoading">
145 145
           <div v-if="selectedKnowledge" class="selected-knowledge">
146 146
             <i class="el-icon-folder folder-icon">
147 147
             </i>
148 148
             <span class="knowledge-name">{{ selectedKnowledge.collectionName }}</span>
149
-            <span class="title-mode-badge">标题模式</span>
149
+            <span class="title-mode-badge">浏览模式</span>
150 150
           </div>
151 151
 
152 152
           <div v-if="!selectedKnowledge" class="empty-state">
@@ -158,7 +158,7 @@
158 158
           <div v-else-if="titleList.length === 0" class="empty-state">
159 159
             <i class="el-icon-notebook-2 empty-icon">
160 160
             </i>
161
-            <p>该知识库暂无标题</p>
161
+            <p>该知识库暂无内容</p>
162 162
           </div>
163 163
 
164 164
           <div v-else class="title-content-container">
@@ -518,7 +518,7 @@ export default {
518 518
       timer: null,
519 519
 
520 520
       // 标题和内容相关状态
521
-      isTitleMode: false, // 是否显示标题模式
521
+      isScanMode: false, // 是否显示浏览模式
522 522
       titleList: [], // 标题列表
523 523
       contentList: [], // 内容列表
524 524
       selectedTitle: null, // 选中的标题
@@ -527,9 +527,9 @@ export default {
527 527
     }
528 528
   },
529 529
   mounted() {
530
-    // 重置状态,确保页面刷新后不会停留在对话模式或标题模式
530
+    // 重置状态,确保页面刷新后不会停留在对话模式或浏览模式
531 531
     this.isChatMode = false;
532
-    this.isTitleMode = false;
532
+    this.isScanMode = false;
533 533
     this.chatMessages = [];
534 534
     this.chatInput = '';
535 535
     this.streamingStarted = false;
@@ -663,12 +663,12 @@ export default {
663 663
       }
664 664
 
665 665
       this.isChatMode = false;
666
-      this.isTitleMode = false;
666
+      this.isScanMode = false;
667 667
     },
668 668
 
669
-    /** 切换到标题模式 */
669
+    /** 切换到浏览模式 */
670 670
     switchToTitleMode() {
671
-      this.isTitleMode = true;
671
+      this.isScanMode = true;
672 672
       this.isChatMode = false;
673 673
       this.titleList = [];
674 674
       this.contentList = [];

Loading…
취소
저장