Skip to content

9.4.3 为什么线上真实问题和离线样本经常差很多? ​

先给结论:离线样本和线上真实问题经常差很多,不是因为用户“不会提问”,而是因为离线样本通常更干净、更完整、更标准,而真实用户问题往往更模糊、更跳跃、更依赖上下文。评测集如果不持续吸收线上样本,迟早会和真实表现脱节。

这是 RAG 项目里非常常见的现象:

  • 离线评测看起来还不错
  • 一上线,用户却不断遇到答偏、漏答、误答

很多人会先怀疑模型、检索器、向量库,但问题常常更早就埋在评测集里了。

为什么离线样本通常更“理想化” ​

离线样本往往来自这些来源:

  • 产品同学手写的问题
  • 文档作者自己总结的问题
  • 开发和测试同学基于材料整理的问题

这些问题有一个共同特点:

  • 提问目标很明确
  • 关键词比较标准
  • 背景上下文默认完整
  • 很少出现口语化、跳步、误写和歧义

可真实用户不是这样提问的。真实线上问题更像:

  • 少量关键词
  • 半句话
  • 上下文没说全
  • 把多个问题混在一起
  • 带错别字、简称、内部黑话
  • 把系统本来不该回答的问题也问进来

所以很多离线样本其实是在测:

  • 理想输入下,系统能不能工作

而线上问题测的是:

  • 杂音输入、歧义输入、缺失输入下,系统还能不能稳住

线上和离线脱节,常见有四类原因 ​

第一类,问题表达方式不同 ​

离线样本常常是完整句子,线上用户则可能只打两个词,或者默认系统知道前文。

这会直接影响:

  • query understanding
  • query rewrite
  • 检索召回

第二类,任务分布不同 ​

离线样本里,常常是团队最关心、最容易定义的问题;但线上问题里,用户会不断提出边角问题和跨边界问题。

这会造成:

  • 你测的不是用户真正高频的问题
  • 你没测到最容易出错的问题

第三类,知识环境在变化 ​

线上系统的文档、版本、权限、时间状态都会变化。离线样本如果长期不更新,很容易继续在测试:

  • 旧版本知识
  • 旧的文档结构
  • 已经过时的业务表述

这样即使评测分数稳定,也不代表系统真的稳定。

第四类,用户目标比问题文本更复杂 ​

很多线上问题表面看是问答,实际上背后还包含:

  • 希望比较方案
  • 希望得到可执行建议
  • 希望系统说明不知道
  • 希望限定在某租户、某产品线、某时间段

而离线样本往往只保留了问题文本,没有保留提问意图和上下文目标。

为什么这件事对 RAG 特别重要 ​

RAG 对输入分布变化很敏感。因为它不是单纯依赖模型参数,而是依赖一整条链路:

  • 问题理解
  • 检索
  • 过滤
  • 重排
  • 上下文构造
  • 生成

只要真实线上问题的风格和离线样本不同,整条链路都会受到影响。所以评测集不能长期停留在“内部整理的标准题”阶段。

更稳的做法:把线上样本回流成离线资产 ​

真正成熟一点的做法,不是把离线和线上分开看,而是建立一个持续回流机制。

第一步,给线上问题打基本标签 ​

至少先给问题补这些信息:

  • 任务类型
  • 是否有明确资料依据
  • 是否需要过滤条件
  • 是否属于资料不足应拒答
  • 当前失败属于检索问题、生成问题,还是端到端问题

没有标签的线上问题,只能堆在日志里,很难变成评测资产。

第二步,优先回流高价值失败样本 ​

不是所有线上问题都要立刻进评测集。优先级更高的通常是:

  • 高频失败问题
  • 高风险错误问题
  • 新版本引入的回归问题
  • 某一类任务集中退化的问题

这样能让评测集更快贴近真实风险。

第三步,补上证据范围和判定规则 ​

线上失败样本刚进入评测集时,常常只有:

  • 用户问题
  • 系统回答
  • 用户反馈

但这还不够。要让它真正可评测,通常还要再补:

  • 正确证据应该来自哪里
  • 什么答案算对
  • 什么说法必须避免

否则它只能成为案例,不能成为稳定样本。

第四步,定期检查评测集分布有没有被旧样本绑架 ​

一个很常见的问题是,随着时间推移,老问题越积越多,新问题加得很慢,结果评测集开始偏向历史场景。

所以你需要定期检查:

  • 不同任务类型占比是否失衡
  • 是否过度集中在某一业务域
  • 是否缺少新版本、新数据源、新表达方式下的问题

一个很实用的观察角度 ​

如果你的系统出现下面这些现象,往往就说明离线评测和线上真实问题已经脱节了:

  • 离线得分稳定,但用户满意度下降
  • FAQ 题表现很好,但复杂问答投诉变多
  • 新文档上线后,旧评测集分数仍然很好
  • 系统在内部演示很稳,但真实用户一问就暴露大量边角错误

这时候不要只继续调模型或调参数,先回头看评测集是否还代表真实流量。

一个最小回流流程示意 ​

python
failed_case = {
    "query": "去年签的企业版合同现在升级套餐还能不能按旧折扣算",
    "source": "online_feedback",
    "task_type": "time_bounded_policy_qa",
    "failure_type": "missing_version_constraint",
    "need_human_review": True
}

if failed_case["need_human_review"]:
    reviewed_case = {
        **failed_case,
        "expected_evidence_scope": {
            "doc_ids": ["contract-upgrade-policy-2025", "pricing-policy-2026"],
            "must_consider_time_boundary": True
        },
        "judging_rule": {
            "must_include": ["合同签约时间", "当前升级规则"],
            "must_not_claim": ["所有老客户都继续沿用旧折扣"]
        }
    }

这类回流结构的重点不是代码,而是把线上失败样本变成可复用的评测样本。

一句话总结 ​

线上真实问题和离线样本之所以经常差很多,是因为真实世界的问题分布本来就在变化;评测集只有持续吸收线上失败样本,才不会越来越像一套自我感觉良好的内部题库。