数字人 SDK 与服务端独立迭代。服务端多发一个遥测字段,客户端应当继续工作;但如果把任务标识从字符串变成数字,客户端又需要知道契约已经变了。
早期 Hanzora 在多个消费点分别收窄消息对象,运行时字段检查和 TypeScript 类型分散维护。当前实现把 SSE 消息校验收进 protocolSchemas.ts,由 schema 推导已知消息类型,再通过 parseSseMessageStrict() 返回四种明确结果。
先判断是哪一种变化
| 结果 |
示例 |
路由行为 |
known |
已注册的 speak_start,字段类型正确 |
进入强类型分发 |
schema_mismatch |
已注册消息的 task_id 变成数字 |
记录契约漂移诊断,跳过强类型分发 |
unknown |
服务端新增客户端尚未登记的消息类型 |
保留兼容透传,不产生契约漂移告警 |
malformed |
null、数组,或 type 不是字符串 |
在路由入口隔离,记录格式错误诊断 |
这四类结果让调用方知道该继续、观察还是隔离。未知类型与已知类型字段错误分别处理,避免服务端正常扩展也触发一片告警。
放宽未知字段,校验已知字段
schema 使用 z.looseObject():已知字段按约定校验,额外字段可以保留。
例如一条正确的 speak_start 多带了服务端统计字段,仍能解析为已知消息;但 task_id 的值类型发生变化,就进入 schema_mismatch。消息名称与 schema 的映射集中维护,运行时校验与 TypeScript 类型从同一处推导,减少两套契约漂移。
兼容路径也要说清楚
MessageRouter 对 malformed 直接返回,字幕处理与原始消息事件都不会收到它。
对 unknown 和 schema_mismatch,路由仍保留诊断记录、字幕兼容处理和原始 message 透传。它们跳过的是强类型业务分发,并非从系统里彻底消失。原始消息订阅者仍然需要按自己的边界检查输入,不能把透传事件当成已经完整校验过的业务对象。
这是 SDK 面向协议升级的取舍:保留接入方观察新消息的能力,同时让已知业务路径只消费符合 schema 的消息。
用坏输入与正常扩展一起回归
本次重跑 73 项相关用例:严格解析 7 项、协议 schema 30 项、消息路由 36 项,全部通过。
回归同时检查正常消息、新增类型、错误字段类型、空值、数组和基础值。路由测试还检查副作用:格式错误不能触发字幕或原始消息回调;未知类型不能触发 action,也不能制造契约漂移告警;已注册消息的错误字段必须留下可识别诊断。
这些测试验证输入分流与路由行为。新消息接入时,仍要补齐对应 schema、分发逻辑和测试,四路解析不能代替业务契约设计。
本文依据 Hanzora SDK 的 sseMessage.ts、protocolSchemas.ts、MessageRouter.ts 与对应测试整理,核对日期为 2026-10-10;项目见 Hanzora。