4. 端侧模型与状态机的接口设计

端侧模型与状态机协同的核心难点,不在于模型本身有多强,而在于两者之间的“接口”是否清晰。模型擅长从传感器、音频、图像等原始数据中提取语义,但它输出的是概率、向量或文本;状态机擅长管理确定性的状态迁移,但它需要明确的输入条件。接口设计的任务,就是把模型的“模糊输出”翻译成状态机可消费的“确定信号”,同时把状态机的“当前状态”反馈给模型,形成闭环。

一、模型输入输出契约

契约是接口的第一层。端侧模型通常以固定张量形状接收输入,例如一个语音唤醒模型可能接收 [1, 16000] 的 16kHz 单声道 PCM 浮点数组,输出 [1, 2] 的 logits 或 [1] 的置信度。契约必须明确四件事:输入张量的形状与数据类型、预处理方式(归一化、重采样、分帧)、输出语义(分类标签、回归值、嵌入向量)、以及推理调用的同步/异步模式。

以“设备是否被拿起”这一事件为例,模型输入可能是三轴加速度计最近 50 个采样点,形状 [1, 50, 3],输出为二分类概率 [p_still, p_picked]。契约中应写明:采样率 50Hz,窗口 1 秒,滑动步长 0.2 秒,输入按每轴均值方差归一化。这样,状态机侧才能稳定地按固定节奏读取输出,而不是每次都要猜测模型内部做了什么。

二、状态观测:让模型“看见”状态机

模型如果只看到原始传感器数据,很容易在相似物理信号下做出与当前状态矛盾的判断。例如设备已经处于“屏幕关闭”状态,模型却因轻微震动反复输出“被拿起”。因此,接口需要把状态机的当前状态作为观测输入之一,注入模型或注入后处理逻辑。

常见做法有两种。第一种是特征拼接:把状态编码为 one-hot 向量,与传感器特征拼接后送入模型。第二种是后处理门控:模型输出先经过一个由状态决定的门控函数,只有当前状态允许的事件才会被放行。前者让模型参与决策,后者让状态机保留最终控制权。端侧资源受限时,后处理门控更轻量,也更容易调试。

三、事件触发与置信度映射

模型输出的是连续置信度,状态机需要的是离散事件。两者之间需要一个映射层,通常包含阈值、迟滞和持续时间三个参数。阈值决定“多确信才算发生”,迟滞防止在阈值附近抖动,持续时间要求事件必须稳定存在若干帧才被确认。

例如,拿起事件要求 p_picked ≥ 0.75 且连续 3 帧成立,才触发 PICKED 事件;放下事件要求 p_picked ≤ 0.35 且连续 5 帧成立,才触发 PUT_DOWN。两个阈值之间的 0.35~0.75 是“不确定区”,状态机保持当前状态不变。这种双阈值加持续时间的结构,比单一阈值稳定得多。

端侧模型与状态机接口数据流 传感器输入 加速度 / 音频 / 图像 预处理契约 归一化 · 分帧 · 重采样 端侧模型推理 输出置信度 / 向量 置信度→事件映射 阈值 · 迟滞 · 持续时间 状态机 IDLE · ACTIVE · SLEEP · ALERT 离散事件 PICKED / PUT_DOWN 状态观测 one-hot / 门控 置信度 p_picked 与事件触发示意 1.0 0.5 0.0 0.75 0.35 触发 PICKED 触发 PUT_DOWN 不确定区:保持当前状态

四、状态迁移的映射方式

事件触发后,状态机如何迁移,取决于映射表。映射表把“当前状态 + 事件”映射为“下一状态 + 动作”。例如:

当前状态事件下一状态动作
IDLEPICKEDACTIVE点亮屏幕、启动交互模型
ACTIVEPUT_DOWNIDLE关闭屏幕、卸载交互模型
ACTIVENO_FACE_30SSLEEP降低采样率、冻结模型
SLEEPPICKEDACTIVE唤醒屏幕、恢复模型
任意ALERTALERT触发高优先级通知

这张表就是接口的“合同”。模型侧只需要保证事件语义稳定,状态机侧只需要保证迁移规则明确,双方通过事件名和置信度参数解耦。当模型升级或替换时,只要事件语义不变,状态机无需改动。

五、工程实现中的关键细节

第一,时间对齐。模型推理有延迟,状态机读取事件时必须知道该事件对应的是哪一帧数据。建议在事件结构中携带时间戳或帧序号,状态机按时间戳排序处理,避免因异步导致的状态错乱。

第二,置信度校准。模型输出的 0.8 不一定代表 80% 的真实概率。端侧部署后,应使用少量现场数据做温度缩放或 Platt 校准,使阈值具有可解释性。校准后的置信度再进入映射层,阈值调整才有依据。

第三,降级策略。当模型推理超时或资源不足时,接口应返回“未知事件”而非阻塞状态机。状态机可以基于超时计数器进入安全状态,例如从 ACTIVE 回退到 IDLE,而不是卡死在等待中。

接口设计的一个实用原则:模型负责“感知”,状态机负责“决策”,映射层负责“翻译”。三者职责分离,任何一方替换或升级,都不会牵动另外两方的内部逻辑。

最后,建议把契约、阈值、映射表集中放在一个配置文件中,模型侧和状态机侧共同读取。这样,调参时不需要重新编译两端代码,也便于在端侧做 A/B 测试。接口设计的目标不是让模型更聪明,而是让整个系统在资源受限、噪声干扰和状态变化中,依然能稳定地做出可预期的行为。