Commit d7e801c438d3e4c2e7f4e34cec224dd23b4a3efc

Authored by YunaiV
1 parent 32e1ef4d

【功能新增】AI:知识库文档的模型变化时,触发重索引(异步)

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -42,6 +42,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment @@ -42,6 +42,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegment
42 .orderByDesc(AiKnowledgeSegmentDO::getId)); 42 .orderByDesc(AiKnowledgeSegmentDO::getId));
43 } 43 }
44 44
  45 + default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {
  46 + return selectList(AiKnowledgeSegmentDO::getKnowledgeId, knowledgeId,
  47 + AiKnowledgeSegmentDO::getStatus, status);
  48 + }
  49 +
45 default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) { 50 default List<AiKnowledgeSegmentProcessRespVO> selectProcessList(Collection<Long> documentIds) {
46 MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>() 51 MPJLambdaWrapper<AiKnowledgeSegmentDO> wrapper = new MPJLambdaWrapperX<AiKnowledgeSegmentDO>()
47 .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId) 52 .selectAs(AiKnowledgeSegmentDO::getDocumentId, AiKnowledgeSegmentProcessRespVO::getDocumentId)
@@ -54,7 +59,7 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment @@ -54,7 +59,7 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment
54 } 59 }
55 60
56 default void updateRetrievalCountIncrByIds(List<Long> ids) { 61 default void updateRetrievalCountIncrByIds(List<Long> ids) {
57 - update( new LambdaUpdateWrapper<AiKnowledgeSegmentDO>() 62 + update(new LambdaUpdateWrapper<AiKnowledgeSegmentDO>()
58 .setSql(" retrieval_count = retrieval_count + 1") 63 .setSql(" retrieval_count = retrieval_count + 1")
59 .in(AiKnowledgeSegmentDO::getId, ids)); 64 .in(AiKnowledgeSegmentDO::getId, ids));
60 } 65 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -99,6 +99,23 @@ public interface AiKnowledgeSegmentService { @@ -99,6 +99,23 @@ public interface AiKnowledgeSegmentService {
99 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); 99 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
100 100
101 /** 101 /**
  102 + * 重新索引知识库下的所有文档段落
  103 + *
  104 + * @param knowledgeId 知识库编号
  105 + */
  106 + void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);
  107 +
  108 + /**
  109 + * 【异步】重新索引知识库下的所有文档段落
  110 + *
  111 + * @param knowledgeId 知识库编号
  112 + */
  113 + @Async
  114 + default void reindexByKnowledgeIdAsync(Long knowledgeId) {
  115 + reindexKnowledgeSegmentByKnowledgeId(knowledgeId);
  116 + }
  117 +
  118 + /**
102 * 根据文档编号删除段落 119 * 根据文档编号删除段落
103 * 120 *
104 * @param documentId 文档编号 121 * @param documentId 文档编号
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -158,6 +158,30 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -158,6 +158,30 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
158 } 158 }
159 } 159 }
160 160
  161 + @Override
  162 + public void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId) {
  163 + // 1.1 校验知识库存在
  164 + AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(knowledgeId);
  165 + // 1.2 获取知识库向量实例
  166 + VectorStore vectorStore = getVectorStoreById(knowledge);
  167 +
  168 + // 2.1 查询知识库下的所有启用状态的段落
  169 + List<AiKnowledgeSegmentDO> segments = segmentMapper.selectListByKnowledgeIdAndStatus(
  170 + knowledgeId, CommonStatusEnum.ENABLE.getStatus());
  171 + if (CollUtil.isEmpty(segments)) {
  172 + return;
  173 + }
  174 + // 2.2 遍历所有段落,重新索引
  175 + for (AiKnowledgeSegmentDO segment : segments) {
  176 + // 删除旧的向量
  177 + deleteVectorStore(vectorStore, segment);
  178 + // 重新创建向量
  179 + writeVectorStore(vectorStore, segment, new Document(segment.getContent()));
  180 + }
  181 + log.info("[reindexKnowledgeSegmentByKnowledgeId][知识库({}) 重新索引完成,共处理 {} 个段落]",
  182 + knowledgeId, segments.size());
  183 + }
  184 +
161 private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { 185 private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) {
162 // 1. 向量存储 186 // 1. 向量存储
163 // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore 187 // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
1 package cn.iocoder.yudao.module.ai.service.knowledge; 1 package cn.iocoder.yudao.module.ai.service.knowledge;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO; 7 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
6 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO; 8 import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
7 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO; 9 import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
  10 +import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
8 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO; 11 import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
9 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper; 12 import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
10 import cn.iocoder.yudao.module.ai.service.model.AiModelService; 13 import cn.iocoder.yudao.module.ai.service.model.AiModelService;
@@ -31,6 +34,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -31,6 +34,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
31 34
32 @Resource 35 @Resource
33 private AiModelService modelService; 36 private AiModelService modelService;
  37 + @Resource
  38 + private AiKnowledgeSegmentService knowledgeSegmentService;
34 39
35 @Override 40 @Override
36 public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) { 41 public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
@@ -47,7 +52,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -47,7 +52,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
47 @Override 52 @Override
48 public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) { 53 public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {
49 // 1.1 校验知识库存在 54 // 1.1 校验知识库存在
50 - validateKnowledgeExists(updateReqVO.getId()); 55 + AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());
51 // 1.2 校验模型配置 56 // 1.2 校验模型配置
52 AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId()); 57 AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());
53 58
@@ -56,7 +61,10 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -56,7 +61,10 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
56 .setEmbeddingModel(model.getModel()); 61 .setEmbeddingModel(model.getModel());
57 knowledgeMapper.updateById(updateObj); 62 knowledgeMapper.updateById(updateObj);
58 63
59 - // TODO @芋艿:如果模型变化,需要 reindex 所有的文档 64 + // 3. 如果模型变化,需要 reindex 所有的文档
  65 + if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {
  66 + knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());
  67 + }
60 } 68 }
61 69
62 @Override 70 @Override
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
@@ -16,8 +16,8 @@ import jakarta.annotation.Resource; @@ -16,8 +16,8 @@ import jakarta.annotation.Resource;
16 import org.springframework.ai.chat.model.ChatModel; 16 import org.springframework.ai.chat.model.ChatModel;
17 import org.springframework.ai.embedding.EmbeddingModel; 17 import org.springframework.ai.embedding.EmbeddingModel;
18 import org.springframework.ai.image.ImageModel; 18 import org.springframework.ai.image.ImageModel;
  19 +import org.springframework.ai.vectorstore.SimpleVectorStore;
19 import org.springframework.ai.vectorstore.VectorStore; 20 import org.springframework.ai.vectorstore.VectorStore;
20 -import org.springframework.ai.vectorstore.milvus.MilvusVectorStore;  
21 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
22 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
23 23
@@ -162,10 +162,10 @@ public class AiModelServiceImpl implements AiModelService { @@ -162,10 +162,10 @@ public class AiModelServiceImpl implements AiModelService {
162 platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); 162 platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
163 163
164 // 创建或获取 VectorStore 对象 164 // 创建或获取 VectorStore 对象
165 - // return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields); 165 + return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
166 // return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields); 166 // return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
167 // return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields); 167 // return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
168 - return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields); 168 +// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
169 } 169 }
170 170
171 } 171 }
172 \ No newline at end of file 172 \ No newline at end of file