RAG 原理与工程实践:让答案有证据,而不是只靠记忆
# RAG 原理与工程实践:让答案有证据,而不是只靠记忆
从数据进入知识库到答案引用,理解完整 RAG 链路;能解释召回、重排、生成和评测分别解决什么问题,并设计 Agentic RAG。
# 先记住一句话
RAG 先从外部知识源检索与问题相关的证据,再把经过筛选的片段放入 Context 生成答案;它解决知识可更新、可追溯和私有化问题,但不能自动保证检索正确或回答真实。
离线:文档 → 清洗 → 分片 → 元数据 → Embedding → 索引
在线:问题 → 查询改写 → 召回 → 过滤/重排 → 上下文 → 生成 → 引用与评测
2
# 离线索引阶段
# 文档加载与清洗
保留标题层级、代码块、表格和来源路径;删除导航、重复页脚等噪声。每个片段至少带文档 ID、路径、标题、更新时间、访问范围和版本。
# Chunking
Chunking 指把长文档拆成适合检索的小片段,本文统一称为文档分片。片段太大,会混入太多无关内容;片段太小,完整语义和上下文又容易断裂。
业内没有固定的分片方式总数,因为很多方案可以叠加使用。为了方便理解和面试表达,可以先归纳为下面八种常见策略:
| 分片方式 | 怎样分片 | 更适合 | 主要问题 |
|---|---|---|---|
| 固定长度分片 | 每隔固定字符数或 Token 数形成一个片段 | 快速建立基线、结构不明显的纯文本 | 可能截断句子、代码或表格 |
| 滑动窗口分片 | 按固定长度移动窗口,并在相邻片段中保留部分重叠 | 需要减轻边界处上下文断裂的文本 | 会产生重复内容,增加索引和 Context 成本 |
| 递归分片 | 依次尝试按标题、段落、句子等自然边界拆分,片段仍过长时再使用更小边界 | Markdown、说明文档和大多数混合文本 | 依赖分隔符设计,仍不理解真正的主题变化 |
| 文档结构分片 | 根据 Markdown 标题、Word 或 PDF 章节、HTML 元素、代码函数、表格行等结构形成片段 | 技术文档、网页、代码、表格和 FAQ | 不同文档类型需要不同解析器 |
| 语义分片 | 先对句子生成 Embedding 向量,再比较相邻句子的向量差异,在语义明显变化的位置形成新片段 | 结构不明显但主题会自然转换的长文章 | 成本更高,阈值不容易调,结果可能不够稳定 |
| 大模型认知分片 | 让大模型理解内容,识别主题边界和完整知识点,再决定片段边界 | 内容复杂、仅靠格式和向量距离不容易判断边界的文档 | 调用成本和延迟更高,结果需要约束和评测 |
| 父子分片 | 用较小的子片段参与检索,命中后返回包含它的较大父片段或完整章节 | 既需要精确召回,又需要完整上下文的场景 | 需要额外保存父子关系,占用更多存储 |
| 命题或知识单元分片 | 把复杂段落拆成能够独立理解的事实、问答或知识点 | 制度、客服知识库和高精度问答 | 预处理更复杂,拆分不当可能丢失事实之间的关系 |
“大模型认知分片” 是便于理解的中文叫法,英文一般称为 LLM-based Chunking。它的重点是让大模型根据内容含义判断知识边界,而不是只按长度或格式拆分。
这八种叫法便于复习,但它们并不完全处于同一层。固定长度、递归、文档结构、语义、大模型认知和知识单元分片主要解决怎样确定片段边界;滑动窗口主要解决怎样补偿片段边界;父子分片主要解决怎样组织索引和返回内容。
文档分片策略
├─ 确定片段边界
│ ├─ 固定长度分片、递归分片
│ ├─ 文档结构分片
│ ├─ 语义分片、大模型认知分片
│ └─ 命题或知识单元分片
├─ 补偿片段边界
│ └─ 滑动窗口与重叠
└─ 组织索引和召回内容
└─ 父子分片:小片段检索,大段内容返回
2
3
4
5
6
7
8
9
10
表格分片、代码 AST 分片、对话轮次分片和图谱分片,通常可以看作文档结构或知识单元分片在特定数据类型上的实现。例如代码应尽量保持函数或类完整,表格应保留表头与行列关系,FAQ 应保持问题和答案成对出现。
# 生产环境怎样组合
实际项目通常不会把所有文档直接交给大模型认知分片。更常见的方案是先利用确定的文档结构,再对超长内容递归处理,只在边界确实难判断时引入语义或大模型认知分片:
解析文档结构
└─ 按标题、段落、表格和代码块做结构分片
└─ 内容仍然过长 → 递归分片
└─ 边界仍不自然 → 语义或大模型认知分片调整边界
└─ 生成小块向量,并保留父章节与元数据
└─ 在线检索小块,向模型返回所需的父级内容
2
3
4
5
6
可以把这套组合记成:结构分片 + 递归兜底 + 语义优化 + 父子检索。它通常比所有文档都使用大模型认知分片更稳定,也更容易控制成本。
参数只能作为实验起点
普通文本可以先尝试每块 300~600 Token、重叠块大小的 10%~20%,父块可以从 1000~2000 Token 开始测试。标题和必要的上级路径可以附加到子块,但表格、代码和 FAQ 尽量不要从中间切开。这些数字不是行业标准,最终仍要结合文档类型、Embedding 模型、查询粒度和 Context 预算调整。
最终标准不是片段长度看起来是否整齐,而是正确证据能否稳定进入前几个检索结果。应使用真实问题集评估 Recall@K、回答正确率、引用准确率、延迟和成本;不少看似是向量模型的问题,根因其实是文档解析错误或分片边界不合理。
# Embedding 与索引
Embedding 将文本映射为向量,用相似度召回语义接近的片段。向量库负责存储、索引和元数据过滤;它不是知识本身,也不等于 RAG 的全部。
这里的向量化,就是使用 Embedding 模型,把一段文本转换成一组能够表示其语义特征的数字。例如:
“怎样防止 Agent 无限循环”
↓ Embedding 模型
[0.12, -0.47, 0.83, ...]
2
3
向量中的单个数字通常没有可以直接解释的业务含义;真正有用的是向量之间的距离。语义越接近的文本,向量通常也越接近,因此 “Agent 怎样设置停止条件” 即使没有复用原问题的全部关键词,也可能召回 “如何防止 Agent 无限循环” 的笔记片段。
一次向量检索可以简化为:
离线:原文片段 → Embedding 模型 → 文档向量 → 写入向量索引
在线:用户问题 → 同一 Embedding 模型 → 查询向量 → 相似度搜索 → 返回原文片段
2
向量化不是摘要、压缩或加密,也不能代替原文。向量库通常要同时保存或关联原文片段、来源、权限和更新时间;检索命中后,真正交给生成模型阅读的仍然是原文。文档与查询还应使用兼容的 Embedding 模型和向量维度,否则无法正确比较。
向量相似只代表语义上可能相关,不代表内容一定正确、完整或有权限访问。因此还需要元数据过滤、重排、引用和评测。
# 在线检索阶段
# 查询改写
对话中的 “它有什么区别” 无法直接检索,需要结合当前主题改写为独立问题。复杂问题还可以拆成多个子查询,但要限制数量并去重。
# 召回、过滤和重排
- 向量检索擅长语义相似;
- 关键词检索擅长专有名词、错误码和精确字符串;
- 混合检索结合两者;
- 元数据过滤先限制用户权限、知识库、语言和时间;
- reranker 对候选片段进行更精细排序。
# 生成与引用
Prompt 应要求只根据证据回答,证据不足就明确说明。引用必须能够回到真实文档和片段,不能让模型自己编路径。
# Agentic RAG 是什么
普通 RAG 通常按固定流程完成一次检索和生成;Agentic RAG 则引入 Agent 决策循环,让模型根据当前检索结果判断:是否需要改写查询、换知识库、补充关键词、读取完整原文,或者结束检索并生成答案。
这里通常仍然只有一个 Agent 和一个决策循环,只是这个循环可能执行多轮检索,并不等于存在多个 Agent。只有把不同数据源或任务交给多个专门 Agent 协作时,才属于 Multi-Agent RAG;它是可选架构,不是 Agentic RAG 的必要条件。
如果 “检索失败后重试三次” 的路径完全由代码预先写死,它仍然更接近普通 Workflow。Agentic 的关键是:模型会结合当前结果,动态决定下一步检索动作。
它更灵活,但也更贵、更慢、更难评测。因此先建立稳定的单次检索基线,再把 “证据不足时补查” 这类局部决策 Agent 化。
# 评测要拆成两层
| 层级 | 关键问题 | 常见指标 |
|---|---|---|
| Retrieval | 正确证据有没有被找回来、排得够不够前 | Recall@K、MRR、nDCG、命中率 |
| Generation | 回答是否由证据支持、是否完整 | 正确性、忠实度、引用准确率、拒答质量 |
可以先这样理解检索指标:Recall@K 看正确资料有没有进入前 K 个结果;MRR 看第一个正确结果排得有多靠前;nDCG 在存在多个不同相关程度的结果时,综合评价整个排序。面试时不必只背公式,先说清每个指标回答的问题。
如果正确文档根本没被召回,继续改 Prompt 通常没有意义;如果证据已在前几位但答案仍错,才重点检查上下文组织和生成规则。
# 贯穿项目的设计
笔记助手可以:
- 构建时解析 Markdown 标题和代码块,保存路径、模块和更新时间;
- 在线查询先按当前知识库和权限过滤,再做混合检索;
- 对前 20 个候选重排,只给模型前 5~8 个片段;
- 回答逐条携带来源链接;
- 没有足够证据时返回 “笔记中暂未覆盖”,并给出待补笔记主题;
- 用真实面试问题建立固定评测集,防止调整分片策略后效果回退。
# 高频面试题与回答
回答顺序:先说结论,再解释原因,最后补一个例子或工程边界。不要逐字背诵,记住这条表达主线即可。
1. 30 秒回答:RAG 的完整流程参考答案
RAG 可以分成离线准备和在线问答两部分。离线先清洗文档、切成有完整语义的片段,生成 Embedding,并把来源、权限和时间一起写入索引;在线收到问题后做检索和权限过滤,再对候选结果重排,把最相关的证据交给模型生成带引用的答案。评测时要分开看两件事:资料有没有找对,以及回答有没有忠实使用资料。
2. RAG 为什么仍然会幻觉?参考答案
因为 RAG 只是把资料找来,不保证资料一定找对,也不保证模型一定照着资料回答。错误可能来自没召回、资料冲突、内容被截断,或者模型自行补充。RAG 能降低无依据回答,但还需要引用检查、证据不足时拒答,并分别评测检索和生成两个阶段。
3. 如何选择 chunk 大小?参考答案
没有一个适合所有文档的固定长度。我会先按标题、段落或代码块等自然结构分片,让每个片段能独立表达一个主题,再用真实问题测试召回和回答效果。片段太小会丢上下文,太大又会混入噪声,最终要结合文档类型、查询粒度和 Context 预算调整。
4. RAG 常见的文档分片方式有哪些?参考答案
常见策略可以归纳为固定长度、滑动窗口、递归、文档结构、语义、大模型认知、父子分片以及命题或知识单元分片。语义分片会先对句子生成向量,再根据相邻句子的向量差异寻找边界;大模型认知分片则让 LLM 直接理解内容并判断知识边界。不过这些策略不完全在同一层:多数方法决定片段边界,滑动窗口负责补偿边界,父子分片负责小片段检索和大段内容返回。实际项目中我会先按文档结构分片,超长内容递归处理,必要时再用语义或大模型认知分片调整边界,并保留父章节和元数据。
5. 为什么权限过滤要在检索阶段完成?参考答案
因为未授权内容只要进入检索结果或模型 Context,就已经产生泄漏风险。服务端必须根据真实用户身份,在召回前或召回时过滤掉无权访问的文档。不能先把秘密交给模型,再靠 Prompt 要求它 “不要说出来”。
6. 什么时候需要 Agentic RAG?参考答案
当一个问题需要跨多个数据源、多次查找,或者第一次检索不足时,我会考虑引入 Agent 决策循环,让模型根据当前结果决定是改写查询、切换数据源、继续补查,还是停止检索并回答。这里通常是一个 Agent 在循环中执行多轮检索,不等于多个 Agent;只有不同 Agent 分工协作时才是 Multi-Agent RAG。如果普通 RAG 一次检索就很稳定,就不必 Agent 化,因为循环会增加延迟、费用和失败路径。
# 接下来学什么
下一篇学习 Memory 与 Checkpoint,重点区分临时上下文、持久记忆和任务状态,理解跨步骤、跨会话以及暂停恢复的问题。