介绍倒数排名融合和混合搜索等技术如何改进 RAG 系统的检索精度。程序员可直接应用于优化智能应用的知识库检索能力。
在 Assembled,我们的工单解决引擎旨在协助客户支持团队,为支持请求推荐可能的答案。这个 pipeline 的大部分环节都采用了 Retrieval Augmented Generation(RAG),因为与 fine-tuning 相比,RAG 的迭代速度更快,也不需要使用客户数据进行训练(许多公司更偏好这一点),而且通常能提供高质量的结果。
然而,我们在使用 RAG 时遇到了一个重大挑战:仅依赖向量搜索,即使同时使用稠密向量和稀疏向量,也不一定能为某些查询提供令人满意的结果。当用户输入特定关键词,而这些关键词又无法准确匹配已有的知识库文章时,这个问题尤其明显。
客户支持团队通常会有多篇主题相近的文章,同时又缺少经过严格整理的知识库。这会导致向量搜索有时向我们的 RAG 引擎返回不相关的结果,降低回答的准确性。习惯传统关键词搜索的用户也会感到困惑:为什么我们的系统找不到正确的文档?对于那些包含醒目但含义模糊的关键词的短查询,这个问题尤为突出。
例如,如果用户询问“premium plan 包含哪些功能?”,向量搜索非常擅长找出介绍不同套餐、客户证言或营销材料的文档。然而,它往往无法找到专门介绍 premium plan 的文章。
为了解决这个问题,我们集成了一套新的关键词搜索基础设施,将关键词搜索结果与向量搜索结果结合起来,以获得最佳效果。在上面的例子中,关键词搜索会聚焦于“features”“premium”和“plan”,将搜索结果收窄到明确匹配这些关键词的文档。
同时使用向量搜索和关键词搜索的 Hybrid Search,既能有效返回语义匹配的文章,也能为用户提供熟悉的传统关键词搜索体验。我们之所以形成这一判断,是因为根据我们在其他机器学习系统中的经验,ensemble model 通常比单一模型表现更好。
为了支持混合存储方案,我们在代码中开发了一层 Document Store 抽象,从而能够集成多种搜索算法。这层抽象很简单,但涵盖了 Document Store 和搜索系统所需的全部核心功能。它负责文档管理和查询,同时不关心底层的具体实现方式,例如向量搜索、关键词搜索等。它看起来是这样的:
借助这层抽象,我们获得了轻松替换不同搜索系统所需的基础能力。一份文档只需上传一次,之后便可以同步到多个 Document Store。同样,我们也可以使用标准化查询,并行搜索多个 Document Store 中的文档。
有意思的是,我们的 Hybrid Search Store 本身也实现了 DocumentStore interface。这意味着,从调用方的角度来看,它交互的对象究竟是单个 Store,还是我们复杂的 Hybrid Store,并没有区别——两者使用的是同一套 interface 和方法。
这种设计可以把所有决定检索哪些文档的逻辑都隐藏在调用方之外,并允许我们单独测试这些逻辑。为了实现 Hybrid Store,我们向其中传入了多个子 Document Store,并将搜索任务并行分发到所有子 Store。
启用多个 Document Store 也带来了技术挑战,尤其是如何确保它们保持同步。不同步的 Document Store 可能引发一些隐蔽的 bug,例如某份文档存在于一个 Store 中,却不存在于另一个 Store 中。为了解决这个问题,我们采取了以下措施:
**Single source of truth:**我们在 PostgreSQL 中维护文档元数据,并在 S3 中保存文档本身,将二者作为 single source of truth。这个 Store 实现了文档存储 interface,但不会参与查询。它只用于保存记录,以便我们在必要时重新同步内容。
**异步更新:**由于存储文章的延迟较高,我们首先更新数据库中的 source of truth,并向 frontend 返回确认。随后,再以异步方式将文档保存到各个子 Store 中。这种方式有助于控制多个 Store 带来的延迟,并确保我们的 Document Store 最终达到一致状态。
**错误处理:**我们还需要处理不同平台上发生的错误。例如,一个 Store 可能遭遇网络中断,而另一个 Store 已成功完成存储过程。我们的 PostgreSQL 数据库会跟踪每个 Store 的同步状态。如果某个 Store 同步失败,我们会采用指数退避策略重试操作,确保所有 Store 最终都能恢复同步。
为了优化搜索性能,我们研究了多种用于合并不同 Document Store 搜索结果的算法。
我们最初尝试为稀疏向量、稠密向量和关键词搜索设计不同的加权机制,目标是找到一组最优权重,从而充分利用每种搜索方法的优势。然而,由于向量搜索分数的分布未知,寻找正确的权重非常困难。这种不确定性让我们很难判断不同权重之间的相对重要性。
此外,经验数据表明,在我们的不同客户中,相似度分数——包括点积和欧氏距离——存在很大差异。不同指标之间的性能差异,使我们无法设计出一套通用的权重方案来合并向量搜索和关键词搜索。针对每位客户分别调整这些权重,也不具备可扩展性。
接下来,在相关文献综述及其展现出的搜索优化效果启发下,我们开始研究 rank fusion 算法(参见 [0] 和 [1])。Rank fusion 算法,尤其是 Reciprocal Rank Fusion(RRF),提供了一个很有前景的替代方案。大多数 rank fusion 算法的工作方式如下:
**分配排名分数:**根据每份文档在各个排序列表中的排名位置,为其分配一个分数。通常,这个分数是排名的倒数,即 1/rank。例如,排名第一的文档得分为 1,排名第二的得分为 0.5,排名第三的得分为 0.33,以此类推。
**分数求和:**针对每份文档,将它在所有排序列表中的分数相加。出现在多个列表中的文档会累积得到更高的综合分数。
**最终排序:**根据综合分数对文档重新排序,生成一个融合了所有独立搜索引擎排名的最终排序列表。
经过大量测试,Reciprocal Rank Fusion(RRF)的表现始终优于我们评估过的许多更复杂的方法。促成这一结果的因素有多个:
**简单且稳健:**RRF 的简单性使其不容易针对特定场景出现过拟合,这也符合奥卡姆剃刀原则。正是这种简单性,增强了它在不同场景下的稳健性。
**几乎不需要调参:**RRF 提供了一种直接且有效的结果排序方式,不需要进行大量参数调优。考虑到我们的客户群体非常多样,使用的知识库也各不相同,这一点尤其有优势。
通过实现 RRF,我们获得了一种灵活且可扩展的搜索结果合并方法。使用 RRF 不仅提高了搜索结果的准确性和相关性,也简化了整体搜索基础设施,从而为多样化的客户群体提供了一套稳健的解决方案。
最后,再补充说明一下我们对搜索引擎的选择。在 Assembled,我们使用 Pinecone 进行向量搜索,使用 Algolia 进行关键词搜索。在对其他服务商进行少量测试之后,我们认为继续优化带来的边际收益并不显著。因此,我们决定不自行托管 Milvus 等开源向量数据库,也不在 Elasticsearch 上自行管理关键词搜索。
使用 Pinecone 和 Algolia 这样的 B2B 解决方案有以下几个优势:
**成本效益:**这些服务的成本相当合理,尤其是与工程师时间成本相比,而且无需在基础设施上进行大量前期投入。
**减少维护工作:**和大多数公司一样,我们的工程资源也很有限。通过把搜索能力交给专业的搜索服务公司,我们可以避免自托管方案带来的沉重维护负担。这让团队能够专注于我们的核心功能:针对客户支持问题生成 AI 回复。
**性能:**尤其是 Algolia,它能够提供低延迟响应、稳健的 API,以及经过高度优化的搜索结果。这些能力很可能优于我们自行基于 Elasticsearch 构建的任何方案。
我们已经看到了一些令人振奋的结果,但仍有大量工作要做。自从我们实现这套框架以来,基于 RAG 的技术又取得了许多进展,例如对 embedding model 进行 fine-tuning、对向量结果应用矩阵变换,以及 HyDE 等。如果你有兴趣帮助我们解决这些问题,可以查看我们正在招聘的职位。