RAG 系统进化论(五):Corrective RAG 与 Self-RAG,让系统发现并纠正错误
本文导读: 找到证据并不意味着证据一定相关、充分或者可信。本文将介绍 Corrective RAG(纠错式 RAG)与 Self-RAG(自我反思式 RAG),通过证据评分、充分性判断、查询改写、检索回退、答案依据检查和拒绝回答,让系统能够发现检索与生成中的错误,并在必要时主动纠正。
上一篇的 Modular RAG(模块化 RAG)让系统能够根据问题选择不同数据源:
政策问题 → 文档知识库
订单问题 → 订单 API
统计问题 → SQL 数据库
复杂问题 → 多数据源并行检索
但是,选对数据源不代表拿到的证据一定正确。
假设用户问:
AX-2048-C 定制版键盘支持七天无理由退货吗?
系统选择了退换货知识库,却检索到三段内容:
证据 A:标准版商品支持七天无理由退货。
证据 B:定制版商品不支持七天无理由退货。
证据 C:2024 年旧政策规定,部分定制商品可以退货。
如果系统直接把三段内容交给大模型,可能出现:
- 把标准版政策错误地用于定制版;
- 引用了已经失效的旧政策;
- 面对冲突证据时自行猜测;
- 知识库没有答案时仍然生成结论。
前面的 RAG 主要关心“怎样找到证据”。
这一阶段开始关心:
找到的证据是否相关、是否足够、是否冲突,以及答案是否真的得到证据支持?
这就是 Corrective RAG 和 Self-RAG 要增加的能力。
Corrective RAG 与 Self-RAG 有什么区别?
两者都会检查检索和生成结果,但关注点略有不同。
| 方式 | 核心思想 |
|---|---|
| Corrective RAG | 评估检索结果,证据不好时改写查询、重新检索或切换数据源 |
| Self-RAG | 在检索和生成过程中加入自反思,判断是否需要检索、证据是否支持回答 |
可以简单理解为:
Corrective RAG:
证据不合格 → 纠正检索过程
Self-RAG:
在检索与生成过程中持续判断“下一步是否合理”
实际系统不必严格二选一,通常会组合两类能力:
严格来说,Self-RAG 最初是一类带 Reflection Token(反思标记)的训练方法,让模型学习何时检索、证据是否相关以及回答是否得到支持。工程实践中不一定重新训练模型,也可以用结构化评估器和条件工作流实现相似能力。
因此,这一篇介绍的是“纠错与自反思”这一能力阶段,而不是逐行复现某一篇论文的算法。
第一步:Document Grading 过滤无关证据
Document Grading(文档评分或文档判定)判断每条检索结果是否与问题有关。
这里的 Grading(判定)不一定是传统的数值排序,也可以输出结构化标签:
relevant:能够帮助回答
irrelevant:与问题无关
outdated:内容已经失效
conflicting:与其他证据冲突
对于开头的例子:
证据 A:标准版退货政策
→ irrelevant,与定制版问题不匹配
证据 B:当前定制版退货政策
→ relevant
证据 C:2024 年旧政策
→ outdated
判定可以由规则、元数据和模型共同完成:
- 产品型号、地区、权限和有效期优先使用确定性规则;
- 内容能否回答问题,可以交给相关性模型或大模型判断;
- 关键业务结论不应只依赖一个不透明分数。
async function gradeEvidence(
question: string,
candidates: Evidence[],
) {
// 先用版本、生效时间和权限等确定性条件排除无效证据
const validCandidates = candidates.filter(
(item) => isActive(item) && isAllowed(item),
);
// 再判断每条证据是否真正能够帮助回答当前问题
const graded = await Promise.all(
validCandidates.map((item) =>
judgeRelevance(question, item),
),
);
// 只保留相关证据,同时记录被删除的原因以便排查
return splitAcceptedAndRejected(graded);
}
Document Grading 解决的是“候选结果中混入错误内容”,但相关证据不一定足够形成完整答案。
第二步:Evidence Sufficiency 判断证据是否足够
Evidence Sufficiency(证据充分性)判断现有证据能否完整回答问题。
“相关”和“充分”是两件事:
问题:
定制版键盘能否退货?质量问题如何处理?
证据:
定制版商品不支持七天无理由退货。
这段证据与问题相关,却只回答了前半部分,没有说明质量问题的处理方式。
充分性检查可以考虑:
- 问题中的每个子问题是否都有证据;
- 关键实体、时间和范围是否一致;
- 是否缺少结论需要的必要字段;
- 多条证据之间是否存在冲突。
async function checkSufficiency(
question: string,
evidence: Evidence[],
) {
// 将复杂问题拆成需要被证明的多个信息点
const requirements = await extractAnswerRequirements(question);
// 检查每个信息点是否至少有一条有效证据支持
const coverage = matchEvidenceToRequirements(
requirements,
evidence,
);
// 返回缺失项,而不只返回一个难以解释的总分
return {
sufficient: coverage.missing.length === 0,
covered: coverage.covered,
missing: coverage.missing,
};
}
明确列出缺失项,系统才知道下一轮应该搜索什么。
第三步:证据不足时纠正检索
当证据不足时,系统不应立即生成答案,而要选择纠正动作。
Query Rewrite Loop:改写后重新检索
Query Rewrite Loop(查询改写循环)根据缺失证据重新组织查询。
原问题:
定制版键盘坏了怎么办?
已经找到:
定制版不支持无理由退货
缺少:
质量问题售后期限和申请步骤
新查询:
AX-2048-C 质量问题 售后期限 申请流程
改写应该围绕缺失信息,而不是简单换一种说法重复搜索。
Search Fallback:切换检索器或数据源
Search Fallback(检索降级或备用检索)指当前来源无法提供答案时,切换到其他来源:
产品手册没有解决方法
→ 查询售后工单
内部知识库没有最新公开政策
→ 查询经过允许的外部来源
向量检索没有命中型号
→ 使用关键词检索
Fallback 不是无限尝试。系统需要限制重试次数、允许的数据源和总成本。
async function correctRetrieval(
state: CorrectiveState,
) {
// 达到最大重试次数后停止循环,避免无限检索
if (state.attempt >= state.maxAttempts) {
return { action: "stop_without_answer" };
}
// 根据充分性检查中的缺失项生成更具体的新查询
const rewrittenQuery = await rewriteForMissingEvidence(
state.question,
state.missingRequirements,
);
// 当前数据源多次失败时,切换到预先允许的备用来源
const nextSource = chooseFallbackSource(
state.currentSource,
state.attempt,
);
// 返回受控的下一步,不在函数内部进行无限递归
return {
action: "retrieve_again",
query: rewrittenQuery,
source: nextSource,
};
}
第四步:检查答案是否得到证据支持
即使证据正确,大模型也可能:
- 加入证据中没有出现的细节;
- 把两个条件错误组合;
- 引用来源 A,却使用来源 B 的结论;
- 把“可以申请售后”说成“保证退款”。
因此,生成后还需要 Answer Grounding(答案证据一致性检查)。
它把答案拆成若干 Claim(可验证陈述),逐条寻找支持证据:
陈述一:定制版不支持七天无理由退货。
→ 当前政策明确支持
陈述二:质量问题可以在 30 天内申请售后。
→ 当前政策明确支持
陈述三:所有质量问题都会全额退款。
→ 没有证据支持
没有得到证据支持的陈述应该删除、改写或标记为不确定。
async function verifyAnswer(
answer: string,
evidence: Evidence[],
) {
// 将答案拆成可以逐条验证的事实陈述
const claims = await extractClaims(answer);
// 为每条陈述寻找直接支持它的证据
const checks = await Promise.all(
claims.map((claim) =>
findSupportingEvidence(claim, evidence),
),
);
// 只有所有关键陈述都得到支持时才返回原答案
return buildGroundingReport(claims, checks);
}
第五步:资料不足时允许拒绝回答
No-answer Detection(无答案识别)判断当前资料不足以给出可靠结论。
一个成熟的 RAG 不应该把“每次都回答”当成成功。
更合理的返回可能是:
当前资料只能确认 AX-2048-C 不支持七天无理由退货,
但没有找到质量问题对应的退款方式。建议联系售后确认。
拒答应该说明:
- 已经确认了什么;
- 还缺少什么;
- 用户可以采取什么下一步;
- 不暴露内部权限或系统敏感信息。
Self-Reflection:让系统在关键节点自检
Self-Reflection(自反思)不是让模型自由地“想很久”,而是在关键节点回答受约束的问题:
当前问题是否需要检索?
这条证据是否相关?
现有证据是否足够?
答案中的每个关键结论是否有来源?
应该继续检索还是停止?
每次反思都应该输出结构化结果,并对应明确动作。
type ReflectionDecision =
| { action: "answer"; reason: string }
| { action: "retrieve_again"; missing: string[] }
| { action: "switch_source"; source: string }
| { action: "refuse"; reason: string };
async function reflectOnState(
state: CorrectiveState,
): Promise<ReflectionDecision> {
// 根据证据覆盖情况、冲突和重试次数判断下一步
return await makeConstrainedDecision({
question: state.question,
evidence: state.evidence,
missing: state.missingRequirements,
attempt: state.attempt,
allowedActions: [
"answer",
"retrieve_again",
"switch_source",
"refuse",
],
});
}
限制可选动作比让模型输出一段自由文本更容易验证和执行。
完整的纠错流程
把这些能力组合起来:
async function answerWithCorrectiveRAG(
question: string,
) {
// 初始化查询、证据和最大重试次数
let state = createCorrectiveState(question, {
maxAttempts: 2,
});
// 在受控次数内执行检索、检查和纠正
while (state.attempt <= state.maxAttempts) {
// 从当前选择的数据源检索候选证据
const candidates = await retrieve(
state.query,
state.currentSource,
);
// 删除无关、过期和无权访问的候选内容
const graded = await gradeEvidence(question, candidates);
state.evidence = mergeEvidence(
state.evidence,
graded.accepted,
);
// 检查问题需要的信息是否已经被证据覆盖
const sufficiency = await checkSufficiency(
question,
state.evidence,
);
// 证据充分时生成答案,并检查答案是否得到证据支持
if (sufficiency.sufficient) {
const answer = await generateAnswer(
question,
state.evidence,
);
const grounding = await verifyAnswer(
answer,
state.evidence,
);
// 只返回通过支持度检查的答案和引用
if (grounding.supported) {
return { answer, citations: grounding.citations };
}
}
// 根据缺失信息改写问题或切换备用数据源
state = await applyCorrection(state, sufficiency.missing);
}
// 多次纠正后仍缺少证据时,明确返回无法可靠回答
return buildNoAnswerResponse(state);
}
这一阶段解决了什么?
Corrective RAG 与 Self-RAG 让系统获得了四项新能力:
- 在生成前过滤无关、过期和不匹配的证据;
- 判断证据是否覆盖问题需要的全部信息;
- 证据不足时改写查询、切换来源或停止;
- 在返回前检查答案是否真正得到证据支持。
RAG 的能力由此从:
选择流程
进一步变成:
选择流程 → 检查证据 → 发现问题 → 尝试纠正
这一阶段仍然存在什么问题?
评估模型也可能判断错误
负责 Grading 和 Reflection 的模型本身并不绝对可靠。
它可能把正确证据判为无关,也可能认为一组不完整证据已经足够。
循环增加延迟和成本
每次重新检索、重新评分和重新生成都会增加耗时与 Token 成本,因此必须设置最大次数和停止条件。
冲突不一定能自动解决
当两个权威来源给出不同结论时,系统不能只选择“看起来更相关”的一个,而要考虑版本、时间和来源优先级,必要时交给人工处理。
文本块仍然难以表达复杂关系
即使系统能够纠错,它处理的主要对象仍是一个个文本块。
如果用户问:
哪些供应商提供了出现 E1007 的零部件?
这些供应商还影响了哪些产品?
过去半年故障是如何沿产品批次扩散的?
答案分散在供应商资料、产品清单和售后记录中,需要沿实体关系进行多跳查找。
下一篇:GraphRAG(基于知识图谱的 RAG)
下一篇进入 GraphRAG。
系统将不只保存孤立文本,还会显式保存:
供应商 A
└── 提供 → 接收器 R7
└── 使用于 → AX-2048-C
└── 出现 → E1007
Corrective RAG 让系统能够检查并纠正证据。
GraphRAG 要解决的是:
当答案藏在跨文档的实体、关系和多跳路径中,系统怎样把这些知识连接起来?