Commit 44bcc9476d7a3a1cdbebfab206640b2dac470c48
1 parent
6ccd0ca6
【功能新增】AI:增加 QdrantVectorStore 向量库的接入
Showing
9 changed files
with
78 additions
and
12 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentServiceImpl.java
| ... | ... | @@ -54,7 +54,6 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic |
| 54 | 54 | private AiKnowledgeService knowledgeService; |
| 55 | 55 | |
| 56 | 56 | @Override |
| 57 | - @Transactional(rollbackFor = Exception.class) | |
| 58 | 57 | public Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO) { |
| 59 | 58 | // 1. 校验参数 |
| 60 | 59 | knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId()); |
| ... | ... | @@ -74,7 +73,6 @@ public class AiKnowledgeDocumentServiceImpl implements AiKnowledgeDocumentServic |
| 74 | 73 | } |
| 75 | 74 | |
| 76 | 75 | @Override |
| 77 | - @Transactional(rollbackFor = Exception.class) | |
| 78 | 76 | public List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO) { |
| 79 | 77 | // 1. 校验参数 |
| 80 | 78 | knowledgeService.validateKnowledgeExists(createListReqVO.getKnowledgeId()); | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
| ... | ... | @@ -115,6 +115,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 115 | 115 | segmentMapper.updateById(newSegment); |
| 116 | 116 | // 3.2 重新向量化,必须开启状态 |
| 117 | 117 | if (CommonStatusEnum.isEnable(oldSegment.getStatus())) { |
| 118 | + newSegment.setKnowledgeId(oldSegment.getKnowledgeId()).setDocumentId(oldSegment.getDocumentId()); | |
| 118 | 119 | writeVectorStore(vectorStore, newSegment, new Document(newSegment.getContent())); |
| 119 | 120 | } |
| 120 | 121 | } |
| ... | ... | @@ -156,9 +157,10 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 156 | 157 | |
| 157 | 158 | private void writeVectorStore(VectorStore vectorStore, AiKnowledgeSegmentDO segmentDO, Document segment) { |
| 158 | 159 | // 1. 向量存储 |
| 159 | - segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId()); | |
| 160 | - segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId()); | |
| 161 | - segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId()); | |
| 160 | + // 为什么要 toString 呢?因为部分 VectorStore 实现,不支持 Long 类型,例如说 QdrantVectorStore | |
| 161 | + segment.getMetadata().put(VECTOR_STORE_METADATA_KNOWLEDGE_ID, segmentDO.getKnowledgeId().toString()); | |
| 162 | + segment.getMetadata().put(VECTOR_STORE_METADATA_DOCUMENT_ID, segmentDO.getDocumentId().toString()); | |
| 163 | + segment.getMetadata().put(VECTOR_STORE_METADATA_SEGMENT_ID, segmentDO.getId().toString()); | |
| 162 | 164 | vectorStore.add(List.of(segment)); |
| 163 | 165 | |
| 164 | 166 | // 2. 更新向量 ID |
| ... | ... | @@ -190,7 +192,8 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService |
| 190 | 192 | .similarityThreshold( |
| 191 | 193 | ObjUtil.defaultIfNull(reqBO.getSimilarityThreshold(), knowledge.getSimilarityThreshold())) |
| 192 | 194 | .filterExpression(new FilterExpressionBuilder() |
| 193 | - .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId()).build()) | |
| 195 | + .eq(VECTOR_STORE_METADATA_KNOWLEDGE_ID, reqBO.getKnowledgeId().toString()) | |
| 196 | + .build()) | |
| 194 | 197 | .build()); |
| 195 | 198 | if (CollUtil.isEmpty(documents)) { |
| 196 | 199 | return ListUtil.empty(); | ... | ... |
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 | 16 | import org.springframework.ai.chat.model.ChatModel; |
| 17 | 17 | import org.springframework.ai.embedding.EmbeddingModel; |
| 18 | 18 | import org.springframework.ai.image.ImageModel; |
| 19 | -import org.springframework.ai.vectorstore.SimpleVectorStore; | |
| 20 | 19 | import org.springframework.ai.vectorstore.VectorStore; |
| 20 | +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; | |
| 21 | 21 | import org.springframework.stereotype.Service; |
| 22 | 22 | import org.springframework.validation.annotation.Validated; |
| 23 | 23 | |
| ... | ... | @@ -162,7 +162,8 @@ public class AiModelServiceImpl implements AiModelService { |
| 162 | 162 | platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); |
| 163 | 163 | |
| 164 | 164 | // 创建或获取 VectorStore 对象 |
| 165 | - return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); | |
| 165 | +// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel); | |
| 166 | + return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel); | |
| 166 | 167 | } |
| 167 | 168 | |
| 168 | 169 | } |
| 169 | 170 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
| ... | ... | @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel |
| 11 | 11 | import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; |
| 12 | 12 | import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; |
| 13 | 13 | import lombok.extern.slf4j.Slf4j; |
| 14 | +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; | |
| 14 | 15 | import org.springframework.ai.openai.OpenAiChatModel; |
| 15 | 16 | import org.springframework.ai.openai.OpenAiChatOptions; |
| 16 | 17 | import org.springframework.ai.openai.api.OpenAiApi; |
| ... | ... | @@ -29,7 +30,9 @@ import org.springframework.context.annotation.Lazy; |
| 29 | 30 | * @author fansili |
| 30 | 31 | */ |
| 31 | 32 | @AutoConfiguration |
| 32 | -@EnableConfigurationProperties(YudaoAiProperties.class) | |
| 33 | +@EnableConfigurationProperties({YudaoAiProperties.class, | |
| 34 | + QdrantVectorStoreProperties.class // 解析 Qdrant 配置 | |
| 35 | +}) | |
| 33 | 36 | @Slf4j |
| 34 | 37 | public class YudaoAiAutoConfiguration { |
| 35 | 38 | ... | ... |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
| ... | ... | @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; |
| 5 | 5 | import cn.hutool.core.lang.Singleton; |
| 6 | 6 | import cn.hutool.core.lang.func.Func0; |
| 7 | 7 | import cn.hutool.core.util.ArrayUtil; |
| 8 | +import cn.hutool.core.util.ReflectUtil; | |
| 8 | 9 | import cn.hutool.core.util.RuntimeUtil; |
| 9 | 10 | import cn.hutool.core.util.StrUtil; |
| 10 | 11 | import cn.hutool.extra.spring.SpringUtil; |
| ... | ... | @@ -26,6 +27,9 @@ import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingModel; |
| 26 | 27 | import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions; |
| 27 | 28 | import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel; |
| 28 | 29 | import com.azure.ai.openai.OpenAIClientBuilder; |
| 30 | +import io.micrometer.observation.ObservationRegistry; | |
| 31 | +import io.qdrant.client.QdrantClient; | |
| 32 | +import io.qdrant.client.QdrantGrpcClient; | |
| 29 | 33 | import lombok.SneakyThrows; |
| 30 | 34 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration; |
| 31 | 35 | import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties; |
| ... | ... | @@ -33,11 +37,14 @@ import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiConnectionPr |
| 33 | 37 | import org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration; |
| 34 | 38 | import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; |
| 35 | 39 | import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; |
| 40 | +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; | |
| 41 | +import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; | |
| 36 | 42 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; |
| 37 | 43 | import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; |
| 38 | 44 | import org.springframework.ai.azure.openai.AzureOpenAiChatModel; |
| 39 | 45 | import org.springframework.ai.chat.model.ChatModel; |
| 40 | 46 | import org.springframework.ai.document.MetadataMode; |
| 47 | +import org.springframework.ai.embedding.BatchingStrategy; | |
| 41 | 48 | import org.springframework.ai.embedding.EmbeddingModel; |
| 42 | 49 | import org.springframework.ai.image.ImageModel; |
| 43 | 50 | import org.springframework.ai.ollama.OllamaChatModel; |
| ... | ... | @@ -57,10 +64,15 @@ import org.springframework.ai.stabilityai.StabilityAiImageModel; |
| 57 | 64 | import org.springframework.ai.stabilityai.api.StabilityAiApi; |
| 58 | 65 | import org.springframework.ai.vectorstore.SimpleVectorStore; |
| 59 | 66 | import org.springframework.ai.vectorstore.VectorStore; |
| 67 | +import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; | |
| 68 | +import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; | |
| 69 | +import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; | |
| 60 | 70 | import org.springframework.ai.zhipuai.ZhiPuAiChatModel; |
| 61 | 71 | import org.springframework.ai.zhipuai.ZhiPuAiImageModel; |
| 62 | 72 | import org.springframework.ai.zhipuai.api.ZhiPuAiApi; |
| 63 | 73 | import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; |
| 74 | +import org.springframework.beans.BeansException; | |
| 75 | +import org.springframework.beans.factory.ObjectProvider; | |
| 64 | 76 | import org.springframework.web.client.RestClient; |
| 65 | 77 | |
| 66 | 78 | import java.io.File; |
| ... | ... | @@ -214,13 +226,14 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 214 | 226 | |
| 215 | 227 | @Override |
| 216 | 228 | public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type, EmbeddingModel embeddingModel) { |
| 217 | - // String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, | |
| 218 | - // url); | |
| 219 | 229 | String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); |
| 220 | 230 | return Singleton.get(cacheKey, (Func0<VectorStore>) () -> { |
| 221 | 231 | if (type == SimpleVectorStore.class) { |
| 222 | 232 | return buildSimpleVectorStore(embeddingModel); |
| 223 | 233 | } |
| 234 | + if (type == QdrantVectorStore.class) { | |
| 235 | + return buildQdrantVectorStore(embeddingModel); | |
| 236 | + } | |
| 224 | 237 | throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); |
| 225 | 238 | // TODO @芋艿:先临时使用 store |
| 226 | 239 | // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的 |
| ... | ... | @@ -456,4 +469,38 @@ public class AiModelFactoryImpl implements AiModelFactory { |
| 456 | 469 | return vectorStore; |
| 457 | 470 | } |
| 458 | 471 | |
| 472 | + private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) { | |
| 473 | + QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration(); | |
| 474 | + QdrantVectorStoreProperties vectorStoreProperties = SpringUtil.getBean(QdrantVectorStoreProperties.class); | |
| 475 | + // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象 | |
| 476 | + QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder( | |
| 477 | + vectorStoreProperties.getHost(), vectorStoreProperties.getPort(), vectorStoreProperties.isUseTls()); | |
| 478 | + if (StrUtil.isNotEmpty(vectorStoreProperties.getApiKey())) { | |
| 479 | + grpcClientBuilder.withApiKey(vectorStoreProperties.getApiKey()); | |
| 480 | + } | |
| 481 | + QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); | |
| 482 | + // 参考 QdrantVectorStoreAutoConfiguration 实现,实现 batchingStrategy | |
| 483 | + BatchingStrategy batchingStrategy = ReflectUtil.invoke(configuration, "batchingStrategy"); | |
| 484 | + | |
| 485 | + // 创建 QdrantVectorStore 对象 | |
| 486 | + ObjectProvider<ObservationRegistry> observationRegistry = new ObjectProvider<>() { | |
| 487 | + | |
| 488 | + @Override | |
| 489 | + public ObservationRegistry getObject() throws BeansException { | |
| 490 | + return SpringUtil.getBean(ObservationRegistry.class); | |
| 491 | + } | |
| 492 | + | |
| 493 | + }; | |
| 494 | + ObjectProvider <VectorStoreObservationConvention> customObservationConvention = new ObjectProvider<>() { | |
| 495 | + | |
| 496 | + @Override | |
| 497 | + public VectorStoreObservationConvention getObject() throws BeansException { | |
| 498 | + return new DefaultVectorStoreObservationConvention(); | |
| 499 | + } | |
| 500 | + | |
| 501 | + }; | |
| 502 | + return configuration.vectorStore(embeddingModel, vectorStoreProperties, qdrantClient, | |
| 503 | + observationRegistry, customObservationConvention, batchingStrategy); | |
| 504 | + } | |
| 505 | + | |
| 459 | 506 | } | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -4,6 +4,11 @@ server: |
| 4 | 4 | --- #################### 数据库相关配置 #################### |
| 5 | 5 | |
| 6 | 6 | spring: |
| 7 | + spring: | |
| 8 | + autoconfigure: | |
| 9 | + exclude: | |
| 10 | + - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 | |
| 11 | + - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 | |
| 7 | 12 | # 数据源配置项 |
| 8 | 13 | datasource: |
| 9 | 14 | druid: # Druid 【监控】相关的全局配置 | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -3,13 +3,15 @@ server: |
| 3 | 3 | |
| 4 | 4 | --- #################### 数据库相关配置 #################### |
| 5 | 5 | spring: |
| 6 | - # 数据源配置项 | |
| 7 | 6 | autoconfigure: |
| 8 | 7 | exclude: |
| 9 | 8 | - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 默认 local 环境,不开启 Quartz 的自动配置 |
| 10 | 9 | - de.codecentric.boot.admin.server.config.AdminServerAutoConfiguration # 禁用 Spring Boot Admin 的 Server 的自动配置 |
| 11 | 10 | - de.codecentric.boot.admin.server.ui.config.AdminServerUiAutoConfiguration # 禁用 Spring Boot Admin 的 Server UI 的自动配置 |
| 12 | 11 | - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 |
| 12 | + - org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration # 禁用 AI 模块的 Qdrant,手动创建 | |
| 13 | + - org.springframework.ai.autoconfigure.vectorstore.milvus.MilvusVectorStoreAutoConfiguration # 禁用 AI 模块的 Milvus,手动创建 | |
| 14 | + # 数据源配置项 | |
| 13 | 15 | datasource: |
| 14 | 16 | druid: # Druid 【监控】相关的全局配置 |
| 15 | 17 | web-stat-filter: | ... | ... |
yudao-server/src/main/resources/application.yaml
| ... | ... | @@ -151,6 +151,12 @@ spring: |
| 151 | 151 | redis: |
| 152 | 152 | index: default-index |
| 153 | 153 | prefix: "default:" |
| 154 | + qdrant: | |
| 155 | + collection-name: knowledge_segment | |
| 156 | + host: 127.0.0.1 | |
| 157 | + port: 6334 | |
| 158 | + use-tls: false | |
| 159 | + api-key: | |
| 154 | 160 | qianfan: # 文心一言 |
| 155 | 161 | api-key: x0cuLZ7XsaTCU08vuJWO87Lg |
| 156 | 162 | secret-key: R9mYF9dl9KASgi5RUq0FQt3wRisSnOcK | ... | ... |