Commit 8e56b81a3a73de6e6d2d4d2e8da9b1285ef69df8

Authored by YunaiV
1 parent 0b1d9ce2

【代码评审】AI 大模型:知识库的逻辑

Showing 14 changed files with 25 additions and 17 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeCreateMyReqVO.java
@@ -32,4 +32,5 @@ public class AiKnowledgeCreateMyReqVO { @@ -32,4 +32,5 @@ public class AiKnowledgeCreateMyReqVO {
32 @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3") 32 @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
33 @NotNull(message = "topK 不能为空") 33 @NotNull(message = "topK 不能为空")
34 private Integer topK; 34 private Integer topK;
  35 +
35 } 36 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
@@ -42,4 +42,5 @@ public class AiKnowledgeDocumentCreateReqVO { @@ -42,4 +42,5 @@ public class AiKnowledgeDocumentCreateReqVO {
42 @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") 42 @Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
43 @NotNull(message = "分块是否保留分隔符不能为空") 43 @NotNull(message = "分块是否保留分隔符不能为空")
44 private Boolean keepSeparator; 44 private Boolean keepSeparator;
  45 +
45 } 46 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -57,17 +57,16 @@ public class AiKnowledgeDO extends BaseDO { @@ -57,17 +57,16 @@ public class AiKnowledgeDO extends BaseDO {
57 * topK 57 * topK
58 */ 58 */
59 private Integer topK; 59 private Integer topK;
60 -  
61 /** 60 /**
62 * 相似度阈值 61 * 相似度阈值
63 */ 62 */
64 private Double similarityThreshold; 63 private Double similarityThreshold;
65 64
66 -  
67 /** 65 /**
68 * 状态 66 * 状态
69 * <p> 67 * <p>
70 * 枚举 {@link CommonStatusEnum} 68 * 枚举 {@link CommonStatusEnum}
71 */ 69 */
72 private Integer status; 70 private Integer status;
  71 +
73 } 72 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
@@ -47,10 +47,12 @@ public class AiKnowledgeDocumentDO extends BaseDO { @@ -47,10 +47,12 @@ public class AiKnowledgeDocumentDO extends BaseDO {
47 * 字符数 47 * 字符数
48 */ 48 */
49 private Integer wordCount; 49 private Integer wordCount;
  50 + // TODO @新:chunk 1)是不是 segment,这样命名保持一致会好点哈?2)Size 是不是改成 Tokens 会统一点;3)defaultChunkSize、defaultChunkSize、minChunkSizeChars、maxNumChunks 这几个字段的命名,可能要微信一起讨论下。尽量命名保持风格统一哈。
50 /** 51 /**
51 * 每个文本块的目标 token 数 52 * 每个文本块的目标 token 数
52 */ 53 */
53 private Integer defaultChunkSize; 54 private Integer defaultChunkSize;
  55 + // TODO @xin:SizeChars 和 wordCount 好像是一个意思,是不是也要统一哈。
54 /** 56 /**
55 * 每个文本块的最小字符数 57 * 每个文本块的最小字符数
56 */ 58 */
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
@@ -27,7 +27,7 @@ public class AiKnowledgeSegmentDO extends BaseDO { @@ -27,7 +27,7 @@ public class AiKnowledgeSegmentDO extends BaseDO {
27 /** 27 /**
28 * 向量库的编号 28 * 向量库的编号
29 */ 29 */
30 - @TableField(updateStrategy = FieldStrategy.ALWAYS) 30 + @TableField(updateStrategy = FieldStrategy.ALWAYS) // TODO @新:尽量规避要这个注解。万一后面加个 status 单独更新,可能会踩坑。
31 private String vectorId; 31 private String vectorId;
32 /** 32 /**
33 * 知识库编号 33 * 知识库编号
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -25,9 +25,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment @@ -25,9 +25,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX&lt;AiKnowledgeSegment
25 .orderByDesc(AiKnowledgeSegmentDO::getId)); 25 .orderByDesc(AiKnowledgeSegmentDO::getId));
26 } 26 }
27 27
  28 + // TODO @新:selectListByXXX 哈
28 default List<AiKnowledgeSegmentDO> selectList(List<String> vectorIdList) { 29 default List<AiKnowledgeSegmentDO> selectList(List<String> vectorIdList) {
29 return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>() 30 return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
30 .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList) 31 .in(AiKnowledgeSegmentDO::getVectorId, vectorIdList)
31 .orderByDesc(AiKnowledgeSegmentDO::getId)); 32 .orderByDesc(AiKnowledgeSegmentDO::getId));
32 } 33 }
  34 +
33 } 35 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
@@ -83,7 +83,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic @@ -83,7 +83,7 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic
83 .setStatus(CommonStatusEnum.ENABLE.getStatus())); 83 .setStatus(CommonStatusEnum.ENABLE.getStatus()));
84 segmentMapper.insertBatch(segmentDOList); 84 segmentMapper.insertBatch(segmentDOList);
85 85
86 - // 3.2 向量化并存储 86 + // 3. 向量化并存储
87 segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId())); 87 segments.forEach(segment -> segment.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, createReqVO.getKnowledgeId()));
88 vectorStore.add(segments); 88 vectorStore.add(segments);
89 return documentId; 89 return documentId;
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -38,9 +38,8 @@ public interface AiKnowledgeSegmentService { @@ -38,9 +38,8 @@ public interface AiKnowledgeSegmentService {
38 */ 38 */
39 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO); 39 void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
40 40
41 -  
42 /** 41 /**
43 - * 段落召回 42 + * 召回段落
44 * 43 *
45 * @param reqVO 召回请求信息 44 * @param reqVO 召回请求信息
46 * @return 召回的段落 45 * @return 召回的段落
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -55,19 +55,19 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -55,19 +55,19 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
55 55
56 @Override 56 @Override
57 public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) { 57 public void updateKnowledgeSegment(AiKnowledgeSegmentUpdateReqVO reqVO) {
58 - // 0 校验 58 + // 1. 校验
59 AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId()); 59 AiKnowledgeSegmentDO oldKnowledgeSegment = validateKnowledgeSegmentExists(reqVO.getId());
  60 +
60 // 2.1 获取知识库向量实例 61 // 2.1 获取知识库向量实例
61 VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId()); 62 VectorStore vectorStore = knowledgeService.getVectorStoreById(oldKnowledgeSegment.getKnowledgeId());
62 // 2.2 删除原向量 63 // 2.2 删除原向量
63 vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId())); 64 vectorStore.delete(List.of(oldKnowledgeSegment.getVectorId()));
64 -  
65 // 2.3 重新向量化 65 // 2.3 重新向量化
66 Document document = new Document(reqVO.getContent()); 66 Document document = new Document(reqVO.getContent());
67 document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId()); 67 document.getMetadata().put(AiKnowledgeSegmentDO.FIELD_KNOWLEDGE_ID, oldKnowledgeSegment.getKnowledgeId());
68 vectorStore.add(List.of(document)); 68 vectorStore.add(List.of(document));
69 69
70 - // 2.1 更新段落内容 70 + // 3. 更新段落内容
71 AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class); 71 AiKnowledgeSegmentDO knowledgeSegment = BeanUtils.toBean(reqVO, AiKnowledgeSegmentDO.class);
72 knowledgeSegment.setVectorId(document.getId()); 72 knowledgeSegment.setVectorId(document.getId());
73 segmentMapper.updateById(knowledgeSegment); 73 segmentMapper.updateById(knowledgeSegment);
@@ -98,14 +98,14 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -98,14 +98,14 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
98 98
99 @Override 99 @Override
100 public List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) { 100 public List<AiKnowledgeSegmentDO> similaritySearch(AiKnowledgeSegmentSearchReqVO reqVO) {
101 - // 0. 校验 101 + // 1. 校验
102 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId()); 102 AiKnowledgeDO knowledge = knowledgeService.validateKnowledgeExists(reqVO.getKnowledgeId());
103 AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); 103 AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
104 104
105 - // 1.1 获取向量存储实例 105 + // 2. 获取向量存储实例
106 VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId()); 106 VectorStore vectorStore = apiKeyService.getOrCreateVectorStore(model.getKeyId());
107 107
108 - // 1.2 向量检索 108 + // 3.1 向量检索
109 List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent()) 109 List<Document> documentList = vectorStore.similaritySearch(SearchRequest.query(reqVO.getContent())
110 .withTopK(knowledge.getTopK()) 110 .withTopK(knowledge.getTopK())
111 .withSimilarityThreshold(knowledge.getSimilarityThreshold()) 111 .withSimilarityThreshold(knowledge.getSimilarityThreshold())
@@ -113,11 +113,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -113,11 +113,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
113 if (CollUtil.isEmpty(documentList)) { 113 if (CollUtil.isEmpty(documentList)) {
114 return ListUtil.empty(); 114 return ListUtil.empty();
115 } 115 }
116 - // 2.1 段落召回 116 + // 3.2 段落召回
117 return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class)); 117 return segmentMapper.selectList(CollUtil.getFieldValues(documentList, "id", String.class));
118 } 118 }
119 119
120 -  
121 /** 120 /**
122 * 校验段落是否存在 121 * 校验段落是否存在
123 * 122 *
@@ -131,4 +130,5 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -131,4 +130,5 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
131 } 130 }
132 return knowledgeSegment; 131 return knowledgeSegment;
133 } 132 }
  133 +
134 } 134 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -23,7 +23,6 @@ public interface AiKnowledgeService { @@ -23,7 +23,6 @@ public interface AiKnowledgeService {
23 */ 23 */
24 Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId); 24 Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId);
25 25
26 -  
27 /** 26 /**
28 * 创建【我的】知识库 27 * 创建【我的】知识库
29 * 28 *
@@ -32,7 +31,6 @@ public interface AiKnowledgeService { @@ -32,7 +31,6 @@ public interface AiKnowledgeService {
32 */ 31 */
33 void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId); 32 void updateKnowledgeMy(AiKnowledgeUpdateMyReqVO updateReqVO, Long userId);
34 33
35 -  
36 /** 34 /**
37 * 校验知识库是否存在 35 * 校验知识库是否存在
38 * 36 *
@@ -49,6 +47,7 @@ public interface AiKnowledgeService { @@ -49,6 +47,7 @@ public interface AiKnowledgeService {
49 */ 47 */
50 PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO); 48 PageResult<AiKnowledgeDO> getKnowledgePageMy(Long userId, PageParam pageReqVO);
51 49
  50 + // TODO @新:knowledgeId 和 validateKnowledgeExists 的 id 是同一个么?如果是的话,建议变量也用 id 哈,然后两边的 id 注释,保持一致
52 /** 51 /**
53 * 根据知识库编号获取向量存储实例 52 * 根据知识库编号获取向量存储实例
54 * 53 *
@@ -56,4 +55,5 @@ public interface AiKnowledgeService { @@ -56,4 +55,5 @@ public interface AiKnowledgeService {
56 * @return 向量存储实例 55 * @return 向量存储实例
57 */ 56 */
58 VectorStore getVectorStoreById(Long knowledgeId); 57 VectorStore getVectorStoreById(Long knowledgeId);
  58 +
59 } 59 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -38,6 +38,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -38,6 +38,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
38 private AiChatModelService chatModelService; 38 private AiChatModelService chatModelService;
39 @Resource 39 @Resource
40 private AiApiKeyService apiKeyService; 40 private AiApiKeyService apiKeyService;
  41 + // TODO @新:chatModelService 和 apiKeyService 可以放到 33 行的 chatModalService 后面。尽量保持,想通类型的变量在一块。例如说,Service 一块,Mapper 一块。
41 42
42 @Override 43 @Override
43 public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) { 44 public Long createKnowledgeMy(AiKnowledgeCreateMyReqVO createReqVO, Long userId) {
@@ -85,6 +86,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService { @@ -85,6 +86,7 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
85 public VectorStore getVectorStoreById(Long knowledgeId) { 86 public VectorStore getVectorStoreById(Long knowledgeId) {
86 AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId); 87 AiKnowledgeDO knowledge = validateKnowledgeExists(knowledgeId);
87 AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId()); 88 AiChatModelDO model = chatModelService.validateChatModel(knowledge.getModelId());
  89 + // 创建或获取 VectorStore 对象
88 return apiKeyService.getOrCreateVectorStore(model.getKeyId()); 90 return apiKeyService.getOrCreateVectorStore(model.getKeyId());
89 } 91 }
90 92
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -146,6 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { @@ -146,6 +146,7 @@ public class AiApiKeyServiceImpl implements AiApiKeyService {
146 public VectorStore getOrCreateVectorStore(Long id) { 146 public VectorStore getOrCreateVectorStore(Long id) {
147 AiApiKeyDO apiKey = validateApiKey(id); 147 AiApiKeyDO apiKey = validateApiKey(id);
148 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform()); 148 AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
  149 + // 创建或获取 VectorStore 对象
149 return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl()); 150 return modelFactory.getOrCreateVectorStore(getEmbeddingModel(id), platform, apiKey.getApiKey(), apiKey.getUrl());
150 } 151 }
151 152
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 47
48 <!-- 向量化,基于 Redis 存储,Tika 解析内容 --> 48 <!-- 向量化,基于 Redis 存储,Tika 解析内容 -->
49 49
50 - <!-- 暂不做经济型,先注释 --> 50 + <!-- 暂不做经济型,先注释 TODO 经济型是啥呀? -->
51 <!-- <dependency>--> 51 <!-- <dependency>-->
52 <!-- <groupId>${spring-ai.groupId}</groupId>--> 52 <!-- <groupId>${spring-ai.groupId}</groupId>-->
53 <!-- <artifactId>spring-ai-transformers-spring-boot-starter</artifactId>--> 53 <!-- <artifactId>spring-ai-transformers-spring-boot-starter</artifactId>-->
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -197,6 +197,7 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -197,6 +197,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
197 }); 197 });
198 } 198 }
199 199
  200 + // TODO @新:貌似可以创建一个大的 VectorStore。然后搜的时候,通过 Filter.Expression 过滤对应的数据。
200 @Override 201 @Override
201 public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) { 202 public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
202 String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url); 203 String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);