在 RAG(检索增强生成)系统中,检索环节的质量直接决定了最终回答的准确性。当面对复杂查询时,单一检索方式往往难以兼顾语义理解与精确匹配的需求。今天我们要聊的「融合检索」,正是通过多维度检索结果的智能融合,突破传统检索瓶颈的关键技术 —— 我们不仅会拆解自定义实现的核心逻辑,还会介绍如何利用成熟框架简化开发,帮你快速落地高效检索方案。
假设我们要回答 “北京市人口分布特点” 这类问题:
我们为每个城市文档构建两种检索器:
python
def create_vector_index_retriever(name):
# 加载文档、分割节点、存入Chroma向量库
city_docs = SimpleDirectoryReader([f"../../data/{name}.txt"]).load_data()
nodes = SentenceSplitter(chunk_size=500).get_nodes_from_documents(city_docs)
# 持久化索引,避免重复构建
if not os.path.exists(f"./storage/vectorindex/{citys_dict[name]}"):
vector_index = VectorStoreIndex(nodes, storage_context=StorageContext.from_defaults())
vector_index.storage_context.persist(...)
return vector_index.as_retriever(similarity_top_k=3)
优势:能捕捉 “人口分布” 与 “区域经济” 的语义关联,适合处理需要深层理解的问题。
python
def create_kw_index_retriever(name):
# 构建关键词表索引,提取文档中的高频词
kw_index = KeywordTableIndex(nodes)
return kw_index.as_retriever(num_chunks_per_query=5)
优势:对 “常住人口”“户籍人口” 等明确关键词反应灵敏,适合精准定位具体数据。
python
async def run_queries(queries, retrievers):
tasks = [retriever.aretrieve(query) for query in queries for retriever in retrievers]
task_results = await tqdm.gather(*tasks) # 带进度条的并行执行
# 整理结果,记录每个检索器和查询的对应关系
results_dict = { (query, i): res for i, (query, res) in enumerate(zip(queries, task_results)) }
return results_dict
python
def rerank_results(results_dict, similarity_top_k=3):
k = 60.0 # RRF算法参数,平衡早期排名的权重
fused_scores = {}
for node_list in results_dict.values():
for rank, node in enumerate(sorted(node_list, key=lambda x: x.score, reverse=True)):
text = node.node_get_content()
fused_scores[text] += 1.0 / (rank + k) # 排名越靠前,贡献的分数越高
# 按融合分数排序,取前k个最相关的片段
return [text_to_node[text] for text in sorted(fused_scores, key=fused_scores.get, reverse=True)][:similarity_top_k]
RRF 算法的核心逻辑:
自己实现融合检索虽然灵活,但需要处理异步任务、结果映射等细节。LlamaIndex 的QueryFusionRetriever
帮我们封装了这些逻辑:
python
from llama_index.core.retrievers import QueryFusionRetriever
def run_main():
query = "北京市有多少人口,是怎么分布的"
vector_retriever = create_vector_index_retriever('北京市')
kw_retriever = create_kw_index_retriever('北京市')
# 一行代码创建融合检索器,支持多种重排序算法
fusion_retriever = QueryFusionRetriever(
[vector_retriever, kw_retriever],
similarity_top_k=3,
num_queries=1, # 可自定义扩展的子查询数量
mode="reciprocal_rerank", # 选择RRF算法
use_async=True, # 开启异步加速
verbose=True # 输出检索过程日志
)
query_engine = RetrieverQueryEngine(fusion_retriever)
response = query_engine.query(query)
框架优势:
reciprocal_rerank
),还支持基于分数的融合(relative_score
)、距离加权(dist_based_score
)等,无需重复造轮子;num_queries
控制查询扩展的子问题数量,use_async
一键开启异步加速,大幅降低开发成本。num_queries=1
),避免生成无关子查询;k
值(默认 60)需根据数据规模调整,数据量越大,k
可适当增大以降低早期排名的权重。融合检索的本质,是通过 “分工协作 + 结果融合” 打破单一检索的局限。无论是自己实现还是用框架,核心都是让不同检索器发挥各自优势,再通过算法整合出更优质的结果。当我们在 RAG 系统中遇到 “检索不准” 的问题时,不妨想想这个思路 —— 就像解决复杂问题需要多学科交叉,高效检索也需要多技术融合。
如果你正在构建知识密集型应用,融合检索绝对是值得深入的方向。点赞收藏这篇文章,后续我们会分享更多 RAG 优化技巧,关注我们,一起攻克检索难题~