AI纠错RAG:这次怎么落地的
最近做产品时遇到个实际问题:RAG 系统上线后,总会蹦出一些似是而非的回答。
我们的场景下用的是向量检索 + BM25 混合,召回 5-10 个文档片段。
背景和问题
先说下场景。我们做的是技术文档问答系统,基于官方文档库给用户提供答案。用户提问后,系统会:
- 检索相关文档片段
- 把片段和问题塞给大模型
- 模型生成回答
这套逻辑看起来没问题,但实际跑起来就暴露出问题了。有次用户问"这个 SDK 支持哪些数据格式",模型回答了一大堆:JSON、CSV、XML、Parquet,甚至还提了"Protobuf"。
查了下文档,实际上就支持 JSON 和 CSV,其他的要么是高级特性要么压根不支持。模型为什么会这样?因为它可能在训练时见过"Protobuf"这个词,又在这个上下文里"觉得"可以往里塞。
这不是单一现象。随便测了一晚上,发现大概 15% 的回答多多少少有问题:有的把 A 产品的特性说成 B 产品的,有的把旧文档内容当成现行的,有的干脆把完全不相关的技术点凑在一起。
用户当然不会买账。技术问答最重要的就是准确,错了还不如不答。
需求梳理
既然问题出现了,就得想个办法。一开始我也想过要不要上重排序(Rerank),或者提升检索质量,但这些更多是解决"找不找得到"的问题,而不是"说得对不对"的问题。
想了想,核心需求其实是:
- 能检测出回答中可能存在的不准确内容
- 能在检测到问题时,触发重新检索或重新生成
- 能控制整体的时延,不能为了纠正错误让用户等太久
换句话说,需要的是一个"自我审查"机制:模型生成回答后,再检查一遍,发现问题就修正。
设计思路
先梳理下流程。如果把 RAG 看成"检索 + 生成",那么 Corrective RAG 就是"检索 + 生成 + 审查 + 修正"。
用一个简单的流程图来表示:
关键在于 D 到 E 这段。怎么判断"是否通过"?不是让用户来判断,也不是人工校验,而是让模型自己来判断。
最朴素的想法:让模型看一遍自己生成的回答和检索到的文档,然后回答"这个回答是否基于检索到的文档内容?如果有不符合的地方,请指出"。
这个思路听上去简单,但落地时会遇到不少问题。先说大概的实现,再说踩过的坑。
实现步骤
1. 检索层先不动
先不折腾检索,用已有的检索能力。我们的场景下用的是向量检索 + BM25 混合,召回 5-10 个文档片段。
不先动检索是有原因的:如果连"找得到"都做不好,后面再怎么纠正也是白搭。但这个前提我们要默认成立。
2. 生成层多给一些约束
生成时可以加一些提示词约束,比如:
- “严格基于检索到的文档内容回答”
- “如果文档中没有相关信息,直接说不知道”
- “不要编造文档中没有提及的内容”
这些提示词有点用,但不能彻底解决问题。模型有时还是会在"语境不完整"的情况下补全内容,因为它觉得这样更"自然"。
3. 增加审查层
这是核心。生成回答后,用同一个(或更强的)模型来审查。
审查的提示词可以这样设计:
任务:审查以下回答是否准确基于检索到的文档。
原始问题:{question}
检索到的文档:
{retrieved_docs}
生成的回答:
{generated_answer}
请回答:
1. 回答中是否有与检索文档内容相矛盾的信息?
2. 回答中是否有检索文档完全未提及的内容?
3. 如果有问题,请指出具体是哪些部分,并说明为什么有问题。
如果回答完全基于文档内容,请回答"审查通过"。
这里有个关键点:审查必须"具体到部分",不能只给一个"通过/不通过"的判断。因为我们需要知道"哪部分有问题",而不是仅仅知道"有问题"。
4. 根据审查结果修正
如果审查结果是"通过",就直接返回。如果有问题,则进入修正流程。
修正可以有两种策略:
- 策略 1:重新检索。可能是检索词不够准确,或者检索库本身就没覆盖到,需要换一个检索策略。
- 策略 2:重新生成。检索到的文档是合适的,但生成时"说多了",需要基于同样的文档再生成一次,这次更加约束。
在我们的实践中,策略 2 更常见。因为检索到的问题片段时,主要还是生成环节"放飞了"。所以默认走重新生成,只有在多次尝试都失败时,才考虑换检索策略。
5. 控制循环次数
不能让系统无限循环下去。一般来说,设置 2-3 次尝试就够了。如果三次都审查不过,就直接告诉用户"回答可能不准确,建议查阅官方文档"。
这不是"认输",而是尊重事实:如果系统的能力边界就是这里,硬要生成一个"好像对"的回答,不如坦诚说"我不确定"。
踩坑记录
坑 1:审查模型本身会犯错
刚开始时,我犯了个错误:认为审查是"客观判断",模型只要看了文档和回答,就能给出正确判断。
实际上不是这样。审查模型也可能"睁眼说瞎话"。有时它会把明显错误的回答判为"通过",有时又会把正确的回答挑刺。
原因在于:审查模型本身也是概率模型,它的判断取决于训练数据、提示词、上下文等多种因素。它不是"真理裁判",只是另一个"生成器"。
解决方法:在审查提示词中加入更多约束,比如要求"逐条说明问题",“给出文档中的具体原文作为对比”。这样即使判断不准,至少能给出依据,人也好介入。
坑 2:审查太严格,系统不敢回答
有一段时间,我把审查做得太严格了,几乎每个回答都会被挑出"可能有问题"。
问题在于,有些"问题"其实是合理的推断。比如文档说"支持 JSON 格式",模型说"支持 JSON 等结构化格式",严格来说"等"字没有文档依据,但从语义上看不算错。
如果把这类"合理推断"都判为错误,系统就变得过于保守,很难给出有用的回答。
解决方法:在审查提示词中明确"语义等价不算问题",“合理的概括和总结不算问题”。重点是"实质性矛盾"和"无关内容",而不是字面一致。
坑 3:修正过程时延太大
加上审查和修正后,整体时延明显增加。尤其是需要重新生成时,用户要等好几秒甚至十几秒。
这对体验有影响。用户问个问题,如果等太久,哪怕回答再准确,也可能不耐烦。
解决方法:只对关键问题做完整审查。比如可以把问题分为"简单"和"复杂",简单的走快速通道,复杂的才走完整审查。或者在第一次生成时并行启动审查,而不是串行。
坑 4:有些"错误"其实是检索问题
不是所有问题都来自生成环节。有时检索到的文档本身就不够准确,或者根本没覆盖到用户问的点。
这种情况下,即使审查发现了问题,重新生成也解决不了,因为输入的文档有问题。
解决方法:在审查时增加一个"文档是否足够"的判断。如果检索到的文档明显不足,就标记为"需要补充检索",然后换个检索词再试。
效果和边界
跑了一段时间后,效果是有改善的。最明显的是"硬编造"的变少了,系统变得更"谨慎"。
一些具体的变化:
- 回答中"不支持的特性"这种说法减少了约 40%
- 用户反馈"回答不准确"的比例从 15% 降到 8% 左右
- 平均时延增加了 1.5-2 倍,但可接受
但也要说下边界。
首先,这个方法不是万能的。如果检索库本身就缺内容,或者文档本身有错,RAG 再怎么纠错也没用。
其次,审查和修正本身也可能引入新的错误。模型可能在修正时"矫枉过正",把本来对的内容改错了。
最后,时延和成本是必须要考虑的。多一次审查就多一次推理,多一次修正就再多一次。如果追求极致准确,成本会线性增加。
所以,真实落地时需要权衡:愿意为准确度付出多少代价?在哪些场景下值得做?在哪些场景下可以"差不多就行"?
结语
做 RAG 时,容易把"生成"当成终点,把模型当成"黑盒"。但实际上,模型生成的只是"概率上的可能",不是"事实本身"。
正确的做法应该是:生成之后,再加一层"事实校验"。这个校验可以是人,也可以是另一个模型,甚至可以是一个规则系统。
但无论用什么方式,核心思路是一样的:承认模型会犯错,并在系统层面增加纠错能力。
这次折腾下来,最大的感受是:别指望一次生成就完美,要给系统留出"修正空间"。这跟写代码一样,第一版能跑就行,后面再改。
技术如此,做产品也是如此。
参考
- Corrective RAG (CRAG) 相关论文和实践
- LangGraph 中关于自我修正的文档
- 开源 RAG 框架中审查机制的实现
版权声明: 本文首发于 指尖魔法屋-AI纠错RAG:这次怎么落地的(https://blog.thinkmoon.cn/post/380-ai-corrective-rag-error-correction-practice/) 转载或引用必须申明原指尖魔法屋来源及源地址!
评论
使用 GitHub 账号登录后即可留言,支持 Markdown。