RAG 面试八股文完全指南
本文全面解析 RAG(检索增强生成)技术栈,覆盖从基本原理到高级范式、评估方法、实际部署挑战,以及 Transformer、Function Call、MCP 等相关知识点。
1. RAG 的工作原理及优势
什么是 RAG?
RAG(Retrieval-Augmented Generation) 是一种将信息检索和文本生成结合的技术范式。核心思想是:在 LLM 生成回答之前,先从外部知识库中检索相关文档,将检索到的内容作为上下文注入 Prompt,再让 LLM 基于这些上下文生成回答。
工作流程
用户提问
↓
Query 向量化(Embedding)
↓
向量数据库检索 → Top-K 相关文档
↓
将文档拼接到 Prompt 中
↓
LLM 基于上下文生成回答
↓
返回给用户
RAG vs 微调
| 维度 | RAG | 微调(Fine-tuning) |
|---|---|---|
| 知识更新 | 更新知识库即可,实时生效 | 需要重新训练模型 |
| 成本 | 低(无需训练 GPU) | 高(需要大量计算资源) |
| 可追溯性 | 可以引用来源文档 | 知识隐含在参数中 |
| 幻觉控制 | 有外部事实约束 | 仍可能产生幻觉 |
| 领域适应 | 添加领域文档即可 | 需要领域数据微调 |
| 数据隐私 | 数据留在本地 | 数据需参与训练 |
RAG 的核心优势
- 减少幻觉:回答有据可查,不是凭空编造
- 知识时效性:随时更新知识库,无需重训模型
- 可解释性:可以追溯答案来源
- 成本低:无需 GPU 训练,只需要向量数据库
- 数据安全:敏感数据不需要输入到模型训练中
2. RAG 如何解决上下文窗口有限的问题
问题本质
LLM 的上下文窗口是有限的(4K ~ 200K tokens),而企业知识库可能包含数百万甚至上亿的文档。不可能把所有文档都塞入 Prompt。
RAG 的解决方案
RAG 的核心思路是**"用检索代替记忆"**:
- 离线阶段:将所有文档切块、向量化,存入向量数据库
- 在线阶段:根据用户问题,只检索最相关的 Top-K 文档块
- 只传递相关内容:将 K 个文档块(通常 3
10 个,每个 200500 tokens)注入 Prompt
这样,即使知识库有 1TB 的数据,实际注入 Prompt 的只有几千 tokens 的精华内容。
本质
RAG 将 LLM 从**"百科全书"模式转变为"开卷考试"模式**——不需要记住所有知识,只需要在需要时能找到正确的参考资料。
3. RAG、Function Call、MCP
RAG
如上所述,RAG 是检索增强生成,让 LLM 在回答前先检索相关知识。
Function Call(函数调用)
Function Call 是 LLM 调用外部工具/API 的能力。
原理
用户:明天北京天气怎么样?
↓
LLM 判断:需要调用天气 API
↓
输出结构化调用:get_weather(city="北京", date="明天")
↓
应用层执行 API → 返回结果
↓
LLM 整合结果:明天北京晴,最高温 25°C...
- LLM 不直接执行函数,而是输出调用意图
- 应用层负责实际执行
- 结果返回后 LLM 进行整合
MCP(Model Context Protocol)
MCP 是 Anthropic 提出的模型上下文协议,是一个开放标准,用于统一 LLM 与外部数据源和工具的连接方式。
原理
MCP 采用 Client-Server 架构:
LLM 应用(MCP Client)
↕ JSON-RPC
MCP Server A(数据库)
MCP Server B(文件系统)
MCP Server C(API 服务)
核心概念
| 概念 | 说明 |
|---|---|
| Resources | 数据暴露(文件、数据库记录等) |
| Tools | 可调用的函数/操作 |
| Prompts | 预定义的 Prompt 模板 |
| Sampling | Server 向 Client 请求 LLM 生成 |
MCP vs Function Call
| 维度 | Function Call | MCP |
|---|---|---|
| 范围 | 单一 LLM 供应商 | 跨供应商通用协议 |
| 标准化 | 各家格式不同 | 统一的 JSON-RPC |
| 发现机制 | 手动定义工具 | Server 自动暴露能力 |
| 双向通信 | 单向(LLM → 工具) | 双向(支持 Sampling) |
三者关系
RAG:LLM + 知识检索
Function Call:LLM + 工具调用
MCP:统一的协议标准,让 LLM 与任意数据源/工具交互
4. WebSocket 和 SSE 的区别
这两种技术常用于 LLM 应用中的流式输出(Streaming)。
SSE(Server-Sent Events)
- 单向通信:只能服务端 → 客户端
- 基于 HTTP:使用标准 HTTP 连接
- 自动重连:内置断线重连机制
- 文本格式:只支持文本数据
- 简单:实现和使用都很简单
Client ──HTTP GET──→ Server
Client ←── data: token1 ── Server
Client ←── data: token2 ── Server
Client ←── data: [DONE] ── Server
WebSocket
- 双向通信:客户端 ↔ 服务端
- 独立协议:ws:// 或 wss://
- 需手动重连:无内置重连
- 支持二进制:文本和二进制都支持
- 更复杂:需要额外的握手和状态管理
Client ──Upgrade──→ Server(握手)
Client ←→ 双向消息 ←→ Server
对比
| 维度 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(Server→Client) | 双向 |
| 协议 | HTTP | ws:// |
| 重连 | 自动 | 手动 |
| 数据类型 | 文本 | 文本 + 二进制 |
| 复杂度 | 低 | 高 |
| 代理/防火墙 | 兼容好(HTTP) | 可能被阻断 |
LLM 场景选型
- SSE:适合大部分 LLM 流式输出场景(如 ChatGPT),客户端只需要接收即可
- WebSocket:适合需要实时双向交互的场景(如语音对话、协同编辑、Agent 长连接)
大部分 LLM API(OpenAI、Anthropic)的流式响应都使用 SSE。
5. 完整的 RAG 流水线
离线阶段(索引构建)
Step 1:数据采集
- 从各种数据源收集文档(PDF、网页、数据库、API)
- 数据清洗:去除噪声、格式标准化
Step 2:文档解析
- PDF 解析:提取文本、表格、图片
- HTML 解析:提取正文,去除导航等无关内容
- 结构化提取:识别标题层级、列表、代码块
Step 3:文本切块(Chunking)
- 将长文档切分为适当大小的块(Chunk)
- 策略:固定大小、语义分割、段落分割等
- 保留元数据:来源、页码、标题等
Step 4:向量化(Embedding)
- 使用 Embedding 模型将每个 Chunk 转为向量
- 常用模型:OpenAI text-embedding-3、BGE、GTE、E5
Step 5:索引存储
- 将向量和原文存入向量数据库
- 建立倒排索引(用于关键词检索)
- 存储元数据用于过滤
在线阶段(检索与生成)
Step 6:Query 处理
- 用户输入 → Query 改写/扩展
- Query 向量化
Step 7:检索
- 向量相似度检索(语义匹配)
- 可选:混合检索(向量 + 关键词)
- 重排序(Reranking)
Step 8:上下文组装
- 将 Top-K 文档块组装到 Prompt 模板中
- 添加系统指令(如"仅根据以下上下文回答")
Step 9:生成
- LLM 基于上下文生成回答
- 可选:引用来源标注
Step 10:后处理
- 格式化输出
- 来源引用
- 安全检查
6. 文本切块策略
关键参数
Chunk Size(块大小)
- 太小(< 100 tokens):语义不完整,检索噪声大
- 太大(> 1000 tokens):包含无关信息,降低检索精度
- 推荐范围:200 ~ 500 tokens
Chunk Overlap(重叠长度)
- 目的:避免关键信息被切断在两个块的交界处
- 推荐:Chunk Size 的 10%~20%
常见策略
| 策略 | 原理 | 适用场景 |
|---|---|---|
| 固定大小 | 按固定 token 数切分 | 通用场景,简单高效 |
| 递归字符 | 按段落→句子→字符递归切分 | 保持语义完整性 |
| 语义分割 | 用 Embedding 检测语义边界 | 高质量需求 |
| 文档结构 | 按标题/章节切分 | 结构化文档 |
| 句子窗口 | 检索句子,但返回周围上下文 | 精确检索 + 完整上下文 |
权衡
小 Chunk ←──────────────────────→ 大 Chunk
精确匹配 ←─ 检索精度 ─→ 更多上下文
上下文不足 ←─ 信息完整 ─→ 包含噪声
碎片化 ←─ 语义连贯 ─→ 主题混杂
数量多 ←─ 索引规模 ─→ 数量少
实际建议
- 通用文档:512 tokens,overlap 50 tokens
- 技术文档:按代码块/函数切分
- 法律/合同:按条款切分
- FAQ:每个 Q&A 对作为一个 Chunk
- 多粒度索引:同时存储不同粒度的 Chunk,检索时自适应选择
7. Embedding 模型选择与评估
如何选择
考虑因素
- 语言支持:是否支持中文?多语言能力如何?
- 向量维度:维度越高表达能力越强,但存储和计算成本也越高
- 最大输入长度:能处理多长的文本?
- 性能:在公开 Benchmark 上的表现
- 部署成本:模型大小、推理速度、是否需要 GPU
主流模型
| 模型 | 维度 | 最大长度 | 特点 |
|---|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 8191 | 商用首选,效果好 |
| BGE-M3 | 1024 | 8192 | 多语言,开源 |
| GTE-Qwen2 | 1536 | 8192 | 中文效果优秀 |
| E5-mistral-7b | 4096 | 32768 | 长文本,效果顶尖 |
| Cohere embed-v3 | 1024 | 512 | 商用,多语言 |
评估指标
检索质量指标
| 指标 | 含义 |
|---|---|
| Recall@K | Top-K 结果中包含正确文档的比例 |
| MRR | 第一个正确结果的排名倒数的平均值 |
| NDCG@K | 考虑排名位置的检索质量 |
| Precision@K | Top-K 中相关文档的比例 |
语义质量指标
| 指标 | 含义 |
|---|---|
| STS(Semantic Textual Similarity) | 语义相似度任务得分 |
| Clustering | 聚类质量 |
| Classification | 向量用于分类的效果 |
实用指标
- 推理速度:每秒处理多少文本
- 存储成本:向量维度 × 文档数 × 4 bytes
- MTEB Leaderboard:综合评测排行榜(最权威)
8. 提升检索质量的技术
Query 层面
1. Query Rewriting(查询改写)
用 LLM 将用户的口语化问题改写为更适合检索的形式。
原始:这个东西怎么用
改写:[产品名] 的使用方法和操作步骤
2. HyDE(Hypothetical Document Embeddings)
让 LLM 先生成一个"假设性答案",用这个答案的向量去检索,而非用问题的向量。
问题:"RAG 的优点是什么?"
假设答案:"RAG 的优点包括减少幻觉、知识实时更新..."
用假设答案的向量去检索 → 匹配更精准
3. Multi-Query(多查询)
将一个问题扩展为多个不同角度的子查询,分别检索后合并结果。
检索层面
4. 混合检索(Hybrid Search)
最终得分 = α × 向量相似度 + (1-α) × BM25 关键词得分
结合语义匹配和关键词匹配的优势。
5. Reranking(重排序)
使用 Cross-Encoder 模型对初步检索的结果进行精排。
初始检索(Bi-Encoder) → Top-50
↓
重排序(Cross-Encoder) → Top-5
Cross-Encoder 比 Bi-Encoder 更准确,但速度慢,所以先粗召回再精排。
6. Metadata Filtering(元数据过滤)
利用文档的元数据(时间、类别、来源)进行预过滤,缩小检索范围。
索引层面
7. 层级索引
- 第一层:文档摘要索引
- 第二层:文档块索引
先用摘要定位相关文档,再在文档内检索具体段落。
8. 父子文档检索
- 用小块检索(精确匹配)
- 返回大块内容(完整上下文)
9. RAG 系统性能评估
检索阶段评估
| 指标 | 计算方式 | 含义 |
|---|---|---|
| Recall@K | 相关文档在 Top-K 中的比例 | 检索的全面性 |
| Precision@K | Top-K 中相关文档的比例 | 检索的精确性 |
| MRR | 1/首个相关文档排名 | 排名质量 |
| NDCG | DCG/IDCG | 排序质量(考虑位置) |
| Hit Rate | 至少检索到 1 个相关文档的比例 | 检索覆盖率 |
生成阶段评估
| 指标 | 含义 |
|---|---|
| Faithfulness | 回答是否忠于检索到的上下文(不编造) |
| Relevance | 回答与问题的相关性 |
| Correctness | 回答的事实正确性 |
| Completeness | 回答是否完整覆盖了问题 |
| Harmfulness | 回答是否包含有害内容 |
端到端评估
| 指标 | 说明 |
|---|---|
| Answer Accuracy | 最终答案的正确率 |
| Latency | 从用户提问到返回答案的时间 |
| Cost | 每次查询的 Token / API 成本 |
常用评估框架
- RAGAS:RAG Assessment,自动评估 RAG 系统的开源工具
- TruLens:基于 LLM 的反馈评估
- DeepEval:包含 RAG 评估指标的测试框架
RAGAS 核心指标
Faithfulness = 回答中可归因于上下文的陈述比例
Context Relevancy = 检索内容中与问题相关的比例
Answer Relevancy = 回答与问题的语义相似度
Context Recall = 检索内容覆盖标准答案的程度
10. 图数据库 / 知识图谱增强检索
何时选择知识图谱
- 实体关系复杂:数据包含大量实体间的关系(人物关系、组织结构)
- 多跳推理:问题需要跨多个文档进行链式推理
- 结构化知识:领域知识有明确的层次和分类体系
- 精确查询:需要精确的属性匹配(而非模糊的语义匹配)
知识图谱 vs 向量数据库
| 维度 | 向量数据库 | 知识图谱 |
|---|---|---|
| 检索方式 | 语义相似度 | 图遍历 / SPARQL |
| 擅长场景 | 模糊语义匹配 | 精确关系查询 |
| 多跳推理 | 弱 | 强 |
| 数据结构 | 非结构化文本 | 三元组(实体-关系-实体) |
| 构建成本 | 低(自动向量化) | 高(需要实体/关系抽取) |
结合使用
用户问题
↓
├── 向量检索 → 相关文档段落
├── 图谱查询 → 相关实体和关系
└── 合并上下文
↓
LLM 生成回答
实际案例
问题:"张三的经理是谁?他管理哪些项目?"
- 向量检索:可能找到包含"张三"的文档,但难以精确追溯关系
- 知识图谱:
张三 →[汇报给]→ 李四 →[管理]→ [项目A, 项目B],直接得到精确答案
11. 高级 RAG 范式
传统 RAG(Naive RAG)
检索 → 生成(一次性)
Advanced RAG
1. 迭代检索(Iterative Retrieval)
在生成过程中多次检索,每次用上一步的输出引导下一次检索。
Query → 检索1 → 初步回答 → 生成新 Query → 检索2 → 完善回答
2. 自适应检索(Adaptive Retrieval)
LLM 自行决定是否需要检索以及何时检索。
代表工作:Self-RAG(Asai et al., 2023)
输入问题
↓
LLM 判断:是否需要检索?
├── 不需要 → 直接生成
└── 需要 → 检索 → 评估相关性 → 生成 → 评估是否需要更多信息
├── 够了 → 输出
└── 不够 → 再次检索
Self-RAG 训练模型输出特殊的 Reflection Token:
[Retrieve]:是否需要检索[IsRel]:检索结果是否相关[IsSup]:生成内容是否有支撑[IsUse]:回答是否有用
3. CRAG(Corrective RAG)
在检索后增加纠正机制:
检索 → 评估检索质量
├── 质量好 → 使用检索结果
├── 质量差 → 转为 Web 搜索
└── 模糊 → 部分使用 + Web 补充
4. Modular RAG
将 RAG 的各个环节模块化,可灵活组合:
[Query 模块] → [路由模块] → [检索模块] → [重排模块] → [生成模块]
↓
决定走 RAG 还是直接生成
12. RAG 部署挑战
数据质量
- 垃圾进,垃圾出:低质量文档会污染检索结果
- 数据更新:如何保持知识库与源数据同步
- 重复数据:重复文档会影响检索质量
检索质量
- 语义鸿沟:用户的问法与文档的表述不一致
- 长尾问题:冷门知识的检索效果差
- 多跳推理:需要综合多个文档的信息
延迟与成本
- Embedding 计算:大规模文档的向量化耗时
- 向量检索:数据量大时检索延迟增加
- LLM 调用:每次查询都需要调用 LLM
- 向量存储:高维向量的存储成本
安全与隐私
- 数据权限:不同用户应该只能检索到有权限的文档
- Prompt Injection:恶意文档可能被注入知识库
- 数据泄露:检索结果可能包含敏感信息
评估与监控
- 缺乏标准化的评估方法
- 线上效果难以实时监控
- 用户反馈收集和利用
可扩展性
- 向量数据库的水平扩展
- Embedding 模型的批量推理
- 多租户隔离
13. RAG 中的幻觉问题
什么是幻觉?
在 RAG 场景中,幻觉指 LLM 生成了与检索到的上下文不一致或没有事实依据的内容。
幻觉类型
| 类型 | 描述 |
|---|---|
| Intrinsic Hallucination | 与上下文矛盾的信息 |
| Extrinsic Hallucination | 上下文中没有、但模型自行编造的信息 |
| 混淆错误 | 把不同文档的信息混在一起 |
| 过度泛化 | 将个别案例错误地推广 |
预防策略
1. Prompt 工程
系统指令:
"仅根据以下提供的上下文回答问题。
如果上下文中没有相关信息,请明确说'根据提供的资料,我无法回答这个问题'。
不要使用你自己的知识来补充。"
2. 检索质量优化
- 提高检索精度 → LLM 得到更相关的上下文
- 过滤低相关性的文档 → 减少干扰信息
- 使用 Reranking → 确保最相关的文档排在前面
3. 引用标注
要求 LLM 在回答中标注每个陈述的来源文档,方便验证。
4. 事实验证
使用额外的 LLM 调用来验证生成内容是否忠于上下文:
验证 Prompt:
"以下回答是否完全基于提供的上下文?是否有任何编造的信息?"
5. 降低 Temperature
- 使用较低的 temperature(0 ~ 0.3)
- 减少生成的随机性,提高忠实度
6. Self-RAG
训练模型自我评估生成内容是否有支撑。
14. GraphRAG vs 传统 RAG
传统 RAG
文档 → 切块 → 向量化 → 向量数据库
查询 → 向量检索 → Top-K → LLM 生成
GraphRAG(微软)
GraphRAG 在传统 RAG 基础上引入了知识图谱和社区结构。
核心流程
文档 → LLM 抽取实体和关系 → 构建知识图谱
↓
图谱社区检测(Leiden 算法)→ 层级社区结构
↓
为每个社区生成摘要
两种查询模式
Local Search(局部搜索)
适合具体问题:
Query → 实体匹配 → 相关社区 → 社区摘要 + 原始文本 → 生成
Global Search(全局搜索)
适合宏观总结性问题:
Query → 所有社区摘要 → Map(分段摘要) → Reduce(合并) → 生成
核心区别
| 维度 | 传统 RAG | GraphRAG |
|---|---|---|
| 索引结构 | 向量索引 | 知识图谱 + 社区层级 |
| 检索方式 | 语义相似度 | 实体关系遍历 + 社区匹配 |
| 全局理解 | 弱(只看局部 Chunk) | 强(社区摘要提供全局视角) |
| 多跳推理 | 弱 | 强(图结构天然支持) |
| 构建成本 | 低 | 高(需要 LLM 抽取实体) |
| 索引更新 | 简单(增量向量化) | 复杂(需要重建图谱) |
| 适用场景 | 事实查询、局部信息 | 复杂关系、全局总结 |
15. RAG 检索结果为 0 的排查
当 RAG 系统返回 0 个检索结果时,按以下顺序排查:
1. 数据层检查
- 知识库是否为空? 检查向量数据库中的文档数量
- 数据是否正确入库? 抽样检查文档和向量是否正确存储
- 索引是否构建完成? 确认向量索引已构建且可用
2. 嵌入层检查
- Embedding 模型是否正常? 测试模型是否能正常输出向量
- Query 和文档用了同一个模型? 不同模型的向量空间不兼容
- 向量维度是否匹配? Query 向量和文档向量维度必须一致
3. 检索层检查
- 相似度阈值是否过高? 降低阈值看是否有结果
- Top-K 是否设置过小? 增大 K 值
- 是否有元数据过滤? 检查过滤条件是否过于严格
- 向量距离度量是否正确? 余弦相似度 vs 欧氏距离
4. Query 层检查
- 用户问题是否与知识库领域相关? 检查是否在覆盖范围内
- 问题语言与文档语言是否一致? 中英文混合可能影响匹配
- 问题是否太短/太模糊? 太短的 Query 向量可能不具有区分性
5. 快速验证
# 1. 直接用已知文档的文本作为 query 检索自身
result = vector_db.search("知识库中已存在的一段文字")
# 如果这样都检索不到,说明索引有问题
# 2. 检查向量相似度分布
scores = [r.score for r in vector_db.search(query, top_k=100)]
print(f"最高分: {max(scores)}, 最低分: {min(scores)}")
# 如果所有分数都很低,说明 query 和文档语义差距大
16. Transformer 架构
整体架构
Transformer(Vaswani et al., 2017)是现代 LLM 的基石架构,采用**编码器-解码器(Encoder-Decoder)**结构:
输入 → [Encoder] → 表示 → [Decoder] → 输出
- Encoder-only:BERT(理解任务)
- Decoder-only:GPT(生成任务)
- Encoder-Decoder:T5(翻译、摘要)
核心组件
1. Self-Attention(自注意力)
核心公式:
Attention(Q, K, V) = softmax(QK^T / √d_k) V
- Q(Query)、K(Key)、V(Value):输入的三种线性变换
- QK^T:计算每对 token 之间的关注程度
- √d_k:缩放因子,防止点积过大导致 softmax 梯度消失
- softmax:归一化为注意力权重
- 乘以 V:加权聚合信息
本质
让每个 token 都能"看到"序列中的所有其他 token,并根据相关性加权聚合信息。
2. Multi-Head Attention
MultiHead(Q, K, V) = Concat(head_1, ..., head_h) W^O
其中 head_i = Attention(QW_i^Q, KW_i^K, VW_i^V)
多个注意力头并行计算,每个头关注不同的模式(语法、语义、位置等)。
3. 位置编码(Positional Encoding)
Self-Attention 本身没有位置概念,需要额外注入位置信息。
- 原始方法:正弦/余弦位置编码
- 现代方法:RoPE(旋转位置编码)、ALiBi
4. Feed-Forward Network(FFN)
FFN(x) = max(0, xW_1 + b_1)W_2 + b_2
两层线性变换 + ReLU 激活,为每个位置独立地进行非线性变换。
5. Layer Normalization + Residual Connection
Output = LayerNorm(x + Sublayer(x))
- 残差连接:缓解深层网络的梯度消失
- 层归一化:稳定训练过程
KV Cache
在 Decoder 推理时,已生成的 token 的 K 和 V 可以缓存,避免重复计算。这是 LLM 推理优化的关键技术。
生成第 n 个 token 时:
- Q:只计算当前 token 的 Q
- K, V:使用缓存的 K, V + 当前 token 的 K, V
Transformer 的关键创新
- 并行化:不像 RNN 需要顺序处理,所有 token 同时计算
- 长距离依赖:任意两个 token 之间的距离都是 O(1)
- 可扩展性:架构简洁,易于扩展到超大规模
计算复杂度
- Self-Attention:O(n²d)(n 是序列长度,d 是维度)
- 这就是为什么上下文窗口扩展是一个挑战(n² 增长)
主要优化方向
| 方向 | 方法 |
|---|---|
| 减少注意力复杂度 | Flash Attention、稀疏注意力 |
| 扩展上下文窗口 | RoPE 外推、长度外推 |
| 推理加速 | KV Cache、投机解码、量化 |
| 训练效率 | 混合精度、梯度检查点 |