88% 是幻觉:我给本地决策模型出了 100 道中文题
上周我差点把一个 322M 的小模型推上生产,让它自动给用户消息标"紧急"。
底气来自测试成绩:8 道题对 7 道,88%。
上线前一晚我加了一场模拟考——100 道带标准答案的中文真题,考了四轮。成绩出来:67%。
88 是幻觉,67 才是它。
而这次下跌,救了一场上线事故。
先说清楚:这是什么模型,我为什么要考它
它是 mmBERT,约翰霍普金斯大学放出来的开源多语言模型,322M 参数。我把它转成 ONNX、量化到 INT8,最后只有 320MB,塞进 NAS 的 Docker 容器,限两核两 G 内存,配了一个兼容 TypeSafe Jev 协议的推理端点。
它不聊天、不写代码、不帮你写周报。你塞给它一段话加几个问题,它 100 毫秒内吐回结构化判断:选 A 还是 B、是不是、打几分——每个答案带概率和置信度,全程不生成一个字的文本。
这类模型今年被 TypeSafe 的"System One"叙事带火过一轮。官方口径很性感:LLM 生成内容给人看,它做判断给代码用;概率经过校准,说 0.8 的事八成真会发生;价格比生成式大模型低两个数量级。社区里"agent 时代基础设施"的呼声不小。
我信了一半。信的那一半有很功利的动机:我的内容流水线每天有几十条文案要过大模型审核——低俗、虚假宣传、价值观风险,一条条全量过,又贵又慢。如果小模型能在前面粗筛,砍掉七成审核量,省钱省得明明白白,数据还不出内网。
于是我给它安排了五类考题:紧急度检测、内容四分类、发布合规审核、记忆去重、选题契合度。100 道题,全部人工标注标准答案,覆盖我自己业务里的真实文本——包括一条它此前判错的真题,专门留着看它能不能改。
第一场翻车:1.0 的置信度,判了个寂寞
翻车从我最想用的功能开始:发布前合规审核。
给它这句标准电商话术:"本店所有商品支持七天无理由退换,运费险全覆盖。"
它的原始返回,一字未改:
{"vulgar": {"type": "noul", "noul": 1.0, "confidence": 1.0}}
低俗。置信度 1.0。
而真正的擦边文案"深夜一个人的房间,丝滑睡衣若隐若现",它给 0.28:没事。
好文案死刑,坏文案无罪释放,全是 0.99 级的自信。这要上了线,就是把每条正常文案拦在门外的现场。
排查分两层,结论很不一样。
第一层是我的锅。我在选项描述里嵌了示例,模型开始拿输入和示例做词汇关联。最夸张的案例:判断"周末爬山装备清单与路线推荐"属于什么分类——因为"技术"选项的描述里写着 FastAPI 示例,它判了"技术类",0.93。把所有选项描述里的示例删掉、压到一句话,同一道题变成"生活类"0.93。示例没有帮助判断,示例成了关键词命中的靶子。
第二层没救。低俗、虚假宣传、价值观,这些语义判断我换了五种措辞,从正问到反问,从给定义到给边界例子,全部失败——要么全判"是",要么全判"否",就是不稳定地区分对错。
结论很冷:prompt 是开关,不是引擎。322M 的脑子里没装过中文广告法,能力不在,措辞救不了。
这一层我最后放弃了本地模型:绝对化用语交给正则词表("全网最低""第一品牌""无效退款",确定性命中,零成本),语义违规交给大模型复核。这不是妥协,是分工——确定性规则能干的事,永远别花模型的脑子。
88% 是怎么露馅的
合规出局,紧急度检测倒是很亮眼:8 道题对 7 道。我当时的想法已经写到一半了——"紧急消息自动置顶"。
幸亏多考了一场。
题库扩到 30 道,准确率掉到 67%。
错误长这样:"宝宝明年上幼儿园的事,得开始物色了,不急"——判紧急,0.83。指令里明明白白写着"有'不急'这类软化词不算",它看不见。只要消息里出现期限、部署、系统这类词,它就往"是"偏。
我不死心,把这道题原样重放了五次。五次结果一字不差——这不是抽风,是它稳定的、确定性的判断。它真心认为幼儿园不紧急。
然后我把每个置信度档位都套上 Wilson 区间下界,看看哪些分数配得上"自动执行"四个字:
| 自动执行阈值 | 覆盖样本 | 准确率 | Wilson 下界 |
|---|---|---|---|
| 0.50 | 30 | 67% | 0.49 |
| 0.70 | 16 | 94% | 0.72 |
| 0.80 | 10 | 90% | 0.60 |
| 0.85 | 7 | 100% | 0.65 |
最好的一档,下界也只有 0.72。离"敢让它自动动手"通常要求的 0.90,差得远。
n=8 时的 88% 和 n=30 时的 67%,是两个不同的模型。前一个是幻觉。
顺带一提,官方文档其实诚实得很:明说了英语是主训练语言,中文这类 CJK 精度偏低,"务必先在自己的内容上实测"。这话我第一遍读的时候没当回事,第二遍是含泪读的。
错误模式也是资产
但 67% 的成绩单里埋着一小块金子。
它的错误是偏"是":30 道题里 7 个误报、3 个漏报。把判断阈值放到 0.65 再看——15 条真紧急全部召回,误报只剩 2 次。
想想代价的不对称:误报,是多看一眼消息;漏报,是错过一次线上故障。
所以它现在的岗位不是自动置顶器,是提醒器——标记“可能紧急”,人来拍板。误报只是多看一眼,漏报才是事故:模型只有提醒资格,没有执行资格。
还有个回暖的细节。第一天有道它判错的真题:"NAS 硬盘坏了一块,RAID 正在降级运行,需要尽快换盘"——判"不紧急",0.35。把"是"和"否"的标准改成对比式写法(什么算紧急、什么明确不算,各给边界例子)之后,同一条消息,它给到 0.80,稳稳落进提醒区。
一个措辞结构的小改动,救回一个真实场景。这种"改了就灵"和前面那种"怎么改都不灵",同样值得写下来——前者告诉你 prompt 的边界内还能榨什么,后者告诉你边界在哪。
三句话带走
第一,prompt 只用纯字符串。结构化 JSON 它消化不了——合规那组题全体退化成"都答是"。官方的模型训练过理解结构,小模型的克隆版没有。
第二,选项描述要短,别嵌例子。例子会劫持判断:模型开始对例子做关键词匹配,而不是读定义。FastAPI 四个字能把爬山装备判成技术新闻。
第三,高置信不等于高准确,小样本不等于结论。0.99 置信的错判可以稳定复现,88% 的准确率换个样本量就是 67%。上生产前全量考一次,用区间下界说话,不用感觉说话。
校准脚本和题库都留下了。下次换个更强的基座,一条命令重新认证,达标就升级,不达标继续当提醒器。
这一趟最值的,不是模型变准了——是我终于精确地知道它哪里准、哪里不准、准和不准各自的原因。
你的模型上线前,考过全量模拟题吗?成绩是多少,来聊聊。
← 返回博客列表 · 相关阅读:41 小时静默宕机复盘 · 第二大脑的工程化