首页 > 教程攻略 > ai资讯 >大模型RAG实战|混合检索:BM25检索+向量检索的LlamaIndex实现

大模型RAG实战|混合检索:BM25检索+向量检索的LlamaIndex实现

来源:互联网 时间:2026-08-21 14:33:09

ThinkRAG大模型RAG实战系列文章,带你深入探索使用LlamaIndex框架,构建本地大模型知识库问答系统。本系列涵盖知识库管理、检索优化、模型本地部署等主题,通过代码与实例,讲解如何打造生产级系统,实现本地知识库的快速检索与智能问答。

大模型RAG实战|混合检索:BM25检索+向量检索的LlamaIndex实现

此前,一篇文章详细介绍了使用Elasticsearch实现混合检索。而今天要说的是另一种效果更好的方案——在实际问答场景中,它比向量数据库自带的混合检索功能更胜一筹。

1 什么是混合检索

目前,大模型RAG系统中普遍采用混合检索来提升检索准确性。

核心思路很简单:针对待回答的问题,同时跑两路检索——一路是

向量语义相似度检索

,另一路是

基于关键词的全文检索

。然后将两路搜到的文本块进行融合处理(包括去重与排序),最终把得分最高的文本块送给大模型生成回答。

这就是混合检索的基本原理。

借助向量数据库(比如Chroma、LanceDB,或者之前提到的Elasticsearch),实现向量检索非常容易。所以实现混合检索的关键问题就落在了:如何做好全文检索?

好在LlamaIndex提供了

BM25检索

的方法,让我们可以轻松补上这块拼图。

2 构建BM25检索器

BM25是一种文本相关度检索方法,核心是判断一个文档和查询语句的匹配程度——搜索引擎里对搜索结果排序,用的就是类似的思路。

这种相关度判断,主要基于词语权重和文档权重的融合。

  • 词频 TF

    (Term Frequency):一个词在文档中间出现的频率越高,文档相关性越高。
  • 逆向文档频率 IDF

    (Inverse Document Frequency):每个词在整个索引中间出现的频率越高,其相关性越低。IDF主要是为了压低像“的”这样高频词的影响,让包含低频专业术语的文档获得更高权重。

TF和IDF结合起来就形成了经典的

TF-IDF算法

。而BM25在此基础上做了进一步优化,主要是降低了长文档的权重。LlamaIndex实现了BM25算法的检索器:

BM25Retriever

【参考资料1】。

不过,该检索器默认使用的tokenizer不支持中文,所以需要引入

Jieba

作为分词器。于是,我们可以定制一个专门针对中文的BM25检索器:

SimpleBM25Retriever

代码如下:

import jieba
from typing import List

def chinese_tokenizer(text: str) -> List[str]:
    return list(jieba.cut(text))

class SimpleBM25Retriever(BM25Retriever):
    @classmethod
    def from_defaults(cls, index, similarity_top_k, **kwargs) -> "BM25Retriever":
        docstore = index.docstore
        return BM25Retriever.from_defaults(
            docstore=docstore, similarity_top_k=similarity_top_k, verbose=True,
            tokenizer=chinese_tokenizer, **kwargs
        )

用向量检索时,我们通常基于index构建检索器。同样,定制的BM25检索器也把index作为参数传入,构建时传递index.docstore。BM25检索的内容基于LlamaIndex框架中的文档存储(docstore)。

之前的文章中提到过,可以采用MongoDB作为文档存储和索引存储。但如果改用

Redis

【参考资料2】会更好——它还可以作为文本提取管道的缓存(ingestion cache)和问答记录存储(chat store)。这样一来,一个RAG系统中就不需要引入太多种类不同的数据库了。

3 实现混合检索

接下来要把向量检索和BM25检索到的结果融合起来。LlamaIndex提供了一个很优雅的方法:

QueryFusionRetriever

【参考资料3】。

基于此,可以定制一个新的类

SimpleFusionRetriever

。先构建向量检索器(vector_retriever)和BM25检索器(bm25_retriever),然后构造QueryFusionRetriever,并设置两者的权重分别为0.6和0.4。向量检索器的权重更高,意味着它在最终排名中会被认为更重要。

代码如下:

class SimpleFusionRetriever(QueryFusionRetriever):
    def __init__(self, vector_index, top_k=2, mode=FUSION_MODES.DIST_BASED_SCORE):
        self.top_k = top_k
        self.mode = mode

        # Build vector retriever from vector index
        self.vector_retriever = VectorIndexRetriever(
            index=vector_index, similarity_top_k=top_k, verbose=True,
        )

        # Build BM25 retriever from document storage
        self.bm25_retriever = SimpleBM25Retriever.from_defaults(
            index=vector_index, similarity_top_k=top_k,
        )

        super().__init__(
            [self.vector_retriever, self.bm25_retriever],
            retriever_weights=[0.6, 0.4],
            similarity_top_k=top_k,
            num_queries=1,  # set this to 1 to disable query generation
            mode=mode,
            use_async=True,
            verbose=True,
        )

融合处理的模式设置为

dist_based_score

【参考资料3】。LlamaIndex目前支持以下四种模式:

class FUSION_MODES(str, Enum):
    RECIPROCAL_RANK = "reciprocal_rerank"       # 应用倒数排序融合
    RELATIVE_SCORE = "relative_score"           # 应用相对得分融合
    DIST_BASED_SCORE = "dist_based_score"       # 应用基于距离的得分融合
    SIMPLE = "simple"                           # 基于原始得分简单重排序

先说说

relative_score

,即相对得分融合算法。它采用以下步骤:

  • 最小-最大归一化

    :将每个检索器的得分归一化到0~1之间,通过减去最小值再除以极差实现。
  • 加权求和

    :归一化后,用加权求和的方式得出最终得分,权重反映各检索器得分在最终排名中的相对重要性。

而我们采用的

dist_based_score

,是基于分布的得分融合算法。作为relative_score的变体,它根据每个结果集得分的均值和标准差进行差异化归一化。这种方法考虑了不同检索器得分的分布特性,因此融合时能更公平地对待每个检索器的贡献。

QueryFusionRetriever还有一个很有趣的特性:它可以将原始查询通过LLM改写生成最多4个新查询,然后对每个检索器都逐一检索。如果有2个检索器,一共就是4×2=8次检索。最终对这些结果用上述算法进行融合,去重并重新排序。

我们这次只是实验混合检索,不希望因为过多的LLM调用而增加延迟,所以将参数num_queries设为1,禁用查询生成。

4 下一步做什么

本文介绍了使用LlamaIndex实现“稠密向量检索 + BM25全文检索”的混合检索方法,属于典型的2路召回。

值得关注的是,IBM最近的研究文章对比了各种检索方式的组合,提出采用

Blended RAG

【参考资料4】——即BM25全文检索 + 稠密向量检索 + 稀疏向量检索的3路召回混合检索方法,可以达到更好的效果。

后续将继续探索使用LlamaIndex实现3路召回混合检索,届时会与大家分享结果。

本文中的代码,都可以在

ThinkRAG

【参考资料5】开源项目中找到。

https://github.com/wzda vid/ThinkRAG

ThinkRAG是基于LlamaIndex框架、前端使用Streamlit开发的大模型知识库RAG系统,可本地部署和离线运行。