Commit 8e56b81a3a73de6e6d2d4d2e8da9b1285ef69df8
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<AiKnowledgeSegment | @@ -25,9 +25,11 @@ public interface AiKnowledgeSegmentMapper extends BaseMapperX<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); |