基于 LlamaIndex 实现 Hybrid‑RAG 召回指南

微软技术分享 微软技术分享 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

前文《基于 LlamaIndex+PostgreSQL 实现 RAG 持久化》已完成向量数据库持久化部署,并封装了 RAGService 基础检索服务,解决了 RAG 系统的向量存储与索引持久化核心能力。但单纯依赖基础向量检索,普遍存在语义漂移、关键词精准匹配能力弱、场景适配性差等工业落地问题,难以适配企业复杂知识库的检索诉求。基于此,本节将重点讲解工业界主流的 Hybrid‑RAG 混合检索方案,并提供完整落地实现方案。

在企业RAG落地场景中,检索召回拥有十余种组合方式,其中5种架构适配90%以上企业业务场景,是工业界通用的落地标准。

组合名称核心组件典型场景落地定位
Hybrid‑RAG向量+BM25+RRF+Rerank绝大多数企业知识库(制度、产品文档、技术手册)通用基线(首选,最广泛)
Naive+Rerank向量检索+Rerank小型知识库、FAQ,文档量少,预算有限轻量替代基线
Hybrid+ParentDocument混合检索+父子文档长文档、白皮书、报告,信息分散基线之上场景增强
Hybrid+Sentence‑Window混合检索+句子窗口合同、法务、论文,需要局部上下文基线之上场景增强
RouterQueryEngine多索引路由多套隔离知识库(人力 / 产品 / 法务分开)多知识库场景专用

环境配置

检索方案依赖BM25检索、重排模型、向量存储、重试机制等工具,统一安装依赖,使用官方镜像源保证安装稳定:

CMD> pip install llama-index-retrievers-bm25 sentence-transformers tenacity psycopg2-binary -i https://pypi.org/simple
CMD> pip list
llama-index-retrievers-bm25              0.8.0
sentence-transformers                    6.0.0
tenacity                                 9.1.4
psycopg2-binary                          2.9.13

所有含Rerank的架构均依赖高精度重排模型,推荐工业界通用的BAAI/bge-reranker-v2-m3模型,支持中英文、精度高、适配企业场景。

建议本地部署模型,代码中可直接加载本地模型路径。

代码调用

架构流程:BM25关键词检索 + 向量语义检索并行召回 → RRF倒数排序融合统一结果 → Rerank模型精细打分过滤假阳性 → 输出最优TopK结果,兼顾语义泛化能力与关键词精准度,检索准确率远超单一方案。

模型召回案例演示

import os
import requests
from typing import List
from llama_index.core.node_parser import SentenceSplitter
from llama_index.retrievers.bm25 import BM25Retriever
from llama_index.core.retrievers import QueryFusionRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SentenceTransformerRerank
from llama_index.core.embeddings import BaseEmbedding
from llama_index.llms.openai_like import OpenAILike
from llama_index.core import (
    VectorStoreIndex,
    SimpleDirectoryReader,
    Settings,
    StorageContext,
    load_index_from_storage,
)

class LocalLlamaServerEmbedding(BaseEmbedding):
    api_base: str
    api_key: str = "dummy"
    # embedding 模型最大上下文 token 数(Qwen3-Embedding-0.6B 默认 512)
    max_tokens: int = 400

    def _get_embedding(self, text: str) -> List[float]:
        url = f"{self.api_base}/embeddings"
        payload = {
            "input": text,
            "model": "Qwen3-Embedding-0.6B-Q8_0.gguf",
        }
        headers = {"Authorization": f"Bearer {self.api_key}"}
        resp = requests.post(url, json=payload, headers=headers, timeout=120)
        if resp.status_code != 200:
            raise RuntimeError(
                f"Embedding 接口请求失败 status={resp.status_code} body={resp.text}"
            )
        data = resp.json()
        return data["data"][0]["embedding"]

    def _get_text_embedding(self, text: str) -> List[float]:
        return self._get_embedding(text)

    def _get_query_embedding(self, query: str) -> List[float]:
        return self._get_embedding(query)

    async def _aget_query_embedding(self, query: str) -> List[float]:
        return self._get_embedding(query)

    async def _aget_text_embedding(self, text: str) -> List[float]:
        return self._get_embedding(text)

def setup_base_env():
    os.environ["OPENAI_API_KEY"] = "dummy"
    os.environ["OPENAI_BASE_URL"] = "http://127.0.0.1:11433/v1"

    llm = OpenAILike(
        model="qwen2.5-1.5b-instruct-q4_k_m.gguf",
        api_base=os.environ["OPENAI_BASE_URL"],
        api_key=os.environ["OPENAI_API_KEY"],
        is_chat_model=True,
        context_window=1024,
        temperature=0.1,
        system_prompt="你是企业文档助手,严格依据检索文档回答,文档没有相关信息请明确告知。",
    )

    # 关键:限制每个 chunk 的 token 数,必须小于 embedding 模型的 512 上限
    Settings.node_parser = SentenceSplitter(chunk_size=380, chunk_overlap=50)

    Settings.llm = llm
    Settings.embed_model = LocalLlamaServerEmbedding(
        api_base="http://127.0.0.1:11434/v1"
    )

# ============================================================
# Hybrid RAG:向量 + BM25 + RRF 融合 + Rerank
# ============================================================
class HybridRAG:
    """Hybrid-RAG:向量检索 + BM25 + RRF 倒数排序融合 + Rerank"""

    def __init__(self, data_dir, persist_dir="./storage_hybrid"):
        self.data_dir = data_dir
        self.persist_dir = persist_dir
        setup_base_env()
        self.index = self._load_or_build_index()
        self.query_engine = self._build_engine()

    def _load_or_build_index(self):
        if os.path.exists(self.persist_dir):
            try:
                storage_context = StorageContext.from_defaults(persist_dir=self.persist_dir)
                idx = load_index_from_storage(storage_context)
                print("HybridRAG 索引加载成功")
                return idx
            except Exception as e:
                print(f"索引加载失败: {e},删除旧索引并重建")
                import shutil
                shutil.rmtree(self.persist_dir, ignore_errors=True)
        try:
            documents = SimpleDirectoryReader(
                self.data_dir,
                required_exts=[".pdf", ".docx", ".txt"],
                recursive=False,
            ).load_data()
        except Exception as err:
            print(f"[文档读取解析失败] {err}")
            raise err

        print(f"读取文档片段 {len(documents)}")
        idx = VectorStoreIndex.from_documents(documents, show_progress=True)
        idx.storage_context.persist(persist_dir=self.persist_dir)
        return idx

    def _build_engine(self):
        vector_retriever = self.index.as_retriever(similarity_top_k=10)
        bm25_retriever = BM25Retriever.from_defaults(
            docstore=self.index.docstore, similarity_top_k=10
        )
        fusion_retriever = QueryFusionRetriever(
            retrievers=[vector_retriever, bm25_retriever],
            similarity_top_k=8,
            mode="reciprocal_rerank",
            num_queries=1,
        )
        post_processors = [
            # 确认该路径下已下载 bge-reranker-v2-m3 模型
            SentenceTransformerRerank(
                model="E:/llamacpp/models/BAAI--bge-reranker-v2-m3", top_n=4
            )
        ]
        engine = RetrieverQueryEngine.from_args(
            retriever=fusion_retriever,
            node_postprocessors=post_processors,
            response_mode="compact",
        )
        return engine

    def query(self, question: str):
        resp = self.query_engine.query(question)
        return {
            "answer": str(resp),
            "sources": [n.metadata for n in resp.source_nodes],
        }

if __name__ == "__main__":

    # 指定文档目录
    rag = HybridRAG("./company_docs")

    # 提问
    res = rag.query("公司报销流程是什么?")
    print("=" * 60)
    print(res["answer"])
    print("=" * 60)
    print("引用来源:")
    for s in res["sources"]:
        print(" -", s.get("file_name", s))

代码运行输出提示信息:

读取文档片段 1
Applying transformations: 100%|███████████████████████| 1/1 [00:02<00:00,  2.89s/it]
Generating embeddings: 100%|██████████████████████████
Generating embeddings: 100%|██████████████████████████| 3/3 [00:06<00:00,  2.03s/it]
Loading weights: 100%|███████████████████████████| 393/393 [00:00<00:00, 4975.27it/s]

============================================================
报销流程如下:

1. **初审**:直属领导审核业务真实性,确认是否为公出,1 个工作日内完成审批。
2. **财务审核**:财务部审核票据合规性、金额计算、预算是否充足。票据不合规直接退回员工修改重提。
3. **部门负责人终审**:金额≥2000 元需要部门负责人审批;金额<2000 元可口头报备直属领导。
4. **打款结算**:审批全部通过后,财务在 5 个工作日内,将报销款项转账至员工本人工资卡。
============================================================
引用来源:
 - post1.txt
 - post1.txt
 - post1.txt
...全文

相关推荐

《模式识别与数据挖掘》配套PPT课件

《模式识别与数据挖掘》配套PPT课件

Bryan Ding

科技成果转化项目进场交易前,评估备案环节需要准备哪些材料?_8.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

BIZKEEN

人工智能通识教育(中职版)课程标准+PPT课件+部分素材+电子活页+在线考核

人工智能通识教育(中职版)课程标准+PPT课件+部分素材+电子活页+在线考核

Bryan Ding

跨境电商供应链管理(AIGC版)课件+教案+参考答案

跨境电商供应链管理(AIGC版)课件+教案+参考答案

Bryan Ding

110KV单电源电网三段式电流保护+距离继电保护整定计算说明报告(23页+6000字)

内容概要:本文是一份关于110kV单电源电网继电保护整定计算的综合性技术报告,全文共23页、约6000字,系统阐述了三段式电流保护与距离保护的设计原理及其整定计算方法。报告深入分析了电力系统在短路故障条件下的电流与阻抗特性,详细推导了电流保护Ⅰ、Ⅱ、Ⅲ段的动作范围、整定原则、灵敏度校验及时间配合机制,确保满足继电保护的选择性、速动性、灵敏性和可靠性“四性”要求。同时,针对长距离或重要输电线路,补充介绍了距离保护的阻抗整定计算方法,并可能结合Matlab进行仿真验证与辅助计算,提升整定结果的准确性与工程实用性。; 适合人群:适用于电气工程及其自动化相关专业的高年级本科生、研究生,以及从事电力系统继电保护设计、运行维护的工程技术人员;要求读者具备电力系统分析、短路计算及继电保护的基础理论知识。; 使用场景及目标:①掌握110kV单电源辐射型电网中三段式电流保护的整定流程与实际计算步骤;②理解距离保护的工作原理及其在复杂工况下的应用优势;③完成实际工程项目的保护配置与整定计算任务,或作为课程设计、毕业设计及科研课题的技术参考资料;④结合Matlab工具实现整定计算的程序化处理与动作特性仿真验证。; 阅读建议:建议读者结合《电力系统继电保护原理》等相关教材同步学习,重点研读整定公式的推导过程与典型算例的计算细节,注意区分电流保护与距离保护的适用场景与配置逻辑,有条件时应利用Matlab等工具对保护动作行为进行建模与仿真,以深化对保护协调配合机制的理解。

欢多天天写代码
评论
收藏 已收藏
分享
返回顶部
微软技术分享
微软技术分享 优质创作者: 编程框架技术领域 优质创作者: 编程框架技术领域领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
微软技术社区 8,600 成员 37,311 内容