embedded-host-platform

03 意图识别:规则优先,模型复核指令

问题

意图识别最初是”规则优先,规则认不出才问模型”。规则是关键词表加句式,快、确定、不需要模型; 模型是兜底。

在 87 句题库上规则的正确率是 96.6%,但这个数是拟合出来的:词表就是照着这批题补的。 为了知道真实水平,另造了一个留出题库:148 句,用模板按固定种子展开,剔除了题库里的原句, 并规定不许照着它补词表。结果:

  规则 模型直接分类
范围内正确 82.8% 91.0%
自信地答错 18 / 134 —

模型更懂换了说法的问题,但管线是规则优先:规则一旦给出判断,模型根本不被调用。 规则答错一个问题,代价是答非所问;规则把一句话错判成指令,代价是设备状态被改了。

另一组 32 句的鲁棒性测试里,”要不要把风扇打开你说呢”“太热了是不是该把通风阈值调到 20 度” 这类征询意见的话,7 句中有 5 句被规则当成命令执行了。

两个判定器的分歧是信号

同一批 148 句,把规则和模型的判断放在一起:

情形 句数 规则 模型
两者一致 104 103 对、1 错 同左
两者不一致 30 对 8、错 22 对 21、错 9

一致时几乎不会错(104 句错 1 句);规则判错的 22 句里,21 句都表现为分歧。 分歧本身就是”这句可能听错了”的信号,而这个信号只有让模型复核时才拿得到。

选项

做法 取舍
维持现状,把错误率写清楚 零风险,但”模型单独判出的指令直接执行”与原则有张力
只复核指令 覆盖所有有副作用的情形,改动面最小;指令要多等 2~4 秒
所有问题都复核 组合正确率有望从约 82% 推向 91%;每句都要调模型,另有 8/148 的误报反问
模型优先、规则兜底 不采用:每句必等模型,结果不再确定,模型缺席时退化最严重
照着留出题库补词表 不采用:只会造出第二个拟合题库
用 JSON Schema 约束模型输出 不采用:实测快一倍多,但准确率没提升,还多出一次错误执行——强迫模型从枚举里挑一个,它就会挑

决定

只复核指令。 规则判出指令时,先让模型对同一句话独立判一次:

规则 模型 结果
判出指令 X 也是 X 执行
判出指令 X 判成别的 不执行,反问”你是想……吗?”,用户确认才执行
判出指令 X 认不出 不执行,反问。模型的沉默不算同意
认不出 判出指令 Y 不执行,反问
模型不可用 — 与原来一样,即时执行

原则:

复核拦不住”规则和模型一起判错”。征询语气那两句两边都读成了命令,所以另在规则层加了一道守卫: 句子里有”是不是该”“要不要”“会不会”“对吧”“假设”这类词时,先于所有指令分支判为提问, 回答风扇状态与通风阈值,由用户自己决定。用户问”要不要开”,是在征求意见,系统不替用户拿主意。 两道措施互补:守卫管语气,复核管两边读法不一致。

结果

没做的

问题类不复核。 问题答错没有副作用:回答里的数字仍然是真实读数,答案写明了答的是哪一类问题。 而对问题追加”你是不是想问……”的误报率约 8/148,会不会让人烦要等真人试用才知道。

还有一个没解决的点:同类系统(如 Rasa)用”同一个分类器前两名的置信度之差”判歧义,可以调松紧; 这里用的是”两个独立判定器是否一致”,规则一侧给不出置信度,没有可调的旋钮,只能全拦或全不拦。 问题类要复核的话,得先解决这一点。

后续:多义与多通道(2026-09-26)

试用中出现了三种”答了,但答错了一半”:

问法 原来的回答 根因
现在温度湿度噪声是多少 只答温度,不说漏了两个 规则同一分句只认一个通道,规则认出了,模型就不被调用
现在环境怎么样 模型理解对了,却回帮助清单 标签格式只装得下一个通道,多通道被当作”复述菜单”整句作废
现在记录了多少个数据 答成在线设备数 标签集合里没有这一类,模型只能挑最像的

最坏的是第一种:每个数字都对,用户还以为问全了。

原则不变:模型不写回答,也不写反问。 曾考虑让模型在拿不准时直接写一句反问,但模型自由写的反问里没有数字, 接地校验拦不住它,而它能承诺系统没有的能力(”要不要我帮你导出昨天的数据?”),用户答一句”要”,系统接不住。 所以模型只给候选标签,反问由代码从候选生成,选项只可能是系统真有的能力。

改动:

复核用的提示词因此与分类分开了:分类为多通道改了写法后,复核一度跟着变,消融实验查出”不是让你关风扇” 从五次都反问变成五次里四次执行关风扇。两档从此各用各的提示词,原文见 02 附录。

后续:影子分类,只记录(2026-09-29)

“温湿度多少”只答湿度,是因为规则认出了”湿度”这个词,模型从未被问到;单独把这句话交给模型,3 次都给出温度加湿度。 规则优先的管线里,规则一旦(部分)认出,就再没有人检查它漏了什么。

scripts/shadow_classifier.py 让”模型怎么看”变得可观测:规则判出一个提问时,等助手自己的模型任务做完、 模型空闲了,再用同一套分类提示词把原话交给模型,两边的判定并排写进 logs/assistant/shadow_*.jsonl。 它不改变任何回答:放在组合根、挂在问答日志上,src/ 的助手一行未动;有新问题进来就取消正在跑的这一次, 不挤占用户下一问的改写。攒下来的分歧是补规则、训练本地模型的真人语料。

没做的:提问也并行、分歧按模型(2026-10-01)

提议是提问也让规则与模型并行判定,分歧时按模型答,首字慢一些可以接受。先离线推演再决定: 342 句题,现行代码给出规则的判定,同一句再让模型用线上的提示词与采样参数独立分类,按三种管线判分。 模型只能把提问改判成另一种提问,判成指令不采纳。

题库 现行 并行·按模型 并行·只补通道 按模型改判(改对/改错)
留出 148(未调过词表) 127 128 127 16 句(8/7,另 1 句前后都错)
题库 87(拟合集) 87 80 86 7 句(0/7)
鲁棒 32 32 31 32 3 句(0/1)

结论是不做。 唯一不受拟合影响的留出题库上净收益 1 句,拟合题库倒退 7 句,换来每个提问多等约 4.4 秒。 模型的改错是成系统的:”吵不吵”“潮湿吗”这类问当前读数的体感问法被判成”是否超标”,”噪声多大”被判成最大值。 两者错的不是同一批句子,”分歧按模型”只是把规则的错换成了模型的错。模型改对的 8 句集中在规则词表的缺口上, 若照着留出题库补进词表,留出题库就成了第二个拟合集,所以不补;要补,应以影子分类攒下的真人问法为依据。