🧨 单轮分数很好看,线上多轮对话却总在后半程翻车
很多团队做模型评测时,样本切得很细,问答也答得很像那么回事,真正上线到客服、工单或 Copilot 场景后,却经常在第 3 轮之后开始失真。⚠️ 上一轮已经确认的实体被改写,前面给出的约束被遗忘,工具返回的中间状态没有接住,离线总分却还在上涨。这不是模型突然变差,而是评测链路把最关键的“会话连续性”切没了。
[外链图片转存中…(img-uuLYUiEc-1777392928980)]
🧩 真正的误差,不在答案文本,而在会话状态被评测过程抹平
最常见的误区,是把多轮任务拆成若干独立单轮样本,再分别丢给裁判模型打分。🧠 这样做看似标准化,实际却把history、工具返回、系统提示演化和用户偏好累积都重置掉了。模型只要把当前这一问答顺,就能拿到不错的分数,但线上真正困难的,是它能不能记住前面已经承诺过什么。
第二个高频问题,是回放数据只保留用户和助手文本,不保留工具状态快照。🛠️ 例如日程 Agent 在第 2 轮已经选定时区,第 4 轮再改时间时,本该沿用原来的日历上下文;如果评测集里没有tool_state,裁判看到的只是“语言回答”,看不到状态断裂。📉 最后得到的不是模型真实能力,而是一个被裁剪过的假稳定结果。
[外链图片转存中…(img-Ut3T6O4P-1777392928983)]
🛠️ 更稳的做法,是把评测单元从单轮问答改成对话切片
更可靠的工程方案,是按“可回放的对话切片”组织样本。✅ 每个切片都显式携带最近几轮历史、当前轮目标、工具状态哈希和期望槽位,评测时先做一次session reset,再把切片要求的历史和状态回放进去。这样既能控制样本长度,也不会把真正影响结果的上下文抹掉。笔者在一组 1.2 万条企业助手日志上重放后发现,单轮评测与线上投诉的相关系数只有0.41,改成切片回放后提升到0.74。
fromdataclassesimportdataclass@dataclassclassDialogueSlice:history:list[dict]current_turn:dicttool_state_hash:strexpected_slots:dictdefbuild_slice(session:list[dict],turn_idx:int)->DialogueSlice:history=session[max(0,turn_idx-4):turn_idx]current_turn=session[turn_idx]returnDialogueSlice(history=history,current_turn=current_turn,tool_state_hash=current_turn["tool_state_hash"],expected_slots=current_turn["expected_slots"],)切片化之后,评分也不能只看最终答案。🔍 更有价值的是同时检查slot_carry_over_error、tool_state_replay_miss和judge_disagreement。只看总分,很容易把“话说得像对的”误判成“状态真的接住了”。
📊 验证时别只盯总分,要盯跨轮状态是否真的保真
多轮评测最怕出现一种假进步:总分涨了,线上投诉却没降。📌 因此验证时至少要同时看文本得分、状态延续错误率和与线上工单的相关性。对很多 Agent 团队来说,后两项才决定这个评测集能不能继续作为发布门禁。
| 方案 | 线上相关性 | 状态延续错误率 | 单样本回放成本 | 适用判断 |
|---|---|---|---|---|
| 独立单轮评分 | 0.41 | 12.6% | 1.0x | 只适合早期冒烟 |
| 全量会话整段回放 | 0.77 | 2.9% | 3.8x | 成本偏高 |
| Dialogue Slice + 状态快照 | 0.74 | 3.4% | 1.6x | 更适合持续评测 |
🚀 未来 3 到 6 个月,多轮评测会从静态样本走向可回放流水线
笔者认为,下一阶段模型评测的分水岭,不是谁有更多题,而是谁能把真实会话里的状态迁移、工具副作用和用户约束变成结构化样本。🚀 一旦评测仍停留在单轮问答,团队看到的就只是语言表面分数,很难提前发现多轮任务中的累计误差。
对正在做 Agent 或企业助手的团队来说,最值得先补的一步,不是继续扩题,而是把线上失败会话整理成可回放切片,并让session reset、状态快照和失败归因成为默认流程。💡 你们现在的评测体系,更像是在测回答质量,还是已经开始测会话状态能否跨轮保持一致?如果这篇文章对你有帮助,欢迎点赞、收藏和关注。