向量库能搜到内容,为什么还不算学会 RAG

刚开始学 RAG,很容易把目标定成一句话:把文档切块、转成向量,再让大模型根据搜索结果回答问题。

这条链路跑通当然重要,但它只证明"系统能返回内容"。被找回来的片段是不是正确、有没有漏掉关键证据、答案是否忠于资料、资料更新后能不能稳定生效,都还没有答案。

所以我更愿意把 RAG 当成一条需要逐段验证的数据链路,而不是某个框架的功能清单。这篇不是一份"我已经全部实测完"的教程,而是我根据 RAG 原始论文、综述和当前官方开发文档整理出的学习路线。目标很具体:知道先学什么、每一步练什么,以及做到什么程度才适合进入下一阶段。

先把 RAG 理解成两个系统,而不是一个聊天框

2020 年的 RAG 原始论文把它描述为参数化记忆与非参数化记忆的结合:模型参数负责语言能力,外部索引负责提供可以更新、可以追溯的知识。查询到来时,检索器先找到相关文档,生成器再结合问题与文档生成答案。

到了工程实现里,这通常可以拆成两条链路:

  • 离线链路:收集资料、清洗、切块、生成向量、写入索引;
  • 在线链路:理解问题、检索候选、排序或过滤、组织上下文、生成答案、返回引用。

这也是学习 RAG 时最先要建立的边界。文档成功入库,不代表查询能命中;查询能命中,不代表命中的片段足够回答;答案读起来顺畅,也不代表它忠于片段。

如果把这些状态揉成一个"效果不错",后面几乎无法定位问题。

第一阶段:先做一个最小闭环,但不要急着换框架

第一个练习不需要复杂知识库。找一份自己熟悉、可以人工核对的短文档,例如产品说明、个人笔记或一个小型开源项目的文档,然后准备 10 到 20 个问题。

问题不要全是能直接复制原句的简单题,至少包括:

  • 文档中明确写出答案的问题;
  • 需要连接两个片段才能回答的问题;
  • 容易被相近术语干扰的问题;
  • 文档里根本没有答案的问题。

最小流程可以是:加载文档,切成片段,为片段生成向量,保存索引;收到问题后检索 Top K 片段,把问题和片段交给模型,并要求资料不足时明确拒答。

下面只是概念伪代码,用来标出学习对象,不代表复制后即可运行:

ini 复制代码
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 和页眉页脚承担的语义完全不同。

这一阶段要练的不是"找到一个万能数字",而是保住文档结构。可以对同一份资料做三组对照:

  1. 固定字符切块;
  2. 按标题和段落切块;
  3. 按文档类型定制,例如 FAQ 保留问题与答案,代码保留函数边界。

每组都用同一批问题测试,并记录三个东西:正确答案需要的证据落在哪个片段、检索结果有没有召回它、片段是否带着足够的标题和来源信息。

这里很容易出现一个反直觉结果:切得更小不一定更准。小片段更容易匹配局部词义,却可能丢掉限定条件;切得太大又会混入无关内容,增加上下文成本。chunk size、overlap 和结构信息应该一起看,而不是单独调一个参数。

还要尽早加入 metadata,例如文档名、章节、更新时间、产品版本和权限范围。它们不只是为了展示引用,后面还会参与过滤、更新和访问控制。

第三阶段:先评估检索,再讨论模型回答

这是我认为最值得刻意练习的一步。

给每个测试问题标出"应该命中的证据片段",形成一份很小的答案集。然后暂时不让大模型回答,只检查检索结果:

  • Top K 中有没有正确证据;
  • 正确证据排在第几位;
  • 返回了多少看似相关但不能回答问题的片段;
  • 无答案问题是否仍被强行匹配。

这样能把"没找到"和"找到了但模型没用好"分开。

语义搜索擅长找到用词不同但含义接近的内容,关键词搜索则对专有名词、型号、错误码和精确短语更敏感。当前 OpenAI Retrieval 文档也提供了混合搜索权重与相关性阈值的调整入口。学习时可以用同一批问题比较关键词、向量和混合检索,但不要一上来就同时调整所有参数。

更稳妥的方法是一次只改变一个变量:先固定数据和切块,比较检索方式;再固定检索方式,调整 Top K;最后才考虑 query rewrite、rerank 或多路召回。

第四阶段:让生成器学会依据证据回答,也学会不回答

检索通过后,再把注意力放到生成阶段。

这里至少需要四条约束:

  • 答案以提供的资料为依据;
  • 关键结论能指向具体来源;
  • 多个来源冲突时暴露冲突,不自行拼成一个确定答案;
  • 资料不足时明确说不知道,并说明缺少什么。

评估时不要只看"像不像正确答案"。至少分开记录:

  • 答案是否回答了用户问题;
  • 答案中的事实是否得到检索片段支持;
  • 引用是否真的包含对应证据;
  • 没有答案时是否越过资料自行发挥。

RAG 综述把常见质量维度概括为上下文相关性、答案忠实度和答案相关性。它们正好对应三类故障:找错了、编多了、答偏了。

第五阶段:再学习高级 RAG,而不是反过来

当最小闭环已经有固定测试集,而且能定位问题发生在哪一层,才适合继续学习高级方法:

  • query rewrite:原始问题不适合直接检索时,先改写或拆解;
  • hybrid search:结合关键词与语义召回;
  • rerank:对候选片段重新排序;
  • parent-child retrieval:小片段负责命中,大片段负责提供完整上下文;
  • context compression:减少重复或低价值上下文;
  • iterative retrieval:回答复杂问题时分多轮检索;
  • metadata filter:按版本、部门、时间或权限缩小范围。

综述通常把 RAG 分成 Naive、Advanced 和 Modular 三类。这个分类适合用来理解技术演进,却不适合变成待安装的功能清单。每增加一个模块,都应该对应测试集中已经观察到的一类失败。

如果基础检索没有评估,增加 rerank 可能只是让错误结果换了顺序;如果文档结构已经在切块时丢失,换更大的模型也补不回来。

一条更适合工程实践的学习顺序

把前面的内容压缩成可执行路线,大致是这样:

第 1 周:看见完整链路

用一份小文档和 10 到 20 个问题跑通加载、切块、索引、检索和生成。强制打印中间结果,不追求界面。

第 2 周:只研究数据与切块

为同一份资料建立三种切块方案,对比正确证据能否进入 Top K,并检查标题、来源和版本信息是否保留。

第 3 周:建立最小评估集

给问题标注标准证据,区分检索命中、答案忠实和最终可用性。每次调整只改一个变量,保留前后结果。

第 4 周:处理真实失败

从 query rewrite、混合检索、rerank 和 metadata filter 中只选一个,解决测试集中已经存在的问题。不要为了"高级"而增加模块。

四周不是掌握 RAG 的承诺,只是一个控制学习范围的方法。真正的进度不应该用"看了多少教程"衡量,而应该看:能否解释一次错误答案究竟错在数据、索引、检索、上下文还是生成。

最后,给每次实验留一张记录表

每次修改至少留下这些字段:

css 复制代码
数据版本:
切块策略:
embedding 模型:
检索方式与 Top K:
是否 rerank:
命中的标准证据:
答案是否被证据支持:
无答案问题是否拒答:
本次只改变了什么:
下一步要验证什么:

这张表看起来没有框架示例那么炫,但它能防止学习过程变成"换一个模型再试试"。

RAG 的入口确实是向量检索,但真正的工程能力,是知道一条答案经过了哪些环节,在哪一层失真,以及下一次只该改哪里。

参考资料

相关推荐
mmsx1 小时前
MapLibre 实战 11|用户说"我的地块丢了":一个 sealed class 图层模型,和四个让我重构三版的坑
android·前端·开源
夫子3961 小时前
【第三部分:第一个 Agent 应用】13. 为 Agent 增加记忆能力:不是记住一切,而是在需要时想起正确的信息
llm·agent
xqchen1 小时前
代码差异可视化方案选型:diff2html 实战指南
前端
名字还没想好☜1 小时前
用 Zustand 做 React 全局状态管理:告别 Context 重渲染,3 个实战模式与持久化
前端
PPPCODE1 小时前
从零手写一个MCP Agent服务:stdio与SSE两种连接模式的踩坑实录
llm·agent·mcp
IMPYLH1 小时前
HTML 的 <section> 元素
前端·html
X1A0RAN2 小时前
密匣 PsdKeep:一个属于你自己的 Chrome 账号记事本
前端·chrome
Java的搬运工2 小时前
Agent 记忆系统难在取舍
agent
YDS8292 小时前
AI Agent 脚手架 —— Service层和Trigger层接口实现
ai·agent·spring ai