From e242a67c745712702d124a179f993291c9e4df54 Mon Sep 17 00:00:00 2001 From: Yzm Date: Fri, 17 Oct 2025 19:55:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(embedding):=20=E6=B7=BB=E5=8A=A0=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E7=BB=B4=E5=BA=A6=E6=94=AF=E6=8C=81=E5=B9=B6=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E5=90=91=E9=87=8F=E5=AD=98=E5=82=A8=E7=AD=96=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在ChatModelVo中添加dimension字段用于存储模型维度 - 重构EmbeddingModelFactory以支持按模型名称和维度创建实例 - 修改向量存储策略接口参数顺序并统一维度处理 - 为OpenAI和ZhiPuAI嵌入提供者添加维度配置支持 - 优化知识库服务中模型选择逻辑,添加回退机制 --- .../java/org/ruoyi/domain/vo/ChatModelVo.java | 5 +++ .../embedding/EmbeddingModelFactory.java | 29 ++++++++-------- .../impl/OllamaEmbeddingProvider.java | 1 + .../impl/OpenAiEmbeddingProvider.java | 1 + .../impl/ZhiPuAiEmbeddingProvider.java | 1 + .../org/ruoyi/service/VectorStoreService.java | 2 +- .../service/impl/VectorStoreServiceImpl.java | 5 ++- .../strategy/AbstractVectorStoreStrategy.java | 29 +++------------- .../impl/MilvusVectorStoreStrategy.java | 33 ++++++++++--------- .../impl/WeaviateVectorStoreStrategy.java | 17 +++++----- .../knowledge/KnowledgeInfoServiceImpl.java | 10 ++++-- 11 files changed, 64 insertions(+), 69 deletions(-) diff --git a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatModelVo.java b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatModelVo.java index 6a2de3cf..062a378a 100644 --- a/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatModelVo.java +++ b/ruoyi-modules-api/ruoyi-chat-api/src/main/java/org/ruoyi/domain/vo/ChatModelVo.java @@ -70,6 +70,11 @@ public class ChatModelVo implements Serializable { @ExcelProperty(value = "是否显示") private String modelShow; + /** + * 模型维度 + */ + private Integer dimension; + /** * 系统提示词 */ diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/EmbeddingModelFactory.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/EmbeddingModelFactory.java index cf0c7b60..3acf8a91 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/EmbeddingModelFactory.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/EmbeddingModelFactory.java @@ -27,20 +27,23 @@ public class EmbeddingModelFactory { private final IChatModelService chatModelService; // 模型缓存,使用ConcurrentHashMap保证线程安全 - private final Map modelCache = new ConcurrentHashMap<>(); + private final Map modelCache = new ConcurrentHashMap<>(); /** * 创建嵌入模型实例 * 如果模型已存在于缓存中,则直接返回;否则创建新的实例 * - * @param embeddingModelId 嵌入模型的唯一标识ID - * @return BaseEmbedModelService 嵌入模型服务实例 + * @param embeddingModelName 嵌入模型名称 + * @param dimension 模型维度大小 */ - public BaseEmbedModelService createModel(Long embeddingModelId) { - return modelCache.computeIfAbsent(embeddingModelId, id -> { - ChatModelVo modelConfig = chatModelService.queryById(id); + public BaseEmbedModelService createModel(String embeddingModelName, Integer dimension) { + return modelCache.computeIfAbsent(embeddingModelName, name -> { + ChatModelVo modelConfig = chatModelService.selectModelByName(embeddingModelName); if (modelConfig == null) { - throw new IllegalArgumentException("未找到模型配置,ID=" + id); + throw new IllegalArgumentException("未找到模型配置,name=" + name); + } + if (modelConfig.getDimension() != null) { + modelConfig.setDimension(dimension); } return createModelInstance(modelConfig.getProviderName(), modelConfig); }); @@ -49,22 +52,22 @@ public class EmbeddingModelFactory { /** * 检查模型是否支持多模态 * - * @param embeddingModelId 嵌入模型的唯一标识ID + * @param embeddingModelName 嵌入模型名称 * @return boolean 如果模型支持多模态则返回true,否则返回false */ - public boolean isMultimodalModel(Long embeddingModelId) { - return createModel(embeddingModelId) instanceof MultiModalEmbedModelService; + public boolean isMultimodalModel(String embeddingModelName) { + return createModel(embeddingModelName, null) instanceof MultiModalEmbedModelService; } /** * 创建多模态嵌入模型实例 * - * @param tenantId 租户ID + * @param embeddingModelName 嵌入模型名称 * @return MultiModalEmbedModelService 多模态嵌入模型服务实例 * @throws IllegalArgumentException 当模型不支持多模态时抛出 */ - public MultiModalEmbedModelService createMultimodalModel(Long tenantId) { - BaseEmbedModelService model = createModel(tenantId); + public MultiModalEmbedModelService createMultimodalModel(String embeddingModelName) { + BaseEmbedModelService model = createModel(embeddingModelName, null); if (model instanceof MultiModalEmbedModelService) { return (MultiModalEmbedModelService) model; } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OllamaEmbeddingProvider.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OllamaEmbeddingProvider.java index 17ed798c..1a179be7 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OllamaEmbeddingProvider.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OllamaEmbeddingProvider.java @@ -30,6 +30,7 @@ public class OllamaEmbeddingProvider implements BaseEmbedModelService { return Set.of(ModalityType.TEXT); } + // ollama不能设置embedding维度,使用milvus时请注意!!创建向量表时需要先设定维度大小 @Override public Response> embedAll(List textSegments) { return OllamaEmbeddingModel.builder() diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OpenAiEmbeddingProvider.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OpenAiEmbeddingProvider.java index e58bfe46..8a0c9f62 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OpenAiEmbeddingProvider.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/OpenAiEmbeddingProvider.java @@ -37,6 +37,7 @@ public class OpenAiEmbeddingProvider implements BaseEmbedModelService { .baseUrl(chatModelVo.getApiHost()) .apiKey(chatModelVo.getApiKey()) .modelName(chatModelVo.getModelName()) + .dimensions(chatModelVo.getDimension()) .build() .embedAll(textSegments); } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/ZhiPuAiEmbeddingProvider.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/ZhiPuAiEmbeddingProvider.java index e221749a..621e47c6 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/ZhiPuAiEmbeddingProvider.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/embedding/impl/ZhiPuAiEmbeddingProvider.java @@ -37,6 +37,7 @@ public class ZhiPuAiEmbeddingProvider implements BaseEmbedModelService { .baseUrl(chatModelVo.getApiHost()) .apiKey(chatModelVo.getApiKey()) .model(chatModelVo.getModelName()) + .dimensions(chatModelVo.getDimension()) .build() .embedAll(textSegments); } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java index 0d2a0d15..2937fc44 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/VectorStoreService.java @@ -16,7 +16,7 @@ public interface VectorStoreService { List getQueryVector(QueryVectorBo queryVectorBo); - void createSchema(String vectorModelName, String kid); + void createSchema(String kid, String embeddingModelName); void removeById(String id,String modelName) throws ServiceException; diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java index a52f3cc4..c9aeeab4 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java @@ -32,10 +32,9 @@ public class VectorStoreServiceImpl implements VectorStoreService { } @Override - public void createSchema(String vectorModelName, String kid) { - log.info("创建向量库schema: vectorModelName={}, kid={}, modelName={}", vectorModelName, kid); + public void createSchema(String kid, String modelName) { VectorStoreService strategy = getCurrentStrategy(); - strategy.createSchema(vectorModelName, kid); + strategy.createSchema(kid, modelName); } @Override diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/AbstractVectorStoreStrategy.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/AbstractVectorStoreStrategy.java index 100e4a3a..d35d9739 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/AbstractVectorStoreStrategy.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/AbstractVectorStoreStrategy.java @@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j; import org.ruoyi.common.core.config.VectorStoreProperties; import org.ruoyi.common.core.utils.StringUtils; import org.ruoyi.service.VectorStoreService; +import org.ruoyi.embedding.EmbeddingModelFactory; /** * 向量库策略抽象基类 @@ -23,34 +24,14 @@ public abstract class AbstractVectorStoreStrategy implements VectorStoreService protected final VectorStoreProperties vectorStoreProperties; + private final EmbeddingModelFactory embeddingModelFactory; + /** * 获取向量模型 */ @SneakyThrows - protected EmbeddingModel getEmbeddingModel(String modelName, String apiKey, String baseUrl) { - EmbeddingModel embeddingModel; - if ("quentinz/bge-large-zh-v1.5".equals(modelName)) { - embeddingModel = OllamaEmbeddingModel.builder() - .baseUrl(baseUrl) - .modelName(modelName) - .build(); - } else if ("baai/bge-m3".equals(modelName)) { - embeddingModel = OpenAiEmbeddingModel.builder() - .apiKey(apiKey) - .baseUrl(baseUrl) - .modelName(modelName) - .build(); - } else if (StringUtils.isNotEmpty(modelName)){ - embeddingModel = OpenAiEmbeddingModel.builder() - .apiKey(apiKey) - .baseUrl(baseUrl) - .dimensions(2048) - .modelName(modelName) - .build(); - } else { - throw new ServiceException("未找到对应向量化模型!"); - } - return embeddingModel; + protected EmbeddingModel getEmbeddingModel(String modelName, Integer dimension) { + return embeddingModelFactory.createModel(modelName, dimension); } /** diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/MilvusVectorStoreStrategy.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/MilvusVectorStoreStrategy.java index 9d60ce8d..8d3d50fa 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/MilvusVectorStoreStrategy.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/MilvusVectorStoreStrategy.java @@ -1,8 +1,8 @@ package org.ruoyi.service.strategy.impl; +import dev.langchain4j.data.document.Metadata; import dev.langchain4j.data.embedding.Embedding; import dev.langchain4j.data.segment.TextSegment; -import dev.langchain4j.data.document.Metadata; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.store.embedding.EmbeddingMatch; import dev.langchain4j.store.embedding.EmbeddingSearchRequest; @@ -17,22 +17,25 @@ import lombok.extern.slf4j.Slf4j; import org.ruoyi.common.core.config.VectorStoreProperties; import org.ruoyi.domain.bo.QueryVectorBo; import org.ruoyi.domain.bo.StoreEmbeddingBo; +import org.ruoyi.embedding.EmbeddingModelFactory; import org.ruoyi.service.strategy.AbstractVectorStoreStrategy; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; -import java.util.stream.IntStream; -// 新增导入 import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.IntStream; @Slf4j @Component public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { - public MilvusVectorStoreStrategy(VectorStoreProperties vectorStoreProperties) { - super(vectorStoreProperties); + + private final Integer DIMENSION = 2048; + + public MilvusVectorStoreStrategy(VectorStoreProperties vectorStoreProperties, EmbeddingModelFactory embeddingModelFactory) { + super(vectorStoreProperties, embeddingModelFactory); } // 缓存不同集合与 autoFlush 配置的 Milvus 连接 @@ -44,7 +47,7 @@ public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { MilvusEmbeddingStore.builder() .uri(vectorStoreProperties.getMilvus().getUrl()) .collectionName(collectionName) - .dimension(2048) + .dimension(DIMENSION) .indexType(IndexType.IVF_FLAT) .metricType(MetricType.L2) .autoFlushOnInsert(autoFlushOnInsert) @@ -57,12 +60,7 @@ public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { } @Override - public String getVectorStoreType() { - return "milvus"; - } - - @Override - public void createSchema(String vectorModelName, String kid) { + public void createSchema(String kid, String modelName) { String collectionName = vectorStoreProperties.getMilvus().getCollectionname() + kid; // 使用缓存获取连接以确保只初始化一次 EmbeddingStore store = getMilvusStore(collectionName, true); @@ -71,8 +69,7 @@ public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { @Override public void storeEmbeddings(StoreEmbeddingBo storeEmbeddingBo) { - EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getEmbeddingModelName(), - storeEmbeddingBo.getApiKey(), storeEmbeddingBo.getBaseUrl()); + EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getEmbeddingModelName(), DIMENSION); List chunkList = storeEmbeddingBo.getChunkList(); List fidList = storeEmbeddingBo.getFids(); @@ -104,8 +101,7 @@ public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { @Override public List getQueryVector(QueryVectorBo queryVectorBo) { - EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getEmbeddingModelName(), - queryVectorBo.getApiKey(), queryVectorBo.getBaseUrl()); + EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getEmbeddingModelName(), DIMENSION); Embedding queryEmbedding = embeddingModel.embed(queryVectorBo.getQuery()).content(); String collectionName = vectorStoreProperties.getMilvus().getCollectionname() + queryVectorBo.getKid(); @@ -153,4 +149,9 @@ public class MilvusVectorStoreStrategy extends AbstractVectorStoreStrategy { embeddingStore.removeAll(filter); log.info("Milvus成功删除 fid={} 的所有向量数据", fid); } + + @Override + public String getVectorStoreType() { + return "milvus"; + } } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/WeaviateVectorStoreStrategy.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/WeaviateVectorStoreStrategy.java index e1975823..3d61f8ac 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/WeaviateVectorStoreStrategy.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/strategy/impl/WeaviateVectorStoreStrategy.java @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.ruoyi.common.core.config.VectorStoreProperties; import org.ruoyi.domain.bo.QueryVectorBo; import org.ruoyi.domain.bo.StoreEmbeddingBo; +import org.ruoyi.embedding.EmbeddingModelFactory; import org.ruoyi.service.strategy.AbstractVectorStoreStrategy; import org.springframework.stereotype.Component; import java.util.*; @@ -35,8 +36,8 @@ public class WeaviateVectorStoreStrategy extends AbstractVectorStoreStrategy { private WeaviateClient client; - public WeaviateVectorStoreStrategy(VectorStoreProperties vectorStoreProperties) { - super(vectorStoreProperties); + public WeaviateVectorStoreStrategy(VectorStoreProperties vectorStoreProperties, EmbeddingModelFactory embeddingModelFactory) { + super(vectorStoreProperties, embeddingModelFactory); } @Override @@ -45,7 +46,7 @@ public class WeaviateVectorStoreStrategy extends AbstractVectorStoreStrategy { } @Override - public void createSchema(String vectorModelName, String kid) { + public void createSchema(String kid, String embeddingModelName) { String protocol = vectorStoreProperties.getWeaviate().getProtocol(); String host = vectorStoreProperties.getWeaviate().getHost(); String className = vectorStoreProperties.getWeaviate().getClassname() + kid; @@ -84,9 +85,8 @@ public class WeaviateVectorStoreStrategy extends AbstractVectorStoreStrategy { @Override public void storeEmbeddings(StoreEmbeddingBo storeEmbeddingBo) { - createSchema(storeEmbeddingBo.getVectorStoreName(), storeEmbeddingBo.getKid()); - EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getEmbeddingModelName(), - storeEmbeddingBo.getApiKey(), storeEmbeddingBo.getBaseUrl()); + createSchema(storeEmbeddingBo.getKid(),storeEmbeddingBo.getEmbeddingModelName()); + EmbeddingModel embeddingModel = getEmbeddingModel(storeEmbeddingBo.getEmbeddingModelName(), null); List chunkList = storeEmbeddingBo.getChunkList(); List fidList = storeEmbeddingBo.getFids(); String kid = storeEmbeddingBo.getKid(); @@ -118,9 +118,8 @@ public class WeaviateVectorStoreStrategy extends AbstractVectorStoreStrategy { @Override public List getQueryVector(QueryVectorBo queryVectorBo) { - createSchema(queryVectorBo.getVectorModelName(), queryVectorBo.getKid()); - EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getEmbeddingModelName(), - queryVectorBo.getApiKey(), queryVectorBo.getBaseUrl()); + createSchema(queryVectorBo.getKid(),queryVectorBo.getEmbeddingModelName()); + EmbeddingModel embeddingModel = getEmbeddingModel(queryVectorBo.getEmbeddingModelName(),null); Embedding queryEmbedding = embeddingModel.embed(queryVectorBo.getQuery()).content(); float[] vector = queryEmbedding.vector(); List vectorStrings = new ArrayList<>(); diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java index e34ababf..6a588185 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java @@ -9,6 +9,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.ruoyi.chain.loader.ResourceLoader; import org.ruoyi.chain.loader.ResourceLoaderFactory; +import org.ruoyi.chat.enums.ChatModeType; import org.ruoyi.common.core.domain.model.LoginUser; import org.ruoyi.common.core.utils.MapstructUtils; import org.ruoyi.common.core.utils.StringUtils; @@ -237,7 +238,7 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { } baseMapper.insert(knowledgeInfo); if (knowledgeInfo != null) { - vectorStoreService.createSchema(knowledgeInfo.getVectorModelName(),String.valueOf(knowledgeInfo.getId())); + vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()), bo.getEmbeddingModelName()); } } else { baseMapper.updateById(knowledgeInfo); @@ -312,8 +313,11 @@ public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { .eq(KnowledgeInfo::getId, kid)); // 通过向量模型查询模型信息 - ChatModelVo chatModelVo = chatModelService.queryById(knowledgeInfoVo.getEmbeddingModelId()); - + ChatModelVo chatModelVo = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName()); + // 未查到指定模型时,回退为向量分类最高优先级模型 + if (chatModelVo == null) { + chatModelVo = chatModelService.selectModelByCategoryWithHighestPriority(ChatModeType.VECTOR.getCode()); + } StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo(); storeEmbeddingBo.setKid(kid); storeEmbeddingBo.setDocId(docId);