瀏覽代碼

【功能新增】AI:新增知识库文档的更新信息与状态

YunaiV 5 月之前
父節點
當前提交
a998168b3b

+ 11 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowl
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
 import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
@@ -47,14 +48,14 @@ public class AiKnowledgeDocumentController {
     }
 
     @PostMapping("/create")
-    @Operation(summary = "新建文档")
+    @Operation(summary = "新建文档(单个)")
     public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
         Long id = documentService.createKnowledgeDocument(reqVO);
         return success(id);
     }
 
     @PostMapping("/create-list")
-    @Operation(summary = "批量新建文档")
+    @Operation(summary = "新建文档(多个)")
     public CommonResult<List<Long>> createKnowledgeDocumentList(
             @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
         List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
@@ -68,4 +69,12 @@ public class AiKnowledgeDocumentController {
         return success(true);
     }
 
+    @PutMapping("/update-status")
+    @Operation(summary = "更新文档状态")
+    public CommonResult<Boolean> updateKnowledgeDocumentStatus(
+            @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
+        documentService.updateKnowledgeDocumentStatus(reqVO);
+        return success(true);
+    }
+
 }

+ 2 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java

@@ -15,6 +15,7 @@ import io.swagger.v3.oas.annotations.Parameters;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.annotation.Resource;
 import jakarta.validation.Valid;
+import org.hibernate.validator.constraints.URL;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -62,7 +63,7 @@ public class AiKnowledgeSegmentController {
             @Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
     })
     public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
-            @RequestParam("url") String url,
+            @RequestParam("url") @URL String url,
             @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
         List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
         return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));

+ 2 - 6
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java

@@ -1,12 +1,9 @@
 package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
 
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 
-// TODO @芋艿:稍后优化
 @Schema(description = "管理后台 - AI 知识库文档更新 Request VO")
 @Data
 public class AiKnowledgeDocumentUpdateReqVO {
@@ -18,8 +15,7 @@ public class AiKnowledgeDocumentUpdateReqVO {
     @Schema(description = "名称", example = "Java 开发手册")
     private String name;
 
-    @Schema(description = "是否启用", example = "1")
-    @InEnum(CommonStatusEnum.class)
-    private Integer status;
+    @Schema(description = "分片最大 Token 数", example = "1000")
+    private Integer segmentMaxTokens;
 
 }

+ 6 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java

@@ -31,4 +31,10 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment
                 .orderByDesc(AiKnowledgeSegmentDO::getId));
     }
 
+    default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {
+        return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
+                .eq(AiKnowledgeSegmentDO::getDocumentId, documentId)
+                .orderByDesc(AiKnowledgeSegmentDO::getId));
+    }
+
 }

+ 11 - 3
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.service.knowledge;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
@@ -10,14 +11,14 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO
 import java.util.List;
 
 /**
- * AI 知识库-文档 Service 接口
+ * AI 知识库文档 Service 接口
  *
  * @author xiaoxin
  */
 public interface AiKnowledgeDocumentService {
 
     /**
-     * 创建文档
+     * 创建文档(单个)
      *
      * @param createReqVO 文档创建 Request VO
      * @return 文档编号
@@ -25,7 +26,7 @@ public interface AiKnowledgeDocumentService {
     Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
 
     /**
-     * 批量创建文档
+     * 创建文档(多个)
      *
      * @param createListReqVO 批量创建 Request VO
      * @return 文档编号列表
@@ -55,6 +56,13 @@ public interface AiKnowledgeDocumentService {
      */
     void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
 
+    /**
+     * 更新文档状态
+     *
+     * @param reqVO 更新状态信息
+     */
+    void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);
+
     /**
      * 校验文档是否存在
      *

+ 32 - 2
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.ai.service.knowledge;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -8,6 +9,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
@@ -88,6 +90,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
                     .setKnowledgeId(createListReqVO.getKnowledgeId())
                     .setContent(content).setContentLength(content.length())
                     .setTokens(tokenCountEstimator.estimate(content))
+                    .setSegmentMaxTokens(createListReqVO.getSegmentMaxTokens())
                     .setStatus(CommonStatusEnum.ENABLE.getStatus()));
         }
         knowledgeDocumentMapper.insertBatch(documentDOs);
@@ -111,11 +114,38 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
     @Override
     public void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO) {
         // 1. 校验文档是否存在
-        validateKnowledgeDocumentExists(reqVO.getId());
+        AiKnowledgeDocumentDO oldDocument = validateKnowledgeDocumentExists(reqVO.getId());
+
         // 2. 更新文档
         AiKnowledgeDocumentDO document = BeanUtils.toBean(reqVO, AiKnowledgeDocumentDO.class);
         knowledgeDocumentMapper.updateById(document);
-        // TODO @芋艿:这里要处理状态的变更
+
+        // 3. 如果处于开启状态,并且最大 tokens 发生变化,则 segment 需要重新索引
+        if (CommonStatusEnum.isEnable(oldDocument.getStatus())
+                && reqVO.getSegmentMaxTokens() != null
+                && ObjUtil.notEqual(reqVO.getSegmentMaxTokens(), oldDocument.getSegmentMaxTokens())) {
+            // 删除旧的文档切片
+            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
+            // 重新创建文档切片
+            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), oldDocument.getContent());
+        }
+    }
+
+    @Override
+    public void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
+        // 1. 校验存在
+        AiKnowledgeDocumentDO document = validateKnowledgeDocumentExists(reqVO.getId());
+
+        // 2. 更新状态
+        knowledgeDocumentMapper.updateById(new AiKnowledgeDocumentDO()
+                .setId(reqVO.getId()).setStatus(reqVO.getStatus()));
+
+        // 3. 处理文档切片
+        if (CommonStatusEnum.isEnable(reqVO.getStatus())) {
+            knowledgeSegmentService.createKnowledgeSegmentBySplitContentAsync(reqVO.getId(), document.getContent());
+        } else {
+            knowledgeSegmentService.deleteKnowledgeSegmentByDocumentId(reqVO.getId());
+        }
     }
 
     @Override

+ 7 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java

@@ -58,6 +58,13 @@ public interface AiKnowledgeSegmentService {
      */
     void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
 
+    /**
+     * 根据文档编号删除段落
+     *
+     * @param documentId 文档编号
+     */
+    void deleteKnowledgeSegmentByDocumentId(Long documentId);
+
     /**
      * 召回段落
      *

+ 16 - 0
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java

@@ -115,6 +115,22 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
         }
     }
 
+    @Override
+    public void deleteKnowledgeSegmentByDocumentId(Long documentId) {
+        // 1. 查询需要删除的段落
+        List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByDocumentId(documentId);
+        if (CollUtil.isEmpty(segments)) {
+            return;
+        }
+
+        // 2. 批量删除段落记录
+        segmentMapper.deleteByIds(convertList(segments, AiKnowledgeSegmentDO::getId));
+
+        // 3. 删除向量存储中的段落
+        VectorStore vectorStore = getVectorStoreById(segments.get(0).getKnowledgeId());
+        vectorStore.delete(convertList(segments, AiKnowledgeSegmentDO::getVectorId));
+    }
+
     @Override
     public void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
         // 1. 校验