向量数据库

向量数据库是一种专门的数据库类型,在 AI 应用程序中发挥着至关重要的作用。

在向量数据库中,查询与传统关系数据库不同。它们不执行精确匹配,而是执行相似性搜索。当将向量作为查询提供时,向量数据库会返回与查询向量“相似”的向量。有关如何在高层次计算此相似性的更多详细信息,请参阅 向量相似性

向量数据库用于将数据与 AI 模型集成。使用它们的第一个步骤是将数据加载到向量数据库中。然后,当要将用户查询发送到 AI 模型时,首先会检索一组类似的文档。然后,这些文档作为用户问题语境的依据,与用户的查询一起发送到 AI 模型。此技术被称为 检索增强生成 (RAG)

以下部分介绍了 Spring AI 接口,用于使用多个向量数据库实现以及一些高级示例用法。

最后一部分旨在揭开向量数据库中相似性搜索的底层方法。

API 概述

本节作为 Spring AI 框架中 `VectorStore` 接口及其相关类的指南。

Spring AI 通过 `VectorStore` 接口提供了一个抽象的 API,用于与向量数据库进行交互。

以下是 `VectorStore` 接口定义

public interface VectorStore {

    void add(List<Document> documents);

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

    List<Document> similaritySearch(String query);

    List<Document> similaritySearch(SearchRequest request);
}

以及相关的 `SearchRequest` 构建器

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() {...}
}

要将数据插入向量数据库,请将其封装在 `Document` 对象中。`Document` 类封装了来自数据源的内容,例如 PDF 或 Word 文档,并包含表示为字符串的文本。它还包含键值对形式的元数据,包括文件名等详细信息。

插入向量数据库后,文本内容将使用嵌入模型转换为数值数组或 `List<Double>`,称为向量嵌入。嵌入模型,例如 Word2VecGLoVEBERT 或 OpenAI 的 `text-embedding-ada-002`,用于将单词、句子或段落转换为这些向量嵌入。

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

接口中的 `similaritySearch` 方法允许检索与给定查询字符串相似的文档。可以通过使用以下参数对这些方法进行微调

  • k:指定要返回的相似文档的最大数量的整数。这通常称为“前 K 个”搜索或“K 个最近邻”(KNN)。

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

  • Filter.Expression:用于传递类似于 SQL 中“where”子句的流畅 DSL(特定领域语言)表达式的类,但它仅适用于 `Document` 的元数据键值对。

  • filterExpression:基于 ANTLR4 的外部 DSL,它接受作为字符串的过滤器表达式。例如,对于国家/地区、年份和 isActive 等元数据键,可以使用如下表达式:country == 'UK' && year >= 2020 && isActive == true.

元数据过滤器 部分中查找有关 Filter.Expression 的更多信息。

模式初始化

某些向量存储需要在使用前初始化其后端模式。默认情况下,不会为您初始化。您必须选择加入,方法是为适当的构造函数参数传递 boolean,或者如果使用 Spring Boot,将 application.propertiesapplication.yml 中的适当 initialize-schema 属性设置为 true。查看正在使用的向量存储的文档,了解特定属性名称。

可用实现

以下是 VectorStore 接口的可用实现

未来版本中可能会支持更多实现。

如果您有需要 Spring AI 支持的向量数据库,请在 GitHub 上提交问题,或者更好的是,提交一个包含实现的拉取请求。

可以在本章的小节中找到每个 VectorStore 实现的信息。

示例用法

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

例如,对于 OpenAI 的 ChatGPT,我们使用 OpenAiEmbeddingModel 和名为 text-embedding-ada-002 的模型。

OpenAI 的 Spring Boot 启动器的自动配置为依赖项注入在 Spring 应用程序上下文中提供了 EmbeddingModel 的实现。

将数据加载到向量存储中的常规用法是您在批处理作业中执行的操作,首先将数据加载到 Spring AI 的 Document 类中,然后调用 save 方法。

给定一个表示要加载到向量数据库中的数据的 JSON 文件的源文件的 String 引用,我们使用 Spring AI 的 JsonReader 加载 JSON 中的特定字段,将其分成小块,然后将这些小块传递给向量存储实现。VectorStore 实现计算嵌入并将 JSON 和嵌入存储在向量数据库中

  @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 模型时,会执行相似性搜索以检索类似的文档,然后将这些文档“填充”到提示中作为用户问题的上下文。

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

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

元数据过滤器

本节介绍您可以针对查询结果使用的各种过滤器。

过滤字符串

您可以将类似 SQL 的过滤表达式作为 String 传递给 similaritySearch 重载之一。

考虑以下示例

  • "country == 'BG'"

  • "genre == 'drama' && year >= 2020"

  • "genre in ['comedy', 'documentary', 'drama']"

Filter.Expression

您可以使用公开流畅 API 的 FilterExpressionBuilder 创建 Filter.Expression 实例。一个简单的示例如下所示

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

您可以使用以下运算符构建复杂的表达式

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

您可以使用以下运算符组合表达式

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

考虑以下示例

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

您还可以使用以下运算符

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

考虑以下示例

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

理解向量