如果你知道 RAG(检索增强生成),那么一定知道这里面的 R 代表 Retrieval 即检索,也一定知道这个环节对于最终的生成质量有多重要。基于向量(Vector)的语义检索是最为熟知的一种基础检索模式,但在实际应用中,如果只是简单地使用一次向量检索,往往很难满足多样化的需求。
所以在模块化 RAG 的时代,很多新的范式与算法都对检索这个环节进行了大量的创新与优化。本文将深入探讨两种在 RAG 应用中可灵活应用的高级检索模式:融合检索(Fusion Retrieval)和递归检索(Recursive Retrieval)。
融合检索(Fusion Retrieval)
融合检索是一种结合多维度检索的方法。简单来说,就是通过多个不同的检索方法进行查询检索,并对召回的结果通过排序算法(比如 RRF)重新排序融合后用于后续生成。融合检索可以组合多路不同维度的检索结果,以弥补单个向量索引在检索精确性上的不足。
如何实现多路不同的检索?
在实际应用中可以根据自身需要选择不同的形式,比如不同角度的重写问题、不同的检索算法、或者不同的索引类型等。
1. 基于问题重写与扩展
借助查询重写(或查询转换器)对输入问题进行扩展,生成多个不同表达形式或不同角度细化的输入问题,并对每个问题分别进行检索,然后对召回的知识块 chunks 通过重排(reranker)模块进行重新排序,并取最终的 top_K 用于后续生成环节。
这种方式利用 LLM 的理解能力将用户意图拆解或变换,从而覆盖更多潜在的匹配项。
2. 基于多种类型的索引
尽管基于高维向量的索引在很多场景下的基于自然语言的语义检索可以工作得不错,但其也并非全能。比如你可能想借助知识图谱索引来更精确地获取实体之间的复杂关系;借助树状的摘要索引来更好地回答概要性的问题等。
因此,你可以同时通过多路不同类型的索引(比如向量索引与关键词索引)来对输入问题进行检索,并对召回的知识块进行重排序处理后获取 top_K。(也可以是同种索引,但是采用不同的检索与评分算法)
3. 基于复合方案的多路检索
这是对上面两种方法的组合。即同时借助于问题扩展与索引扩展来实现更多路的知识召回,再通过重新排序获得 top_K。这种组合方法由于召回了更多的 chunks,有利于获取更相关的知识,但同时也增加了系统性能的消耗与模型使用的成本,在实际使用时需要根据测试结果进行取舍。
融合检索的关键技术
实现融合检索并不复杂,不管在 LangChain 还是 LlamaIndex 框架中,都有转换器 (rewriter)、检索器 (retriever)、排序器 (reranker) 的概念与组件,可以自行组合来实现融合检索。
- 查询扩展:借助 LLM 自行实现;或者借助已有组件,比如 LlamaIndex 中的 QueryTransform 组件。
- Reranker:自行实现 RRF 算法函数,或者借助 Cohere Reranker 这样的专业排序模型实现。
- 检索融合:扩展已有的 Retriever 组件,实现自定义融合检索器;有的框架有现成的融合检索器,比如 LlamaIndex 的 QueryFusionRetriever。
以下是一个典型的融合检索器扩展代码示例(Python):
from typing import List
from langchain.schema import Document, BaseRetriever, NodeWithScore
from langchain.schema.query_bundle import QueryBundle
import asyncio
class FusionRetriever(BaseRetriever):
"""
基于多个检索器构造融合检索器
参数:检索器列表与 top_k
"""
retrievers: List[BaseRetriever]
similarity_top_k: =
() -> :
._retrievers = retrievers
._similarity_top_k = similarity_top_k
().__init__()
() -> [NodeWithScore]:
querys = rewrite_query(query_bundle.query_str, num=)
results_dict = asyncio.run(run_queries(querys, ._retrievers))
final_results = rerank_results(results_dict, similarity_top_k=._similarity_top_k)
final_results


