此版本仍在开发中,尚未被视为稳定版本。最新的快照版本请使用 Spring AI 1.0.0-SNAPSHOT!spring-doc.cn

矢量数据库

矢量数据库是一种特殊类型的数据库,在 AI 应用程序中起着至关重要的作用。spring-doc.cn

在矢量数据库中,查询不同于传统的关系数据库。 它们执行相似性搜索,而不是完全匹配。 当给定一个向量作为查询时,向量数据库会返回与查询向量“相似”的向量。 有关如何在高级别计算此相似性的更多详细信息,请参阅 向量相似性.spring-doc.cn

矢量数据库用于将数据与 AI 模型集成。 使用它们的第一步是将您的数据加载到矢量数据库中。 然后,当要将用户查询发送到 AI 模型时,首先检索一组类似的文档。 然后,这些文档用作用户问题的上下文,并与用户的查询一起发送到 AI 模型。 这种技术被称为检索增强生成 (RAG)。spring-doc.cn

以下部分描述了使用多个矢量数据库实现的 Spring AI 接口和一些高级示例用法。spring-doc.cn

最后一部分旨在揭开向量数据库中相似性搜索的基本方法的神秘面纱。spring-doc.cn

API 概述

本节作为 Spring AI 框架中接口及其关联类的指南。VectorStorespring-doc.cn

Spring AI 提供了一个抽象的 API,用于通过接口与矢量数据库进行交互。VectorStorespring-doc.cn

以下是接口定义:VectorStorespring-doc.cn

public interface VectorStore {

    void add(List<Document> documents);

    Optional<Boolean> delete(List<String> idList);

    List<Document> similaritySearch(String query);

    List<Document> similaritySearch(SearchRequest request);
}

以及相关的构建器:SearchRequestspring-doc.cn

public class SearchRequest {

	public final String query;
	private int topK = 4;
	private double similarityThreshold = SIMILARITY_THRESHOLD_ALL;
	private Filter.Expression filterExpression;

	public static SearchRequest query(String query) { return new SearchRequest(query); }

	private SearchRequest(String query) { this.query = query; }

	public SearchRequest withTopK(int topK) {...}
	public SearchRequest withSimilarityThreshold(double threshold) {...}
	public SearchRequest withSimilarityThresholdAll() {...}
	public SearchRequest withFilterExpression(Filter.Expression expression) {...}
	public SearchRequest withFilterExpression(String textExpression) {...}

	public String getQuery() {...}
	public int getTopK() {...}
	public double getSimilarityThreshold() {...}
	public Filter.Expression getFilterExpression() {...}
}

要将数据插入向量数据库,请将其封装在对象中。 该类封装数据源(如 PDF 或 Word 文档)中的内容,并包含表示为字符串的文本。 它还包含键值对形式的元数据,包括文件名等详细信息。DocumentDocumentspring-doc.cn

插入向量数据库后,文本内容将使用嵌入模型转换为数字数组或 ,称为向量嵌入。嵌入模型(如 Word2VecGLoVEBERT 或 OpenAI )用于将单词、句子或段落转换为这些向量嵌入。float[]text-embedding-ada-002spring-doc.cn

向量数据库的作用是存储和促进这些嵌入的相似性搜索。它本身不会生成嵌入向量。对于创建向量嵌入,应使用 。EmbeddingModelspring-doc.cn

接口中的方法允许检索类似于给定查询字符串的文档。可以使用以下参数对这些方法进行微调:similaritySearchspring-doc.cn

  • k:一个整数,指定要返回的相似文档的最大数量。这通常被称为 “top K” 搜索或 “K 最近邻” (KNN)。spring-doc.cn

  • threshold:介于 0 到 1 之间的双精度值,其中接近 1 的值表示较高的相似度。默认情况下,例如,如果将阈值设置为 0.75,则仅返回相似度高于此值的文档。spring-doc.cn

  • Filter.Expression:用于传递流畅的 DSL(域特定语言)表达式的类,其功能类似于 SQL 中的“where”子句,但它仅适用于 .Documentspring-doc.cn

  • filterExpression:基于 ANTLR4 的外部 DSL,接受筛选表达式作为字符串。例如,对于 country、year 和 等元数据键,您可以使用如下表达式:isActivecountry == 'UK' && year >= 2020 && isActive == true.spring-doc.cn

元数据过滤器 部分。Filter.Expressionspring-doc.cn

Schema 初始化

某些 vector store 要求在使用前初始化其后端 schema。 默认情况下,它不会为您初始化。 您必须通过传递适当的构造函数参数来选择加入,或者如果使用 Spring Boot,则将适当的属性设置为 in 或 。 有关特定属性名称,请查看您正在使用的矢量存储的文档。booleaninitialize-schematrueapplication.propertiesapplication.ymlspring-doc.cn

分批处理策略

使用矢量存储时,通常需要嵌入大量文档。 虽然一次调用嵌入所有文档似乎很简单,但这种方法可能会导致问题。 嵌入模型将文本作为标记处理,并且具有最大标记限制,通常称为上下文窗口大小。 此限制限制了可在单个嵌入请求中处理的文本量。 尝试在一次调用中嵌入过多的标记可能会导致错误或嵌入被截断。spring-doc.cn

为了解决此令牌限制,Spring AI 实现了批处理策略。 这种方法将大型文档集分解为适合嵌入模型的最大上下文窗口的较小批次。 批处理不仅可以解决令牌限制问题,还可以提高性能并更有效地使用 API 速率限制。spring-doc.cn

Spring AI 通过接口提供此功能,该接口允许根据令牌计数在子批处理中处理文档。BatchingStrategyspring-doc.cn

核心接口定义如下:BatchingStrategyspring-doc.cn

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

此接口定义了一个方法 ,该方法采用文档列表并返回文档批次列表。batchspring-doc.cn

默认实现

Spring AI 提供了一个名为 的默认实现。 此策略根据文档的令牌计数对文档进行批处理,确保每个批处理不超过计算的最大输入令牌计数。TokenCountBatchingStrategyspring-doc.cn

主要特点 :TokenCountBatchingStrategyspring-doc.cn

  1. 使用 OpenAI 的最大输入令牌计数 (8191) 作为默认上限。spring-doc.cn

  2. 合并预留百分比(默认为 10%),以便为潜在开销提供缓冲。spring-doc.cn

  3. 计算实际的最大输入令牌计数,如下所示:actualMaxInputTokenCount = originalMaxInputTokenCount * (1 - RESERVE_PERCENTAGE)spring-doc.cn

该策略估计每个文档的令牌计数,在不超过最大输入令牌计数的情况下将它们分组为批次,如果单个文档超过此限制,则引发异常。spring-doc.cn

您还可以自定义 以更好地满足您的特定要求。这可以通过在 Spring Boot 类中创建具有自定义参数的新实例来完成。TokenCountBatchingStrategy@Configurationspring-doc.cn

下面是如何创建自定义 bean 的示例:TokenCountBatchingStrategyspring-doc.cn

@Configuration
public class EmbeddingConfig {
    @Bean
    public BatchingStrategy customTokenCountBatchingStrategy() {
        return new TokenCountBatchingStrategy(
            EncodingType.CL100K_BASE,  // Specify the encoding type
            8000,                      // Set the maximum input token count
            0.1                        // Set the reserve percentage
        );
    }
}

在此配置中:spring-doc.cn

  1. EncodingType.CL100K_BASE:指定用于分词的编码类型。使用此编码类型来准确估计令牌计数。JTokkitTokenCountEstimatorspring-doc.cn

  2. 8000:设置最大输入令牌计数。此值应小于或等于嵌入模型的最大上下文窗口大小。spring-doc.cn

  3. 0.1:设置预留百分比。从最大输入令牌计数中要预留的令牌的百分比。这会为处理过程中可能增加的令牌计数创建缓冲区。spring-doc.cn

默认情况下,此构造函数用于内容格式设置和元数据处理。如果需要自定义这些参数,可以将 full 构造函数与其他参数一起使用。Document.DEFAULT_CONTENT_FORMATTERMetadataMode.NONEspring-doc.cn

定义后,此应用程序中的实现将自动使用此自定义 bean,从而替换默认策略。TokenCountBatchingStrategyEmbeddingModelspring-doc.cn

内部使用 a (具体来说 ) 来计算令牌计数以实现高效批处理。这可确保根据指定的编码类型进行准确的令牌估计。TokenCountBatchingStrategyTokenCountEstimatorJTokkitTokenCountEstimatorspring-doc.cn

此外,还允许您传入自己的接口实现,从而提供灵活性。此功能使您能够使用根据您的特定需求量身定制的自定义代币计数策略。例如:TokenCountBatchingStrategyTokenCountEstimatorspring-doc.cn

TokenCountEstimator customEstimator = new YourCustomTokenCountEstimator();
TokenCountBatchingStrategy strategy = new TokenCountBatchingStrategy(
		this.customEstimator,
    8000,  // maxInputTokenCount
    0.1,   // reservePercentage
    Document.DEFAULT_CONTENT_FORMATTER,
    MetadataMode.NONE
);

自定义实现

虽然提供了强大的默认实现,但您可以自定义批处理策略以满足您的特定需求。 这可以通过 Spring Boot 的自动配置来完成。TokenCountBatchingStrategyspring-doc.cn

要自定义批处理策略,请在 Spring Boot 应用程序中定义一个 bean:BatchingStrategyspring-doc.cn

@Configuration
public class EmbeddingConfig {
    @Bean
    public BatchingStrategy customBatchingStrategy() {
        return new CustomBatchingStrategy();
    }
}

然后,此应用程序中的实现将自动使用此自定义。BatchingStrategyEmbeddingModelspring-doc.cn

Spring AI 支持的向量存储配置为使用默认的 . SAP Hana 矢量存储当前未配置为批处理。TokenCountBatchingStrategy

VectorStore 实现

以下是该接口的可用实现:VectorStorespring-doc.cn

未来版本可能支持更多实施。spring-doc.cn

如果你有一个需要 Spring AI 支持的向量数据库,请在 GitHub 上打开一个问题,或者更好的是,提交一个带有实现的拉取请求。spring-doc.cn

有关每种实现的信息,请参阅本章的小节。VectorStorespring-doc.cn

示例用法

要计算向量数据库的嵌入向量,您需要选择与正在使用的更高级别 AI 模型匹配的嵌入模型。spring-doc.cn

例如,对于 OpenAI 的 ChatGPT,我们使用 和 一个名为 .OpenAiEmbeddingModeltext-embedding-ada-002spring-doc.cn

Spring Boot Starters对 OpenAI 的自动配置使 的实现在 Spring 应用程序上下文中可用于依赖项注入。EmbeddingModelspring-doc.cn

将数据加载到向量存储中的一般用法是你在类似批处理的工作中做的事情,首先将数据加载到 Spring AI 的类中,然后调用该方法。Documentsavespring-doc.cn

给定对源文件的引用,该源文件表示 JSON 文件,其中包含我们要加载到矢量数据库中的数据,我们使用 Spring AI 加载 JSON 中的特定字段,该字段将它们拆分为小块,然后将这些小块传递给矢量存储实现。 该实现计算嵌入向量并将 JSON 和嵌入向量存储在向量数据库中:StringJsonReaderVectorStorespring-doc.cn

  @Autowired
  VectorStore vectorStore;

  void load(String sourceFile) {
            JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
                    "price", "name", "shortDescription", "description", "tags");
            List<Document> documents = jsonReader.get();
            this.vectorStore.add(documents);
  }

稍后,当用户问题传递到 AI 模型时,将执行相似性搜索以检索相似文档,然后将这些文档作为用户问题的上下文“塞入”提示中。spring-doc.cn

   String question = <question from user>
   List<Document> similarDocuments = store.similaritySearch(this.question);

可以将其他选项传递到方法中,以定义要检索的文档数和相似性搜索的阈值。similaritySearchspring-doc.cn

元数据过滤器

本节介绍可用于查询结果的各种筛选条件。spring-doc.cn

Filter String

您可以将类似 SQL 的筛选条件表达式作为 a 传递给其中一个重载。StringsimilaritySearchspring-doc.cn

请考虑以下示例:spring-doc.cn

Filter.Expression (筛选.表达式)

您可以创建一个 的实例 with 公开 Fluent API。 一个简单的示例如下:Filter.ExpressionFilterExpressionBuilderspring-doc.cn

FilterExpressionBuilder b = new FilterExpressionBuilder();
Expression expression = this.b.eq("country", "BG").build();

您可以使用以下运算符构建复杂的表达式:spring-doc.cn

EQUALS: '=='
MINUS : '-'
PLUS: '+'
GT: '>'
GE: '>='
LT: '<'
LE: '<='
NE: '!='

您可以使用以下运算符组合表达式:spring-doc.cn

AND: 'AND' | 'and' | '&&';
OR: 'OR' | 'or' | '||';

考虑以下示例:spring-doc.cn

Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();

您还可以使用以下运算符:spring-doc.cn

IN: 'IN' | 'in';
NIN: 'NIN' | 'nin';
NOT: 'NOT' | 'not';

请考虑以下示例:spring-doc.cn

Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();