RAG深度解析:从朴素检索到自主决策的演进全景
一句话定位:RAG(Retrieval-Augmented Generation,检索增强生成)是给LLM外挂一个"可随时查阅的知识库"——模型在生成回答前先检索相关文档,将检索结果注入提示词,从而在不重新训练的情况下获取最新、私有、可溯源的知识。
一、为什么需要RAG:LLM的知识困境
1.1 LLM的三大知识缺陷
大语言模型在预训练阶段学到的知识存在三个结构性缺陷:
1. 知识截止(Knowledge Cutoff):模型的知识停留在训练数据的最后时间点。GPT-4的训练数据截止于2023年4月,之后发生的事情它不知道。即使后续模型延长了截止日期,时间 gap 永远存在。
2. 幻觉(Hallucination):当模型被问到训练数据中没覆盖的问题时,它不会说"我不知道",而是会生成看似合理但完全虚构的内容。这是因为LLM的本质是"预测下一个最可能的词",而非"检索事实"。
3. 私有知识缺失:企业内部文档、行业数据库、个人笔记——这些从未出现在训练语料中的知识,LLM完全无法触及。
1.2 三种解决路线的对比
| 方案 | 原理 | 优势 | 劣势 |
|---|---|---|---|
| 重新训练 | 用新数据重新训练模型 | 知识内化到权重 | 成本极高、周期长、每次更新都要重来 |
| 微调(Fine-tuning) | 在基础模型上用领域数据继续训练 | 可塑造风格和格式 | 适合"怎么说",不适合频繁变化的"说什么" |
| RAG | 检索外部知识注入提示词 | 知识实时更新、可溯源、无需重训 | 增加检索延迟、依赖知识库质量 |
核心区别:微调改变模型的"行为模式"(怎么说),RAG改变模型的"知识来源"(说什么)。两者互补——微调让模型学会行业术语和输出格式,RAG让模型获取最新事实。正如业界共识:RAG管"说什么",微调管"怎么说"。
二、RAG核心架构:检索-增强-生成三段式
2.1 基本工作流
RAG的标准流程分为三个阶段,形成"检索→增强→生成"的管线:
用户提问 │ ▼ ┌──────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐ │ 查询编码 │───>│ 向量检索 │───>│ 上下文组装 │───>│ LLM生成 │ │ (Embed) │ │ (Retrieve) │ │ (Augment) │ │ (Generate)│ └──────────┘ └──────────────┘ └──────────┘ └──────────┘ │ │ ┌─────┴─────┐ │ │ 向量数据库 │ ▼ │ (Vector DB)│ 用户看到的回答 └───────────┘ (含引用来源)
2.2 离线建库阶段
在用户提问之前,需要先将知识库构建好。这个过程是离线的、一次性的(后续增量更新):
步骤1:文档切分(Chunking)
将长文档切分为语义连贯的小块(chunk),通常按段落或固定长度切分:
# 按固定长度切分(带重叠) def chunk_text(text, chunk_size=512, overlap=50): chunks = [] start = 0 while start < len(text): end = start + chunk_size chunk = text[start:end] chunks.append(chunk) start += chunk_size - overlap # 重叠防止语义断裂 return chunks # 更好的方式:按段落/标题切分 # 使用 LangChain 的 RecursiveCharacterTextSplitter from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""] )
切分策略对RAG质量影响极大:
太大:检索精度下降(一个chunk包含多个话题,不相关内容稀释信号)
太小:上下文不完整(检索到的片段无法独立理解)
重叠:防止句子在切分处断裂丢失语义
步骤2:向量化(Embedding)
将每个文本块通过嵌入模型转换为高维向量,使语义相近的文本在向量空间中距离更近:
from sentence_transformers import SentenceTransformer model = SentenceTransformer('BAAI/bge-large-zh-v1.5') embeddings = model.encode(chunks, normalize_embeddings=True) # 输出: [[0.012, -0.034, 0.056, ...], ...] 每个chunk一个768维向量常用嵌入模型对比:
| 模型 | 维度 | 中文支持 | 特点 |
|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 好 | 闭源API,效果好但贵 |
| BGE-large-zh-v1.5 | 1024 | 优秀 | 开源,中文场景首选 |
| E5-large-v2 | 1024 | 好 | 开源,多语言 |
| Cohere embed-v3 | 1024 | 好 | 闭源API,压缩性能好 |
步骤3:存入向量数据库
将向量和原始文本存入向量数据库,建立索引以便快速相似性搜索:
import faiss # FAISS 内存索引(适合中小规模) dimension = 1024 index = faiss.IndexFlatIP(dimension) # 内积相似度(向量已归一化) index.add(embeddings) faiss.write_index(index, "knowledge.faiss")
常用向量数据库:
| 数据库 | 特点 | 适用规模 |
|---|---|---|
| FAISS | 内存计算、无服务端 | 百万级以下 |
| Chroma | 轻量、嵌入式 | 原型/小型 |
| Milvus | 分布式、高可用 | 十亿级 |
| Qdrant | Rust实现、过滤强 | 千万级 |
| pgvector | PostgreSQL扩展 | 已有PG的系统 |
| Pinecone | 全托管SaaS | 不想运维 |
2.3 在线检索阶段
步骤1:查询编码
将用户的问题用同一个嵌入模型编码为向量:
query = "RAG和微调有什么区别?" query_vector = model.encode([query], normalize_embeddings=True)
步骤2:相似性搜索
在向量数据库中找到与查询最相似的top-k个文本块:
k = 5 scores, indices = index.search(query_vector, k) # 返回最相似的5个chunk及其相似度分数
步骤3:重排序(Reranking)——可选但强烈推荐
向量检索速度快但精度有限,重排序模型用更深的交叉注意力对候选结果重新打分:
from sentence_transformers import CrossEncoder reranker = CrossEncoder('BAAI/bge-reranker-large') pairs = [(query, chunks[i]) for i in indices[0]] rerank_scores = reranker.predict(pairs) # 按重排序分数重新排列双阶段检索(先向量召回top-50 → 再重排序top-5)是当前RAG的工程标配。
2.4 生成阶段
将检索到的文本块组装进提示词,交给LLM生成最终回答:
context = "\n\n".join([chunks[i] for i in reranked_indices[:5]]) prompt = f"""你是一个知识助手。请根据以下参考资料回答用户问题。 如果参考资料中没有相关信息,请明确说明"根据现有资料无法回答"。 ## 参考资料 {context} ## 用户问题 {query} ## 回答(请标注引用来源) """三、RAG三代演进:Naive → Advanced → Modular
RAG技术经历了三代演进,每一代都在解决前一代的核心缺陷。
3.1 第一代:Naive RAG(朴素RAG)
架构:用户查询 → 向量检索 → 拼接top-k → LLM生成
核心缺陷:
检索缺失:如果用户问题表述与文档措辞不一致,向量相似度低,检索不到
无重排序:直接用向量相似度排序,精度不足
无查询优化:用户原话直接编码,未做任何改写
3.2 第二代:Advanced RAG(高级RAG)
在Naive RAG前后增加优化模块:
检索前优化(Pre-Retrieval):
查询重写(Query Rewriting):让LLM将模糊查询改写为更清晰的检索查询
"RAG和微调的区别" → "RAG检索增强生成与微调fine-tuning在知识更新和模型行为上的差异对比"
查询分解(Query Decomposition):将复杂问题拆分为子问题
"RAG相比微调在成本、时效性、可解释性上的优劣" → 拆为3个子问题分别检索
查询扩展(Query Expansion):用同义词/相关词扩展检索范围
"大模型安全" → 扩展为 ["LLM安全", "AI对抗攻击", "提示词注入", "模型越狱"]
HyDE(Hypothetical Document Embedding):让LLM先假设性回答,用假答案去检索
模型先对问题生成一个"假答案",用假答案的向量去检索——因为假答案的措辞更接近文档
检索后优化(Post-Retrieval):
重排序(Reranking):如上所述,双阶段检索
上下文压缩(Context Compression):对检索到的长文本做摘要
上下文过滤:用相关性模型过滤掉低质量chunk
去重:去除内容重复的chunk
3.3 第三代:Modular RAG(模块化RAG)
将RAG拆解为可插拔的模块,按需编排:
| 模块 | 功能 | 可选策略 |
|---|---|---|
| 检索 | 从知识库获取信息 | 向量检索 / 关键词检索 / 混合检索 / 图谱检索 |
| 路由 | 决定是否需要检索 | 直接回答 / 检索后回答 / 多跳检索 |
| 融合 | 多源结果合并 | RRF(倒数排名融合)/ 加权融合 |
| 排序 | 对候选结果排序 | 交叉编码器 / LLM打分 |
| 记忆 | 跨轮对话状态 | 对话历史压缩 / 实体记忆 |
| 生成 | LLM生成回答 | 标准生成 / 自我反思 / 多步推理 |
| 校验 | 检查输出质量 | 事实核查 / 置信度评估 / 引用验证 |
四、前沿RAG架构:从被动检索到自主决策
4.1 Self-RAG(自反思RAG)
核心思想:让模型自己决定是否需要检索,并在生成后自我评估回答质量。
模型在生成过程中输出特殊标记(reflection tokens):
| 标记 | 含义 | 取值 |
|---|---|---|
[Retrieve] | 是否需要检索 | yes / no / continue |
[IsRel] | 检索结果是否相关 | relevant / irrelevant |
[IsSup] | 生成的回答是否被检索内容支持 | fully / partially / no |
[IsUse] | 回答对用户是否有用 | 5 / 4 / 3 / 2 / 1 |
工作流:
用户提问 → [Retrieve=yes] → 检索 → [IsRel=relevant] → 生成句子 ↓ [IsSup=fully] → [IsUse=5] → 输出 [IsSup=no] → 重新检索/重写
模型在每句话生成时都会自问:"这句话有检索证据支持吗?"如果答案是否,则重新检索。
4.2 Corrective RAG(CRAG,纠正式RAG)
核心思想:在检索后增加一个"检索评估器",根据检索质量动态调整策略。
检索结果 → 评估器打分 → [Correct: 高置信] → 直接生成 → [Incorrect: 低置信] → 触发网络搜索补充 → [Ambiguous: 中等] → 两者结合
关键创新:引入了"知识精炼"步骤——将检索到的文档做去噪和关键信息提取,而非直接拼接原文。
4.3 Adaptive RAG(自适应RAG)
核心思想:用分类器判断查询复杂度,动态选择检索深度。
用户提问 → 复杂度分类器 → [简单问题] → 单次向量检索 → 生成 → [中等问题] → 单次检索 + 重排序 → 生成 → [复杂问题] → 多跳检索 + 查询分解 → 生成
分类器实现:可用轻量LLM或传统ML模型,基于查询长度、实体数量、推理步骤数等特征判断复杂度。
4.4 GraphRAG(图谱RAG)
核心思想:将文档构建为知识图谱,利用图结构进行多跳推理。
构建过程:
LLM从文档中抽取实体和关系
构建知识图谱(节点=实体,边=关系)
使用社区检测算法(如Leiden)将图分为社区
为每个社区生成摘要
检索方式:
局部检索:从问题出发,找到相关实体的一跳邻居
全局检索:遍历社区摘要,回答宏观性问题
混合检索:局部+全局结合
优势:传统向量检索无法回答"这本书中所有角色之间的关系是什么"这类全局性问题,GraphRAG可以。
4.5 Agentic RAG(智能体RAG)
核心思想:将RAG嵌入Agent框架,让LLM自主决定何时检索、检索什么、检索几次。
用户提问 → Agent思考 → [需要检索?] ├─ 是 → 选择检索工具 → 检索 → 评估结果 → [足够?] │ ├─ 是 → 生成回答 │ └─ 否 → 重写查询再检索 └─ 否 → 直接回答
与其他架构的关系:Agentic RAG是Self-RAG、CRAG、Adaptive RAG的"集大成者"——它将检索决策交给Agent的推理循环,而非固定管线。
五、RAG vs 微调 vs 长上下文:如何选
这是工程实践中最常被问到的问题。三者的本质区别在于知识存储位置不同。
5.1 技术本质对比
| 维度 | RAG | 微调 | 长上下文 |
|---|---|---|---|
| 知识位置 | 外部数据库(显式) | 模型权重(隐式) | 上下文窗口(临时) |
| 更新成本 | 低(更新数据库) | 高(重新训练) | 零(直接放入prompt) |
| 知识规模 | 无限(数据库扩展) | 受模型容量限制 | 受上下文窗口限制 |
| 延迟 | 中(检索耗时) | 低(无检索) | 高(长输入推理慢) |
| 可溯源性 | 强(可引用来源) | 弱(知识内化后不可追溯) | 强(上下文可见) |
| 适合 | 频繁变化的事实知识 | 稳定的行为模式/领域术语 | 单次分析大量文档 |
| 不适合 | 需要低延迟 | 知识频繁更新 | 文档量巨大(成本高) |
5.2 选择决策树
知识是否频繁变化? ├─ 是 → RAG(动态知识库) └─ 否 → 需要改变模型的输出风格/格式吗? ├─ 是 → 微调(塑造行为模式) └─ 否 → 文档总量大吗? ├─ 大(数千+)→ RAG ├─ 小(几十份)→ 长上下文(简单直接) └─ 中等 → RAG + 微调结合
5.3 何时组合使用
实际生产中,三者经常组合:
微调 + RAG:微调让模型学会行业术语和输出格式,RAG提供最新事实
长上下文 + RAG:长上下文处理当前对话的完整历史,RAG检索外部知识
三者结合:微调塑造风格,RAG提供知识,长上下文处理当前文档
六、RAG评估:如何衡量质量
6.1 RAGAS评估框架
RAGAS(RAG Assessment)是当前最流行的RAG评估框架,定义了四个核心维度:
| 维度 | 含义 | 评估方式 |
|---|---|---|
| Faithfulness(忠实度) | 回答是否基于检索内容,无幻觉 | LLM判断每个声明是否被context支持 |
| Answer Relevancy(回答相关性) | 回答是否切题 | LLM从答案反向生成问题,计算与原问题的相似度 |
| Context Precision(上下文精确度) | 检索到的内容中有多少是相关的 | 排序质量评估(相关chunk是否排在前面) |
| Context Recall(上下文召回率) | 标准答案中的信息是否都被检索到 | 对比ground truth和检索内容 |
6.2 端到端评估指标
| 层级 | 指标 | 说明 |
|---|---|---|
| 检索层 | Recall@k | top-k结果中包含正确答案的比例 |
| 检索层 | MRR | 正确答案的平均倒数排名 |
| 检索层 | NDCG | 考虑排序质量的归一化指标 |
| 生成层 | Faithfulness | 幻觉率(1 - 幻觉率 = 忠实度) |
| 生成层 | Answer Relevancy | 回答与问题的语义相关度 |
| 系统层 | 端到端准确率 | 最终回答的正确率 |
| 系统层 | 延迟 | 从提问到回答的端到端时间 |
七、工程实践中的关键决策
7.1 切分策略选择
| 策略 | 适用场景 | 优势 | 风险 |
|---|---|---|---|
| 固定长度 | 通用文本 | 简单可控 | 可能切断语义 |
| 按段落 | 结构化文档 | 保持语义完整 | 段落长度不均 |
| 按标题/章节 | Markdown/HTML | 语义最强 | chunk大小差异大 |
| 语义切分 | 高精度场景 | 语义最优 | 实现复杂 |
| 递归切分 | 通用(推荐) | 平衡效果与复杂度 | 需调参 |
7.2 混合检索
单一向量检索的局限在于它擅长语义匹配但不擅长精确匹配。混合检索同时使用向量检索和关键词检索(BM25),然后融合结果:
# 混合检索 vector_results = vector_db.search(query, k=20) # 语义召回 keyword_results = bm25_search(query, k=20) # 关键词召回 # RRF融合 def rrf_fusion(rank_lists, k=60): scores = {} for rank_list in rank_lists: for rank, doc in enumerate(rank_list): scores[doc] = scores.get(doc, 0) + 1 / (k + rank) return sorted(scores, key=scores.get, reverse=True) final_results = rrf_fusion([vector_results, keyword_results])7.3 常见踩坑清单
嵌入模型不匹配:建库用的嵌入模型和查询用的不一致,导致向量空间不对齐
chunk过大:一个chunk包含多个话题,检索精度暴跌
无重排序:只做向量检索不做rerank,精度上限低
top-k过大:塞入过多不相关内容,导致LLM"注意力分散"
无引用溯源:生成回答不标注来源,用户无法验证
知识库不更新:向量索引过期,检索到的是旧信息
无评估闭环:不知道RAG效果如何,盲目调参
只做向量检索:忽略了精确匹配场景(如产品编号、人名)
八、RAG与MCP的协同
RAG和MCP不是竞争关系,而是互补——RAG负责知识注入,MCP负责工具连接:
用户提问 → Agent推理 │ ├─ 需要知识? → RAG检索向量库 → 注入context │ ├─ 需要行动? → MCP调用工具 → 执行操作 │ └─ 都需要? → 先RAG检索知识 → 再MCP调用工具
实际案例:财务Agent回答"本月营收与上月相比差异原因"
RAG检索内部知识库中的财务分析模板和历史报告
MCP调用SAP Server查询本月和上月营收数据
LLM结合RAG提供的分析框架和MCP返回的数据,生成差异分析报告
九、总结速查
| 维度 | 核心洞察 |
|---|---|
| 本质 | 检索外部知识注入LLM提示词,解决知识截止、幻觉、私有知识缺失 |
| 架构 | 离线建库(切分→嵌入→入库)+ 在线检索(查询→检索→重排→生成) |
| 三代演进 | Naive(直连)→ Advanced(前后优化)→ Modular(可插拔) |
| 前沿架构 | Self-RAG(自反思)、CRAG(纠正式)、Adaptive(自适应)、GraphRAG(图谱)、Agentic(智能体) |
| vs 微调 | RAG管"说什么"(知识),微调管"怎么说"(风格) |
| vs 长上下文 | 文档量大用RAG,文档量小用长上下文 |
| 评估 | RAGAS四维度:忠实度、回答相关性、上下文精确度、上下文召回 |
| 工程要点 | 切分策略、混合检索、重排序、引用溯源、评估闭环 |
| 与MCP协同 | RAG注入知识,MCP调用工具,两者互补 |
参考资料
Lewis et al. "Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks" (NeurIPS 2020) — RAG奠基论文
Gao et al. "Retrieval-Augmented Generation for Large Language Models: A Survey" (arXiv 2024)
Self-RAG: Asai et al. "Learning to Retrieve, Generate, and Critique through Self-Reflection"
CRAG: Yan et al. "Corrective Retrieval Augmented Generation"
GraphRAG: Edge et al. "From Local to Global: A Graph RAG Approach to Query-Focused Summarization"
RAGAS: Es et al. "RAGAS: Automated Evaluation of Retrieval Augmented Generation"
arXiv 2506.00054 "RAG: A Comprehensive Survey of Architectures, Enhancements, and Robustness"
