矢量数据库
矢量数据库是一种特殊类型的数据库,在 AI 应用程序中起着至关重要的作用。
在矢量数据库中,查询不同于传统的关系数据库。 它们执行相似性搜索,而不是完全匹配。 当给定一个向量作为查询时,向量数据库会返回与查询向量“相似”的向量。 有关如何在高级别计算此相似性的更多详细信息,请参阅 向量相似性.
矢量数据库用于将数据与 AI 模型集成。 使用它们的第一步是将您的数据加载到矢量数据库中。 然后,当要将用户查询发送到 AI 模型时,首先检索一组类似的文档。 然后,这些文档用作用户问题的上下文,并与用户的查询一起发送到 AI 模型。 这种技术被称为检索增强生成 (RAG)。
以下部分描述了使用多个矢量数据库实现的 Spring AI 接口和一些高级示例用法。
最后一部分旨在揭开向量数据库中相似性搜索的基本方法的神秘面纱。
API 概述
本节将作为VectorStore
接口及其关联的类。
Spring AI 提供了一个抽象的 API,用于通过VectorStore
接口。
这是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 文档)的内容,并包含表示为字符串的文本。
它还包含键值对形式的元数据,包括文件名等详细信息。
插入向量数据库后,文本内容将转换为数字数组或float[]
,称为向量嵌入,使用嵌入模型。嵌入模型,例如 Word2Vec、GLoVE 和 BERT,或 OpenAI 的text-embedding-ada-002
用于将单词、句子或段落转换为这些向量嵌入。
向量数据库的作用是存储和促进这些嵌入的相似性搜索。它本身不会生成嵌入向量。为了创建向量嵌入,EmbeddingModel
应该利用。
这similaritySearch
方法允许检索类似于给定查询字符串的文档。可以使用以下参数对这些方法进行微调:
-
k
:一个整数,指定要返回的相似文档的最大数量。这通常被称为 “top K” 搜索或 “K 最近邻” (KNN)。 -
threshold
:范围从 0 到 1 的双精度值,其中值越接近 1 表示相似度越高。默认情况下,例如,如果将阈值设置为 0.75,则仅返回相似度高于此值的文档。 -
Filter.Expression
:用于传递流畅的 DSL(域特定语言)表达式的类,其功能类似于 SQL 中的“where”子句,但它仅适用于Document
. -
filterExpression
:基于 ANTLR4 的外部 DSL,接受筛选表达式作为字符串。例如,使用 country、year 和isActive
,您可以使用如下表达式:country == 'UK' && year >= 2020 && isActive == true.
查找更多信息Filter.Expression
在 Metadata Filters 部分。
Schema 初始化
某些 vector store 要求在使用前初始化其后端 schema。
默认情况下,它不会为您初始化。
您必须通过传递boolean
对于适当的构造函数参数,或者,如果使用 Spring Boot,则设置适当的initialize-schema
property 设置为true
在application.properties
或application.yml
.
有关特定属性名称,请查看您正在使用的矢量存储的文档。
分批处理策略
使用矢量存储时,通常需要嵌入大量文档。 虽然一次调用嵌入所有文档似乎很简单,但这种方法可能会导致问题。 嵌入模型将文本作为标记处理,并且具有最大标记限制,通常称为上下文窗口大小。 此限制限制了可在单个嵌入请求中处理的文本量。 尝试在一次调用中嵌入过多的标记可能会导致错误或嵌入被截断。
为了解决此令牌限制,Spring AI 实现了批处理策略。 这种方法将大型文档集分解为适合嵌入模型的最大上下文窗口的较小批次。 批处理不仅可以解决令牌限制问题,还可以提高性能并更有效地使用 API 速率限制。
Spring AI 通过BatchingStrategy
接口,该接口允许根据文档的令牌计数在子批处理中处理文档。
核心BatchingStrategy
接口定义如下:
public interface BatchingStrategy {
List<List<Document>> batch(List<Document> documents);
}
此接口定义了一个方法batch
,它采用文档列表并返回文档批次列表。
默认实现
Spring AI 提供了一个名为TokenCountBatchingStrategy
.
此策略根据文档的令牌计数对文档进行批处理,确保每个批处理不超过计算的最大输入令牌计数。
主要特点TokenCountBatchingStrategy
:
-
使用 OpenAI 的最大输入令牌计数 (8191) 作为默认上限。
-
合并预留百分比(默认为 10%),以便为潜在开销提供缓冲。
-
计算实际的最大输入令牌计数,如下所示:
actualMaxInputTokenCount = originalMaxInputTokenCount * (1 - RESERVE_PERCENTAGE)
该策略估计每个文档的令牌计数,在不超过最大输入令牌计数的情况下将它们分组为批次,如果单个文档超过此限制,则引发异常。
您还可以自定义TokenCountBatchingStrategy
以更好地满足您的特定要求。这可以通过在 Spring Boot 中创建具有自定义参数的新实例来完成@Configuration
类。
以下是如何创建自定义的示例TokenCountBatchingStrategy
豆:
@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
);
}
}
在此配置中:
-
EncodingType.CL100K_BASE
:指定用于分词的编码类型。此编码类型由JTokkitTokenCountEstimator
准确估计令牌计数。 -
8000
:设置最大输入令牌计数。此值应小于或等于嵌入模型的最大上下文窗口大小。 -
0.1
:设置预留百分比。从最大输入令牌计数中要预留的令牌的百分比。这会为处理过程中可能增加的令牌计数创建缓冲区。
默认情况下,此构造函数使用Document.DEFAULT_CONTENT_FORMATTER
用于内容格式设置和MetadataMode.NONE
用于元数据处理。如果需要自定义这些参数,可以将 full 构造函数与其他参数一起使用。
定义后,此自定义TokenCountBatchingStrategy
bean 将自动被EmbeddingModel
implementations 替换默认策略。
这TokenCountBatchingStrategy
内部使用TokenCountEstimator
(具体来说,JTokkitTokenCountEstimator
) 计算令牌计数以实现高效批处理。这可确保根据指定的编码类型进行准确的令牌估计。
此外TokenCountBatchingStrategy
通过允许您传入自己的TokenCountEstimator
接口。此功能使您能够使用根据您的特定需求量身定制的自定义代币计数策略。例如:
TokenCountEstimator customEstimator = new YourCustomTokenCountEstimator();
TokenCountBatchingStrategy strategy = new TokenCountBatchingStrategy(
this.customEstimator,
8000, // maxInputTokenCount
0.1, // reservePercentage
Document.DEFAULT_CONTENT_FORMATTER,
MetadataMode.NONE
);
自定义实现
而TokenCountBatchingStrategy
提供了强大的默认实现,您可以自定义 Batching 策略以满足您的特定需求。
这可以通过 Spring Boot 的自动配置来完成。
要自定义批处理策略,请定义BatchingStrategy
bean 在 Spring Boot 应用程序中:
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customBatchingStrategy() {
return new CustomBatchingStrategy();
}
}
此自定义BatchingStrategy
将由EmbeddingModel
实现。
Spring AI 支持的向量存储配置为使用默认的TokenCountBatchingStrategy .
SAP Hana 矢量存储当前未配置为批处理。 |
VectorStore 实现
以下是VectorStore
接口:
-
Azure 矢量搜索 - Azure 矢量存储。
-
Apache Cassandra - Apache Cassandra 矢量存储。
-
Chroma Vector Store (Chroma矢量存储) - Chroma 矢量存储。
-
Elasticsearch 矢量存储 - Elasticsearch 矢量存储。
-
GemFire 矢量存储 - GemFire 矢量存储。
-
MariaDB 矢量存储 - MariaDB 矢量存储。
-
Milvus Vector Store - Milvus 矢量存储。
-
MongoDB Atlas 矢量存储 - MongoDB Atlas 矢量存储。
-
Neo4j 矢量存储区 - Neo4j 矢量存储区。
-
OpenSearch 矢量存储 - OpenSearch 矢量存储。
-
Oracle 矢量存储 - Oracle Database 矢量存储。
-
PgVector Store - PostgreSQL/PGVector 矢量存储。
-
Pinecone Vector Store - PineCone 矢量存储。
-
Qdrant 向量存储 - Qdrant 向量存储。
-
Redis 矢量存储 - Redis 矢量存储。
-
SAP Hana 矢量存储 - SAP HANA 矢量存储。
-
Typesense Vector Store - Typesense 矢量存储。
-
Weaviate 矢量存储 - Weaviate 矢量存储。
-
SimpleVectorStore - 持久向量存储的简单实现,适用于教育目的。
未来版本可能支持更多实施。
如果你有一个需要 Spring AI 支持的向量数据库,请在 GitHub 上打开一个问题,或者更好的是,提交一个带有实现的拉取请求。
每个VectorStore
实现可以在本章的小节中找到。
示例用法
要计算向量数据库的嵌入向量,您需要选择与正在使用的更高级别 AI 模型匹配的嵌入模型。
例如,对于 OpenAI 的 ChatGPT,我们使用OpenAiEmbeddingModel
和一个名为text-embedding-ada-002
.
Spring Boot Starters对 OpenAI 的自动配置使EmbeddingModel
在 Spring 应用程序上下文中用于依赖项注入。
将数据加载到向量存储中的一般用法是您在类似批处理的工作中执行的作,首先将数据加载到 Spring AI 的Document
类,然后调用save
方法。
给定一个String
引用一个源文件,该文件表示一个 JSON 文件,其中包含我们要加载到向量数据库中的数据,我们使用 Spring AI 的JsonReader
加载 JSON 中的特定字段,JSON 将它们拆分为小块,然后将这些小块传递给 Vector Store 实现。
这VectorStore
implementation 计算嵌入向量并将 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(this.question);
其他选项可以传递到similaritySearch
定义要检索的文档数和相似性搜索的阈值的方法。
元数据过滤器
本节介绍可用于查询结果的各种筛选条件。
Filter String
您可以将类似 SQL 的筛选条件表达式作为String
复制到similaritySearch
重载。
请考虑以下示例:
-
"country == 'BG'"
-
"genre == 'drama' && year >= 2020"
-
"genre in ['comedy', 'documentary', 'drama']"
Filter.Expression (筛选.表达式)
您可以创建Filter.Expression
替换为FilterExpressionBuilder
公开 Fluent API。
一个简单的示例如下:
FilterExpressionBuilder b = new FilterExpressionBuilder();
Expression expression = this.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();