RAG 实战第六课:给 RAG 装个质检员,检索错了自动纠错
生命不息,折腾不止。RAG 最怕的不是答不上来,是检索错了还一本正经地胡编——今天给它装个「质检员」,检索结果不合格就自动纠错。
前五课咱们从零手搓了一条文档问答链路:向量检索加混合检索(第二课)、reranker 精排加打分(第三课)、答案带出处加评测(第四课)、多轮记忆加 query 改写(第五课)。这条线越走越顺,但有个致命前提一直没点破:检索返回的东西,咱们默认它是对的。
可问题是,向量召回出来的 top-k 里,经常混着几条跟问题八竿子打不着的文档。传统 RAG 不管这个,捡到啥就往 prompt 里塞,模型一看上下文里全是杂音,就开始一本正经地胡说八道。这不是模型笨,是咱们把垃圾喂给了它。
今天收官这一课,就把这个洞补上:给 RAG 装一个「质检员」,先检查检索结果质量,合格才用,不合格就纠正。这套玩法有个正经名字,叫 CRAG(Corrective RAG,纠错式检索增强),2024 年初中科大、UCLA 和 DeepMind 的作者在一篇论文里提出来的(arXiv:2401.15884,官方代码 github.com/HuskyInSalt/CRAG)。
一、RAG 的命门:检索错了,后面全白搭
先用大白话说清楚传统 RAG 为什么容易翻车。
传统 RAG 的流程就三步:检索 → 塞进 prompt → 生成。中间没有任何「检查」环节,检索返回啥就用啥。一旦 top-k 召回质量差(语义漂移、关键词歧义、知识库本身就没答案),模型拿到的是一堆无关文本,它只能硬编。
CRAG 的作者把这个问题点得很透:LLM 本来就容易幻觉,你再用低质量检索去「补」,等于火上浇油。所以他们提的思路是——检索结果不能盲信,得先评估,评估不合格就纠正。
二、CRAG 的核心:一个「质检员」加三档判断
CRAG 在「检索」和「生成」之间,插进了一个轻量级的检索评估器(retrieval evaluator)。它的活儿很简单:拿「问题 + 每篇检索到的文档」当输入,给每篇文档打一个相关性分数,范围 -1 到 1。
然后根据分数做三档判断:
- Correct(合格):至少有一篇文档分数超过上阈值 τ+,说明知识库里有答案,直接精炼后使用。
- Incorrect(不合格):所有文档分数都低于下阈值 τ-,说明本地知识库根本没答案,把检索结果全部丢掉,转去联网搜索。
- Ambiguous(模棱两可):卡在中间,本地检索半信半疑,那就本地 + 联网两头都要,合并起来用。
论文里原版用的是微调过的 T5-large 当评估器,阈值也是分数据集调的(PopQA 上阈值 0.59、下阈值 -0.99;通用实现常取 0.5 和 -0.5)。微调 T5 对咱们来说太重了,工程上有个更省事的替代方案——直接拿 LLM 当质检员,效果完全不输,代价就是多调几次 API。
三、动手:拿 LLM 当质检员,给每篇文档打分
不搞花哨的,就用 OpenAI 兼容的 SDK 调一个聊天模型,让它只回一个数字:
1 | from openai import OpenAI |
几个心得:
temperature=0基本是必须的,打分这种活儿要的是稳定,不是创意。- 让模型「只输出一个数字」,别出是非题,否则它容易长篇大论给你解释。
- 分数范围定在 -1 到 1 而不是 0 到 1,是为了跟论文对齐,阈值好套。
四、三档判断加「拆条重组」,把纠正动作落地
拿到每篇文档的分数后,先汇总判断走哪条路:
1 | UPPER = 0.5 # 至少一篇 >= 0.5 → 合格 |
拿到 correct 之后,也不是把整篇文档全塞进去——文档里可能一大半是废话。CRAG 这里有个很骚的操作叫 decompose-then-recompose(拆条重组):
- Decompose(拆):把每篇文档切成更小的「知识条」(knowledge strips),比如按 200 字切、留 20 字重叠。
- Filter(滤):复用上面的质检员,给每条单独打分,只保留过阈值的条。
- Recompose(合):把留下来的条按原顺序拼回去。
这样就把一篇 2000 字的文档里真正有用的那几百字抠了出来,喂给模型的上下文干净又精炼。代码大概是这个形状:
1 | def decompose(doc: str, strip_size: int = 200, overlap: int = 20) -> list[str]: |
incorrect 和 ambiguous 要联网搜索,Tavily、Brave、Bing 都有现成 API(各家的价格与额度以官方文档为准),搜回来的文档同样过一遍 refine 再拼进 prompt。整套动作接上咱们前面几课写好的检索器,就是一条「会自我纠错」的 RAG 了。
五、上生产线前的三个提醒
CRAG 不是银弹,用之前心里得有数:
- 成本会上来。每次查询多了 N+1 次打分调用(N 是召回数)加可能的联网搜索。小知识库、低并发完全扛得住;量大就得分批评估或复用缓存。
- 阈值不是死的。0.5 / -0.5 是通用起步值,你实际跑一跑,看看「合格、模棱两可、不合格」三档的分布,再按业务调整。
- 质检员自己也可能瞎。LLM 打分偶尔抽风,给关键场景出个「分数兜底」逻辑(比如
refine里全滤了就退回原文),别让纠正机制反而把好文档丢了。
到这,RAG 实战六课就收官了。回头看这条线挺有意思:从「能查」(第一课),到「查得准」(第二、三课),到「答得可信」(第四课),到「记得住、会追问」(第五课),最后到「自我纠错」(这一课)——一个能扛事的检索增强问答系统,该有的零件咱们都亲手装过一遍了。
生命不息,折腾不止。RAG 实战系列到此收尾,下一篇起开新系列「Claude Code 实战」——第一课不聊安装(8 月那篇中转站教程已经把基本盘铺好),直接从 MCP 和自定义 Slash Command 这些进阶玩法入手,把 Claude Code 从「会聊天的助手」调成「能自己把活干完的工程师」。咱们下篇见。