RAG 多路召回架构:向量 + 关键词 + 知识图谱的融合实战

📅 2026/7/6 13:29:20 👁️ 阅读次数 📝 编程学习
RAG 多路召回架构:向量 + 关键词 + 知识图谱的融合实战

检索增强生成(RAG)已经成为企业大模型应用的事实标准。但随着文档数量、查询复杂度和业务要求的提升,单一向量检索的局限性越来越明显:向量擅长语义匹配,却容易漏掉专有名词和精确事实;关键词检索精准,但缺乏语义泛化能力;知识图谱能捕获实体关系,但构建成本高。多路召回(Multi-Channel Retrieval)正是在这个背景下成为生产级 RAG 的标配架构。

一、为什么单一检索不够用了向量检索基于嵌入空间中的相似度,对于自然语言表达的语义近义词非常有效。例如,“如何提升模型推理速度”和“大模型推理加速方法”在向量空间中通常很接近。但向量检索在以下场景表现不佳:-专有名词和编号:产品型号、身份证号、订单号等精确匹配需求。-低频概念:训练数据中出现较少的专业术语,嵌入质量差。-多条件组合查询:需要同时满足多个属性的约束,例如“2024年发布且支持中文的向量数据库”。-关系推理:需要跨越多个文档片段推断实体关系,例如“某客户的所有关联订单”。因此,生产环境的 RAG 通常需要结合多种检索方式,取长补短。## 二、三路召回的核心机制### 1. 向量召回(Dense Retrieval)向量召回负责语义层面的匹配。它的核心流程包括:- 将文档切分为 chunk- 使用嵌入模型(如 BGE-M3、OpenAI text-embedding-3)生成向量- 存储到向量数据库(如 Milvus、Qdrant、Weaviate)- 查询时同样生成向量,按余弦相似度或点积排序返回 Top-Kpythonfrom langchain.embeddings import OpenAIEmbeddingsfrom langchain.vectorstores import Chromaembeddings = OpenAIEmbeddings(model="text-embedding-3-small")vectorstore = Chroma.from_documents(chunks, embeddings)results = vectorstore.similarity_search(query, k=10)text向量召回的优势在于语义泛化,但需要注意嵌入模型的领域适配。通用嵌入在医疗、法律、金融等专业领域往往效果一般,建议使用领域数据微调或选用专用嵌入模型。### 2. 关键词召回(Sparse Retrieval)关键词召回使用传统信息检索方法,如 BM25、TF-IDF,或者基于稀疏向量的模型(如 SPLADE、BGE-M3 的稀疏向量)。它擅长处理精确匹配、专有名词和 acronym。pythonfrom rank_bm25 import BM25Okapicorpus = [chunk.page_content for chunk in chunks]tokenized_corpus = [doc.split(" ") for doc in corpus]bm25 = BM25Okapi(tokenized_corpus)doc_scores = bm25.get_scores(query.split(" "))top_indices = np.argsort(doc_scores)[-10:][::-1]text关键词召回通常不需要重新训练,实现成本低,适合作为向量召回的互补通道。### 3. 知识图谱召回(Graph Retrieval)知识图谱召回将文档中的实体、关系和事件抽取为图结构,支持复杂的关系查询和推理。例如:- “某疾病的常见并发症”- “某公司的所有子公司及其产品”- “某项目的负责人和上游依赖”cypherMATCH (p:Product)-[:OWNED_BY]->(c:Company {name: 'OpenAI'})RETURN p.name, p.release_datetext知识图谱构建通常涉及实体识别、关系抽取、实体对齐和图存储。对于已有结构化数据的业务,可以直接将数据库导入图数据库;对于非结构化文档,则需要使用 LLM 或 NLP 工具抽取三元组。## 三、多路召回的融合策略多路召回的核心挑战在于:如何将不同通道的结果融合为一个统一的候选集,并排序返回给 LLM。常见方法包括:### 1. 简单合并与去重最直接的方法是将各路结果合并,按相似度或得分排序,去除重复。这种方法实现简单,但忽略了不同召回通道的得分分布差异。### 2. Reciprocal Rank Fusion(RRF)RRF 是一种经典的融合方法,不需要对得分归一化,适用于不同召回通道的不可比得分:pythondef reciprocal_rank_fusion(rank_lists, k=60): scores = {} for rank_list in rank_lists: for rank, doc_id in enumerate(rank_list): scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1) return sorted(scores.items(), key=lambda x: x[1], reverse=True)vector_rank = [d.metadata["id"] for d in vector_results]bm25_rank = [d.metadata["id"] for d in bm25_results]graph_rank = [d["id"] for d in graph_results]fused = reciprocal_rank_fusion([vector_rank, bm25_rank, graph_rank])textRRF 稳健、易实现,是生产环境中的常见选择。### 3. 学习排序(LTR)对于数据量大的场景,可以使用轻量级模型对多路召回结果进行重排序。例如,使用 Cross-Encoder 或小型 LLM 对候选文档与查询的相关性打分,再重新排序。LTR 能显著提升最终精度,但增加了推理成本和延迟。## 四、多路召回的工程架构一个典型的多路召回 RAG 架构如下:text用户查询 ├── 向量召回 → 向量数据库 → Top-K 候选 ├── 关键词召回 → 倒排索引/BM25 → Top-K 候选 ├── 知识图谱召回 → 图数据库 → 子图/实体路径 └── 融合排序 → 重排序模型 → 最终上下文 → LLM 生成text工程落地时需要注意:-索引一致性:不同通道的文档 chunk 需要统一 ID,便于去重和融合。-查询改写:复杂查询在进入各通道前可进行重写或扩展,例如 HyDE(假设文档嵌入)。-延迟控制:知识图谱和重排序可能较慢,可以设置超时和降级策略。-结果解释:记录每条候选来自哪个通道,便于调试和优化。## 五、总结RAG 的检索层已经从“向量一条腿走路”演变为“多路召回协同作战”。向量、关键词和知识图谱三种召回方式各有优劣,只有根据业务场景合理组合,才能在召回率、精度和延迟之间取得平衡。多路召回不是简单的技术堆砌,而是需要精细的融合策略、统一的索引管理和持续的效果评估。对于正在构建企业知识库的团队来说,多路召回是通往生产级 RAG 的必经之路。