不少 RAG 初学者会把文档成功入库、向量检索能够返回结果视为阶段性完成,但真正影响问答质量的问题往往才刚刚出现。检索片段是否准确、关键证据是否遗漏、生成答案是否忠于资料,都需要分层验证。要真正掌握 RAG,重点是看清整条数据链路,并能定位每一次失败发生在哪个环节。
刚开始学 RAG,很容易把目标定成一句话:把文档切块、转成向量,再让大模型根据搜索结果回答问题。
这条链路跑通当然重要,但它只证明“系统能返回内容”。被找回来的片段是不是正确、有没有漏掉关键证据、答案是否忠于资料、资料更新后能不能稳定生效,都还没有答案。
所以我更愿意把 RAG 当成一条需要逐段验证的数据链路,而不是某个框架的功能清单。这篇不是一份“我已经全部实测完”的教程,而是我根据 RAG 原始论文、综述和当前官方开发文档整理出的学习路线。目标很具体:知道先学什么、每一步练什么,以及做到什么程度才适合进入下一阶段。
2020 年的 RAG 原始论文把它描述为参数化记忆与非参数化记忆的结合:模型参数负责语言能力,外部索引负责提供可以更新、可以追溯的知识。查询到来时,检索器先找到相关文档,生成器再结合问题与文档生成答案。
到了工程实现里,这通常可以拆成两条链路:
这也是学习 RAG 时最先要建立的边界。文档成功入库,不代表查询能命中;查询能命中,不代表命中的片段足够回答;答案读起来顺畅,也不代表它忠于片段。
如果把这些状态揉成一个“效果不错”,后面几乎无法定位问题。
第一个练习不需要复杂知识库。找一份自己熟悉、可以人工核对的短文档,例如产品说明、个人笔记或一个小型开源项目的文档,然后准备 10 到 20 个问题。
问题不要全是能直接复制原句的简单题,至少包括:
最小流程可以是:加载文档,切成片段,为片段生成向量,保存索引;收到问题后检索 Top K 片段,把问题和片段交给模型,并要求资料不足时明确拒答。
下面只是概念伪代码,用来标出学习对象,不代表复制后即可运行:
chunks = split(load(document))
index = embed_and_store(chunks)
contexts = retrieve(index, question, top_k=5)
answer = generate(question, contexts, require_citations=true)
这一步的学习目标不是记住 API,而是能够打印出每个中间结果:原始文档、切块结果、检索分数、最终上下文和答案引用。看不见中间状态的 RAG,很快会变成只能反复调整提示词的黑盒。
LangChain 当前的 RAG 文档同样把索引过程拆成加载、切分、向量化和存储。LlamaIndex 则把完整流程概括为加载、索引、存储、查询和评估五个阶段。这两个划分很适合做第一张学习地图。
不少入门示例会给出一个固定 chunk size,然后直接进入向量检索。但真实资料不是等长的:标题、正文、表格、代码、FAQ 和页眉页脚承担的语义完全不同。
这一阶段要练的不是“找到一个万能数字”,而是保住文档结构。可以对同一份资料做三组对照:
每组都用同一批问题测试,并记录三个东西:正确答案需要的证据落在哪个片段、检索结果有没有召回它、片段是否带着足够的标题和来源信息。
这里很容易出现一个反直觉结果:切得更小不一定更准。小片段更容易匹配局部词义,却可能丢掉限定条件;切得太大又会混入无关内容,增加上下文成本。chunk size、overlap 和结构信息应该一起看,而不是单独调一个参数。
还要尽早加入 metadata,例如文档名、章节、更新时间、产品版本和权限范围。它们不只是为了展示引用,后面还会参与过滤、更新和访问控制。
这是我认为最值得刻意练习的一步。
给每个测试问题标出“应该命中的证据片段”,形成一份很小的答案集。然后暂时不让大模型回答,只检查检索结果:
这样能把“没找到”和“找到了但模型没用好”分开。
语义搜索擅长找到用词不同但含义接近的内容,关键词搜索则对专有名词、型号、错误码和精确短语更敏感。当前 OpenAI Retrieval 文档也提供了混合搜索权重与相关性阈值的调整入口。学习时可以用同一批问题比较关键词、向量和混合检索,但不要一上来就同时调整所有参数。
更稳妥的方法是一次只改变一个变量:先固定数据和切块,比较检索方式;再固定检索方式,调整 Top K;最后才考虑 query rewrite、rerank 或多路召回。
检索通过后,再把注意力放到生成阶段。
这里至少需要四条约束:
评估时不要只看“像不像正确答案”。至少分开记录:
RAG 综述把常见质量维度概括为上下文相关性、答案忠实度和答案相关性。它们正好对应三类故障:找错了、编多了、答偏了。
当最小闭环已经有固定测试集,而且能定位问题发生在哪一层,才适合继续学习高级方法:
综述通常把 RAG 分成 Naive、Advanced 和 Modular 三类。这个分类适合用来理解技术演进,却不适合变成待安装的功能清单。每增加一个模块,都应该对应测试集中已经观察到的一类失败。
如果基础检索没有评估,增加 rerank 可能只是让错误结果换了顺序;如果文档结构已经在切块时丢失,换更大的模型也补不回来。
把前面的内容压缩成可执行路线,大致是这样:
用一份小文档和 10 到 20 个问题跑通加载、切块、索引、检索和生成。强制打印中间结果,不追求界面。
为同一份资料建立三种切块方案,对比正确证据能否进入 Top K,并检查标题、来源和版本信息是否保留。
给问题标注标准证据,区分检索命中、答案忠实和最终可用性。每次调整只改一个变量,保留前后结果。
从 query rewrite、混合检索、rerank 和 metadata filter 中只选一个,解决测试集中已经存在的问题。不要为了“高级”而增加模块。
四周不是掌握 RAG 的承诺,只是一个控制学习范围的方法。真正的进度不应该用“看了多少教程”衡量,而应该看:能否解释一次错误答案究竟错在数据、索引、检索、上下文还是生成。
每次修改至少留下这些字段:
数据版本:
切块策略:
embedding 模型:
检索方式与 Top K:
是否 rerank:
命中的标准证据:
答案是否被证据支持:
无答案问题是否拒答:
本次只改变了什么:
下一步要验证什么:
这张表看起来没有框架示例那么炫,但它能防止学习过程变成“换一个模型再试试”。
RAG 的入口确实是向量检索,但真正的工程能力,是知道一条答案经过了哪些环节,在哪一层失真,以及下一次只该改哪里。