向量搜索
随着生成式 AI 的兴起,向量数据库在数据库领域获得了强劲的发展势头。这类数据库能够高效地存储和查询高维向量,使其非常适合语义搜索、推荐系统和自然语言理解等任务。
向量搜索是一种通过比较向量表示(也称为嵌入)来检索语义相似数据的技术,而不是依赖传统的精确匹配查询。这种方式能够构建出超越基于关键词检索的、智能且具备上下文感知能力的应用程序。
在 Spring Data 的语境下,向量搜索为构建智能且具备上下文感知能力的应用程序开辟了新的可能性,尤其在自然语言处理、推荐系统和生成式 AI 等领域。通过使用大家熟悉的仓库抽象来对基于向量的查询进行建模,Spring Data 让开发人员能够以 Spring Data 编程模型所特有的简洁性与一致性,无缝地集成支持相似度、具备向量能力的数据库。
向量模型
为了以类型安全且符合惯用法的方式支持向量搜索,Spring Data 引入了以下核心抽象:
Vector
Vector 类型表示一个 n 维数值嵌入,通常由嵌入模型生成。在 Spring Data 中,它被定义为对一个浮点数数组的轻量封装,从而确保不可变性与一致性。该类型既可以用作搜索查询的输入,也可以作为领域实体上的一个属性,用于存储相关联的向量表示。
Vector vector = Vector.of(0.23f, 0.11f, 0.77f);
在领域模型中使用 Vector,您就无需再处理原始的数组或数字列表,从而以一种类型安全且更具表达力的方式来处理向量数据。这一抽象也便于与各种向量数据库和库进行集成。它还允许实现特定于厂商的优化,例如无法映射为标准浮点(IEEE 754 所定义的 float 和 double)表示的二进制向量或量化向量。一个领域对象可以拥有一个向量属性,该属性可用于相似性搜索。如下例所示:
将向量与领域对象相关联,会导致该向量作为实体生命周期的一部分被加载和存储,这可能会在检索与持久化操作上引入额外的开销。
搜索结果
SearchResult<T> 类型封装了一次向量相似性查询的结果。它既包含匹配到的领域对象,也包含一个相关度评分,用以表示该对象与查询向量的匹配程度。这一抽象为处理结果排序提供了一种结构化方式,并让开发人员能够同时便捷地处理数据及其上下文相关度。
在本例中,searchByCountryAndEmbeddingNear 方法返回一个 SearchResults<Comment> 对象,其中包含一组 SearchResult<Comment> 实例。每个结果都包含匹配到的 Comment 实体及其相关度评分。
相关度评分是一个数值,用于表示匹配到的向量与查询向量的对齐程度。根据评分所代表的是距离还是相似度,评分越高既可能表示匹配越接近,也可能表示匹配越远。
用于计算该评分的评分函数可能因底层数据库、索引或输入参数而异。
评分、相似度与评分函数
Score 类型持有一个数值,用于表示搜索结果的相关度。它可用于根据结果与查询向量的相似度对结果进行排序。Score 类型通常是一个浮点数,其含义(越高越好还是越低越好)取决于所使用的具体相似度函数。评分是向量搜索的副产品,并非成功执行搜索操作所必需。评分值不属于领域模型,因此最好作为带外数据来表示。
通常,评分由 ScoringFunction 计算得出。用于计算该评分的实际评分函数可能取决于底层数据库,并可从搜索索引或输入参数中获取。
Spring Data 为常用的函数声明了如下常量:
欧氏距离
计算 n 维空间中的直线距离,涉及平方差之和的平方根。
余弦相似度
先计算点积,再将其结果除以两个向量长度的乘积进行归一化,以此衡量两个向量之间的夹角。
点积
计算逐元素相乘之和。
相似度函数的选择会同时影响搜索的性能与语义,并且通常由所使用的底层数据库或索引决定。Spring Data 会适配数据库原生的评分函数能力,以及评分是否可用于限制结果。
向量搜索方法
向量搜索方法在仓库中使用与标准 Spring Data 查询方法相同的约定来定义。这些方法返回 SearchResults<T>,并要求提供一个 Vector 参数来定义查询向量。具体实现取决于底层数据存储的内部机制及其在向量搜索方面的能力。
如果您刚接触 Spring Data 仓库,请务必先熟悉仓库定义与查询方法的基础知识。
通常,您可以通过两种方式来声明搜索方法:
-
查询派生
-
声明基于字符串的查询
向量搜索方法必须声明一个 Vector 参数来定义查询向量。
派生搜索方法
派生搜索方法利用方法名来派生出查询。在声明搜索方法时,向量搜索支持以下关键词来执行向量搜索:
表 1. 查询谓词关键词
| 逻辑关键词 | 关键词表达式 |
|---|---|
NEAR | Near, IsNear |
WITHIN | Within, IsWithin |
派生搜索方法通常更易于阅读和维护,因为它们依靠方法名来表达查询意图。但是,派生搜索方法要求在 Near/Within 关键词的第二个参数上声明一个 Score、Range<Score> 或 ScoreFunction,以便按评分限制搜索结果。
注解搜索方法
注解方法可以完全控制查询的语义与参数。与派生方法不同,它们不依赖方法名约定。
由于对实际查询有了更多控制权,Spring Data 可以对查询及其参数做出更少的假设。例如,Similarity 归一化会在查询内部利用原生评分函数,将给定的相似度归一化为评分谓词值,反之亦然。如果一个注解查询没有定义评分(例如),那么返回的 SearchResult<T> 中的评分值将为零。
排序
默认情况下,搜索结果按其评分排序。您可以使用 Sort 参数来覆盖排序方式:
示例 1. 在仓库搜索方法中使用 Sort
interface CommentRepository extends Repository<Comment, String> {
SearchResults<Comment> searchByEmbeddingNearOrderByCountry(Vector vector, Score score);
SearchResults<Comment> searchByEmbeddingWithin(Vector vector, Score score, Sort sort);
}
请注意,自定义排序不允许将评分表达为排序条件。您只能引用领域属性。