答案要能追溯到出处
RAG 最危险的失败不是"没找到",而是找到了东西、答得也流畅、但内容是编的。
前一种用户知道系统不行,后一种用户会当真。而后者恰恰不会触发任何报错——HTTP 200、有引用、语气确定,人一眼看不出问题。
防住它靠两件事:每句话都能追溯,以及没依据时必须说不知道。
1. 为什么"有引用"不等于"没编"
一个常见的误解是:答案后面挂了引用链接,就是有依据的。
不是。模型可能引用了正确的文档,但用那段文档推导出文档里没有的结论。形式上引用在,实质上内容是补的。这类错误在人工抽检时最容易被放过——评审看到引用存在就点通过了。
所以判定的单位不是"整条答案",而是答案里的每一句断言。
2. 逐句溯源怎么测
标准做法(也是 RAGAS 一类框架的 faithfulness 指标所做的事)是两步:
第一步,把答案拆成断言。 一段回答通常能拆出若干条可独立判定的陈述。"这款产品在欧洲支持三年保修,但电池只保一年"是两条断言,不是一条。
第二步,逐条去上下文里比对。 每条断言要么能在送进去的切片里找到出处,要么找不到。
得分就是"有出处的断言数 / 总断言数"。这个指标的意义在于它把"编了多少"量化了,而不是笼统地说"答案对不对"。
相关的指标分层在《RAG 评测指标怎么选》里讲得更细——faithfulness 属于生成层,和检索层的 Recall@K 分开看,才知道该动哪一段。
2.1 用小模型做判定,但要抽样人工复核
逐句判定要跑很多次,通常用较小的模型当裁判。代价是裁判本身有偏差(位置偏差、长度偏差、对同族模型输出的偏好),所以必须定期抽样人工复核,看机器分和人工判的一致性。
一致性下降时,第一件事是换裁判,而不是调阈值。
3. 拒答:必须显式允许
这是最容易被漏掉、也最容易在生产上出事的一条。
如果不明确告诉模型"信息不足时说不知道",它会用训练数据里的知识把坑填上。填出来的东西看起来合理,语气确定,而且会被当成事实往下传。
所以系统提示词里必须写清楚失败行为:
只依据编号 [1]~[5] 的内容回答。
每一句结论后面用 [n] 标注出处编号。
内容中没有的信息,直接说"根据现有资料无法回答",并说明缺少哪方面的资料。
不要根据常识补充,不要推测。
三条要素缺一不可:限定依据范围、要求标注出处、明确拒答的说法。
3.1 拒答也要有质量
"我不知道"四个字不是合格的拒答。好的拒答要说清楚三件事:
- 缺的是什么("现有资料只覆盖到 2025 年的政策")
- 用户下一步能做什么("建议咨询人工客服")
- 已知的部分("能确认的是……,不确定的是……")
最后一条尤其重要:把确定的和不确定的分开说,比一句"不知道"有用得多。这也正好对应《RAG 链路与失败模式》里提到的——把"没找到"和"找到了但用错"分开处理。
4. 引用怎么展示
前端侧的几个具体要求:
引用要精确到切片,不要精确到文档。 标"来自用户手册"等于没标——手册两百页,用户没法验证。要能落到具体的章节或段落。
引用要可点开。 用户点击后能看到原文片段,最好能高亮与答案相关的那几句。这一步做不了,溯源就只是装饰。
引用编号和上下文顺序对应。 如果为了让模型更好用而把切片重排过,编号要跟着排,否则用户点 [3] 看到的可能是另一段。
部分无出处要显式标记。 如果一句断言找不到出处,有两个出口:删掉这句重答一次,或者保留但在界面上标为"未经资料确认"。混在带引用的句子里一起展示,就是把风险转嫁给用户。
全部无出处要拒答,不是逐句打标。 如果整条答案里没有任何一句能找到出处,说明这次检索根本没提供可用依据——这时候正确的出口是拒答并说明缺什么,而不是给每一句都挂上"未经资料确认"。后者形式上很严谨,实际上是把一条完全没有依据的答案原样递给了用户。
三种结果对应三个出口,判定的顺序是"先看有没有全部命中,再看部分命中的怎么处理"——这一步必须在生成之后、展示之前完成,不能留给用户自己去分辨。
5. 拒答率本身是个指标
上线之后要盯拒答率,它会告诉你很多事:
拒答率突然升高 —— 多半是检索坏了(召回掉了)、或者索引没同步(新文档还没进库)。这是上游问题,不是模型变保守了。
拒答率持续为零 —— 更可疑。说明系统什么都在答,通常意味着拒答的约束没生效,或者模型在硬编。
拒答率高但用户满意度不低 —— 正常,说明用户接受"不知道",这比硬答更可信。
把拒答率、faithfulness、用户反馈(点"没帮助"、转人工)放在一起看,才能区分"检索问题""生成问题"和"语料本身就没有答案"这三种情况。
6. 一句话的判断标准
随便抽一条线上回答,问:答案里的每一句话,能不能立刻定位到它的出处?
找得到,溯源做对了。找不到但答案看起来很顺,那就是最危险的那种失败——它现在没被发现,不代表它没在发生。