Skip to content

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方法...
}

向量存储最佳实践

  1. 选择合适的嵌入模型:不同嵌入模型有不同特点,选择适合应用场景的模型
  2. 合理分块文档:文档分块大小会影响检索质量,通常500-1000个token较为合适
  3. 设置适当的重叠:分块重叠可以保持上下文连贯性
  4. 混合检索策略:结合关键词和语义搜索提高结果质量
  5. 定期更新索引:保持向量存储与最新数据同步
  6. 考虑隐私和安全:确保敏感信息得到适当保护

总结

Spring AI的向量存储和检索功能为开发RAG应用提供了强大支持。通过统一的抽象接口,开发人员可以方便地集成各种向量存储实现,结合文档处理和嵌入功能,构建智能信息检索和知识问答系统。向量存储是AI应用中的关键组件,Spring AI简化了其使用流程,使开发人员能够专注于业务功能而非底层实现细节。