RAG 检索增强生成:从基础架构到高级范式
摘要: 围绕检索增强生成(RAG)技术体系,从基础三段式架构(检索-增强-生成)出发,逐层拆解文档解析与Chunking策略、向量数据库选型与BM25+向量混合检索、Embedding模型对比及Reranking精排机制,并深入分析上下文注入方式与引用溯源策略。在此基础上,系统介绍了GraphRAG、Agentic RAG和Multi-hop RAG三种高级范式的核心原理、架构差异与适用场景,为构建生产级RAG系统提供从预处理到高级检索的完整技术路线参考。
1. RAG 基础架构 — 检索 + 增强 + 生成三段式
核心思想
让 LLM 在生成答案之前,先去外部知识库"翻资料",用检索到的真实信息来增强回答的准确性和时效性。

1.1 为什么需要 RAG?
大语言模型存在几个根本性局限:
| 问题 | 说明 | 示例 |
|---|---|---|
| 知识截止 | 训练数据有截止日期,不知道训练后的事件 | "2025年诺贝尔奖得主是谁?"无法回答 |
| 幻觉(Hallucination) | 模型会自信地编造不存在的事实 | 捏造论文标题、虚构 API |
| 私有知识盲区 | 企业内部文档、个人笔记不在训练数据中 | "公司内部保险理赔流程是什么?" |
| 缺乏溯源 | 无法告知答案的来源出处 | 用户无法验证信息可信度 |
RAG 的解决方案:不靠模型记忆,靠实时检索。 就像考试时允许你翻课本——你不需要把所有知识背下来,只要知道怎么查找就行。
1.2 RAG 三段式架构
RAG 的完整流程分为三个阶段,我们可以用一条管线来表示:
RAG 完整架构
================================================================================
┌──────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ │ │ │ │ │
│ 用户查询 │────>│ ① 检索 (Retrieval) │────>│ ② 增强 (Augmentation)│
│ "Q: ..." │ │ │ │ │
│ │ │ 从知识库中找到最相关 │ │ 把检索结果嵌入到 │
│ │ │ 的文档片段(Chunks) │ │ Prompt 的上下文中 │
└──────────┘ └──────────────────────┘ └──────────┬───────────┘
▲ │
│ ┌─────────────────┐ │
│ │ 向量数据库 │ │
│ │ (Vector Store) │ │
│ │ ┌───┐ ┌───┐ │ │
│ │ │C1 │ │C2 │ │ │
│ │ │C3 │ │...│ │ │
│ │ └───┘ └───┘ │ │
│ └─────────────────┘ │
│ ▼
│ ┌──────────────────────┐
│ │ │
└───────────────────────│ ③ 生成 (Generation) │
返回 Top-K 相关 │ │
文档片段 │ LLM 基于「用户问题 │
│ + 检索到的文档」 │
│ 生成最终答案 │
└──────────┬───────────┘
│
▼
┌──────────────┐
│ 最终答案 │
│ + 引用来源 │
└──────────────┘三个阶段详细拆解:
阶段一:检索 (Retrieval)
用户问题 ──> 查询预处理 ──> Embedding ──> 向量相似度搜索 ──> Top-K 文档
(改写/扩展) (向量化) (cosine/Dot) (K=5~20)
直观理解:把问题变成一个"搜索向量",去知识库中找离它最近的 K 个文档片段。
阶段二:增强 (Augmentation)
Top-K 文档 ──> 排序/过滤 ──> 拼接到 Prompt ──> 构建增强后的上下文
(Reranking)
直观理解:把找回来的资料整理好,"粘贴"到给 LLM 的 Prompt 里。
阶段三:生成 (Generation)
增强 Prompt ──> LLM 推理 ──> 生成答案 ──> 附加引用
直观理解:LLM 阅读你提供的资料后,回答用户的问题,并标注信息来源。1.3 一个具体的例子
假设用户问:"公司年假政策是什么?"
步骤 1 — 检索:
Query "公司年假政策" → Embedding → 向量搜索
→ 找到最相关的 5 个文档片段:
- Chunk 23: "员工入职满1年享有5天年假..." (相似度 0.92)
- Chunk 47: "年假跨年可累计,最多累计..." (相似度 0.88)
- Chunk 15: "请假流程需在OA系统中提交..." (相似度 0.79)
...
步骤 2 — 增强:
构建 Prompt:
"""
请根据以下文档回答用户问题:
文档1: 员工入职满1年享有5天年假...
文档2: 年假跨年可累计,最多累计...
文档3: 请假流程需在OA系统中提交...
用户问题:公司年假政策是什么?
请基于以上文档回答,并标注引用来源。
"""
步骤 3 — 生成:
LLM 输出:
"根据公司政策,年假规定如下:
1. 入职满1年的员工享有5天年假【来源:文档1】
2. 年假可跨年累计,上限为15天【来源:文档2】
3. 需通过OA系统提交请假申请【来源:文档3】"1.4 RAG vs 微调(Fine-tuning)
专家建议:对于知识更新场景,RAG 是首选。微调更适合改变模型的"行为模式"(如语气、输出格式),两者可以互补使用。
这是两个解决"模型不知道"问题的主流方案,但思路完全不同:
| 维度 | RAG | 微调(Fine-tuning) |
|---|---|---|
| 原理 | 外部检索,不改模型参数 | 修改模型权重 |
| 知识更新 | 即时(更新数据库即可) | 需要重新训练 |
| 成本 | 低(只需 embedding + 向量存储) | 高(需要 GPU 训练) |
| 可解释性 | 高(可追溯来源) | 低(知识融入权重,不可追溯) |
| 适用场景 | 知识密集型、频繁更新的知识 | 风格/格式/行为调整 |
| 幻觉控制 | 强(答案被检索结果约束) | 弱(可能学到错误知识) |
2. 检索前处理 — Chunking 策略、文档解析
检索的质量,有一半取决于"前置处理"做得好不好。如果文档切得太碎(失去上下文)或太大(检索不精确),都会严重影响后续效果。
2.1 文档解析(Document Parsing)
在切分之前,首先要从各种格式中提取出纯文本,文档解析 Pipeline 具体如下:
PDF ──┐
Word ──┤
HTML ──┤ ┌──────────────┐
Markdown──┼──> 格式解析器 ──> 纯文本 ──> │ 文本清洗 │
PPT ──┤ (Parser) │ - 去掉页眉页脚│
Excel ──┤ │ - 合并断行 │
图片 ──┤ OCR 识别 ──────────────> │ - 统一编码 │
(扫描件)──┘ └──────┬───────┘
│
▼
清洗后的纯文本
(Ready for Chunking)常见文档解析工具:
| 工具/库 | 适用格式 | 特点 |
|---|---|---|
PyPDF2 / pdfplumber | 基础 PDF 文本提取 | |
Unstructured | PDF/Word/HTML/PPT/图片 | 一站式文档预处理,支持 OCR |
LlamaParse | PDF(尤其是复杂表格) | LlamaIndex 出品,专为 RAG 优化 |
Azure Document Intelligence | 多格式 | 云服务,表格识别强 |
Marker | PDF → Markdown | 开源,识别质量好 |
Docling (IBM) | PDF/Word/PPT/HTML | 保留文档结构,输出 Markdown/JSON |
关键注意事项:
- 表格处理:普通 PDF 提取常常把表格变成混乱文本。需要用专门的表格识别工具。
- 图片中的文字:扫描件 PDF 需要 OCR。多模态模型可以直接理解图片,但成本更高。
- 排版结构:保留标题层级(H1/H2/H3)对后续切分有帮助——结构化的文档更容易切出语义完整的片段。
2.2 Chunking 策略(文档切分)
Chunking 是 RAG 中最容易被低估的环节,切分策略直接影响检索的相关性和生成答案的完整性。
2.2.1 固定大小切分(Fixed-size Chunking)
最基础的方法:按固定字符数切分,加上重叠(Overlap)窗口。
- 优点:简单、快、易于实现。
- 缺点:可能在句子中间切断,丢失语义完整性。
固定大小切分示例 (Chunk Size=500, Overlap=50)
原文: "...人工智能技术正在深刻改变世界。从2010年代深度学习兴起,
到2020年代大语言模型的爆发,每一次技术浪潮都带来了..."
[共 1500 字]
切分结果:
┌──────────────────────┐
│ Chunk 1: [0:500] │ ← 字符 0~500
│ ... │
│ ...深度学习兴起, │
└──────────────────────┘
┌──────────────────────┐
│ Chunk 2: [450:950] │ ← 字符 450~950 (重叠 50 字)
│ 深度学习兴起,... │
│ ...大语言模型的... │
└──────────────────────┘
┌──────────────────────┐
│ Chunk 3: [900:1400] │ ← 字符 900~1400
│ 大语言模型的爆发... │
│ ... │
└──────────────────────┘
┌──────────────────────┐
│ Chunk 4: [1350:1500] │
│ ... │
└──────────────────────┘推荐参数:
| 场景 | Chunk Size | Overlap | 说明 |
|---|---|---|---|
| 短问答(FAQ) | 256~512 tokens | 10%~20% | 小片段足够定位答案 |
| 一般知识检索 | 512~1024 tokens | 10%~20% | 通用场景 |
| 长文档理解 | 1024~2048 tokens | 15%~25% | 需要完整上下文 |
| 代码检索 | 按函数/类 | 按需 | 不要用固定大小切代码! |
tokens vs 字符:英文 1 token ≈ 0.75 词 ≈ 4 字符;中文 1 token ≈ 1.5~2 字符。用 token 计数更准确,因为 LLM 按 token 计费。
2.2.2 语义切分(Semantic Chunking)
核心思路:在语义边界处切分,而不是按字符数。
语义切分 vs 固定大小切分
原文段落:
"## 第一章:深度学习基础
深度学习是机器学习的一个分支,它使用多层神经网络来学习数据的层次化表示。
与传统机器学习不同,深度学习可以自动从原始数据中提取特征,无需手工设计。
### 1.1 神经网络
神经网络由多个神经元层组成,每一层都对输入进行非线性变换..."
差的分割(固定大小,在句子中间切断):
Chunk A: "...它与传统机器学习不同,深度学"
Chunk B: "习可以自动从原始数据中提取特征..."
好的分割(沿语义边界):
Chunk A: "## 第一章:深度学习基础\n\n深度学习是机器学习的一个分支..."
[完整的段落,包含标题]
Chunk B: "### 1.1 神经网络\n\n神经网络由多个神经元层组成..."
[完整的子节,自包含]实现方法:
- 按段落/标题切分:以 Markdown 标题(
#,##)或双换行作为切分点。 - 按句子切分 + 合并:先按句子切,然后合并相邻句子直到接近 Chunk Size 上限。
- Embedding 相邻性切分:计算相邻段落的 Embedding,在"语义跳变"处切分。
基于 Embedding 的语义切分
1. 将文档按句子拆分: [S1, S2, S3, S4, S5, S6, S7, S8, ...]
2. 计算每个句子的 Embedding 向量
3. 计算相邻句子的余弦相似度:
S1 S2 S3 S4 S5 S6 S7 S8
│ │ │ │ │ │ │
0.85 0.91 0.88 0.42 ←跳变! 0.87 0.90
│
在这里切分(语义发生了变化)
4. 在相似度低于阈值的地方切分LlamaIndex 的 SentenceWindowNodeParser 是一个很好的实现参考:它按句子切分,但检索时会带回前后窗口的句子。
2.2.3 递归字符切分(Recursive Character Splitting)
LangChain 的 RecursiveCharacterTextSplitter 是最常用的方法。它按照优先级从高到低尝试切分符:
递归切分优先级(中文环境)
1. "\n\n" — 段落分隔符(最高优先级,尽量在段落边界切)
│ 如果段落还是太大...
2. "\n" — 行分隔符
│ 如果还是太大...
3. "。" — 中文句号
│ 如果还是太大...
4. ";" — 中文分号
│ 如果还是太大...
5. "," — 中文逗号
│ 如果还是太大...
6. 字符级 — 强制按字符切分(最后手段)
目标:尽量在更"自然"的边界切分,保持语义完整。代码示例(Python):
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", ";", ",", " "]
)
chunks = splitter.split_text(document)2.2.4 高级切分策略
| 策略 | 原理 | 适用场景 | 代表工具 |
|---|---|---|---|
| Agentic Chunking | 用 LLM 判断每个句子的最佳归属 | 高精度需求 | 自定义实现 |
| Small-to-Big | 检索用小粒度段,生成时带回大粒度段 | 兼顾检索精度和上下文完整性 | LlamaIndex |
| Multi-representation | 每个段落生成摘要,用摘要做检索,原文做生成 | 长文档、非结构化数据 | LangChain Indexing API |
| Propositions | 将每个段落拆分为原子性命题(一句话一个事实) | 高精度事实检索 | 学术前沿 |
3. 检索 — Vector Database、混合检索、Embedding 选择、Reranking
检索是 RAG 的核心引擎。从技术栈角度看,检索涉及三个关键决策:用什么向量库、用什么 Embedding 模型、用什么检索策略。
3.1 Vector Database(向量数据库)
3.1.1 什么是向量数据库?
普通数据库按"精确匹配"查询(WHERE name = '张三'),向量数据库按"语义相似"查询:"找和这句话意思最接近的文档"。
关系型数据库 vs 向量数据库
关系型数据库:
Query: "找到价格=100的商品" → 精确匹配 → {"id": 42, "price": 100}
向量数据库:
Query: "适合冬天穿的保暖外套" → Embedding → [0.12, -0.34, 0.88, ...]
↓
相似度搜索(ANN: 近似最近邻)
↓
[羽绒服, 棉衣, 加厚卫衣, ...]核心概念:
| 概念 | 说明 |
|---|---|
| Embedding Vector | 文档/查询的数值化表示,通常 768~4096 维 |
| 相似度度量 | Cosine Similarity(最常用)、Euclidean Distance、Dot Product |
| ANN(近似最近邻) | 牺牲少量精度换取极快速度的搜索算法(HNSW、IVF、PQ) |
| Collection/Index | 向量的逻辑分组,类似关系库中的"表" |
| Metadata Filtering | 基于标签/日期的预过滤(如"只看2024年的文档") |
3.1.2 主流向量数据库对比
向量数据库选择矩阵:
轻量 / 本地 重量级 / 云原生
──────────────────────────────────────────────────────────────>
Chroma Qdrant Weaviate Milvus Pinecone
嵌入到 app 中 单机部署 分布式集群 全托管云服务
零配置 高性能 海量数据 零运维
Python 优先 Rust 核心 10亿+向量 $$选择建议:原型阶段用 Chroma,上线用 Qdrant 或 Milvus,不差钱且不想运维用 Pinecone。
| 数据库 | 类型 | 特点 | 最佳场景 | GitHub Stars |
|---|---|---|---|---|
| Chroma | 嵌入式 / 轻量 | 极简 API,Python 原生,零配置 | 原型开发、小项目 | 15K+ |
| Qdrant | 单机 / 集群 | Rust 编写,高性能,过滤强,支持量化 | 生产环境、中等规模 | 20K+ |
| Weaviate | 单机 / 集群 | GraphQL 接口,内置模块化(可内置 Embedding) | 企业应用、多模态 | 11K+ |
| Milvus | 分布式集群 | CNCF 项目,云原生,支持 100 亿级向量 | 海量数据、大规模生产 | 30K+ |
| Pinecone | Serverless 云服务 | 全托管,零运维,弹性伸缩 | 不想管基础设施 | 商业产品 |
| FAISS | 库(非数据库) | Meta 出品,极致性能,纯内存 | 高吞吐离线检索 | 30K+ |
3.1.3 向量搜索的基本流程
向量检索流程
┌─────────────────────────────────────────────────────────┐
│ │
│ 1. 索引阶段(离线,一次性) │
│ │
│ 文档 ──> Chunking ──> Embedding ──> 存入向量库 │
│ │ │
│ ┌─────────────┘ │
│ ▼ │
│ ┌──────────┐ │
│ │ VectorDB │ │
│ │ [768 维] │ │
│ │ [768 维] │ │
│ │ [768 维] │ ← 百万/亿级向量 │
│ │ ... │ │
│ └──────────┘ │
│ │
│ 2. 查询阶段(在线) │
│ │
│ 查询 "如何请假" ──> Embedding ──> [768 维向量] │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 相似度计算 │ │
│ │ cosine(query, d) │ │
│ │ Top-K=5 │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ [Doc23, Doc47, Doc15, ...] │
│ score: 0.92, 0.88, 0.79 │
└─────────────────────────────────────────────────────────┘3.2 BM25 + 向量混合检索(Hybrid Retrieval)
3.2.1 纯向量检索的盲区
向量检索按"语义相似"工作,但有些情况下会失效:
| 场景 | 问题 | 示例 |
|---|---|---|
| 精确关键词 | "AK-47" 和 "步枪" 语义相似但不同 | 用户搜索 "AK-47",可能返回 "M16 步枪" |
| 专有名词 | 罕见名词未被模型充分学习 | 公司内部产品代号 "Project-X" |
| 数字/代码 | Embedding 对精确数字不敏感 | "Error code 500" vs "Error 502" |
| 短查询 | 短查询信息量不够,向量表达能力弱 | "请假"(只有一个词) |
3.2.2 BM25:经典的关键词检索
BM25 是搜索引擎的经典算法(TF-IDF 的现代改进版),按关键词匹配工作。BM25 检索原理(简化)
给定查询 "年假 政策"
对每个文档,BM25 考虑三个因素:
1. 词频(TF):"年假" 在文档中出现几次?
2. 逆文档频率(IDF):"年假" 在整个语料库中是常见词还是稀有词?
3. 文档长度归一化:长文档不会仅因为"更长"而获得更高分
BM25 分数 = Σ (IDF × TF × (k1+1)) / (TF + k1×(1-b+b×docLen/avgDocLen))
优点:擅长精确匹配、专有名词、数字
缺点:不懂近义词、不懂语义("年假"和"带薪休假"对 BM25 是完全不同的)3.2.3 混合检索架构
将 BM25 和向量检索的结果融合,取两者的交集/并集。混合检索 (Hybrid Retrieval) 架构示意如下:
用户查询: "2024年的销售报告"
│
┌────────────┴────────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ BM25 检索 │ │ 向量检索 │
│ (关键词匹配) │ │ (语义匹配) │
│ │ │ │
│ 结果: │ │ 结果: │
│ Doc A (0.82) │ │ Doc C (0.91) │
│ Doc B (0.75) │ │ Doc A (0.85) │
│ Doc E (0.68) │ │ Doc D (0.78) │
└───────┬───────┘ └───────┬───────┘
│ │
└────────────┬────────────┘
│
▼
┌─────────────────────┐
│ 融合策略: │
│ │
│ 1. RRF (Reciprocal │
│ Rank Fusion) │
│ 2. 加权求和 │
│ 3. 级联过滤 │
└──────────┬──────────┘
│
▼
最终排序结果:
Doc A (融合分最高)
Doc C
Doc D
Doc B
...融合策略详解:
| 策略 | 公式 | 优点 | 缺点 |
|---|---|---|---|
| RRF(推荐) | score = Σ 1/(k+rank) | 无需调参,效果稳定 | 不区分检索器权重 |
| 加权求和 | score = α×BM25 + (1-α)×Vector | 可调权重 | 需要校准分数尺度 |
| 级联过滤 | BM25 初筛 → 向量精排 | 速度快 | 可能漏掉纯语义相关的文档 |
最佳实践:大多数生产环境使用 RRF 融合。α 值(向量权重)通常设为 0.7~0.8(即偏向语义搜索)。
3.3 Embedding Model 选择
Embedding 模型是 RAG 的"翻译官"——把文本翻译成向量。选对模型事半功倍。
3.3.1 评估维度
| 维度 | 说明 |
|---|---|
| 检索精度 | MTEB(Massive Text Embedding Benchmark)排行榜上的分数 |
| 向量维度 | 维度越高表达能力越强,但存储和搜索成本也越高 |
| 最大输入长度 | 能否一次嵌入长文本(通常 512~8192 tokens) |
| 多语言支持 | 中文场景需要中文优化模型 |
| 推理成本 | 本地推理 vs API 调用,速度和费用 |
| 领域适配 | 通用模型 vs 领域微调模型(医疗、法律、代码) |
3.3.2 主流 Embedding 模型
Embedding 模型选型指南 (2025-2026)
API 服务(省心,有成本) 本地部署(免费,需 GPU)
─────────────────────────────────────────────────────────────>
OpenAI Cohere BGE GTE E5
text-embed embed-v3 bge-m3 gte-Qwen2 multilingual
voyager-2 1024 维 1024 维 2048 维 1024 维
3072 维 (效果极好) (中文最强) (阿里) (微软)
(默认选择)| 模型 | 维度 | 最大长度 | 中文支持 | MTEB 排名 | 类型 |
|---|---|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 8192 | 中等 | Top 5 | API |
| OpenAI text-embedding-3-small | 1536 | 8192 | 中等 | Top 15 | API(便宜 5 倍) |
| Cohere embed-v3 | 1024 | 512 | 好 | Top 3 | API |
| BGE-M3(智源) | 1024 | 8192 | 极好 | Top 10 | 开源 |
| GTE-Qwen2-7B(阿里) | 3584 | 32768 | 极好 | Top 3 | 开源(较大) |
| E5-mistral-7b(微软) | 4096 | 32768 | 好 | Top 5 | 开源(较大) |
| Jina embeddings v3 | 1024 | 8192 | 好 | Top 15 | API/开源 |
BGE-M3 是由北京智源人工智能研究院(BAAI)推出的一款开源通用向量嵌入模型,名字中的 M3 代表其三大核心亮点:多功能(Multi-functionality)、多语言(Multi-linguality)和多粒度(Multi-granularity)。它在 RAG(检索增强生成)和语义搜索领域被广泛应用。
中文场景首选:BGE-M3(轻量)或 GTE-Qwen2(精度优先)。如果用 API,Cohere embed-v3 对中文支持较好。
3.3.3 Embedding 的使用注意事项
查询 vs 文档 Embedding
许多 Embedding 模型区分"查询模式"和"文档模式":
文档侧(离线索引时):
"员工入职满1年享有5天年假" ──> [embed_document] ──> [768 维向量]
查询侧(在线检索时):
"年假几天?" ──> [embed_query] ──> [768 维向量]
↑ ↑
注意:查询和文档应该用不同的编码方式!
原因:查询通常是短句/关键词,文档是长段落。如果同一个模型处理,
查询向量可能"漂移"到与文档向量不同的子空间。
带有"指令前缀"(Instruction-tuned Embedding):
查询:embed_query("Represent this sentence for searching relevant passages: 年假几天?")
文档:embed_document("年假政策:员工满1年享有...")3.4 Reranking(重排序)
Reranking(重排序) 是指对初步检索出来的文档或文本块(Chunks)进行重新评分和排序的过程。它通常作为检索增强生成(RAG)或智能搜索系统中的“精排”环节,能够大幅提升最终输入给大语言模型(LLM)的内容精度。
3.4.1 为什么需要 Reranking?
在海量知识库中进行搜索时,系统通常遵循“先快后准”的两阶段策略:
- 初筛(召回):使用向量检索或关键词匹配(如 BM25),从数百万条数据中快速捞出前 100 条可能相关的文档。这一步速度快,但不够精准。
- 重排(Reranking):使用专门的 Reranker 模型,将这 100 条结果与用户提问进行深度的语义比对,计算出极其精确的“相关性得分”,最后重新排序并选出最相关的 Top-K(例如前 5 条)喂给 LLM。
向量搜索的 Top-K 结果是"粗筛",因为:
- Embedding 是压缩表示,会丢失细节信息
- 相似度分数是全局计算的,不理解查询的具体意图
- 片段之间没有比较——排第二的可能其实比第一更相关
Reranking 是精排:用一个更强的模型对 Top-K 候选重新打分。
检索的两阶段架构
第一阶段:粗筛(高召回) 第二阶段:精排(高精度)
───────────────────────── ────────────────────────
从 100 万份文档中 从 50 个候选中
快速捞出 50 个候选 精确找出最相关的 5 个
方法:向量搜索 + BM25 方法:Cross-Encoder Reranker
特点:快、召回率高 特点:慢但准、理解上下文
耗时:~10ms 耗时:~200ms(对 50 个文档)3.4.2 Bi-Encoder vs Cross-Encoder
这是理解 Reranking 的关键概念:
Bi-Encoder(双编码器) Cross-Encoder(交叉编码器)
───────────────────────── ──────────────────────────────
文档 ──> Encoder ──> Doc Vec 查询+文档 ──> Encoder ──> 相关分
查询 ──> Encoder ──> Query Vec (拼接在一起输入) 0.95
然后计算: │
score = cosine(Query Vec, Doc Vec) │
│ 特点:
│ • 查询和文档一起过模型
特点: • 完全交互,精度极高
• 文档向量可预先计算并存储 • 无法预先计算,每次都要推理
• 检索时只需计算相似度,极快 • 慢 ~100 倍
• 精度有损(压缩表示) • 成本高类比理解:
- Bi-Encoder = 先给每个学生打分(标准化考试),再比较分数
- Cross-Encoder = 派出一个面试官,一个一个地和学生深入交谈后打分
3.4.3 主流 Reranking 模型
| 模型 | 特点 | 语言 | 速度 |
|---|---|---|---|
| Cohere Rerank v3 | 商业最强,支持多语言 | 100+ 语言 | 快 (API) |
| BGE-Reranker-v2-m3 | 智源开源,中文好 | 多语言 | 中等 |
| Jina Reranker v2 | 多语言,效果好 | 100+ 语言 | 中等 |
| mxbai-rerank | 轻量开源 | 英文为主 | 快 |
| ColBERT / colbertv2 | 延迟交互,介于 Bi 和 Cross 之间 | 英文 | 较快 |
| 开源 / 自部署模型(推荐私有化知识库使用) | |||
| 这些模型通常基于交叉编码器(Cross-Encoder)架构,直接将 Query(查询)和 Document(文档)拼接输入,虽然计算开销大,但语义理解精度极高。 |
- BGE 系列 (BAAI):由北京智源人工智能研究院开发,包含
bge-reranker-large、bge-reranker-v2-m3等。其中 M3 版本强化了多语言和中英混合场景,是目前中文开源社区最受欢迎的模型之一。 - Qwen 系列 (阿里通义千问):如
qwen3-rerank,依托阿里强大的底座能力,支持长文本和多语言,是目前性能顶尖的开源/API选择。 - GTE 系列 (阿里):例如
gte-rerank-v2,针对自然语言处理中的重排序进行了专门优化。 - Jina Reranker:由 Jina AI 提供(如
jina-reranker-v2),以极长的上下文处理能力和多语言支持闻名,适合复杂的文档检索。 - Cohere v3 (开源版本):Cohere 开源的版本,性能表现优异。
- 商业闭源 API(免部署、开箱即用),适合不想维护 GPU 硬件、追求极致效果的企业应用。
- Cohere Rerank:全球公认重排序效果最好的 API 之一,支持 100 多种语言,常作为评测各类重排模型的行业基准。
- 通义千问 Rerank API:阿里云百炼平台提供的模型服务,集成方便,具备顶尖的问答检索与语义相似度排序能力。
- Bocha Reranker (博查):国内优秀的语义排序模型,参数量小但推理速度快,性价比高。
代码示例(Cohere Rerank):
import cohere
co = cohere.Client("your-api-key")
results = co.rerank(
query="年假如何申请?",
documents=[chunk1, chunk2, chunk3, chunk4, chunk5],
top_n=3, # 返回前 3 个
model="rerank-v3.5"
)
for r in results.results:
print(f"Index: {r.index}, Score: {r.relevance_score}")4. 增强与生成 — 上下文注入方式、引用与溯源
4.1 上下文注入方式
检索到了文档,怎么把它们"喂"给 LLM?这看似简单,其实是影响回答质量的关键。
4.1.1 直接拼接(Naive Concatenation)
最简单的方式:把所有检索到的片段直接拼接到 Prompt 里。
Prompt 模板(直接拼接)
"""
你是一个知识助手。请根据以下参考文档回答用户问题。
如果文档中没有相关信息,请如实告知。
参考文档:
---
[文档 1] {chunk_1}
[文档 2] {chunk_2}
[文档 3] {chunk_3}
用户问题:{user_query}
请基于以上文档回答,标注引用来源(文档编号)。
"""优点:简单直接。 缺点:
- 文档之间可能有冲突信息,LLM 不知道该信谁
- 无关文档(检索噪音)会占用宝贵的 Context Window
- 文档顺序可能导致 LLM 对靠前的文档给予更多注意力(Primacy Bias)
4.1.2 Map-Reduce / Refine 模式
当检索到的文档太多(超出 Context Window)时使用:
步骤 1 — Map:每个文档独立总结
┌─────────────────┐
│ Chunk 1 ──> LLM ──> 摘要 1: "年假5天,入职满1年..."
│ Chunk 2 ──> LLM ──> 摘要 2: "年假可跨年累计..."
│ Chunk 3 ──> LLM ──> 摘要 3: "OA系统提交请假..."
│ Chunk 4 ──> LLM ──> 摘要 4: "年假不休视为放弃..."
│ Chunk 5 ──> LLM ──> 摘要 5: "特殊病假可转年假..."
└─────────────────┘
│
▼
步骤 2 — Reduce:合并所有摘要生成最终答案
"根据以下摘要回答:{摘要1}\n{摘要2}\n{摘要3}\n{摘要4}\n{摘要5}\n\n问题:{query}"
Refine 模式(更精细)
不是并发总结,而是顺序精炼:
初始回答 = LLM(Chunk1, query)
│
回答_v2 = LLM(回答, Chunk2) ← 用新文档精炼已有回答
│
回答_v3 = LLM(回答_v2, Chunk3) ← 继续精炼
│
...
▼
最终回答4.1.3 Metadata 增强
关键原则:永远把最相关、最权威的文档放在前面(或使用更醒目的标记),给 LLM 明确的优先级信号。
给每个检索片段附加元数据,帮助 LLM 更好地判断文档的可信度和相关性。
带 Metadata 的注入 Prompt
"""
参考文档(按相关性排序):
[文档 1] 来源:员工手册-v3.2 | 更新日期:2024-06-15 | 相关度:0.95
内容:员工入职满1年享有5天带薪年假,工作年限每增加1年年假增加1天...
[文档 2] 来源:OA系统帮助文档 | 更新日期:2024-03-01 | 相关度:0.88
内容:年假申请方法:登录OA → 人事管理 → 请假申请 → 选择"年假"...
[文档 3] 来源:2023年旧版手册(注意:可能已过期) | 更新日期:2023-01-01 | 相关度:0.75
内容:年假制度:入职满1年享有3天年假...
用户问题:{user_query}
"""4.2 引用与溯源(Citation & Attribution)
RAG 的一大优势就是"可溯源"。好的引用设计让用户能验证信息的真实性。
4.2.1 引用格式
在 Prompt 中明确要求 LLM 输出引用标注:
System Prompt 中的引用指令
"""
在回答时,你必须遵守以下引用规则:
1. 每一条事实性陈述后,标注来源文档编号:例如 [1]
2. 如果多个文档支持同一事实,列出所有来源:例如 [1][3]
3. 不要因为任何原因编造不存在于文档中的信息
4. 如果文档中信息相互矛盾,指出矛盾并告知用户:例如 "文档1和文档3给出了不同的年假天数,可能是版本更新导致的"
"""回答示例:
根据公司政策,年假规定如下:
1. 入职满1年的员工享有5天带薪年假 [1]
2. 工作年限每增加1年,年假增加1天,上限15天 [1][2]
3. 年假申请需在OA系统中提交 [3]
4. 年假可跨年累计,但累计天数不超过当年应得年假的2倍 [2]
注:2023年旧版手册 [4] 中记载的年假天数为3天,但当前最新版(2024年6月更新)[1] 已调整为5天。4.2.2 溯源策略对比
| 策略 | 方法 | 优点 | 缺点 |
|---|---|---|---|
| 内联引用 | 在回答文本中直接标注 [N] | 自然、精确 | 需要 LLM 配合 |
| 脚注式 | 回答末尾列出来源 | 不打断阅读 | 对应关系不够精确 |
| 侧边栏展示 | UI 侧边栏同步显示来源 | 阅读体验好 | 需要前端配合 |
| 高亮联动 | 点击来源高亮对应文本 | 交互性最强 | 实现成本高 |
5. 高级 RAG — GraphRAG、Agentic RAG、Multi-hop RAG
基础 RAG(单次检索 + 单次生成)在许多复杂场景下存在局限。高级 RAG 方法试图解决这些问题。
5.1 GraphRAG(知识图谱 RAG)
5.1.1 基础 RAG 的"关系盲区"
基础 RAG 只能检索孤立的片段,看不到片段之间的关系。
基础 RAG 看到的是:
[片段 A] "张三是 Alpha 项目的技术负责人"
[片段 B] "Alpha 项目是公司 AI 平台的核心组件"
[片段 C] "公司 AI 平台部署在 AWS 上"
LLM 把这三个片段拼接在一起,生成回答。
但它无法回答这样的问题:
"AI 平台的技术负责人需要向哪些团队汇报?"
因为从三个片段中无法推断出团队汇报关系。GraphRAG 的解法:先把文档变成知识图谱(节点 + 边),然后基于图谱检索。
文档 → 知识图谱转换:
原始文档:
"张三是 Alpha 项目的技术负责人。Alpha 项目是公司 AI 平台的核心组件。
公司 AI 平台由李四担任产品经理,部署在 AWS 上。张三向王五汇报。"
提取出的知识图谱:
┌──────┐
│ 王五 │
└───┬──┘
│ 汇报关系
┌───▼──┐ 负责 ┌──────────┐
│ 张三 │──────────────────>│ Alpha 项目 │
└──────┘ └─────┬────┘
│ 是...核心组件
┌─────▼──────────┐
┌──────┐ 产品经理 │ 公司 AI 平台 │
│ 李四 │──────────────────>│ │───部署于───> AWS
└──────┘ └────────────────┘5.1.2 GraphRAG 架构
GraphRAG 的流程有两个阶段:
阶段一:索引阶段(离线)
文档 ──> 实体识别 ──> 关系抽取 ──> 知识图谱构建 ──> 社区检测
(NER) (Relation Extr) (Nodes+Edges) (Community)
│
▼
┌──────────────┐
│ 图数据库 │
│ (Neo4j等) │
└──────────────┘
阶段二:查询阶段(在线)
用户查询 ──> 图谱搜索 ──> 相关知识 ──> LLM 生成答案
│
├── 局部检索(Local Search):从相关实体出发,收集邻居信息
│
└── 全局检索(Global Search):对社区摘要进行聚合分析
全局检索的具体流程:
1. 将知识图谱划分为社区(Community):紧密相连的节点组
2. 每个社区生成摘要(LLM):"这个社区主要涉及..."
3. 查询时:
a. 向量搜索找到相关社区摘要
b. 将相关社区的完整信息加载到 Context
c. LLM 综合社区信息生成答案5.1.3 GraphRAG 的优劣势
| 优势 | 劣势 |
|---|---|
| 理解实体间的关系和连接 | 索引成本极高(每份文档都要 LLM 提取实体关系) |
| 擅长"总结性"问题(如"三个主题是什么") | 对简单事实查询可能过度复杂 |
| 全局分析能力强 | 图构建质量依赖实体关系提取的准确度 |
| 可发现隐藏的关联模式 | 维护和更新图结构较复杂 |
适用场景:
- 需要理解跨文档关系的问题(如法律案件关联分析)
- 高层次总结("公司所有项目的技术栈分布")
- 探索性查询("列举与 AI 平台相关的所有决策人")
代表实现:
- Microsoft GraphRAG:微软开源,最知名的图 RAG 实现,支持全局/局部搜索
- Neo4j + LangChain:用图数据库存储,结合 LangChain 的 RAG 流程
5.2 Agentic RAG(智能体 RAG)
5.2.1 从"被动检索"到"主动决策"
基础 RAG 是固定的管道:检索 → 增强 → 生成。Agentic RAG 把决策权交给 Agent——让 Agent 自己判断需要检索什么、怎么检索、是否需要二次检索。
基础 RAG vs Agentic RAG
基础 RAG(固定管道): Agentic RAG(自主决策):
┌─────────────────┐ ┌─────────────────────────┐
│ 查询 → 检索 → 生成 │ │ Agent Loop │
└─────────────────┘ │ │
│ 1. 分析问题 │
│ 2. 是否需检索? │
│ ├─ 是 → 去哪里找? │
│ └─ 否 → 直接回答 │
│ 3. 检索后信息够吗? │
│ ├─ 够 → 生成答案 │
│ └─ 不够 → 换个方式再找 │
│ 4. 答案质量过关吗? │
│ ├─ 是 → 输出 │
│ └─ 否 → 反思修正 │
└─────────────────────────┘5.2.2 Agentic RAG 的核心能力
| 能力 | 说明 | 示例 |
|---|---|---|
| 查询重写 | 将用户原始查询改写为更利于检索的形式 | "那个蓝色按钮怎么不见了" → "设置页面中蓝色按钮的显示问题" |
| 查询分解 | 将复杂问题拆分为多个子查询 | "对比 A 和 B 的性能" → Q1: "A 的性能指标", Q2: "B 的性能指标" |
| 工具选择 | 决定用哪个知识库/工具检索 | "内部文档" vs "代码库" vs "API文档" |
| 迭代检索 | 第一轮检索不够 → 分析缺失 → 第二轮检索 | "找到了年假天数,但没找到申请流程" → 重新搜索"年假申请" |
| 自我反思 | 检查答案是否回答了问题,信息是否完整 | "已回答了天数但缺少申请方法,需要补充" |
| 多源融合 | 从不同来源获取信息并整合 | 员工手册 + OA 系统文档 + 最新公告 |
5.2.3 Agentic RAG 的典型工作流
Agentic RAG 处理复杂查询示例
用户问题:"我想了解公司年假政策,包括天数和申请流程,
以及去年的年假还能不能用。"
Agent 思考路径:
Step 1: 分析 → 这个问题需要三个信息:天数、流程、跨年规则
→ 分解为 3 个子查询
Step 2: ToolCall("search_knowledge_base", query="年假天数 入职年限")
→ 结果:5天起,每年+1天,上限15天 ✓
Step 3: ToolCall("search_knowledge_base", query="年假申请流程 OA")
→ 结果:OA系统 → 人事管理 → 请假申请 ✓
Step 4: ToolCall("search_knowledge_base", query="年假跨年 累计 过期")
→ 结果:可跨年累计,最多累计当年2倍
→ 但不确定"去年年假"的具体状态
Step 5: 反思 → 关于"去年的还能不能用"信息不够
→ ToolCall("search_knowledge_base", query="2024年度年假 2025年 使用期限")
→ 结果:上一年度年假需在次年3月31日前使用完毕 ✓
Step 6: 整合所有信息,生成最终答案,标注来源。5.3 Multi-hop RAG(多跳检索)
5.3.1 什么是"多跳"?
某些问题需要"跳转"多次才能回答——答案分散在不同文档中,需要链式推理。
单跳 vs 多跳
单跳问题(一轮检索即可):
Q: "公司年假有多少天?"
→ 检索 "年假天数" → 找到一块相关文档 → 回答
多跳问题(需要多轮检索):
Q: "Alpha 项目的技术负责人的直属上级是谁?"
Hop 1: 检索 "Alpha 项目 技术负责人"
→ 答案:张三
Hop 2: 基于 Hop 1 的结果,检索 "张三 直属上级"
→ 答案:王五
最终回答:Alpha 项目技术负责人张三的直属上级是王五。5.3.2 Multi-hop RAG 的实现策略
策略一:ReAct 式迭代检索(最常用)
Agent Loop:
Thought: 我需要先知道 Alpha 项目的技术负责人是谁
Action: search("Alpha 项目 技术负责人")
Observation: 张三是 Alpha 项目技术负责人
Thought: 现在我需要知道张三的直属上级
Action: search("张三 直属上级 汇报")
Observation: 张三向王五汇报
Thought: 我已经有了完整的信息链,可以回答
Answer: 王五策略二:IRCoT(Interleaving Retrieval with Chain-of-Thought)
检索和推理交错进行:
检索1 "Alpha 项目" → 推理:"这是一个技术项目"
→ 检索2 "技术负责人 张三" → 推理:"张三管理这个项目"
→ 检索3 "张三 上级" → 推理:"上级是王五"
→ 回答
每一步检索都基于前一步的推理结果来优化。策略三:Tree-of-Thought + 检索
同时探索多条推理路径:
Q: "谁是谁的上级?"
│
┌───────────────┼───────────────┐
▼ ▼ ▼
搜索"项目组" 搜索"技术部" 搜索"管理层"
│ │ │
▼ ▼ ▼
张三→李四 张三→王五 张三→赵六
(不太确定) (置信度高) (可能性低)
│
▼
确认答案:王五5.3.3 Multi-hop RAG 的难点
| 难点 | 说明 | 缓解方法 |
|---|---|---|
| 错误传播 | 第一跳的检索错误会传导到后续所有步骤 | 每步引入验证步骤;保留多条候选路径 |
| "链断"问题 | 中间步骤无法找到相关信息 | 设计回退策略;提前构建知识图谱 |
| 过度检索 | Agent 陷入无限循环搜索 | 限制最大检索次数(通常 3~5 次) |
| 效率 | 多次 LLM 调用和检索,延迟高 | 并行化独立检索;缓存中间结果 |
6. 总结
RAG 从简单的"检索+生成"演进为包含智能切分、混合检索、重排序、图谱推理和自主决策的完整体系,技术栈总览:
层级 技术要点
═══════════════════════════════════════════════════════════
高级 RAG │ GraphRAG / Agentic RAG / Multi-hop RAG
─────────────┼────────────────────────────────────────────
增强层 │ 上下文注入策略 / Map-Reduce / 引用溯源
─────────────┼────────────────────────────────────────────
精排层 │ Reranking (Cross-Encoder)
─────────────┼────────────────────────────────────────────
检索层 │ 向量检索 / BM25 / 混合检索 (RRF)
─────────────┼────────────────────────────────────────────
Embedding │ 模型选择 / 维度权衡 / Query vs Doc
─────────────┼────────────────────────────────────────────
预处理层 │ Chunking / 文档解析 / Metadata 标注
═══════════════════════════════════════════════════════════关键要点:
- RAG 解决的是 LLM 知识盲区问题,不是"替代"微调,而是互补方案。
- Chunking 策略比很多人想象的更重要——差的切分等于差的检索。
- 混合检索(向量+BM25)是生产环境的最佳实践。
- Reranking 虽然多一步,但质量提升显著,性价比极高。
- Agentic RAG 是趋势——让 Agent 自主决定检索策略,而不是固定管道。