离线评测分很高,上线效果差——可能出了什么问题?
谁在问:二三面·追问;简历里写了「评测 XX 分」之后的必然一刀
口语化问法
- 你们离线评测 90 多分,那上线之后用户反馈怎么样?如果不一致,会是什么原因?
- 有没有遇到过测的时候好好的、上线就不行的情况?后来查出来是什么?
- 假设现在线上投诉翻倍但你的评测集分数一点没掉,你先干什么?
考察意图
这道题几乎不会单独出现,它是「你们怎么评测的」之后的第二刀。面试官在判断:
- 你的第一个动作是什么。 没排过障的人第一反应是罗列可能原因(像背清单);排过障的人第一反应是做一个能把问题一分为二的动作。
- 你能不能区分「评测集的问题」和「系统的问题」。 这两类的修法完全不同,混在一起谈说明没真查过。
- 你有没有意识到「分数没掉」本身就是一条重要证据。 分数没掉说明你测的东西和线上失败的东西不是同一件事——这比分数掉了更值得警惕。
参考答案
60 分答案(及格线)
可能的原因有几个:一是评测集和线上真实用户的问法不一样,评测集偏理想化;二是判分标准和用户真实感受不一致,比如我们判「要点命中」,用户在意的是有没有解决问题;三是可能有数据泄漏,prompt 是对着评测集调出来的,有点过拟合;四是线上环境不一样,多轮对话、上下文更长。我会先看一下线上的 bad case,然后补充评测集。
这个答案的原因罗列是对的,也覆盖了主要方向,所以能过。
它止步于 60 分是因为——它是一份清单,不是一个流程。 面试官接一句「那你先查哪个」,就会发现候选人没有次序,只有并列。而在真实排障里,次序就是全部。
90 分答案(有生产经验的回答)
我不会先罗列原因,我会先做一个动作把问题切成两半。
第一刀:把线上失败的那批输入,原样搬到离线环境跑一遍。 注意是「原样」——连同它当时的对话历史、系统提示、检索到的文档、工具返回值一起搬,不能只把用户最后那句 query 抠出来。然后看结果:
- 离线也错 → 这是评测集侧的问题:系统的能力本来就不行,只是你的评测集根本没测这类输入。
- 离线是对的 → 这是环境侧的问题:同样的输入,两边行为不同,差异在链路里,不在模型能力里。
这一刀和 RAG 排障里「手工把正确文档塞进 prompt、切开检索侧和生成侧」是同一个手法:固定住一个变量,把问题一分为二。
如果落在评测集侧,往下切三条:
- 分布不同——最常见,而且不报错。做法是把评测集和线上采样各自统计长度分布、轮次分布、意图占比,摆在一张表里。我们真踩过一次:评测集 87% 是完整问句,线上 61% 是残句和关键词;线上 44% 发生在第二轮以后带指代,评测集里一条都没有;线上 12% 问的是知识库里根本没有的内容,评测集里也是零。指标为零的维度在看板上不是低分,是不可见。
- 判据不同——离线判「要点命中」,用户判「有没有帮我把事办了」。要点全命中但结论埋在第三段,离线满分线上被骂。而且这个偏差会随着你对着判据优化而放大。
- 泄漏或过拟合——prompt 是对着评测集一轮轮调出来的,或者评测集和开发集同源。这时分数上涨里有多少是真提升,你分不清。判断办法是拿从不参与迭代的保留集跑一次。
如果落在环境侧,往下切三条:
- 上下文不同——离线是干净的单轮调用,线上是第 7 轮,前面堆了三次工具返回、一次压缩、一条用户中途改主意的指令。这对应上下文的四种失败形态(污染 / 干扰 / 混淆 / 冲突),只测第一轮的评测集一种都测不出来。
- 版本不同——模型被上游静默升级、prompt 模板走的是另一个分支、检索索引没同步、工具的返回 schema 变了。
- 运行时不同——超时降级到了小模型、限流触发、输出打到 max_tokens 被截断、并发下的缓存串味。这类的共同特征是只在高峰期出现,用平峰的采样永远抓不到。
最后一句我一定会说:分数没掉本身就是最强的证据——它说明我测的东西和线上失败的东西不是同一件事。 所以这次事故的收尾,一定包含往评测集里补进这批真实失败,否则同一个问题会换个形式再来一次。
追问链
「原样搬到离线跑」听起来简单,实际有什么坑?
期望最大的坑是搬不全 —— 只搬了最后一句query,没搬对话历史、系统提示的实际取值、检索命中的文档、工具真实返回,于是复现不出来、误判成「环境问题」。由此反推trace要能一键导出可离线重放的输入包;时间/状态相关的输入要冻结外部依赖信号说「就把 query 拿过来跑一遍」→ 最常见的失手点,没真做过;提「今天的汇率、当时的权限这类依赖要冻结」→ 很强的信号确认是评测集分布脱节,你怎么量化这个「脱节」?
期望给可执行的对比维度:长度、轮次、意图、库里有没有答案、语言/渠道;再把线上query做embedding聚类,找评测集完全没覆盖的簇 —— 新簇通常就是新意图或新渠道。判据不是分布相同,是主要类别都非零、高风险类别不欠采样信号只说「感觉不太一样」→ 没做过;承认「有意过采样高风险类别,所以总分不能当线上表现的估计」→ 理解得很透离线复现是对的、落在环境侧,接下来怎么切?
期望按可排除的成本排,不按可能性:① 比版本(模型 ID /prompt模板 / 索引 / 工具),看配置就行 → ②diff上下文(长度、条数、有没有触发过压缩)→ ③ 查运行时(降级、限流、截断、超时重试),只能靠trace标记位。运行时问题只在高峰出现,要按时间段切信号上来就怀疑模型能力 → 方向反了,能力问题第一刀就该被复现出来;主动提max_tokens截断与静默降级 → 最容易漏也最常中的两条这次修完,怎么保证下次不再发生?
期望收尾的强制动作是把这批真实失败补进评测集,且补最小可复现输入而不是整条trace;再把「评测集 vs 线上采样」的分布对账做成每月例行;上线上采样评测,让「离线涨、线上不动」被自动发现。边界要认:这套只防同类复发,新形态靠拒答率、转人工率、点踩率兜底信号只说「加强测试」→ 空话;说「补的是最小可复现输入」→ 维护过评测集,知道整条 trace 补进去会又慢又脆线上还在出问题、老板要求今天恢复,但你判断根因是评测集要重建、至少两周,这两件事怎么排?
期望止血优先于根因,而且止血不需要知道根因。按代价从小到大:① 回滚到上一个已知稳定版本 → ② 网关分流,把受影响最大的那类流量切回旧版或转人工,缩小爆炸半径 → ③ 加一条最窄兜底:检索置信度低于阈值就回「我不确定,帮你转人工」,宁可拒答不要乱答。这三条都可逆,重建评测集不可逆,两条线并行不该互等;止血期间捞到的失败样本正好是重建的原料信号坚持「先查清根因再动」→ 技术没错,工程上是灾难;把可逆的止血与不可逆的重建拆成两条并行线 → 真在生产里扛过事故
评分要点
- 第一动作是切一刀(把线上失败输入原样搬到离线跑),不是罗列原因清单
- 明确区分评测集侧与环境侧两大类,并各自能往下展开
- 评测集侧说得出:分布 / 判据 / 泄漏三条,且知道分布问题不报错
- 环境侧说得出:上下文 / 版本 / 运行时三条,且知道运行时问题只在高峰出现
- 意识到「原样搬」的最大坑是搬不全,并由此反推 trace 的记录要求
- 收尾包含把真实失败补进评测集,且补的是最小可复现输入
- 压力面能把**止血(可逆)与根因(不可逆投入)**分开并行