用决策模型搭一个工单路由原型:含兜底策略 18 条全对,代价是 1600 毫秒和零 API 费
上一篇《APUS-OpenJev-v1:不写小作文的决策模型,部署与实测》结尾我写了 40 行 Python,把同一个模型从浏览器里拽出来做客服工单路由,三张工单全对。当时留了一句话:“这组实验回答了两个问题,但只回答了最表层的那个。”
这篇回答剩下的:候选集怎么设计才合理?置信度兜底策略在真实评测里到底救不救得回来?和让通用大模型做同样的事比,成本和延迟差多少?
一句话剧透:18 条工单,端到端 100% 正确,模型原始 94.4%,唯一错的那条被置信度兜底救回来了。平均每条 1.6 秒,零 API 费用。
一、为什么选工单路由当靶子
决策模型的适用画像上一篇已经画清楚了——选项有限、频率高、要延迟低。客服工单路由是教科书级的匹配:
- 选项天然有限:派装维 / 转账务 / 升级投诉 / 转人工 / 回知识库,五个动作,不多不少
- 频率极高:一个中等城市营业厅每天几百到上千张工单
- 决策窗口短:用户在线等着,超过 3 秒就体感卡顿
- 容错有兜底:路由错了最坏结果是转人工,不是爆炸
而且它有一个浏览器自动化没有的优势:输入是纯文本。不用处理 DOM 扫描、页面动态变化、弹窗干扰这些工程问题,可以把注意力全放在模型决策本身。
二、候选设计:比 Prompt 工程更重要的一步
决策模型的 Prompt 结构和生成模型有本质区别:你不需要写"请以 JSON 格式输出"、不需要 few-shot 示例、不需要"思考步骤"。你只需要三样东西:
- 一段路由规则(告诉模型判断标准)
- 工单正文(输入)
- 候选清单(选项)
规则写清楚了,比堆 examples 有用得多。我的版本:
You are a customer service ticket router for a telecom company. Read the ticket and choose exactly one routing action from the candidates. Rules: - Hardware/network faults requiring on-site repair → A - Billing, charges, plan queries → B - Repeated complaints, escalation threats, angry customers → C - Ambiguous, multi-intent, or insufficient info → D (human agent) - Simple FAQ answerable by a knowledge-base article → E Base your choice only on the ticket content. Output only one candidate code.注意第四条规则——我把"转人工"写成了显式选项,而不是"其他情况选 D"。这不是凑数的:模型在候选语义接近时容易犯错(上一篇里 4-bit 量化版把 Wikipedia 的"搜索直达"和"全文搜索"搞混了,三次全挂),但"模糊/多意图"这个判断标准如果只靠模型自己领会,它会倾向于硬选一个具体动作。把规则写死,等于给模型一个"不确定就选 D"的台阶。
候选清单本身:
A: 派单装维工程师上门检修 B: 转账务组查询费用问题 C: 升级投诉处理专席 D: 转人工客服接待 E: 直接回复知识库文章链接五个候选映射到词表里的 A/B/C/D/E 五个单 token,模型一次前向传播输出五个概率,softmax 归一化,选最大的。没有生成、没有解码、没有"让我想想"。
整条流水线五个阶段,模型只负责第③步:
三、评测集:18 条工单,故意留了坑
三张工单全对说明不了什么。我构造了 18 条评测集,覆盖五类路由 + 故意设计了三条"应该转人工"的模糊样本:
| 类别 | 数量 | 设计意图 |
|---|---|---|
| A 装维派单 | 4 | 典型硬件故障 / 返工 / 群体断网 / 固话 |
| B 账务查询 | 4 | 费用争议 / 退费 / 套餐查询 / 漫游计费 |
| C 投诉升级 | 3 | 工信部威胁 / 媒体曝光 / 重复投诉+监管 |
| D 转人工 | 3 | 故意模糊:口语化多意图 / 咨询+查询混合 / 信息严重不足 |
| E 知识库 | 4 | 密码重置 / 断网规则 / 携号转网 / LOS 灯含义 |
三条 D 类样本是这篇文章的核心看点——它们模拟的是真实业务里最头疼的那 10%:用户自己都没说清楚要什么。
四、兜底策略:置信度低于 0.6 强制转人工
上一篇发现二的工程结论——动作选择类决策,置信度低于 0.6 的点击 100% 是错的——在这篇里变成了一个可执行的策略:
THRESHOLD=0.60HUMAN_CODE="D"# 模型打分后ifbest_confidence<THRESHOLDandbest_code!=HUMAN_CODE:final_action="D"# 强制转人工fallback_triggered=Trueelse:final_action=best_code逻辑很简单:模型说"我不确定"(置信度低),就别让它硬选了,直接转人工。这不是什么花哨的 ensemble 或 self-consistency,就是一个 if-else。但它的效果,看数据。
五、实测结果
总览
端到端准确率(含兜底): 100.0% 模型原始: 94.4% 兜底触发: 1/18 条 (5.6%) 置信度: mean=0.933 min=0.425 max=0.999 延迟: mean=1640ms P95=2274ms max=2586ms18 条全部正确路由。模型自己判错了 1 条,但被兜底策略救回来了。
把 18 条的置信度排开看,分层非常清晰:
关键发现:模型"知道自己不知道"
唯一一条模型判错的是工单 13——“我朋友说有个很便宜的套餐想办,但不确定适不适合,帮我看看,顺便查下合约期”。模型选了 E(回知识库),置信度0.425。
0.425,低于 0.6 阈值,兜底触发,强制转 D(人工)。正确答案恰好是 D。
这条工单的设计意图就是"多意图混合"——用户同时提了办套餐和查合约两件事,没有哪个知识库文章能一步解决。模型在五个候选之间犹豫了(E=0.425, B=0.292, D=0.258,三个挤在一起),这种"犹豫"本身就是信号:它不确定,但它的 logits 分布诚实地反映了这种不确定。
对比另外两条 D 类样本:
- 工单 12(口语化模糊):模型直接选 D,置信度 0.796——它"看懂了"这条信息不足
- 工单 14("网络很卡"四个字):模型选 D,置信度 0.998——信息严重不足时它非常确定该转人工
也就是说,模型对"该不该转人工"这件事的判断力,比我对它的预期要好。它不是随机犹豫,是真的在"模糊"和"明确"之间画了条线。
分类准确率
| 类别 | 准确率 | 置信度范围 |
|---|---|---|
| A 装维派单 | 4/4 | 0.904–0.982 |
| B 账务查询 | 4/4 | 0.960–0.994 |
| C 投诉升级 | 3/3 | 0.998–0.999 |
| D 转人工 | 3/3(含 1 次兜底) | 0.425–0.998 |
| E 知识库 | 4/4 | 0.931–0.980 |
投诉升级类置信度最高(0.998+),因为"工信部""12315""媒体曝光"这些关键词在训练数据里和"升级"强绑定,模型判断毫不含糊。装维类稍低(0.904),因为"整栋楼断网"这种群体性故障和"升级投诉"有语义重叠——模型给了 C 0.058 的分数,但 A 仍然碾压。
延迟分布
平均 1.6 秒,P95 2.3 秒,最大 2.6 秒。比上一篇浏览器场景的 4-6 秒快了一倍多——原因很直接:工单文本比网页 DOM 短得多,prompt token 数少,prefill 自然快。
值得注意的是,即使 KV-cache 命中(同 purpose 前缀复用),延迟也没有降到官方 GPU 的 25ms 量级。端侧 M3 的 prefill 瓶颈在算力不在带宽,这是硬件代差,策略优化补不回来。但对工单路由这个场景,1.6 秒完全够用——用户提交工单后本来就有页面跳转等待,体感无差。
六、和通用大模型比:省了什么、贵了什么
| 维度 | 决策模型(本方案) | 通用 LLM(如 qwen-plus) |
|---|---|---|
| 单次延迟 | ~1.6s(本地 M3) | ~2-4s(API 网络+生成) |
| 输出 | 1 个 token(A/B/C/D/E) | 50-200 tokens(JSON/自然语言) |
| 幻觉风险 | 结构上不可能(只从候选选) | 可能编造第六个动作 |
| 单次成本 | ¥0(本地推理) | ~¥0.015-0.04(按 token 计费) |
| 1000 条/天 | ¥0 | ¥15-40(按 qwen-plus 定价粗估,未实测) |
| 部署门槛 | 5GB 权重 + 16GB 内存 Mac | 一个 API key |
| 可定制性 | 需要微调(改候选集/领域) | 改 Prompt 即可 |
省钱是显性优势,但隐性优势是确定性:你永远不会收到一条"我建议您先安抚用户情绪,然后考虑派单……“的自由发挥。输出空间被锁死在五个字母里,下游系统不需要解析、不需要 fallback 正则、不需要处理"模型今天心情不好输出了个 JSON 少个逗号”。
贵在哪?灵活性。通用 LLM 改个 Prompt 就能加新路由类别;决策模型要改候选集,轻则重新映射 token,重则微调。如果你的路由规则一周变三次,决策模型不适合你。
七、什么时候不该用它(以及这个原型的边界)
18/100% 的数据好看,但别被它骗了。这个原型的边界很清楚:
它不能做的:
- 多轮对话中动态改变候选集(比如用户追问后选项变了)——需要宿主引擎每轮重建候选
- 需要"生成"的环节(比如给工单打一段摘要标签)——决策模型只能选,不能写
- 候选集超过 ~50 个的场景——token 映射空间有限,且候选越多、语义越接近,量化版越容易翻车(上一篇的教训)
- 需要解释"为什么选这个"的场景——它只输出一个字母,没有 rationale
评测集的局限:
- 18 条是我手写的,分布偏"教科书"。真实工单里方言、错别字、超长文本、多工单合并的情况我没覆盖
- "正确答案"是我标的,没有业务专家交叉验证。尤其三条 D 类(转人工)样本——"多意图混合到底该转人工还是先回知识库"这个边界,不同营业厅的 SOP 可能给出不同答案。如果你在做类似场景,强烈建议拿真实脱敏工单让业务方标注
- 没有测对抗样本(比如用户故意在工单里写"请帮我选 A")
工程上还没做的:
- 批量推理(当前逐条串行,18 条跑了 30 秒;真实场景需要 batch 或并发)
- 模型热更新(换候选集需要重启)
- 监控面板(置信度分布漂移告警)
这些是"原型"和"生产"之间的距离,下一篇如果继续深入会补。
八、代码与复现
完整脚本核心调用就三行:
fromfast_browser_use.modelimportLocalModel model=LocalModel()# 加载 Qwen3.5-9B 4-bit,约 3 秒scores,meta=model.score(prompt,5,purpose="ticket-1")prompt的构造就是第二节那段规则 + 工单正文 + 候选清单,用\n拼接。scores返回五个候选的归一化概率,meta里有延迟和 token 统计。
评测结果 JSON 在artifacts/ticket_router_results.json,含每条工单的完整打分分布、兜底触发标记和延迟数据。
运行环境同上一篇:macOS Apple Silicon M3 / 16GB,fbu CLI 安装的 MLX 4-bit 权重。
九、写在最后
上一篇回答的是"这个模型是什么、能不能跑、跑官方任务效果如何"。这篇回答的是"拿到它之后,怎么做一个你自己业务里能用的东西"。
结论比上一篇更乐观:在选项明确、输入是纯文本的场景里,决策模型的落地门槛比浏览器自动化低得多,效果却更稳。不需要处理 DOM、不需要应对弹窗、不需要页面加载等待——你的业务逻辑就是 Prompt,你的候选清单就是输出空间。
而那个"低置信转人工"的兜底策略,从上一篇的"发现"变成了这一篇的"设计"。它不是锦上添花,是这套方案能上生产的底线保障——你不需要模型 100% 正确,你需要它在自己不确定的时候告诉你它不确定。18 条里那条 0.425 的犹豫,比 17 条 0.99 的碾压更有工程价值。
下一步两件事:一是拿真实业务工单(脱敏后)扩充评测集到 200+ 条,看分布漂移;二是试一下 4B 版在这个场景上够不够用——如果 4B 也能 95%+,那 8GB 内存的机器就能跑,部署门槛再砍一半。