第 57 页 / 共 100 页 / 飞书修订版 7

7.A Multi-Index RAG pipeline

此文档由 学习AI的1000天 翻译制作(抖音,B站,YouTube)

邮箱: szqshan@gmail.com | 微信: szqshan
网址: www.xueai.org

==================================================

GFwwbRUKAob0xkx0YaKcMSxqncC.bin

我们已经分别构建了语义搜索(使用向量嵌入)和词汇搜索(使用BM25)的实现。现在是时候将它们结合成一个统一的搜索管道,充分利用两种方法的优势。

多索引架构

我们的VectorIndex和BM25Index类共享几乎相同的API——它们都有add_document()和search()方法。这种一致性使得将它们包装在一个名为Retriever的新类中变得非常简单。

image1.jpeg

Retriever充当协调器,将用户查询转发给两个索引,收集它们的结果,并使用一种称为倒数排名融合的技术将结果合并。

理解倒数排名融合

合并来自不同搜索方法的结果并不像简单地连接列表那么简单。每种方法使用不同的评分系统,因此我们需要一种方法来公平地标准化和组合它们的排名。

image2.jpeg

以下是倒数排名融合的工作原理示例。假设我们搜索关于"INC-2023-Q4-011"的信息,得到以下结果:

  • VectorIndex返回:第2节(排名1),第7节(排名2),第6节(排名3)
  • BM25Index返回:第6节(排名1),第2节(排名2),第7节(排名3)
image3.jpeg

我们将这些结果合并到一个表格中,显示每个文本块在两个索引中的排名,然后应用RRF公式:

RRF_score(d) = Σ(1 / (k + rank_i(d)))

其中 k 是一个常数(通常为 60,但我们使用 1 以获得更清晰的结果),rank_i(d) 是文档 d 在第 i 个排名中的位置。

image4.jpeg

For our example:

  • Section 2: 1.0/(1+1) + 1.0/(1+2) = 0.833
  • Section 7: 1.0/(1+2) + 1.0/(1+3) = 0.583
  • Section 6: 1.0/(1+3) + 1.0/(1+1) = 0.75
image5.jpeg

最终排名变为:Section 2 (0.833),Section 6 (0.75),Section 7 (0.583)。这符合直觉 - Section 2 在两个索引中都表现良好,因此排名上升到顶部。

实现细节

Retriever 类封装了多个搜索索引并提供统一的接口:

class Retriever:

def __init__(self, *indexes: SearchIndex):

if len(indexes) == 0:

raise ValueError("至少必须提供一个索引")

self._indexes = list(indexes)

def add_document(self, document: Dict[str, Any]):

for index in self._indexes:

index.add_document(document)

def search(self, query_text: str, k: int = 1, k_rrf: int = 60):

# 从所有索引获取结果

all_results = []

for idx, results in enumerate(all_results):

for rank, (doc, _) in enumerate(results):

# 跟踪文档在各个索引中的排名

# 应用 RRF 评分公式

# 返回合并和排序后的结果

关键洞察是,通过在不同搜索实现中维护一致的 API,我们可以轻松地组合它们而不产生紧耦合。

测试混合方法

还记得我们之前遇到的问题吗?当搜索"what happened with INC-2023-Q4-011?"时,仅使用向量的方法返回了意外的结果?网络安全事件(第10节)排在第一位,但财务分析(第3节)排在第二位,而不是更相关的软件工程部分。

使用我们的混合检索器,现在得到了更好的结果:

  • 第10节:网络安全分析 - 事件响应报告(最相关)
  • 第2节:软件工程 - Phoenix项目稳定性增强(第二相关)
  • 第5节:法律发展(第三)

这展示了如何通过结合语义搜索和词汇搜索来克服单独使用任一方法的局限性。

Extensibility

image6.jpeg

这种架构的优美之处在于其可扩展性。由于所有索引都实现了相同的 SearchIndex 协议,具有 add_document() 和 search() 方法,你可以轻松添加新的搜索方法:

image7.jpeg

想要添加基于关键词的索引?基于图的搜索?专门的领域索引?只需实现相同的接口,Retriever 就会自动将其纳入融合过程中。

这种模块化方法使每个搜索实现保持专注和可测试性,同时为在最终系统中结合它们的优势提供了一种简洁的方式。

==================================================



此文档由 学习AI的1000天 翻译制作(抖音,B站,YouTube)
邮箱: szqshan@gmail.com | 微信: szqshan
网址: www.xueai.org