RAG系统如何学会“拒答”:可答性判断的设计与实践

发布时间:2026/8/31 4:14:52
RAG系统如何学会“拒答”:可答性判断的设计与实践 我在几个知识库问答项目里反复遇到同一个现象用户问的是“新员工试用期工资怎么算”检索链路明明找回了公司薪酬制度文档但生成答案却把转正后的绩效规则混了进去。更麻烦的是用户问“今年年会奖品是什么”这种知识库里根本没写的事系统也会一本正经地给出一个看起来很像样的答案。这说明一个被低估的问题——检索系统真正要解决的不只是召回率和排序精度而是能不能区分一个问题它能回答、另一个问题它不能回答。过去很长一段时间我把这类问题归结为“生成模型幻觉”。后来在多个项目里看日志才发现根子往往更靠前系统根本没有为“不可回答”设计出口。它不具备“可答性判断”的能力。一个 RAG 项目一旦进入生产区分“可回答”和“不可回答”不是锦上添花而是比召回精度更基础的能力。因为持续在无证据情况下编造答案会让用户对一个知识库助手彻底失去信任。错误答案比没有答案更有害尤其当用户真把系统输出当成事实。1. 检索答非所问根源是整条链路缺少一道可答性闸门1.1 一个现象三种可能原因当系统回答错误时我们很容易把责任推到生成模型身上。但拆开整个流程看会发现错误至少可能来自三层。第一层在检索层。查询被 embedding 后没有找回真正能回答问题的文档。第二层在重排层。正确文档虽然进了候选列表但被重排模型排到了很低的位置生成模型根本看不到。第三层在生成层。上下文里塞入多篇部分相关、但不真正覆盖问题条件的文档模型从中抓出几句零散信息拼出一个看起来合理的回答。还有一个更隐蔽的原因系统根本没有“无法回答”这个出口。无论检索结果多差prompt 都要求模型“根据材料回答”模型在句子补全习惯的推动下只能继续编造。所以错误答案不是某一步单独造成的而是整条链路缺少一道闸门上游没有过滤掉不能回答的问题下游也没有阻止基于不足证据的生成。1.2 核心判断系统需要“可答性判断”而不是“更多召回”RAG 项目的常见优化思路是提高 top-k 召回率、换更强的 embedding、加入重排模型。这些动作解决的是“答案在不在候选里”的问题。但“答案在候选里”不等于“模型能正确回答”。如果知识库中根本没有答案再强的检索也只能带回一堆边缘相关文档。如果知识库里只有部分相关文档缺少问题中的关键约束条件再好的重排也无法让模型凭空补全信息。生成模型本身又不擅长在多个候选片段之间做逻辑缺口的判断它只会选择最符合语言习惯的那个组合。我自己的经验是可答性判断应当被设计成一个独立的决策环节而不是检索或生成过程里顺带完成的事情。它需要回答两个问题当前查到的文档有没有覆盖用户问题中的全部关键条件如果覆盖不全应该拒答还是带着限制条件回答这样才能把“能不能回答”从模糊的生成过程中剥离出来。1.3 为什么这道闸门在项目里总是被漏掉原因很现实。大多数项目在开发时先用一条有标准答案的样例跑通流程再把演示做得好看。到评测阶段准备的也基本是正例答案语义接近就算通过。这个过程中没有人构造“知识库里没有答案”的负例也没有人测试“证据不足但有点相关”的边界场景。于是模型学习到的行为模式是“无论如何都要给出完整回答”。一旦上线用户问的问题不会按照你的文档范围出现答错率就会突然放大。日志里那条一本正经的错误回答往往不是模型突发意外而是从开发阶段就埋下的设计缺口。如果一个检索系统在立项时就把“不可答”纳入需求它的架构会很不一样需要负例数据集需要拒答策略需要“证据不足”话术需要人工兜底入口甚至需要前端展示相关文档链接。这些功能在 demo 阶段不明显但决定了系统能不能长期使用。2. 让系统学会“拒绝回答”比提高召回率更难在哪2.1 检索系统的优化目标从来都不包含拒答离线评估 RAG 时大家最常看 Recallk、MRR、命中率。这些指标只回答一个问题正确文档有没有被排在前面。它们从不回答另一个问题当前情况下系统是否应该拒绝回答。模型被训练成尽量把更像相关的内容往前排。这跟知识库问答里“宁缺毋滥”的业务诉求天然冲突。在客服场景多返回一篇无关文档可能把模型引到错误答案在制度查询、医疗、法律等场景一个错误的“事实”会造成信任崩塌。当评估指标根本不考核拒答时整个系统也没有改进它的动力。你很难说服团队为一个“不答”的功能投入资源因为现有指标看不出收益。但生产日志会告诉你用户真正流失的时刻往往就是系统用肯定的语气给出错误信息的那一刻。2.2 可回答不等于检索到相似文本证据完整性才是关键这是最容易误解的地方。一个 query 看起来和知识库某篇文档高相似不代表系统能回答。举个例子。用户问“试用期员工是否可以享受年假”。知识库文档 A 有年假政策但没有提到试用期员工规定。因为文档 A 同时包含“试用期”“年假”这些词检索时它可能被判定为高相关。真正需要回答的问题是“试用期员工”这个群体是否适用文档 A 并没有覆盖这个约束条件所以它不能支撑回答。可答性判断关心的不是“文档像不像”而是三个要素是否同时成立问题中的每个实体或术语能否在证据中找到来源问题中的关系或动作例如“是否享受”“如何计算”是否被证据直接支持问题中的约束条件例如“试用期”“离职时”“跨地区”是否没有被遗漏。这种判断比“整体相似度”更严格也更接近人判断一份材料能不能回答提问的方式。很多人以为 RAG 的难点在“找到更相关的文本”真正难的是“判断一组文本是否足以回答”。前者是排序问题后者是逻辑问题。2.3 不可答的正确姿势先识别证据缺口再决定是拒答还是降级我见过不少团队把“不可答”简单处理成“不知道”然后让模型说一句“抱歉我无法回答”。这样做确实能降低幻觉但用户体验并不好因为它放弃了已经检索到的部分有效信息。更合理的方式是识别证据缺口类型。完全没有相关文档时可以回答“知识库中未找到相关信息请尝试换一种表述或联系人工”文档有部分相关但缺少关键条件时可以回答“找到了相关材料但现有材料无法确认你问的具体情况”同时提供文档链接多篇文档相互矛盾时应该说明信息不一致而不是任选一篇生成。所以我建议把可答性决策从二分类扩展成三到四分类完全不可答、证据不足但部分相关、证据充分但有冲突、证据充分。这样产品就能用更细致的提示去引导用户而不是把系统变成一个动不动就闭门谢客的黑盒。3. 三种可落地的可答性判断方案3.1 不管选哪种先把最小流程固定下来可答性判断应该放在哪里常见流程是这样的query 进入检索召回一批候选文档经过重排得到排序结果然后做可答性判断最后决定是生成答案还是拒答。更稳妥的做法是生成前后都做。生成前判断是否有足够证据生成后判断答案是否忠于证据。前者防止无材料硬编后者防止模型在已有材料中挑选错误信息。最小流程可以固定为五步输入问题、取回候选文档、计算相关性分数、判断文档是否覆盖问题要素、决定生成或拒答。先把这个流程写出来再选具体实现。下面是一段示例结构主要为了说明逻辑不能直接当生产代码# 可答性判断最小流程示例结构需结合你的检索库实现 def should_answer(query, docs): if not docs: return False, empty_candidates relevant_score compute_relevance(query, docs) if relevant_score config.min_score: return False, low_relevance need_terms extract_required_terms(query) covered_terms extract_covered_terms(docs) missing need_terms - covered_terms if missing: return False, fmissing_evidence:{missing} return True, ok真实环境中extract_required_terms可能用实体识别也可能用大模型抽取extract_covered_terms可能依赖文档结构和段落切分。先把每个函数想清楚再考虑用什么模型。3.2 方案一检索分数加规则阈值适合快速验证最容易落地的是用一个综合分数做阈值。分数可以来自向量相似度、重排模型得分、关键词覆盖比例。分数低于阈值就拒答。优点是实现成本低不引入额外模型适合快速验证。缺点也很明显不同 query 的分数分布差异很大。事实型问题有时候检索分数很低但确实可答语义模糊的问题可能分数很高但并不可答。如果用一个全局固定阈值会同时出现误杀和漏放。更稳妥的做法是先对线上日志做分桶统计画出可答和不可答样本的分数分布再定阈值。不要拍脑袋。可以配合条件规则当问题里出现“是否”“能否”“有没有规定”等判断型词时把阈值调高一点因为这类问题更容易在证据不足时被强行回答。配置可以设计成类似下面这种结构{ strategy: score_threshold, top_k: 5, min_score: 0.65, high_risk_keywords: [是否, 能否, 是否支持, 有没有规定], high_risk_min_score: 0.75, fallback_message: 当前知识库中未能找到足够依据请补充更具体的信息或咨询人工。 }注意这只是一个结构示例。具体阈值必须结合你使用的向量模型、重排模型和知识库内容来调整。3.3 方案二训练专门的可答性判别模型如果团队有标注能力和维护节奏可以训练一个二分类模型。输入是 query 和候选文档输出是可答概率。这个模型不负责生成答案只负责判断证据是否充分。样本要覆盖几类正例一篇文档完整覆盖 query 的实体、关系和约束条件。简单负例文档与 query 完全无关。边界负例文档部分相关但缺少关键条件多篇文档拼起来才能回答文档之间存在冲突。边界负例最重要。模型能不能理解“证据不足”完全取决于这一部分数据。如果只拿“完全相关”和“完全无关”训练模型学习到的还是文本相似度而不是证据完整性。这类方案的优点是一旦训练好判断速度快不受大模型 API 波动影响。缺点是需要持续维护标注集。知识库更新后原本不可答的问题可能变得可答原本可答的问题也可能因为文档失效而变得不可答。所以负例集不是一次性的。3.4 方案三让生成模型先自评证据再作答第三种方案是把可答性判断交给生成模型。在 prompt 中要求模型先找出支持答案的证据指出缺失信息再决定是否回答。这个方案非常灵活尤其适合使用大模型 API 或本地大模型并且上下文窗口比较充裕的场景。但要注意大模型自评并不可靠。如果它已经见过相似问题可能把记忆当成证据如果 prompt 里写了“如果不知道就说不知道”它可能在某些场景严格执行在另一些场景又过度自信。自评更像是兜底不应该作为唯一闸门。我建议的使用方式是组合先用检索分数过滤掉明显低质量的候选再用生成模型抽取问题要素和缺失条件最后要求生成答案时引用证据 chunk 编号。这样即使自评偶尔失灵前一层也能拦住大部分风险。三种方案各有边界这里做一个对比方案实现成本判断质量维护成本适用场景分数阈值低中低低快速验证、小知识库、对正确性要求不极端的场景判别模型中高中高中有标注团队、长期稳定的知识库问答生成模型自评中中不稳定中大模型能力充足、需要灵活处理复杂证据组合方案高高高医疗、法律、客服等强约束业务注意不要一上来就追求“组合方案”。先用阈值方案把日志和评估链路建起来再逐步加入判别模型或生成模型自评这个顺序更稳。4. 落地过程中最容易踩的坑4.1 只准备“可回答”样例模型永远不会学会说不知道很多项目的评测集里几乎全是“有标准答案”的问题。模型训练数据里也都是成功用例。这时候无论召回还是生成都会形成“必须回答”的强先验。解决方法是建立负例集。不需要一开始做几千条20 到 30 条就够。覆盖完全无关输入、知识库不存在的新政策、问题超出文档范围、文档之间互相矛盾、需要人工判断的数据。把这些负例和正例混合在一起做回归测试阈值、prompt 和判别模型才有真正的参考价值。4.2 用答案相似度当唯一指标会掩盖拒答能力缺失如果只看 BLEU、ROUGE、语义相似度模型只要输出一个流畅答案就可能得高分。即使系统经常在无证据情况下编造指标也看不出来因为指标只关注生成结果像不像标准答案不关注答案是否忠于知识库。建议在离线评估里增加几个指标可答正确率本应回答时答对的比例。拒答准确率不应回答时成功拒答的比例。幻觉率输出中的关键实体或数字无法在证据中找到的比例。误拒率本可回答却被拒答的比例。评估时把可答样本和不可答样本分开统计。只看可答准确率系统会偏向强行回答只看拒答准确率系统又会偏向闭门谢客。只有同时看两组指标才能判断它到底有没有学会“判断”。4.3 上下文越长越像证据噪声会穿透所有过滤规则另一个常见坑是为了给模型更多信息把检索 top_k 设得很大或者把多个不直接相关的片段全部拼入 prompt。上下文越长模型越容易从某个片段里抽出孤立词汇来生成答案而不是基于完整证据链。分数阈值在这种场景下也可能失效。因为长上下文里总会有几句看起来相关相关性分数会被拉高但真正支撑回答的一句话可能被淹没在噪声里。通常建议是精简上下文。先控制 top_k比如取 3 到 5 篇再按重排分数截断不要全量塞入如果 prompt 允许要求模型在回答前引用证据片段编号。同时知识库侧的段落拆分会直接影响效果段落太短则信息不完整段落太长则噪声太高。这需要针对你的文档场景反复调。4.4 强行做二元判断把“部分可答”也推给了幻觉如果系统只允许“可答”或“不可答”那么“证据不足但相关”这类情况就会落入两边。要么强行回答并冒险要么直接拒答让用户觉得系统很弱。更合理的是三档策略。完全不可答时给出“未找到相关信息”的提示证据充足时正常回答证据不足但部分相关时只输出有证据支持的部分并在尾部说明“现有材料未包含 XX 信息”再附上相关文档链接。这样既降低幻觉也保留有用信息。这种策略不是技术问题还是产品决策问题。产品经理和技术负责人需要接受“系统可以给出不完整的回答”而不是要求每个问题都必须有完美答案。5. 把可答性判断沉淀成一套可复用流程5.1 一条从样例到上线的最小执行路径如果现在要新做一个 RAG 项目我会先按下面这几步走。第一步准备 20 条以上覆盖正反样例的测试集其中至少包含 5 条不可答样本和 5 条证据不足样本。第二步对每条样例写出预期行为完全回答、带限制回答、拒绝回答。第三步先把检索和重排结果打印出来人工检查证据文档是否覆盖问题的全部关键要素。第四步选一个可答性判断策略用测试集调阈值。调阈值时同时记录误拒率和幻觉率不要只盯准确率。第五步上线后接日志反馈把用户的追问、无点击、转人工操作作为“拒答质量”的信号。这个执行路径的关键是先跑通判断链路再优化模型。很多人一上来就折腾 embedding 和重排弄了很久才发现问题根本不在排序而在“没有判断”。5.2 当系统给出错误答案时按这条链路排查如果系统回答了一个它本不该回答的问题我通常会按下面的顺序排查。先看问题本身。query 是否包含知识库完全不存在的新产品、新政策如果是可答性判断层应该直接拦住。再看检索结果。top_k 里是否真的存在覆盖问题要素的文档如果只有部分相关说明那就是“证据完整性”判断没生效。再看重排结果。正确文档是否被压到了第 6 位以后导致生成模型根本没选到如果是调整 top_k 或重排权重。再看可答性判断。阈值是不是太宽松是否只用了相关性分数没检查实体覆盖再看提示词和生成。模型是不是忽略了“只能基于证据回答”的指令是不是被上下文里某句孤立描述带偏最后看知识库本身。原始文档是不是存在矛盾或者版本过旧导致模型选错了来源。这个排查链路的核心是从输入逐层往外走不要一开始就怀疑生成模型。大多数错误答案不是模型“太笨”而是前面某一层把无证据信息当成了证据。5.3 给长期维护团队的一份评估检查表进入长期维护后可答性判断最怕回归。知识库更新、prompt 调整、模型换版本都可能让原本工作的拒答策略失效。可以按下面这个检查表做日常回归检查项方法通过标准不可答样例覆盖手工构造或从线上日志采集至少覆盖完全无关、知识库缺失、证据不足、证据冲突四类离线评估分开统计可答与不可答子集幻觉率低于目标线、误拒率低于目标线线上日志记录拒答比例、用户追问次数拒答比例不能长期为零也不能突然飙高知识库更新新增文档后回归负例集原来不可答的问题不能因为新增文档而变回强答需要重新判断提示词变更每次 prompt 修改都跑回归不引入新的幻觉不降低可答正确率这些工作看起来繁琐但正是可答性判断能不能长期维持的关键。5.4 拒答不是模型缺陷而是产品功能最后想强调一点可答性判断不是为了让系统越来越胆小而是让它在信息不完整时保持诚实。一个知识库助手如果用户问“离职后还能用公司邮箱吗”而知识库只写了在职员工邮箱规则它最应该做的是给出在职规则同时说明“没有找到离职后的规定”。这不是退步而是一种更可信的知识服务。要做到这一点需要业务方接受“系统可以说不知道”需要客服团队准备人工兜底流程也需要前端把相关文档链接作为答案的一部分。技术侧的阈值、判别模型、提示词只是手段产品侧对“不知道”的容忍度才是这套机制能落地的前提。落地一个 RAG 项目别把“回答得更准”当成唯一目标。先让系统学会区分“能回答”和“不能回答”它才有资格被长期使用。具体的第一步不是调 embedding也不是换更大的模型而是先构造一批不可回答的测试问题让系统开始学会拒绝。