mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-13 18:45:06 +08:00
【新增】AI 知识库: AiVectorFactory 负责管理不同 EmbeddingModel 对应的 VectorStore
This commit is contained in:
@ -2,6 +2,8 @@ package cn.iocoder.yudao.framework.ai.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactory;
|
||||
import cn.iocoder.yudao.framework.ai.core.factory.AiModelFactoryImpl;
|
||||
import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactory;
|
||||
import cn.iocoder.yudao.framework.ai.core.factory.AiVectorFactoryImpl;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatModel;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.deepseek.DeepSeekChatOptions;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||
@ -10,22 +12,15 @@ import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatModel;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.xinghuo.XingHuoChatOptions;
|
||||
import com.alibaba.cloud.ai.tongyi.TongYiAutoConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
|
||||
import org.springframework.ai.document.MetadataMode;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
|
||||
import org.springframework.ai.tokenizer.TokenCountEstimator;
|
||||
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
|
||||
import org.springframework.ai.transformers.TransformersEmbeddingModel;
|
||||
import org.springframework.ai.vectorstore.RedisVectorStore;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import redis.clients.jedis.JedisPooled;
|
||||
|
||||
/**
|
||||
* 芋道 AI 自动配置
|
||||
@ -43,6 +38,12 @@ public class YudaoAiAutoConfiguration {
|
||||
return new AiModelFactoryImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AiVectorFactory aiVectorFactory() {
|
||||
return new AiVectorFactoryImpl();
|
||||
}
|
||||
|
||||
|
||||
// ========== 各种 AI Client 创建 ==========
|
||||
|
||||
@Bean
|
||||
@ -85,30 +86,31 @@ public class YudaoAiAutoConfiguration {
|
||||
}
|
||||
|
||||
// ========== rag 相关 ==========
|
||||
@Bean
|
||||
@Lazy // TODO 芋艿:临时注释,避免无法启动
|
||||
public EmbeddingModel transformersEmbeddingClient() {
|
||||
return new TransformersEmbeddingModel(MetadataMode.EMBED);
|
||||
}
|
||||
// TODO @xin 免费版本
|
||||
// @Bean
|
||||
// @Lazy // TODO 芋艿:临时注释,避免无法启动」
|
||||
// public EmbeddingModel transformersEmbeddingClient() {
|
||||
// return new TransformersEmbeddingModel(MetadataMode.EMBED);
|
||||
// }
|
||||
|
||||
/**
|
||||
* TODO @xin 抽离出去,根据具体模型走
|
||||
* TODO @xin 默认版本先不弄,目前都先取对应的 EmbeddingModel
|
||||
*/
|
||||
@Bean
|
||||
@Lazy // TODO 芋艿:临时注释,避免无法启动
|
||||
public RedisVectorStore vectorStore(TransformersEmbeddingModel transformersEmbeddingModel, RedisVectorStoreProperties properties,
|
||||
RedisProperties redisProperties) {
|
||||
var config = RedisVectorStore.RedisVectorStoreConfig.builder()
|
||||
.withIndexName(properties.getIndex())
|
||||
.withPrefix(properties.getPrefix())
|
||||
.build();
|
||||
|
||||
RedisVectorStore redisVectorStore = new RedisVectorStore(config, transformersEmbeddingModel,
|
||||
new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
|
||||
properties.isInitializeSchema());
|
||||
redisVectorStore.afterPropertiesSet();
|
||||
return redisVectorStore;
|
||||
}
|
||||
// @Bean
|
||||
// @Lazy // TODO 芋艿:临时注释,避免无法启动
|
||||
// public RedisVectorStore vectorStore(TongYiTextEmbeddingModel tongYiTextEmbeddingModel, RedisVectorStoreProperties properties,
|
||||
// RedisProperties redisProperties) {
|
||||
// var config = RedisVectorStore.RedisVectorStoreConfig.builder()
|
||||
// .withIndexName(properties.getIndex())
|
||||
// .withPrefix(properties.getPrefix())
|
||||
// .build();
|
||||
//
|
||||
// RedisVectorStore redisVectorStore = new RedisVectorStore(config, tongYiTextEmbeddingModel,
|
||||
// new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
|
||||
// properties.isInitializeSchema());
|
||||
// redisVectorStore.afterPropertiesSet();
|
||||
// return redisVectorStore;
|
||||
// }
|
||||
|
||||
@Bean
|
||||
@Lazy // TODO 芋艿:临时注释,避免无法启动
|
||||
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi;
|
||||
import cn.iocoder.yudao.framework.ai.core.model.suno.api.SunoApi;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
|
||||
/**
|
||||
@ -25,6 +26,18 @@ public interface AiModelFactory {
|
||||
*/
|
||||
ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
|
||||
|
||||
/**
|
||||
* 基于指定配置,获得 EmbeddingModel 对象
|
||||
* <p>
|
||||
* 如果不存在,则进行创建
|
||||
*
|
||||
* @param platform 平台
|
||||
* @param apiKey API KEY
|
||||
* @param url API URL
|
||||
* @return ChatModel 对象
|
||||
*/
|
||||
EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url);
|
||||
|
||||
/**
|
||||
* 基于默认配置,获得 ChatModel 对象
|
||||
*
|
||||
|
@ -21,6 +21,7 @@ import com.alibaba.cloud.ai.tongyi.image.TongYiImagesModel;
|
||||
import com.alibaba.cloud.ai.tongyi.image.TongYiImagesProperties;
|
||||
import com.alibaba.dashscope.aigc.generation.Generation;
|
||||
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
|
||||
import com.alibaba.dashscope.embeddings.TextEmbedding;
|
||||
import com.azure.ai.openai.OpenAIClient;
|
||||
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiAutoConfiguration;
|
||||
import org.springframework.ai.autoconfigure.azure.openai.AzureOpenAiChatProperties;
|
||||
@ -37,6 +38,7 @@ import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiConnectionProperties;
|
||||
import org.springframework.ai.autoconfigure.zhipuai.ZhiPuAiImageProperties;
|
||||
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.image.ImageModel;
|
||||
import org.springframework.ai.model.function.FunctionCallbackContext;
|
||||
import org.springframework.ai.ollama.OllamaChatModel;
|
||||
@ -97,6 +99,21 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url) {
|
||||
String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url);
|
||||
return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
|
||||
// TODO @xin 先测试一个
|
||||
switch (platform) {
|
||||
case TONG_YI:
|
||||
return buildTongYiEmbeddingModel(apiKey);
|
||||
default:
|
||||
throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ChatModel getDefaultChatModel(AiPlatformEnum platform) {
|
||||
//noinspection EnhancedSwitchMigration
|
||||
@ -239,7 +256,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
|
||||
/**
|
||||
* 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiChatModel(
|
||||
* ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
|
||||
*ZhiPuAiConnectionProperties, ZhiPuAiChatProperties, RestClient.Builder, List, FunctionCallbackContext, RetryTemplate, ResponseErrorHandler)}
|
||||
*/
|
||||
private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
|
||||
url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
|
||||
@ -249,7 +266,7 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
|
||||
/**
|
||||
* 可参考 {@link ZhiPuAiAutoConfiguration#zhiPuAiImageModel(
|
||||
* ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
|
||||
*ZhiPuAiConnectionProperties, ZhiPuAiImageProperties, RestClient.Builder, RetryTemplate, ResponseErrorHandler)}
|
||||
*/
|
||||
private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
|
||||
url = StrUtil.blankToDefault(url, ZhiPuAiConnectionProperties.DEFAULT_BASE_URL);
|
||||
@ -315,4 +332,15 @@ public class AiModelFactoryImpl implements AiModelFactory {
|
||||
return new StabilityAiImageModel(stabilityAiApi);
|
||||
}
|
||||
|
||||
// ========== 各种创建 EmbeddingModel 的方法 ==========
|
||||
|
||||
/**
|
||||
* 可参考 {@link TongYiAutoConfiguration#tongYiTextEmbeddingClient(TextEmbedding, TongYiConnectionProperties)}
|
||||
*/
|
||||
private EmbeddingModel buildTongYiEmbeddingModel(String apiKey) {
|
||||
TongYiConnectionProperties connectionProperties = new TongYiConnectionProperties();
|
||||
connectionProperties.setApiKey(apiKey);
|
||||
return new TongYiAutoConfiguration().tongYiTextEmbeddingClient(SpringUtil.getBean(TextEmbedding.class), connectionProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package cn.iocoder.yudao.framework.ai.core.factory;
|
||||
|
||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
|
||||
/**
|
||||
* AI Vector 模型工厂的接口类
|
||||
* @author xiaoxin
|
||||
*/
|
||||
public interface AiVectorFactory {
|
||||
|
||||
|
||||
/**
|
||||
* 基于指定配置,获得 VectorStore 对象
|
||||
* <p>
|
||||
* 如果不存在,则进行创建
|
||||
*
|
||||
* @param embeddingModel 嵌入模型
|
||||
* @param platform 平台
|
||||
* @param apiKey API KEY
|
||||
* @param url API URL
|
||||
* @return VectorStore 对象
|
||||
*/
|
||||
VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url);
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package cn.iocoder.yudao.framework.ai.core.factory;
|
||||
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import cn.hutool.core.lang.func.Func0;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
|
||||
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.vectorstore.RedisVectorStore;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
|
||||
import redis.clients.jedis.JedisPooled;
|
||||
|
||||
/**
|
||||
* AI Vector 模型工厂的实现类
|
||||
* 使用 redisVectorStore 实现 VectorStore
|
||||
*
|
||||
* @author xiaoxin
|
||||
*/
|
||||
public class AiVectorFactoryImpl implements AiVectorFactory {
|
||||
|
||||
@Override
|
||||
public VectorStore getOrCreateVectorStore(EmbeddingModel embeddingModel, AiPlatformEnum platform, String apiKey, String url) {
|
||||
String cacheKey = buildClientCacheKey(VectorStore.class, platform, apiKey, url);
|
||||
return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
|
||||
// TODO 芋艿 @xin 这两个配置取哪好呢
|
||||
// TODO 不同模型的向量维度可能会不一样,目前看貌似是以 index 来做区分的,维度不一样存不到一个 index 上
|
||||
String index = "default-index";
|
||||
String prefix = "default:";
|
||||
var config = RedisVectorStore.RedisVectorStoreConfig.builder()
|
||||
.withIndexName(index)
|
||||
.withPrefix(prefix)
|
||||
.build();
|
||||
RedisProperties redisProperties = SpringUtils.getBean(RedisProperties.class);
|
||||
RedisVectorStore redisVectorStore = new RedisVectorStore(config, embeddingModel,
|
||||
new JedisPooled(redisProperties.getHost(), redisProperties.getPort()),
|
||||
true);
|
||||
redisVectorStore.afterPropertiesSet();
|
||||
return redisVectorStore;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static String buildClientCacheKey(Class<?> clazz, Object... params) {
|
||||
if (ArrayUtil.isEmpty(params)) {
|
||||
return clazz.getName();
|
||||
}
|
||||
return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.vectorstore.RedisVectorStore;
|
||||
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
@ -38,7 +39,7 @@ import redis.clients.jedis.JedisPooled;
|
||||
*/
|
||||
@AutoConfiguration(after = RedisAutoConfiguration.class)
|
||||
@ConditionalOnClass({JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class})
|
||||
//@ConditionalOnBean(JedisConnectionFactory.class)
|
||||
@ConditionalOnBean(JedisConnectionFactory.class)
|
||||
@EnableConfigurationProperties(RedisVectorStoreProperties.class)
|
||||
public class RedisVectorStoreAutoConfiguration {
|
||||
|
||||
|
Reference in New Issue
Block a user