Commit 588c9fe323e2f48f1c3a6fa7159755279049cc09

Authored by YunaiV
1 parent 44bcc947

【功能新增】AI:增加 RedisVectorStore 向量库的接入

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -32,6 +32,7 @@ import org.springframework.stereotype.Service; @@ -32,6 +32,7 @@ import org.springframework.stereotype.Service;
32 32
33 import java.util.Collections; 33 import java.util.Collections;
34 import java.util.List; 34 import java.util.List;
  35 +import java.util.Map;
35 import java.util.Objects; 36 import java.util.Objects;
36 37
37 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 38 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -48,9 +49,14 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM @@ -48,9 +49,14 @@ import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_SEGM
48 @Slf4j 49 @Slf4j
49 public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService { 50 public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService {
50 51
51 - public static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId";  
52 - public static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId";  
53 - public static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId"; 52 + private static final String VECTOR_STORE_METADATA_KNOWLEDGE_ID = "knowledgeId";
  53 + private static final String VECTOR_STORE_METADATA_DOCUMENT_ID = "documentId";
  54 + private static final String VECTOR_STORE_METADATA_SEGMENT_ID = "segmentId";
  55 +
  56 + private static final Map<String, Class<?>> VECTOR_STORE_METADATA_TYPES = Map.of(
  57 + VECTOR_STORE_METADATA_KNOWLEDGE_ID, String.class,
  58 + VECTOR_STORE_METADATA_DOCUMENT_ID, String.class,
  59 + VECTOR_STORE_METADATA_SEGMENT_ID, String.class);
54 60
55 @Resource 61 @Resource
56 private AiKnowledgeSegmentMapper segmentMapper; 62 private AiKnowledgeSegmentMapper segmentMapper;
@@ -257,7 +263,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService @@ -257,7 +263,7 @@ public class AiKnowledgeSegmentServiceImpl implements AiKnowledgeSegmentService
257 } 263 }
258 264
259 private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) { 265 private VectorStore getVectorStoreById(AiKnowledgeDO knowledge) {
260 - return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId()); 266 + return modelService.getOrCreateVectorStore(knowledge.getEmbeddingModelId(), VECTOR_STORE_METADATA_TYPES);
261 } 267 }
262 268
263 private VectorStore getVectorStoreById(Long knowledgeId) { 269 private VectorStore getVectorStoreById(Long knowledgeId) {
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
@@ -13,6 +13,7 @@ import org.springframework.ai.vectorstore.VectorStore; @@ -13,6 +13,7 @@ import org.springframework.ai.vectorstore.VectorStore;
13 13
14 import javax.annotation.Nullable; 14 import javax.annotation.Nullable;
15 import java.util.List; 15 import java.util.List;
  16 +import java.util.Map;
16 17
17 /** 18 /**
18 * AI 模型 Service 接口 19 * AI 模型 Service 接口
@@ -125,8 +126,9 @@ public interface AiModelService { @@ -125,8 +126,9 @@ public interface AiModelService {
125 * 获得 VectorStore 对象 126 * 获得 VectorStore 对象
126 * 127 *
127 * @param id 编号 128 * @param id 编号
  129 + * @param metadataFields 元数据的定义
128 * @return VectorStore 对象 130 * @return VectorStore 对象
129 */ 131 */
130 - VectorStore getOrCreateVectorStore(Long id); 132 + VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);
131 133
132 } 134 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Service; @@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
22 import org.springframework.validation.annotation.Validated; 22 import org.springframework.validation.annotation.Validated;
23 23
24 import java.util.List; 24 import java.util.List;
  25 +import java.util.Map;
25 26
26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
27 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; 28 import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
@@ -151,7 +152,7 @@ public class AiModelServiceImpl implements AiModelService { @@ -151,7 +152,7 @@ public class AiModelServiceImpl implements AiModelService {
151 } 152 }
152 153
153 @Override 154 @Override
154 - public VectorStore getOrCreateVectorStore(Long id) { 155 + public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {
155 // 获取模型 + 密钥 156 // 获取模型 + 密钥
156 AiModelDO model = validateModel(id); 157 AiModelDO model = validateModel(id);
157 AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId()); 158 AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
@@ -162,8 +163,9 @@ public class AiModelServiceImpl implements AiModelService { @@ -162,8 +163,9 @@ public class AiModelServiceImpl implements AiModelService {
162 platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel()); 163 platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
163 164
164 // 创建或获取 VectorStore 对象 165 // 创建或获取 VectorStore 对象
165 -// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel);  
166 - return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel); 166 +// return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
  167 + return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
  168 +// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
167 } 169 }
168 170
169 } 171 }
170 \ No newline at end of file 172 \ No newline at end of file
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiAutoConfiguration.java
@@ -12,12 +12,12 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; @@ -12,12 +12,12 @@ import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
12 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; 12 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; 14 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties;
  15 +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
15 import org.springframework.ai.openai.OpenAiChatModel; 16 import org.springframework.ai.openai.OpenAiChatModel;
16 import org.springframework.ai.openai.OpenAiChatOptions; 17 import org.springframework.ai.openai.OpenAiChatOptions;
17 import org.springframework.ai.openai.api.OpenAiApi; 18 import org.springframework.ai.openai.api.OpenAiApi;
18 import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator; 19 import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
19 import org.springframework.ai.tokenizer.TokenCountEstimator; 20 import org.springframework.ai.tokenizer.TokenCountEstimator;
20 -import org.springframework.ai.transformer.splitter.TokenTextSplitter;  
21 import org.springframework.boot.autoconfigure.AutoConfiguration; 21 import org.springframework.boot.autoconfigure.AutoConfiguration;
22 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 22 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
23 import org.springframework.boot.context.properties.EnableConfigurationProperties; 23 import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -31,7 +31,8 @@ import org.springframework.context.annotation.Lazy; @@ -31,7 +31,8 @@ import org.springframework.context.annotation.Lazy;
31 */ 31 */
32 @AutoConfiguration 32 @AutoConfiguration
33 @EnableConfigurationProperties({YudaoAiProperties.class, 33 @EnableConfigurationProperties({YudaoAiProperties.class,
34 - QdrantVectorStoreProperties.class // 解析 Qdrant 配置 34 + QdrantVectorStoreProperties.class, // 解析 Qdrant 配置
  35 + RedisVectorStoreProperties.class, // 解析 Redis 配置
35 }) 36 })
36 @Slf4j 37 @Slf4j
37 public class YudaoAiAutoConfiguration { 38 public class YudaoAiAutoConfiguration {
@@ -200,32 +201,6 @@ public class YudaoAiAutoConfiguration { @@ -200,32 +201,6 @@ public class YudaoAiAutoConfiguration {
200 // return new TransformersEmbeddingModel(MetadataMode.EMBED); 201 // return new TransformersEmbeddingModel(MetadataMode.EMBED);
201 // } 202 // }
202 203
203 - /**  
204 - * TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel  
205 - */  
206 -// @Bean  
207 -// @Lazy // TODO 芋艿:临时注释,避免无法启动  
208 -// public RedisVectorStore vectorStore(TransformersEmbeddingModel embeddingModel, RedisVectorStoreProperties properties,  
209 -// RedisProperties redisProperties) {  
210 -// var config = RedisVectorStore.RedisVectorStoreConfig.builder()  
211 -// .withIndexName(properties.getIndex())  
212 -// .withPrefix(properties.getPrefix())  
213 -// .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId", Schema.FieldType.NUMERIC))  
214 -// .build();  
215 -//  
216 -// RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,  
217 -// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),  
218 -// properties.isInitializeSchema());  
219 -// redisVectorStore.afterPropertiesSet();  
220 -// return redisVectorStore;  
221 -// }  
222 - @Bean  
223 - @Lazy // TODO 芋艿:临时注释,避免无法启动  
224 - public TokenTextSplitter tokenTextSplitter() {  
225 - //TODO @xin 配置提取  
226 - return new TokenTextSplitter(500, 100, 5, 10000, true);  
227 - }  
228 -  
229 @Bean 204 @Bean
230 @Lazy // TODO 芋艿:临时注释,避免无法启动 205 @Lazy // TODO 芋艿:临时注释,避免无法启动
231 public TokenCountEstimator tokenCountEstimator() { 206 public TokenCountEstimator tokenCountEstimator() {
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactory.java
@@ -8,6 +8,8 @@ import org.springframework.ai.embedding.EmbeddingModel; @@ -8,6 +8,8 @@ import org.springframework.ai.embedding.EmbeddingModel;
8 import org.springframework.ai.image.ImageModel; 8 import org.springframework.ai.image.ImageModel;
9 import org.springframework.ai.vectorstore.VectorStore; 9 import org.springframework.ai.vectorstore.VectorStore;
10 10
  11 +import java.util.Map;
  12 +
11 /** 13 /**
12 * AI Model 模型工厂的接口类 14 * AI Model 模型工厂的接口类
13 * 15 *
@@ -96,13 +98,16 @@ public interface AiModelFactory { @@ -96,13 +98,16 @@ public interface AiModelFactory {
96 98
97 /** 99 /**
98 * 基于指定配置,获得 VectorStore 对象 100 * 基于指定配置,获得 VectorStore 对象
99 - * <p> 101 + *
100 * 如果不存在,则进行创建 102 * 如果不存在,则进行创建
101 * 103 *
102 * @param type 向量存储类型 104 * @param type 向量存储类型
103 * @param embeddingModel 向量模型 105 * @param embeddingModel 向量模型
  106 + * @param metadataFields 元数据字段
104 * @return VectorStore 对象 107 * @return VectorStore 对象
105 */ 108 */
106 - VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type, EmbeddingModel embeddingModel); 109 + VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
  110 + EmbeddingModel embeddingModel,
  111 + Map<String, Class<?>> metadataFields);
107 112
108 } 113 }
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/factory/AiModelFactoryImpl.java
@@ -19,6 +19,7 @@ import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; @@ -19,6 +19,7 @@ import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
19 import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel; 19 import cn.iocoder.yudao.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
20 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi; 20 import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
21 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel; 21 import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
  22 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
22 import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration; 23 import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAutoConfiguration;
23 import com.alibaba.cloud.ai.dashscope.api.DashScopeApi; 24 import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
24 import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi; 25 import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
@@ -39,12 +40,13 @@ import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; @@ -39,12 +40,13 @@ import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration;
39 import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration; 40 import org.springframework.ai.autoconfigure.qianfan.QianFanAutoConfiguration;
40 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration; 41 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreAutoConfiguration;
41 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties; 42 import org.springframework.ai.autoconfigure.vectorstore.qdrant.QdrantVectorStoreProperties;
  43 +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
  44 +import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
42 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration; 45 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiAutoConfiguration;
43 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties; 46 import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties;
44 import org.springframework.ai.azure.openai.AzureOpenAiChatModel; 47 import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
45 import org.springframework.ai.chat.model.ChatModel; 48 import org.springframework.ai.chat.model.ChatModel;
46 import org.springframework.ai.document.MetadataMode; 49 import org.springframework.ai.document.MetadataMode;
47 -import org.springframework.ai.embedding.BatchingStrategy;  
48 import org.springframework.ai.embedding.EmbeddingModel; 50 import org.springframework.ai.embedding.EmbeddingModel;
49 import org.springframework.ai.image.ImageModel; 51 import org.springframework.ai.image.ImageModel;
50 import org.springframework.ai.ollama.OllamaChatModel; 52 import org.springframework.ai.ollama.OllamaChatModel;
@@ -67,20 +69,26 @@ import org.springframework.ai.vectorstore.VectorStore; @@ -67,20 +69,26 @@ import org.springframework.ai.vectorstore.VectorStore;
67 import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; 69 import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
68 import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; 70 import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
69 import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore; 71 import org.springframework.ai.vectorstore.qdrant.QdrantVectorStore;
  72 +import org.springframework.ai.vectorstore.redis.RedisVectorStore;
70 import org.springframework.ai.zhipuai.ZhiPuAiChatModel; 73 import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
71 import org.springframework.ai.zhipuai.ZhiPuAiImageModel; 74 import org.springframework.ai.zhipuai.ZhiPuAiImageModel;
72 import org.springframework.ai.zhipuai.api.ZhiPuAiApi; 75 import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
73 import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi; 76 import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
74 import org.springframework.beans.BeansException; 77 import org.springframework.beans.BeansException;
75 import org.springframework.beans.factory.ObjectProvider; 78 import org.springframework.beans.factory.ObjectProvider;
  79 +import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
76 import org.springframework.web.client.RestClient; 80 import org.springframework.web.client.RestClient;
  81 +import redis.clients.jedis.JedisPooled;
77 82
78 import java.io.File; 83 import java.io.File;
79 import java.time.Duration; 84 import java.time.Duration;
80 import java.util.List; 85 import java.util.List;
  86 +import java.util.Map;
81 import java.util.Timer; 87 import java.util.Timer;
82 import java.util.TimerTask; 88 import java.util.TimerTask;
83 89
  90 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  91 +
84 /** 92 /**
85 * AI Model 模型工厂的实现类 93 * AI Model 模型工厂的实现类
86 * 94 *
@@ -225,7 +233,9 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -225,7 +233,9 @@ public class AiModelFactoryImpl implements AiModelFactory {
225 } 233 }
226 234
227 @Override 235 @Override
228 - public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type, EmbeddingModel embeddingModel) { 236 + public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
  237 + EmbeddingModel embeddingModel,
  238 + Map<String, Class<?>> metadataFields) {
229 String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type); 239 String cacheKey = buildClientCacheKey(VectorStore.class, embeddingModel, type);
230 return Singleton.get(cacheKey, (Func0<VectorStore>) () -> { 240 return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
231 if (type == SimpleVectorStore.class) { 241 if (type == SimpleVectorStore.class) {
@@ -234,23 +244,10 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -234,23 +244,10 @@ public class AiModelFactoryImpl implements AiModelFactory {
234 if (type == QdrantVectorStore.class) { 244 if (type == QdrantVectorStore.class) {
235 return buildQdrantVectorStore(embeddingModel); 245 return buildQdrantVectorStore(embeddingModel);
236 } 246 }
  247 + if (type == RedisVectorStore.class) {
  248 + return buildRedisVectorStore(embeddingModel, metadataFields);
  249 + }
237 throw new IllegalArgumentException(StrUtil.format("未知类型({})", type)); 250 throw new IllegalArgumentException(StrUtil.format("未知类型({})", type));
238 - // TODO @芋艿:先临时使用 store  
239 - // TODO @芋艿:@xin:后续看看,是不是切到阿里云之类的  
240 - // String prefix = StrUtil.format("{}#{}:", platform.getPlatform(), apiKey);  
241 - // var config = RedisVectorStore.RedisVectorStoreConfig.builder()  
242 - // .withIndexName(cacheKey)  
243 - // .withPrefix(prefix)  
244 - // .withMetadataFields(new RedisVectorStore.MetadataField("knowledgeId",  
245 - // Schema.FieldType.NUMERIC))  
246 - // .build();  
247 - // RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);  
248 - // RedisVectorStore redisVectorStore = new RedisVectorStore(config,  
249 - // embeddingModel,  
250 - // new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),  
251 - // true);  
252 - // redisVectorStore.afterPropertiesSet();  
253 - // return redisVectorStore;  
254 }); 251 });
255 } 252 }
256 253
@@ -469,21 +466,65 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -469,21 +466,65 @@ public class AiModelFactoryImpl implements AiModelFactory {
469 return vectorStore; 466 return vectorStore;
470 } 467 }
471 468
  469 + /**
  470 + * 参考 {@link QdrantVectorStoreAutoConfiguration} 的 vectorStore 方法
  471 + */
  472 + @SneakyThrows
472 private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) { 473 private QdrantVectorStore buildQdrantVectorStore(EmbeddingModel embeddingModel) {
473 QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration(); 474 QdrantVectorStoreAutoConfiguration configuration = new QdrantVectorStoreAutoConfiguration();
474 - QdrantVectorStoreProperties vectorStoreProperties = SpringUtil.getBean(QdrantVectorStoreProperties.class); 475 + QdrantVectorStoreProperties properties = SpringUtil.getBean(QdrantVectorStoreProperties.class);
475 // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象 476 // 参考 QdrantVectorStoreAutoConfiguration 实现,创建 QdrantClient 对象
476 QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder( 477 QdrantGrpcClient.Builder grpcClientBuilder = QdrantGrpcClient.newBuilder(
477 - vectorStoreProperties.getHost(), vectorStoreProperties.getPort(), vectorStoreProperties.isUseTls());  
478 - if (StrUtil.isNotEmpty(vectorStoreProperties.getApiKey())) {  
479 - grpcClientBuilder.withApiKey(vectorStoreProperties.getApiKey()); 478 + properties.getHost(), properties.getPort(), properties.isUseTls());
  479 + if (StrUtil.isNotEmpty(properties.getApiKey())) {
  480 + grpcClientBuilder.withApiKey(properties.getApiKey());
480 } 481 }
481 QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build()); 482 QdrantClient qdrantClient = new QdrantClient(grpcClientBuilder.build());
482 - // 参考 QdrantVectorStoreAutoConfiguration 实现,实现 batchingStrategy  
483 - BatchingStrategy batchingStrategy = ReflectUtil.invoke(configuration, "batchingStrategy");  
484 -  
485 // 创建 QdrantVectorStore 对象 483 // 创建 QdrantVectorStore 对象
486 - ObjectProvider<ObservationRegistry> observationRegistry = new ObjectProvider<>() { 484 + QdrantVectorStore vectorStore = configuration.vectorStore(embeddingModel, properties, qdrantClient,
  485 + getObservationRegistry(), getCustomObservationConvention(),
  486 + ReflectUtil.invoke(configuration, "batchingStrategy"));
  487 + // 初始化索引
  488 + vectorStore.afterPropertiesSet();
  489 + return vectorStore;
  490 + }
  491 +
  492 + /**
  493 + * 参考 {@link RedisVectorStoreAutoConfiguration} 的 vectorStore 方法
  494 + */
  495 + private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel,
  496 + Map<String, Class<?>> metadataFields) {
  497 + // 创建 JedisPooled 对象
  498 + RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
  499 + JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort());
  500 + // 创建 RedisVectorStoreProperties 对象
  501 + RedisVectorStoreAutoConfiguration configuration = new RedisVectorStoreAutoConfiguration();
  502 + RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class);
  503 + RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel)
  504 + .indexName(properties.getIndex()).prefix(properties.getPrefix())
  505 + .initializeSchema(properties.isInitializeSchema())
  506 + .metadataFields(convertList(metadataFields.entrySet(), entry -> {
  507 + String fieldName = entry.getKey();
  508 + Class<?> fieldType = entry.getValue();
  509 + if (Number.class.isAssignableFrom(fieldType)) {
  510 + return RedisVectorStore.MetadataField.numeric(fieldName);
  511 + }
  512 + if (Boolean.class.isAssignableFrom(fieldType)) {
  513 + return RedisVectorStore.MetadataField.tag(fieldName);
  514 + }
  515 + return RedisVectorStore.MetadataField.text(fieldName);
  516 + }))
  517 + .observationRegistry(getObservationRegistry().getObject())
  518 + .customObservationConvention(getCustomObservationConvention().getObject())
  519 + .batchingStrategy(ReflectUtil.invoke(configuration, "batchingStrategy"))
  520 + .build();
  521 + // 初始化索引
  522 + redisVectorStore.afterPropertiesSet();
  523 + return redisVectorStore;
  524 + }
  525 +
  526 + private static ObjectProvider<ObservationRegistry> getObservationRegistry() {
  527 + return new ObjectProvider<>() {
487 528
488 @Override 529 @Override
489 public ObservationRegistry getObject() throws BeansException { 530 public ObservationRegistry getObject() throws BeansException {
@@ -491,16 +532,15 @@ public class AiModelFactoryImpl implements AiModelFactory { @@ -491,16 +532,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
491 } 532 }
492 533
493 }; 534 };
494 - ObjectProvider <VectorStoreObservationConvention> customObservationConvention = new ObjectProvider<>() { 535 + }
495 536
  537 + private static ObjectProvider<VectorStoreObservationConvention> getCustomObservationConvention() {
  538 + return new ObjectProvider<>() {
496 @Override 539 @Override
497 public VectorStoreObservationConvention getObject() throws BeansException { 540 public VectorStoreObservationConvention getObject() throws BeansException {
498 return new DefaultVectorStoreObservationConvention(); 541 return new DefaultVectorStoreObservationConvention();
499 } 542 }
500 -  
501 }; 543 };
502 - return configuration.vectorStore(embeddingModel, vectorStoreProperties, qdrantClient,  
503 - observationRegistry, customObservationConvention, batchingStrategy);  
504 } 544 }
505 545
506 } 546 }
yudao-server/src/main/resources/application.yaml
@@ -149,10 +149,12 @@ spring: @@ -149,10 +149,12 @@ spring:
149 ai: 149 ai:
150 vectorstore: # 向量存储 150 vectorstore: # 向量存储
151 redis: 151 redis:
152 - index: default-index  
153 - prefix: "default:" 152 + initialize-schema: true
  153 + index: knowledge_index # Redis 中向量索引的名称:用于存储和检索向量数据的索引标识符,所有相关的向量搜索操作都会基于这个索引进行
  154 + prefix: "knowledge_segment:" # Redis 中存储向量数据的键名前缀:这个前缀会添加到每个存储在 Redis 中的向量数据键名前,每个 document 都是一个 hash 结构
154 qdrant: 155 qdrant:
155 - collection-name: knowledge_segment 156 + initialize-schema: true
  157 + collection-name: knowledge_segment # Qdrant 中向量集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
156 host: 127.0.0.1 158 host: 127.0.0.1
157 port: 6334 159 port: 6334
158 use-tls: false 160 use-tls: false