向量大模型
向量大模型拓展指南
拓展概述
BladeX平台的向量大模型基于LangChain4j定制开发,支持OpenAI、HuggingFace、Qianfan、Ollama等主流向量模型提供商。采用标准化接口设计,兼容LangChain4j生态中的所有向量模型。本指南详细介绍如何扩展新的向量大模型,包括架构设计、代码实现和最佳实践。
技术特色
- 标准化接口设计:遵循LangChain4j的EmbeddingModel规范,兼容性强
- 广泛生态支持:支持LangChain4j生态中的所有向量模型
- 统一抽象管理:通过模板类实现不同提供商的统一管理
- 高性能优化:优化的批量处理和缓存机制,提升向量化效率
- 灵活配置管理:支持数据库配置和属性文件配置,动态更新
🏗️ 一、技术架构了解
1.1 向量模型模块结构
rag/engine/provider/
├── AbstractEmbeddingModelTemplate.java # 抽象模板基类
├── EmbeddingModelTemplate.java # 向量模型接口
├── RagFactory.java # 向量模型工厂
├── builder/
│ └── BladeEmbeddingModelBuilder.java # 向量模型构建器
└── model/
├── OpenAIEmbeddingModelTemplate.java # OpenAI实现
├── HuggingFaceEmbeddingModelTemplate.java # HuggingFace实现
├── QianfanEmbeddingModelTemplate.java # 千帆实现
├── OllamaEmbeddingModelTemplate.java # Ollama实现
└── GitHubModelsEmbeddingModelTemplate.java # GitHub Models实现
1.2 支持的向量模型提供商
BladeX平台目前支持以下向量大模型:
提供商 | 状态 | 特点 | 适用场景 |
---|---|---|---|
OpenAI | ✅ 已支持 | 高质量向量,多维度选择 | 通用文本向量化 |
HuggingFace | ✅ 已支持 | 开源模型丰富,免费使用 | 研究开发环境 |
Qianfan | ✅ 已支持 | 百度千帆,中文优化 | 中文文本处理 |
Ollama | ✅ 已支持 | 本地部署,隐私保护 | 私有化部署 |
GitHub Models | 🔨 示例扩展 | 免费试用,开发友好 | 开发测试环境 |
Cohere | ⏳ 可扩展 | 多语言支持 | 国际化应用 |
Voyage AI | ⏳ 可扩展 | 专业向量服务 | 企业级应用 |
1.3 核心组件架构
🔧 二、核心组件详解
2.1 向量模型接口 (EmbeddingModelTemplate)
向量模型接口定义了统一的向量化操作规范:
public interface EmbeddingModelTemplate {
/**
* 获取模型名称
*/
String getName();
/**
* 获取向量维度
*/
int getDimension();
/**
* 获取原始的向量模型实例
*/
EmbeddingModel getEmbeddingModel();
/**
* 将文本转换为向量
*/
Embedding embedText(String text);
/**
* 批量将文本转换为向量
*/
List<Embedding> embedTexts(List<String> texts);
/**
* 将文档转换为向量
*/
Embedding embedDocument(Document document);
/**
* 批量将文档转换为向量
*/
List<Embedding> embedDocuments(List<Document> documents);
}
核心功能特性:
- 统一接口规范:标准化的向量化操作接口
- 批量处理支持:支持单个和批量文本向量化
- 文档级处理:支持Document对象的向量化
- 维度信息获取:提供向量维度查询功能
2.2 抽象模板类 (AbstractEmbeddingModelTemplate)
抽象模板类提供了通用的向量化处理逻辑:
public abstract class AbstractEmbeddingModelTemplate implements EmbeddingModelTemplate {
protected final String name;
protected final int dimension;
protected final EmbeddingModel embeddingModel;
protected AbstractEmbeddingModelTemplate(String name, int dimension, EmbeddingModel embeddingModel) {
this.name = name;
this.dimension = dimension;
this.embeddingModel = embeddingModel;
}
@Override
public String getName() {
return name;
}
@Override
public int getDimension() {
return dimension;
}
@Override
public EmbeddingModel getEmbeddingModel() {
return embeddingModel;
}
@Override
public Embedding embedText(String text) {
try {
Response<Embedding> response = embeddingModel.embed(text);
return response.content();
} catch (Exception e) {
throw new RuntimeException("文本向量化失败: " + e.getMessage(), e);
}
}
@Override
public List<Embedding> embedTexts(List<String> texts) {
try {
Response<List<Embedding>> response = embeddingModel.embedAll(texts);
return response.content();
} catch (Exception e) {
throw new RuntimeException("批量文本向量化失败: " + e.getMessage(), e);
}
}
}
主要职责:
- 通用逻辑封装:封装常用的向量化操作逻辑
- 异常处理统一:统一的异常处理和错误信息包装
- 性能优化:批量处理优化和缓存机制
- 接口标准化:确保所有实现类遵循统一规范
2.3 向量模型工厂 (RagFactory)
工厂类负责根据配置创建对应的向量模型实例:
@RequiredArgsConstructor
public class RagFactory {
public EmbeddingModelTemplate createEmbeddingModelTemplate(String name, ModelConfig config) {
BladeEmbeddingModelBuilder builder = BladeEmbeddingModelBuilder.builder()
.name(name)
.dimension(config.getDimension())
.modelType(config.getModelType())
.modelName(config.getModelName())
.apiKey(config.getApiKey())
.secretKey(config.getSecretKey())
.baseUrl(config.getBaseUrl())
.timeout(config.getTimeout())
.build();
return switch (config.getModelType()) {
case RagConstant.MODEL_TYPE_OPENAI -> builder.buildOpenAI();
case RagConstant.MODEL_TYPE_OLLAMA -> builder.buildOllama();
case RagConstant.MODEL_TYPE_HUGGINGFACE -> builder.buildHuggingFace();
case RagConstant.MODEL_TYPE_QIANFAN -> builder.buildQianfan();
case RagConstant.MODEL_TYPE_GITHUB -> builder.buildGitHubModels();
default -> {
log.warn("不支持的向量模型类型: {}", config.getModelType());
yield null;
}
};
}
}
主要职责:
- 模型类型识别:根据配置自动识别向量模型提供商
- 实例创建管理:创建对应的向量模型实例并进行配置验证
- 配置参数转换:将数据库配置转换为模型配置对象
- 异常处理:处理不支持的模型类型和配置错误
2.4 模型构建器 (BladeEmbeddingModelBuilder)
构建器类提供了灵活的向量模型创建方式:
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BladeEmbeddingModelBuilder {
private String name;
private int dimension;
private String modelType;
private String modelName;
private String apiKey;
private String secretKey;
private String baseUrl;
private Long timeout;
/**
* 构建OpenAI向量模型
*/
public OpenAIEmbeddingModelTemplate buildOpenAI() {
return new OpenAIEmbeddingModelTemplate(this);
}
/**
* 构建GitHub Models向量模型
*/
public GitHubModelsEmbeddingModelTemplate buildGitHubModels() {
return new GitHubModelsEmbeddingModelTemplate(this);
}
/**
* 构建Ollama向量模型
*/
public OllamaEmbeddingModelTemplate buildOllama() {
return new OllamaEmbeddingModelTemplate(this);
}
/**
* 构建HuggingFace向量模型
*/
public HuggingFaceEmbeddingModelTemplate buildHuggingFace() {
return new HuggingFaceEmbeddingModelTemplate(this);
}
}
核心特性:
- 建造者模式:提供灵活的配置方式
- 类型安全:编译时检查配置参数
- 扩展友好:易于添加新的向量模型类型
- 配置验证:内置配置参数验证机制
🚀 三、扩展新的向量模型
3.1 创建GitHub Models实现类
基于LangChain4j的GitHub Models集成,展示完整的实现过程:
@Slf4j
public class GitHubModelsEmbeddingModelTemplate extends AbstractEmbeddingModelTemplate {
private static final int DEFAULT_DIMENSION = 1536;
private static final String DEFAULT_MODEL = "text-embedding-3-small";
public GitHubModelsEmbeddingModelTemplate(BladeEmbeddingModelBuilder builder) {
super(
builder.getName(),
builder.getDimension() > 0 ? builder.getDimension() : DEFAULT_DIMENSION,
createGitHubModelsEmbeddingModel(
builder.getModelName(),
builder.getApiKey(),
builder.getTimeout()
)
);
}
/**
* 创建GitHub Models向量模型实例
*/
private static EmbeddingModel createGitHubModelsEmbeddingModel(String modelName,
String gitHubToken, Long timeout) {
try {
GitHubModelsEmbeddingModel.GitHubModelsEmbeddingModelBuilder builder =
GitHubModelsEmbeddingModel.builder()
.gitHubToken(gitHubToken)
.modelName(StringUtil.isBlank(modelName) ? DEFAULT_MODEL : modelName)
.logRequestsAndResponses(false)
.maxRetries(3);
if (timeout != null && timeout > 0) {
builder.timeout(Duration.ofMillis(timeout));
}
return builder.build();
} catch (Exception e) {
log.error("创建GitHub Models向量模型失败", e);
throw new RuntimeException("创建GitHub Models向量模型失败: " + e.getMessage(), e);
}
}
/**
* 获取支持的模型列表
*/
public static List<String> getSupportedModels() {
return Arrays.asList(
"text-embedding-3-small",
"text-embedding-3-large",
"text-embedding-ada-002"
);
}
/**
* 获取模型的默认维度
*/
public static int getDefaultDimension(String modelName) {
return switch (modelName) {
case "text-embedding-3-small" -> 1536;
case "text-embedding-3-large" -> 3072;
case "text-embedding-ada-002" -> 1536;
default -> DEFAULT_DIMENSION;
};
}
}
3.2 注册到工厂和构建器
在RagConstant
中添加常量:
public class RagConstant {
// 现有常量...
public static final String MODEL_TYPE_GITHUB = "github";
public static final String PREFIX_GITHUB = "github-";
}
在BladeEmbeddingModelBuilder
中添加构建方法:
/**
* 构建GitHub Models向量模型
*/
public GitHubModelsEmbeddingModelTemplate buildGitHubModels() {
return new GitHubModelsEmbeddingModelTemplate(this);
}
在RagFactory
中添加case:
return switch (modelType) {
case RagConstant.MODEL_TYPE_OPENAI -> builder.buildOpenAI();
case RagConstant.MODEL_TYPE_GITHUB -> builder.buildGitHubModels(); // 新增
case RagConstant.MODEL_TYPE_OLLAMA -> builder.buildOllama();
case RagConstant.MODEL_TYPE_HUGGINGFACE -> builder.buildHuggingFace();
case RagConstant.MODEL_TYPE_QIANFAN -> builder.buildQianfan();
default -> {
log.warn("不支持的向量模型类型: {}", modelType);
yield null;
}
};
3.3 添加Maven依赖
在pom.xml
中添加GitHub Models的依赖:
<!-- GitHub Models向量模型支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-github-models</artifactId>
<version>1.0.1-beta6</version>
</dependency>
<!-- 核心LangChain4j依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.0.1-beta6</version>
</dependency>
3.4 前端配置支持
在前端model.vue
中添加GitHub Models配置:
const MODEL_CONFIG = {
// 现有配置...
github: {
label: 'GitHub Models',
value: 'github',
group: ['embedding'],
baseUrl: 'https://models.inference.ai.azure.com',
icon: 'img/embedding/github.png',
models: [
'text-embedding-3-small',
'text-embedding-3-large',
'text-embedding-ada-002'
]
}
}
// 在向量模型配置表单中添加GitHub特定配置
const getModelSpecificConfig = (type) => {
const configs = {
github: {
apiKeyLabel: 'GitHub Token',
apiKeyPlaceholder: '请输入GitHub Personal Access Token',
defaultModel: 'text-embedding-3-small',
supportedModels: [
{ label: 'text-embedding-3-small (1536维)', value: 'text-embedding-3-small' },
{ label: 'text-embedding-3-large (3072维)', value: 'text-embedding-3-large' },
{ label: 'text-embedding-ada-002 (1536维)', value: 'text-embedding-ada-002' }
]
}
}
return configs[type] || {}
}
🎛️ 四、高级特性实现
4.1 批量处理优化
实现高效的批量向量化处理:
@Override
public List<Embedding> embedTexts(List<String> texts) {
if (texts == null || texts.isEmpty()) {
return Collections.emptyList();
}
// 分批处理,避免单次请求过大
int batchSize = getBatchSize();
List<Embedding> allEmbeddings = new ArrayList<>();
for (int i = 0; i < texts.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, texts.size());
List<String> batch = texts.subList(i, endIndex);
try {
Response<List<Embedding>> response = embeddingModel.embedAll(batch);
allEmbeddings.addAll(response.content());
} catch (Exception e) {
log.error("批量向量化失败,批次: {}-{}", i, endIndex, e);
throw new RuntimeException("批量向量化失败: " + e.getMessage(), e);
}
}
return allEmbeddings;
}
/**
* 获取批处理大小
*/
protected int getBatchSize() {
// 不同提供商的批处理大小限制不同
return switch (getProviderType()) {
case "openai", "github" -> 100;
case "huggingface" -> 50;
case "ollama" -> 10;
default -> 20;
};
}
4.2 缓存机制实现
添加向量缓存以提升性能:
@Component
public class EmbeddingCacheManager {
private final Cache<String, Embedding> embeddingCache;
public EmbeddingCacheManager() {
this.embeddingCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(Duration.ofHours(24))
.recordStats()
.build();
}
public Embedding getOrCompute(String cacheKey, Function<String, Embedding> computer) {
String cacheKey = generateCacheKey(cacheKey);
return embeddingCache.get(cacheKey, key -> computer.apply(key));
}
/**
* 生成缓存键
*/
private String generateCacheKey(String text) {
return DigestUtils.md5Hex(text);
}
/**
* 获取缓存统计信息
*/
public CacheStats getCacheStats() {
return embeddingCache.stats();
}
}
4.3 异步处理支持
实现异步向量化处理:
@Service
public class AsyncEmbeddingService {
private final ExecutorService executorService;
private final EmbeddingModelTemplate embeddingModel;
public AsyncEmbeddingService(EmbeddingModelTemplate embeddingModel) {
this.embeddingModel = embeddingModel;
this.executorService = Executors.newFixedThreadPool(10);
}
/**
* 异步文本向量化
*/
public CompletableFuture<Embedding> embedTextAsync(String text) {
return CompletableFuture.supplyAsync(() -> {
try {
return embeddingModel.embedText(text);
} catch (Exception e) {
throw new RuntimeException("异步向量化失败: " + e.getMessage(), e);
}
}, executorService);
}
/**
* 异步批量文本向量化
*/
public CompletableFuture<List<Embedding>> embedTextsAsync(List<String> texts) {
return CompletableFuture.supplyAsync(() -> {
try {
return embeddingModel.embedTexts(texts);
} catch (Exception e) {
throw new RuntimeException("异步批量向量化失败: " + e.getMessage(), e);
}
}, executorService);
}
}
4.4 多模型路由策略
实现智能的模型选择策略:
@Component
public class EmbeddingModelRouter {
private final Map<String, EmbeddingModelTemplate> modelTemplates;
private final LoadBalancer loadBalancer;
/**
* 根据文本特征选择最适合的模型
*/
public EmbeddingModelTemplate selectModel(String text, String language) {
// 根据语言选择模型
if ("zh".equals(language)) {
return modelTemplates.get("qianfan"); // 中文优先使用千帆
}
// 根据文本长度选择模型
if (text.length() > 8000) {
return modelTemplates.get("openai-large"); // 长文本使用大模型
}
// 默认使用负载均衡
return loadBalancer.selectModel(modelTemplates.values());
}
/**
* 根据成本效益选择模型
*/
public EmbeddingModelTemplate selectCostEffectiveModel(List<String> texts) {
int totalTokens = texts.stream().mapToInt(String::length).sum();
if (totalTokens < 1000) {
return modelTemplates.get("github"); // 小量文本使用免费模型
} else if (totalTokens < 10000) {
return modelTemplates.get("openai-small"); // 中等文本使用小模型
} else {
return modelTemplates.get("huggingface"); // 大量文本使用开源模型
}
}
}
🧪 五、测试验证指南
5.1 单元测试
为新向量模型创建完整的单元测试:
@ExtendWith(MockitoExtension.class)
class GitHubModelsEmbeddingModelTemplateTest {
@Mock
private BladeEmbeddingModelBuilder mockBuilder;
private GitHubModelsEmbeddingModelTemplate embeddingTemplate;
@BeforeEach
void setUp() {
when(mockBuilder.getName()).thenReturn("test-github");
when(mockBuilder.getDimension()).thenReturn(1536);
when(mockBuilder.getModelName()).thenReturn("text-embedding-3-small");
when(mockBuilder.getApiKey()).thenReturn("test-token");
when(mockBuilder.getTimeout()).thenReturn(30000L);
embeddingTemplate = new GitHubModelsEmbeddingModelTemplate(mockBuilder);
}
@Test
void testEmbedText() {
// 测试单个文本向量化
String testText = "Hello, world!";
Embedding embedding = embeddingTemplate.embedText(testText);
assertThat(embedding).isNotNull();
assertThat(embedding.vector()).hasSize(1536);
assertThat(embedding.vector().get(0)).isInstanceOf(Float.class);
}
@Test
void testEmbedTexts() {
// 测试批量文本向量化
List<String> testTexts = Arrays.asList(
"Hello, world!",
"How are you?",
"Nice to meet you!"
);
List<Embedding> embeddings = embeddingTemplate.embedTexts(testTexts);
assertThat(embeddings).hasSize(3);
embeddings.forEach(embedding -> {
assertThat(embedding).isNotNull();
assertThat(embedding.vector()).hasSize(1536);
});
}
@Test
void testGetSupportedModels() {
// 测试支持的模型列表
List<String> supportedModels = GitHubModelsEmbeddingModelTemplate.getSupportedModels();
assertThat(supportedModels).isNotEmpty();
assertThat(supportedModels).contains("text-embedding-3-small");
assertThat(supportedModels).contains("text-embedding-3-large");
}
@Test
void testGetDefaultDimension() {
// 测试默认维度获取
int dimension1 = GitHubModelsEmbeddingModelTemplate.getDefaultDimension("text-embedding-3-small");
int dimension2 = GitHubModelsEmbeddingModelTemplate.getDefaultDimension("text-embedding-3-large");
assertThat(dimension1).isEqualTo(1536);
assertThat(dimension2).isEqualTo(3072);
}
}
5.2 集成测试
创建端到端的集成测试:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {
"blade.rag.embedding-models.github-test.model-type=github",
"blade.rag.embedding-models.github-test.model-name=text-embedding-3-small",
"blade.rag.embedding-models.github-test.api-key=${GITHUB_TOKEN}",
"blade.rag.embedding-models.github-test.dimension=1536"
})
class GitHubModelsIntegrationTest {
@Autowired
private RagFactory ragFactory;
@Test
void testGitHubModelsIntegration() {
// 测试工厂创建
ModelConfig config = new ModelConfig();
config.setModelType("github");
config.setModelName("text-embedding-3-small");
config.setApiKey(System.getenv("GITHUB_TOKEN"));
config.setDimension(1536);
EmbeddingModelTemplate template = ragFactory.createEmbeddingModelTemplate("github-test", config);
assertThat(template).isNotNull();
assertThat(template).isInstanceOf(GitHubModelsEmbeddingModelTemplate.class);
assertThat(template.getName()).isEqualTo("github-test");
assertThat(template.getDimension()).isEqualTo(1536);
}
@Test
void testRealApiCall() {
// 测试真实API调用(需要有效的GitHub Token)
String githubToken = System.getenv("GITHUB_TOKEN");
assumeTrue(githubToken != null, "需要设置GITHUB_TOKEN环境变量");
ModelConfig config = new ModelConfig();
config.setModelType("github");
config.setApiKey(githubToken);
EmbeddingModelTemplate template = ragFactory.createEmbeddingModelTemplate("github-real", config);
String testText = "This is a test for GitHub Models embedding.";
Embedding embedding = template.embedText(testText);
assertThat(embedding).isNotNull();
assertThat(embedding.vector()).isNotEmpty();
}
}
5.3 性能测试
验证向量模型的性能表现:
@Test
void testPerformance() {
GitHubModelsEmbeddingModelTemplate template = createTestTemplate();
List<String> testTexts = generateTestTexts(100);
// 预热
template.embedTexts(testTexts.subList(0, 10));
// 性能测试
long startTime = System.currentTimeMillis();
List<Embedding> embeddings = template.embedTexts(testTexts);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
double avgLatency = (double) duration / testTexts.size();
log.info("向量化性能: 总时间={}ms, 平均延迟={}ms/text", duration, avgLatency);
assertThat(embeddings).hasSize(100);
assertThat(avgLatency).isLessThan(100); // 平均延迟应小于100ms
}
private List<String> generateTestTexts(int count) {
List<String> texts = new ArrayList<>();
for (int i = 0; i < count; i++) {
texts.add("This is test text number " + i + " for performance testing.");
}
return texts;
}
📊 六、性能优化策略
6.1 连接池优化
配置HTTP客户端连接池:
@Configuration
public class EmbeddingHttpConfig {
@Bean
public HttpClient embeddingHttpClient() {
return HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.executor(Executors.newFixedThreadPool(20))
.build();
}
@Bean
public WebClient embeddingWebClient() {
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(
HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.responseTimeout(Duration.ofSeconds(30))
.keepAlive(true)
.wiretap(true)
))
.build();
}
}
6.2 批处理优化
实现智能批处理策略:
@Component
public class BatchEmbeddingProcessor {
private final Map<String, List<EmbeddingRequest>> pendingRequests = new ConcurrentHashMap<>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(5);
@PostConstruct
public void init() {
// 定时处理批次
scheduler.scheduleAtFixedRate(this::processBatches, 100, 100, TimeUnit.MILLISECONDS);
}
/**
* 添加向量化请求到批次
*/
public CompletableFuture<Embedding> addRequest(String modelName, String text) {
EmbeddingRequest request = new EmbeddingRequest(text, new CompletableFuture<>());
pendingRequests.computeIfAbsent(modelName, k -> new ArrayList<>()).add(request);
return request.getFuture();
}
/**
* 处理所有待处理的批次
*/
private void processBatches() {
pendingRequests.forEach((modelName, requests) -> {
if (requests.size() >= getBatchSize(modelName) ||
isTimeoutReached(requests.get(0))) {
processBatch(modelName, new ArrayList<>(requests));
requests.clear();
}
});
}
private void processBatch(String modelName, List<EmbeddingRequest> requests) {
try {
EmbeddingModelTemplate template = getTemplate(modelName);
List<String> texts = requests.stream()
.map(EmbeddingRequest::getText)
.collect(Collectors.toList());
List<Embedding> embeddings = template.embedTexts(texts);
// 返回结果
for (int i = 0; i < requests.size(); i++) {
requests.get(i).getFuture().complete(embeddings.get(i));
}
} catch (Exception e) {
// 处理异常
requests.forEach(request ->
request.getFuture().completeExceptionally(e));
}
}
}
6.3 缓存策略优化
实现多级缓存策略:
@Component
public class MultiLevelEmbeddingCache {
private final Cache<String, Embedding> l1Cache; // 内存缓存
private final RedisTemplate<String, Embedding> l2Cache; // Redis缓存
public MultiLevelEmbeddingCache(RedisTemplate<String, Embedding> redisTemplate) {
this.l2Cache = redisTemplate;
this.l1Cache = Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(Duration.ofMinutes(30))
.removalListener((key, value, cause) -> {
// L1缓存淘汰时写入L2缓存
if (cause == RemovalCause.SIZE || cause == RemovalCause.EXPIRED) {
l2Cache.opsForValue().set((String) key, (Embedding) value, Duration.ofHours(24));
}
})
.build();
}
public Embedding getOrCompute(String cacheKey, Function<String, Embedding> computer) {
// 1. 尝试L1缓存
Embedding embedding = l1Cache.getIfPresent(cacheKey);
if (embedding != null) {
return embedding;
}
// 2. 尝试L2缓存
embedding = l2Cache.opsForValue().get(cacheKey);
if (embedding != null) {
l1Cache.put(cacheKey, embedding); // 回写L1缓存
return embedding;
}
// 3. 计算并缓存
embedding = computer.apply(cacheKey);
l1Cache.put(cacheKey, embedding);
return embedding;
}
}
🔍 七、监控和调试
7.1 指标收集
添加详细的性能指标:
@Component
public class EmbeddingMetrics {
private final MeterRegistry meterRegistry;
private final Counter requestCounter;
private final Timer embeddingTimer;
private final Gauge cacheHitRate;
public EmbeddingMetrics(MeterRegistry meterRegistry, EmbeddingCacheManager cacheManager) {
this.meterRegistry = meterRegistry;
this.requestCounter = Counter.builder("embedding.requests.total")
.description("Total embedding requests")
.register(meterRegistry);
this.embeddingTimer = Timer.builder("embedding.duration")
.description("Embedding processing duration")
.register(meterRegistry);
this.cacheHitRate = Gauge.builder("embedding.cache.hit.rate")
.description("Embedding cache hit rate")
.register(meterRegistry, cacheManager, manager ->
manager.getCacheStats().hitRate());
}
public void recordRequest(String provider, String model, int textCount,
boolean success, Duration duration) {
requestCounter.increment(
Tags.of(
Tag.of("provider", provider),
Tag.of("model", model),
Tag.of("success", String.valueOf(success))
)
);
embeddingTimer.record(duration,
Tags.of(
Tag.of("provider", provider),
Tag.of("model", model),
Tag.of("batch_size", String.valueOf(textCount))
)
);
}
}
7.2 健康检查
实现向量模型健康检查:
@Component
public class EmbeddingHealthIndicator implements HealthIndicator {
private final Map<String, EmbeddingModelTemplate> embeddingModels;
@Override
public Health health() {
Health.Builder builder = Health.up();
boolean allHealthy = true;
for (Map.Entry<String, EmbeddingModelTemplate> entry : embeddingModels.entrySet()) {
String modelName = entry.getKey();
EmbeddingModelTemplate template = entry.getValue();
try {
// 测试向量化
Embedding testEmbedding = template.embedText("health check");
if (testEmbedding != null && !testEmbedding.vector().isEmpty()) {
builder.withDetail(modelName, "UP");
} else {
builder.withDetail(modelName, "DOWN - 空向量响应");
allHealthy = false;
}
} catch (Exception e) {
builder.withDetail(modelName, "DOWN - " + e.getMessage());
allHealthy = false;
}
}
return allHealthy ? builder.build() : builder.down().build();
}
}
7.3 日志配置
配置结构化日志:
@Aspect
@Component
public class EmbeddingLoggingAspect {
@Around("execution(* org.springblade.modules.aigc.rag.engine.provider.model.*.*(..))")
public Object logEmbeddingOperation(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
String requestId = UUID.randomUUID().toString();
MDC.put("requestId", requestId);
MDC.put("operation", methodName);
try {
log.info("开始向量化操作: method={}, args={}", methodName, args.length);
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
log.info("向量化操作完成: duration={}ms", duration);
return result;
} catch (Exception e) {
log.error("向量化操作失败: {}", e.getMessage(), e);
throw e;
} finally {
MDC.clear();
}
}
}
🚨 八、常见问题解决
8.1 编译问题
常见编译错误
Q: LangChain4j依赖冲突
- 原因: 版本不兼容或依赖传递冲突
- 解决: 使用Maven dependency:tree检查冲突,排除冲突依赖
Q: 找不到EmbeddingModel类
- 原因: 缺少LangChain4j核心依赖
- 解决: 添加langchain4j核心依赖
8.2 运行时问题
运行时异常
Q: API调用失败
- 检查: API密钥、网络连接、请求格式
- 解决: 验证配置参数,查看API文档
Q: 向量维度不匹配
- 检查: 模型配置的维度与实际输出是否一致
- 解决: 更新配置或使用正确的模型
8.3 性能问题
性能优化建议
Q: 向量化速度慢
- 检查: 批处理大小、网络延迟
- 优化: 调整批处理参数、使用缓存
Q: 内存占用过高
- 检查: 缓存策略、向量存储
- 优化: 调整缓存大小、使用压缩存储
💡 九、最佳实践总结
9.1 开发规范
编码规范
- 命名一致性:向量模型实现类命名遵循
{Provider}EmbeddingModelTemplate
格式 - 异常处理:统一使用RuntimeException包装异常,提供清晰的错误信息
- 日志记录:在关键操作点添加结构化日志
- 配置验证:在构造函数中验证必要的配置参数
- 文档完善:为每个新模型编写详细的使用文档
9.2 性能考虑
性能要求
- 批处理优化:合理设置批处理大小,平衡延迟和吞吐量
- 缓存策略:实现多级缓存,减少重复计算
- 连接池管理:配置合适的连接池参数
- 异步处理:对于大批量处理使用异步方式
9.3 扩展建议
扩展指导
- 遵循LangChain4j规范:严格按照LangChain4j的接口设计
- 充分测试:编写完整的单元测试和集成测试
- 性能监控:添加必要的性能指标和监控
- 文档维护:及时更新相关文档和示例代码
- 社区贡献:考虑将通用实现贡献给LangChain4j社区
通过遵循本指南,开发者可以快速、安全地为BladeX平台添加新的向量大模型支持,充分利用LangChain4j生态系统的强大能力,实现高质量的文本向量化服务。