Appearance
Spring AI 向量存储与检索
向量存储是实现RAG(检索增强生成)和其他AI应用的关键组件。Spring AI提供了强大的向量存储和检索功能,用于存储、管理和检索文本嵌入。本文将介绍Spring AI中的向量存储技术及其应用场景。
向量存储基础
向量存储(Vector Store)是一种专门存储和索引向量嵌入的数据库。在AI应用中,文本、图像等内容通过嵌入模型转换为向量,然后存储在向量数据库中以支持相似性搜索。
Spring AI提供了统一的接口VectorStore
来操作各种向量存储实现:
java
public interface VectorStore {
void add(List<Document> documents);
void add(Embedding embedding);
List<Document> similaritySearch(String query);
List<Document> similaritySearch(String query, int k);
List<Document> similaritySearch(Embedding queryEmbedding);
List<Document> similaritySearch(Embedding queryEmbedding, int k);
// 其他方法...
}
嵌入服务
嵌入(Embedding)是将文本转换为数值向量的过程。Spring AI通过EmbeddingClient
接口提供嵌入功能:
java
@Service
public class EmbeddingService {
private final EmbeddingClient embeddingClient;
public EmbeddingService(EmbeddingClient embeddingClient) {
this.embeddingClient = embeddingClient;
}
public Embedding createEmbedding(String text) {
return embeddingClient.embed(text);
}
public List<Embedding> createEmbeddings(List<String> texts) {
return embeddingClient.embed(texts);
}
}
集成向量存储
Spring AI支持多种向量存储实现,包括内存存储、Redis、PostgreSQL、Chroma、Milvus等。
内存向量存储
内存向量存储适用于开发和测试环境:
java
@Configuration
public class VectorStoreConfig {
@Bean
public VectorStore inMemoryVectorStore(EmbeddingClient embeddingClient) {
return new InMemoryVectorStore(embeddingClient);
}
}
Redis向量存储
Redis向量存储适用于需要持久化和共享的场景:
xml
<!-- 依赖配置 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis</artifactId>
<version>0.8.0</version>
</dependency>
java
@Configuration
public class RedisVectorStoreConfig {
@Bean
public RedisVectorStore redisVectorStore(
RedisTemplate<String, String> redisTemplate,
EmbeddingClient embeddingClient) {
return new RedisVectorStore(redisTemplate, embeddingClient);
}
}
PostgreSQL向量存储
PostgreSQL向量存储利用pgvector扩展实现高效向量检索:
xml
<!-- 依赖配置 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector</artifactId>
<version>0.8.0</version>
</dependency>
java
@Configuration
public class PgVectorStoreConfig {
@Bean
public PgVectorStore pgVectorStore(
JdbcTemplate jdbcTemplate,
EmbeddingClient embeddingClient) {
return new PgVectorStore(jdbcTemplate, embeddingClient);
}
}
文档处理与索引
向量存储通常与文档处理结合使用,Spring AI提供了文档处理工具:
java
@Service
public class DocumentIndexingService {
private final VectorStore vectorStore;
private final EmbeddingClient embeddingClient;
private final DocumentReader documentReader;
// 构造函数注入
public void indexDocument(String filePath) throws IOException {
// 读取文档
Resource resource = new FileSystemResource(filePath);
Document document = documentReader.read(resource);
// 文档分块
DocumentSplitter splitter = new TokenTextSplitter(500, 100);
List<Document> chunks = splitter.split(document);
// 索引文档块
vectorStore.add(chunks);
}
public void indexDocumentCollection(String directoryPath) throws IOException {
DirectoryDocumentReader directoryReader = new DirectoryDocumentReader(documentReader);
Resource directoryResource = new FileSystemResource(directoryPath);
List<Document> documents = directoryReader.read(directoryResource);
// 文档分块
DocumentSplitter splitter = new TokenTextSplitter(500, 100);
List<Document> chunks = new ArrayList<>();
for (Document doc : documents) {
chunks.addAll(splitter.split(doc));
}
// 批量索引
vectorStore.add(chunks);
}
}
实现检索增强生成(RAG)
RAG是结合检索和生成的强大模式,Spring AI简化了RAG实现:
java
@Service
public class RAGService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
public String generateWithContext(String query) {
// 从向量存储检索相关文档
List<Document> relevantDocs = vectorStore.similaritySearch(query, 3);
// 提取上下文
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// 构建带上下文的提示
Prompt prompt = new Prompt("""
根据以下上下文回答问题。如果上下文中没有答案,请说明无法从提供的信息中找到答案。
上下文:
%s
问题: %s
""".formatted(context, query));
// 生成回答
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}
高级检索策略
混合检索
结合关键词搜索和语义搜索提高检索质量:
java
@Service
public class HybridSearchService {
private final VectorStore vectorStore;
private final KeywordSearchEngine keywordEngine;
public List<Document> hybridSearch(String query, int k) {
// 向量相似度搜索
List<Document> semanticResults = vectorStore.similaritySearch(query, k);
// 关键词搜索
List<Document> keywordResults = keywordEngine.search(query, k);
// 合并结果
Map<String, Document> combinedResults = new LinkedHashMap<>();
// 先添加语义搜索结果
for (Document doc : semanticResults) {
combinedResults.put(doc.getId(), doc);
}
// 再添加关键词搜索结果
for (Document doc : keywordResults) {
combinedResults.put(doc.getId(), doc);
}
// 返回结果
return new ArrayList<>(combinedResults.values());
}
}
重排序
对检索结果进行重排序提高相关性:
java
@Service
public class RerankerService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
public List<Document> rerankResults(String query, List<Document> initialResults) {
// 使用LLM为每个文档评分
List<ScoredDocument> scoredDocs = new ArrayList<>();
for (Document doc : initialResults) {
String scorePrompt = String.format(
"评估以下文档与查询的相关性,仅返回0到10的分数,不要有其他解释。\n\n" +
"查询: %s\n\n" +
"文档: %s\n\n" +
"分数(0-10):",
query, doc.getContent());
ChatResponse response = chatClient.call(new Prompt(scorePrompt));
String scoreText = response.getResult().getOutput().getContent().trim();
double score = Double.parseDouble(scoreText);
scoredDocs.add(new ScoredDocument(doc, score));
}
// 根据分数排序
scoredDocs.sort((a, b) -> Double.compare(b.score(), a.score()));
// 返回重排序的文档
return scoredDocs.stream().map(ScoredDocument::document).collect(Collectors.toList());
}
private record ScoredDocument(Document document, double score) {}
}
向量存储的缓存策略
为提高性能,可以实现向量存储的缓存:
java
@Service
public class CachedVectorStore implements VectorStore {
private final VectorStore delegate;
private final Cache<String, List<Document>> queryCache;
public CachedVectorStore(VectorStore delegate) {
this.delegate = delegate;
this.queryCache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(30, TimeUnit.MINUTES)
.build();
}
@Override
public List<Document> similaritySearch(String query, int k) {
String cacheKey = query + "_" + k;
try {
return queryCache.get(cacheKey, () -> delegate.similaritySearch(query, k));
} catch (ExecutionException e) {
// 缓存失败时回退到直接查询
return delegate.similaritySearch(query, k);
}
}
// 实现其他VectorStore方法...
}
向量存储最佳实践
- 选择合适的嵌入模型:不同嵌入模型有不同特点,选择适合应用场景的模型
- 合理分块文档:文档分块大小会影响检索质量,通常500-1000个token较为合适
- 设置适当的重叠:分块重叠可以保持上下文连贯性
- 混合检索策略:结合关键词和语义搜索提高结果质量
- 定期更新索引:保持向量存储与最新数据同步
- 考虑隐私和安全:确保敏感信息得到适当保护
总结
Spring AI的向量存储和检索功能为开发RAG应用提供了强大支持。通过统一的抽象接口,开发人员可以方便地集成各种向量存储实现,结合文档处理和嵌入功能,构建智能信息检索和知识问答系统。向量存储是AI应用中的关键组件,Spring AI简化了其使用流程,使开发人员能够专注于业务功能而非底层实现细节。