W7 · Routing:混淆矩阵与兜底
本周交付:
Intent(6 类)+drill w7(30 条标注样本 + 混淆矩阵 + 阈值扫描)
预计耗时:主块 4h(设计意图体系 0.5h / 标注样本 1.5h / 写演练 1h / 分析 1h)
前置:W6 通过(bash run.sh drill w6绿)
1. 一句话
给分类器一个"我可以说不知道"的出口,然后用混淆矩阵和阈值曲线把"错在哪一边"摊开给业务方看。
2. 为什么必须有 UNKNOWN
没有兜底类别的分类器,一定会自信地分错。
你给 5 类,用户问"你们公司还招人吗",模型不会说"我不知道",它会挑最接近的那一类——大概是"商品咨询"。然后这条不该被处理的请求就进入了商品咨询的处理路径,走完全程,给出一个煞有介事的回答。
UNKNOWN 的作用就是给模型一个合法的出口。但这个出口有两个硬要求:
- 必须存在(枚举里有它)
- 必须能被真实触发(W7 验收:触发 ≥ 3 次)
第二条容易被忽略。如果你的样本里全都是典型售后问题,UNKNOWN 永远不触发,这条分支等于没测过——上线后第一次遇到闲聊就直接裸奔。
3. 为什么是 6 类
分类粒度的上限由下游动作决定。每一类必须对应一个明确不同的处理路径;如果两类走的是同一套动作,那它们就该是一类。
| 意图 | 下游动作 | 特殊规则 |
|---|---|---|
| REFUND 退款 | 查订单 → 查政策 → 生成 | 涉及钱,W10 必须过护栏 |
| RETURN 退货 | 查订单 → 查政策 → 生成 | 涉及货,流程不同 |
| LOGISTICS 物流异常 | 查订单 →查轨迹→ 生成 | 数据源不同 |
| PRODUCT 商品咨询 | 查政策 → 生成 | 不需要订单号 |
| COMPLAINT 投诉 | 直接转人工 | 自动回复激化矛盾,成本远高于人工接一次 |
| UNKNOWN 无法归类 | 直接转人工 | 兜底 |
单独说 COMPLAINT:humanOnly()写死在枚举上,不交给 prompt 判断。这类"无论模型多准都不该自动处理"的路径,必须在代码里硬编码。
4. 指标怎么定义(这段要写进 README)
大多数人只报一个"准确率"。这个数字在这里没用,因为它把三种完全不同的结果混成一种:
| 结果 | 定义 | 业务含义 |
|---|---|---|
| 正确 | 预测 = 标注 | 正常处理 |
| 错分 | 预测成了某个具体意图,但和标注不一致 | 走了错的流程,可能造成损失 |
| 弃权 | 预测为 UNKNOWN(转人工) | 没自动处理,但也没出错 |
对应的两个指标:
- 弃权率= 弃权 / 总数 → 自动化的覆盖率(体验)
- 错分率= 错分 / (总数 − 弃权) →在它敢答的问题里错多少(风险)
分母扣掉弃权,才是诚实的"准确率"。把弃权算进分母算出来的 90%,可能真实是"70% 转人工 + 20% 全对"。
5. 验收
cd教程/W1-W4/ecom-agent-labbashrun.sh drill w7实际输出(脚本模型,30 条样本):
混淆矩阵(行 = 标注,列 = 预测) REFUND RETURN LOGISTICS PRODUCT COMPLAINT UNKNOWN REFUND 4 . . . . 2 RETURN . 4 . . . 1 LOGISTICS . . 4 . . 1 PRODUCT . . . 4 . 1 COMPLAINT . . 1 . 3 1 UNKNOWN . . . . . 4 阈值 0.60:正确 23 · 错分 1 · 弃权 6 错分率 4%(错分/敢答的题)· 弃权率 20% --- 阈值扫描 --- 阈值 弃权率 错分率 自动作答量 0.30 10% 7% 27/30 0.55 10% 7% 27/30 0.60 20% 4% 24/30 0.90 20% 4% 24/30 0.95 87% 0% 4/30 错分明细: 你们发货太慢了 标注 COMPLAINT → 预测 LOGISTICS 弃权明细: 退款到哪了 标注 REFUND → 转人工 物流太差了,下次不买了 标注 COMPLAINT → 转人工 能不能便宜点 标注 PRODUCT → 转人工 ... W7 路由:通过 4 / 失败 0判定标准:
- 错分率 < 20%(当前 4%)
- 「无法归类」被真实触发 ≥ 3 次(当前弃权 6 次)
- 混淆矩阵行和 = 样本数(30)
- 至少 3 类意图被正确识别
4.1 矩阵里的那一格说明了什么
COMPLAINT → LOGISTICS那一格,就是"你们发货太慢了"被判成物流问题。这句话里确实有"发货"两个字,关键词规则把它吃走了。
这就是矩阵的价值:如果你只看"错分率 4%",你会觉得没问题;看到这一格,你会知道"情绪表达 + 业务词"是这类句子的固定模式,应该在 prompt 里加一条 few-shot。
4.2 阈值扫描:这不是调参,是业务决策
阈值从 0.60 提到 0.95,弃权率从 20% 涨到 87%,错分率只从 4% 降到 0。
高阈值买到的是"安心",不是"准确":它把大量本来答对的题也转给了人工。转人工有成本(人力),错答也有成本(赔偿、投诉),两条线的交点在哪,只有业务方知道。
你的职责不是替他选,而是把两条线的代价算清楚端上去。
6. 本周踩的坑
坑 1:第一版混淆矩阵是一条完美对角线。
25 条样本全对、错分 0、弃权 2。看起来很棒,实际是自己考自己——我的关键词规则和标注是我一个人写的,规则怎么划,标注就怎么标。
修法是加 5 条"难样本",专门挑关键词规则会栽的句子:
"物流太差了,下次不买了" 标注 COMPLAINT(规则判成 LOGISTICS) "你们发货太慢了" 标注 COMPLAINT(规则判成 LOGISTICS) "能不能便宜点" 标注 PRODUCT(规则判成 UNKNOWN) "客服说三天到,现在还没到" 标注 LOGISTICS(规则判成 UNKNOWN) "东西质量不行,我要退" 标注 RETURN(规则判成 UNKNOWN)加上之后矩阵才有非对角线的格子,才说明这套评测能发现问题。
这一条比代码重要:对角线矩阵不是好消息,是评测集太简单。
坑 2:歧义样本不能藏。
"退款到哪了"同时命中 REFUND 和 LOGISTICS。我一开始想把它删掉(“太吵了”),后来留下了,标注 REFUND,接受它被弃权。
真实语料里这类占比很高,藏起来只会让你的线上表现比评测差。
坑 3:以为 UNKNOWN 触发靠"模型说不知道"。
实际上是三条路径都可能落 UNKNOWN:无关键词(0.30)、多意图竞争(0.55 被阈值拦)、模型明确返回 UNKNOWN。三条都要有样本覆盖。
7. 自检清单
- 能画出自己的 6×6 混淆矩阵,并指出最大的非对角线格子
- 能解释"错分率"和"弃权率"的分母为什么不同
- 能说出为什么 COMPLAINT 要写死在代码里而不是交给 prompt
- 阈值扫描能跑出至少 3 行,并能对着数字讲清取舍
drill w7全绿w7-routing.csv已经生成,用 Excel 打开看过
8. 下一步
到这里,链路能跑、路由能分、错误能定位。但有一个问题一直没解决:
如果模型不收敛怎么办?
死循环是 Agent 系统里最经典的事故:一轮 3 秒,用户等 30 秒看到超时,而你的日志里每一条都"正常"。W8 用四道刹车解决它。