向量数据库:AI应用的核心技术

什么是向量数据库?AI应用背后的数据枢纽.

什么是向量数据库?AI应用背后的数据枢纽

在AI应用开发中,向量数据库(Vector Database)已经成为不可或缺的基础设施。相比传统关系型数据库的精确匹配查询,向量数据库的核心能力是相似性搜索(Similarity Search)——当你输入一个向量作为查询条件时,它返回的是与该向量”最相似”的结果,而不是完全相等的数据。

这种设计非常适合大语言模型(LLM)的场景:我们需要根据用户问题从知识库中检索相关文档,然后将这些文档作为上下文发送给AI模型。这个过程就是RAG(检索增强生成)的核心思路。

工作流程简述

向量数据库的典型使用流程是这样的:先把你的数据(PDF、Word、文本等)加载到向量数据库中;当用户提出问题时,系统先从向量数据库中检索出最相似的文档;最后把这些文档和问题一起发送给AI模型,由模型生成回答。

下面我们来详细看看Spring AI框架中向量数据库的相关API设计。

Spring AI VectorStore API详解

Spring AI提供了抽象的VectorStore接口来统一操作各种向量数据库,同时提供了只读的VectorStoreRetriever接口。

VectorStoreRetriever:只读查询接口

如果你只需要从向量数据库查询数据,不需要写入操作,可以使用VectorStoreRetriever接口。这是一个函数式接口,遵循最小权限原则:

@FunctionalInterface
public interface VectorStoreRetriever {

    List similaritySearch(SearchRequest request);

    default List similaritySearch(String query) {
        return this.similaritySearch(SearchRequest.builder().query(query).build());
    }
}

这种设计很适合那些只需要查询功能的场景,比如只读的文档检索系统。

VectorStore:完整的增删改查

VectorStore接口继承了VectorStoreRetrieverDocumentWriter,同时支持读取和写入操作:

public interface VectorStore extends DocumentWriter, VectorStoreRetriever {

    default String getName() {
        return this.getClass().getSimpleName();
    }

    void add(List documents);

    void delete(List idList);

    void delete(Filter.Expression filterExpression);

    default void delete(String filterExpression) { ... }

    default  Optional getNativeClient() {
        return Optional.empty();
    }
}

通过这个接口,你可以完成以下操作:

  • add: 向向量数据库添加文档
  • delete: 根据ID或过滤条件删除文档
  • similaritySearch: 执行相似性搜索
  • getNativeClient: 获取底层数据库的原生客户端(如果有)

SearchRequest:搜索参数配置

搜索时的各种参数通过SearchRequest的Builder模式来配置:

public class SearchRequest {

    public static final double SIMILARITY_THRESHOLD_ACCEPT_ALL = 0.0;
    public static final int DEFAULT_TOP_K = 4;

    private String query = "";
    private int topK = DEFAULT_TOP_K;
    private double similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
    @Nullable
    private Filter.Expression filterExpression;

    public static class Builder {
        private final SearchRequest searchRequest = new SearchRequest();

        public Builder query(String query) {
            Assert.notNull(query, "Query can not be null.");
            this.searchRequest.query = query;
            return this;
        }

        public Builder topK(int topK) {
            Assert.isTrue(topK >= 0, "TopK should be positive.");
            this.searchRequest.topK = topK;
            return this;
        }

        public Builder similarityThreshold(double threshold) {
            Assert.isTrue(threshold >= 0 && threshold <= 1, 
                "Similarity threshold must be in [0,1] range.");
            this.searchRequest.similarityThreshold = threshold;
            return this;
        }

        public Builder filterExpression(@Nullable Filter.Expression expression) {
            this.searchRequest.filterExpression = expression;
            return this;
        }

        public Builder filterExpression(@Nullable String textExpression) {
            this.searchRequest.filterExpression = (textExpression != null)
                ? new FilterExpressionTextParser().parse(textExpression) : null;
            return this;
        }

        public SearchRequest build() {
            return this.searchRequest;
        }
    }
}

核心参数说明:

  • query: 搜索关键词,会被embedding模型转换为向量
  • topK: 返回最相似的K个结果,默认为4
  • similarityThreshold: 相似度阈值,范围0-1,值越大表示要求越严格
  • filterExpression: 元数据过滤条件,支持类似SQL的语法

Document:数据载体

要往向量数据库插入数据,需要把数据封装成Document对象。Document包含原始文本内容(content)和元数据(metadata,key-value形式):

// 示例:创建Document
Document doc = new Document("这是一段需要向量化的文本内容", 
    Map.of("source", "file.pdf", "author", "张三"));
documentStore.add(List.of(doc));

插入时,文本内容会被Embedding模型转换成向量(float[]数组)。常用的embedding模型包括OpenAI的text-embedding-ada-002、BERT、Word2Vec等。

需要注意的是,向量数据库本身不负责生成向量,它只负责存储向量和执行相似度搜索。生成向量的工作由EmbeddingModel完成。

Schema初始化:别忘了这一步

有些向量数据库需要提前创建索引或schema,默认情况下Spring AI不会自动帮你初始化。需要在构造方法中传入initialize-schema=true参数,或者在application.properties中配置:

# application.properties 示例
spring.ai.vectorstore.pgvector.initialize-schema=true

具体属性名取决于你使用的向量数据库实现(Milvus、Elasticsearch、PostgreSQL等各有不同)。

批处理策略:解决Token数量限制

当你需要一次性向量化大量文档时,不能简单地一次性调用embedding模型——大多数模型都有上下文窗口限制(比如OpenAI的embedding模型最大支持8191个token)。

Spring AI通过BatchingStrategy接口来解决这个问题:

public interface BatchingStrategy {
    List> batch(List documents);
}

默认实现:TokenCountBatchingStrategy

Spring AI提供了默认的TokenCountBatchingStrategy,会根据token数量自动将文档分组:

  • 默认使用OpenAI的8191作为最大token限制
  • 预留10%的buffer防止超出限制
  • 单个文档如果超过限制会抛出异常

自定义批处理策略

如果默认配置不满足需求,可以自定义TokenCountBatchingStrategy

@Configuration
public class EmbeddingConfig {
    @Bean
    public BatchingStrategy customTokenCountBatchingStrategy() {
        return new TokenCountBatchingStrategy(
            EncodingType.CL100K_BASE,  // 分词编码类型
            8000,                      // 最大输入token数
            0.1                        // 预留百分比
        );
    }
}

这里的关键参数:

  • EncodingType: 指定分词编码,CL100K_BASE是OpenAI常用的编码
  • 8000: 最大token数,应该小于模型的上下文窗口大小
  • 0.1: 预留10%的buffer空间

实战建议

在实际项目中使用向量数据库时,有几个注意点:

  1. 选型问题: 如果是中小规模项目,PostgreSQL的pgvector插件足够用了;大规模场景可以考虑Milvus或Weaviate
  2. 索引优化: 大多数向量数据库支持多种索引类型(如HNSW、IVF),选择合适的索引能显著提升查询性能
  3. 过滤条件: 尽量在查询时使用metadata过滤,可以大幅减少向量搜索的范围
  4. 批量写入: 大量数据导入时记得启用批处理策略,避免触发API的rate limit

Spring AI的VectorStore抽象做得不错,基本主流的向量数据库都有对应的实现,切换成本不高。如果你在做AI应用开发这块,可以先从简单的pgvector开始上手。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注