最近我把工作台上那台自制的语音助手“小智”强制断网了。断网之后它照样能响应唤醒词也能开关灯、倒计时但一聊天气、一问百科就直接哑火——这逼着我沿着一次完整的唤醒过程把设备端和服务端的分工重新捋了一遍。折腾几天下来光是日志就看了几十屏最后结论挺有意思断网不可怕可怕的是设计时根本没想过设备离线后该干嘛。这篇就当一次踩坑笔记给同样在做语音助手、物联网设备或者边缘智能项目的朋友参考。“小智”本身不复杂硬件就是一块 ESP32-S3 加一块 INMP441 麦克风外接继电器和扬声器跑本地唤醒词模型再接一个简单 HTTP 服务做云端语义处理。这类结构在现在的智能音箱、智能家居终端里非常常见所以整个排查思路和设计取舍应该能直接迁移到你的项目里。我尽量把这几天踩过的坑、想明白的道理、实测到的数据都写清楚。1. 一声“小智”背后设备端与服务端的完整分工1.1 唤醒之前本地音频管道和低功耗设计很多人以为“唤醒”是网络请求触发的实际上完全不是。在“小智”这个设备上唤醒词检测整个过程都在本地芯片上完成和云端一毛钱关系都没有。硬件链路是这样的INMP441 麦克风通过 I2S 接口持续采样采样率 16kHz、16bit、单声道数据不断写入 DMA 环形缓冲区。这块 ESP32-S3 上跑着一个量化后的唤醒词模型模型输入是最近 1.5 到 2 秒的音频切片输出就是“是否有人喊了小智”。语音唤醒的模型我用过 onnx-wakeword 那一套思路也试过自己训练一个极小的关键词分类器本质都一样在端侧用低精度推理做二分类只认“小智”这两个音节。为什么唤醒必须放在本地三个原因非常现实。第一是时延本地推理一次大概几十毫秒如果先把音频传到服务器再等结果最顺利也要一秒钟起步用户连续喊两三遍设备还不动体验直接崩。第二是隐私唤醒词前后的音频片段其实都属于敏感数据只要在本地处理完且不被上传至少用户心里能踏实一点。第三就是断网可用性这也是我今天要重点聊的唤醒是设备最后的“本地能力底线”如果连唤醒都要联网那设备断网就真的成了砖头。这里顺带提一句低功耗语音唤醒。我最初版本是让 ESP32 一直以全速跑音频采集和推理实测电流能到 80 到 100 毫安用电池供电撑不了多久。后来改成两级唤醒空闲时主芯片进入深度睡眠只留一个超低功耗的语音活动检测器监听环境声音检测到疑似人声再唤醒主芯片去跑唤醒词模型。这个思路和 STM32U575 的 STOP3 模式有点像都是保留必要外设和 RAM其余全部断电只在外部事件到来时快速唤醒。改完以后待机电流压到了微安级电池续航的问题才算解决。1.2 唤醒成功那一刻本地命令先分流“小智”被唤醒之后设备会进入一个短暂的“聆听”状态这时候麦克风继续采集音频但处理逻辑不一样了。我设计了一套最简单的命令词分流机制。唤醒后的 3 秒内设备拿音频去匹配本地命令词表命令词表里包括“开灯”“关灯”“倒计时五分钟”“现在是几点”“定个闹钟”这类固定句式。命中命令词就直接在本地执行整个过程中不产生任何网络流量从唤醒到执行完成大概 150 到 300 毫秒。用户体感就是“刚说完话灯就开了”。只有本地命令词表没有命中时设备才会把音频打成请求发给服务端。服务端那边再接大词表语音识别、自然语言理解、知识库查询最后把结果转成结构化 JSON 返回。这样做的好处很直接本地先做一次“能者多劳”把最简单、最常用、即时性要求最高的活儿都留在设备端只有真正需要语义理解、动态数据、在线内容时才上云。我经常用一个“小区门卫”的类比来解释这种分流逻辑。唤醒词识别就像小区门口的保安先确认来的人是不是住户一旦确认是住户小区里能解决的事比如开门、开灯、倒垃圾就直接解决不用每次都给物业总部打电话。只有遇到住户解决不了的问题比如“今天会不会下雨”才需要电话联系外部专家。这个类比放在架构里也很贴切本地能力覆盖得越广对服务端的依赖就越小断网后的存活能力也越强。1.3 服务端返回后设备端还要做“最后一公里”当音频真的被送上服务端之后整条链路就变成经典的客户端-服务端协作模型。设备端把音频 POST 到服务端接口服务端先做 ASR 识别成文本再做 NLU 解析用户意图然后去天气接口、百科接口或者对话模型那边拿答案最后返回一个内容相对固定的 JSON。这个 JSON 一般包含意图类型、回答文本、需要执行的设备动作、TTS 播报文本等。设备端拿到之后再做最后一步解析 JSON控制 GPIO、播放音频、点亮状态灯。“最后一公里”特别容易被忽视但它恰恰是断网时问题最集中的地方。比如服务端返回慢、连接超时、返回格式变了吗、JSON 字段缺失、TTS 音频还没下载完设备就进入休眠每个环节都能让体验瞬间崩塌。就好比手机上的语音支付唤醒和生物识别是设备本地完成的但真正扣款、查余额必须走服务端中间任何一条链路断了用户感知到的就是“按下支付没反应”根本分不清是设备问题还是网络问题。所以我现在写代码有个习惯所有发往服务端的请求都要求设置“连接超时”和“读取超时”并且服务端返回的 JSON 要用 schema 校验不能默认“服务端永远可靠”。这一课不是从教科书上学来的是我在断网实测里被一次假死坑出来的后面第 4 节会详细说。2. 断网之后设备还剩什么本地能力与服务端依赖的真实边界2.1 断网后照样能跑的功能做了断网实验之后我才真正把“设备端能力”和“服务端依赖”分清楚。先列一份设备断网后依然正常的清单这些都是实际验证过的唤醒词识别模型跑在本地完全不依赖网络。本地命令词“开灯”“关灯”“倒计时”“定闹钟”“播放提示音”这些逻辑全部在设备状态机里完成。局域网控制通过本地局域网协议比如 MQTT控制同一网段下的其他智能设备比如把灯、插座、空调这些接到同一个局域网外网断掉不影响它们之间的通信。本地 RTC 时间只要设备没彻底断电RTC 能继续走时就算断电重启也会在联网时同步一次并缓存上次校时结果。关键在于一条判断标准这个功能所需的“判断依据”和“执行资源”是否都在设备本地。开灯需要的信息是“用户说出开灯”加上“被控设备的地址”这些都在本机或局域网内所以断网不影响。倒计时也一样计时器逻辑完全在本地跑闹钟到点之后播放一段预置音频也没有任何外网依赖。有一个容易被忽略的小点很多智能设备带“本地场景联动”比如“天黑自动开灯”“有人移动就录像”这类自动化如果完全跑在局域网网关或设备内部断网后仍然有效。我见过不少产品把联动判断放在云端一旦断网全家设备就像集体失忆这就是典型的本地能力没设计好。2.2 一断网就崩的功能有能跑的自然就有断网即崩的。整理一下这次“小智”断网后暴露出来的失效功能大词表语音识别和语义理解设备本地只放了一个关键词模型想理解“帮我找个附近开着的川菜馆”这种句子必须靠服务端的大模型。天气、新闻、百科、股票这类动态信息查询数据在第三方服务或云端数据库里设备本地没有持有。在线 TTS我用的云端语音合成断网后没有新音频文件可以播报。账号绑定、云同步、设备远程查看这些天然依赖服务端断网后全部不可用。这些能力的共同特点是数据量太大、计算太复杂或者信息是实时变化的不可能全部塞进一个嵌入式设备里。就像让一个门店店员背下整个连锁集团的库存系统和客户关系系统根本不现实遇到这类问题还是得打电话回总部。有意思的是断网实验其实是一种非常高效的产品自检方法。很多产品经理在评审时喜欢在原型图里画“在线”“离线”两套界面但真正上线后到底哪些功能在断网时会崩往往没人说得清。我后来做了一个“设备老化测试全自动执行脚本”把几十条常用指令在断网状态下自动跑一遍过一遍就生成一份能力清单谁依赖服务端、谁本地就能干活一目了然。这个方法也推荐你们试试。2.3 用户视角断网后“小智”还能做什么光说技术边界太抽象我从用户视角重新描述一下断网后的实际体验。第一种情况只是外网断了但局域网还通。“小智”能正常唤醒能执行所有本地命令词也能控制局域网里的灯和插座。你让它“打开客厅灯”它会立刻照做你问它“上海天气怎么样”它会停顿一下然后说“网络好像出了点问题我暂时没法查天气你可以试试其他本地功能”。这种克制而清楚的降级反馈其实比假装听懂然后胡诌一通要可信得多。第二种情况完全离线局域网也没有。唤醒词和本地命令词仍然正常工作但所有需要外网的内容都会被拦截统一回复“网络不可用”。倒计时、闹钟、本地音频播放这些照样好使。换句话说如果家里断网了“小智”至少还能当一个带语音控制的定时器和开关面板。第三种情况弱网信号时不时断一下。这是最难受的因为用户很难判断设备到底听没听到。我实测下来本地命令命中时基本感觉不到延迟毕竟不出网但云端请求就有概率卡在超时边界上一会儿成功一会儿失败。为了减少这种“薛定谔的响应”我做了降级策略连续两次云请求超时就把设备切到“离线模式”不再发起云端请求等健康检查恢复后再切回来。下面这张表可以更直观地表示三种场景的能力差异场景本地唤醒本地命令词局域网控制云端语义/问答设备反馈外网断局域网通正常正常正常不可用明确提示网络异常完全离线正常正常不可用不可用明确提示网络异常弱网/不稳定正常正常可能不稳定间歇可用超时后自动降级用户其实不怕功能缺失怕的是设备毫无反馈地“死掉”。所以断网设计的第一原则不是强行让所有功能可用而是把能用的留下来把不能用的明确告诉用户。3. 一次真实断网实测从唤醒到响应的全程拆解3.1 实验环境与三种断网模拟方式先把实验环境摆出来方便想复现的朋友对照。设备端用的是一块 ESP32-S3 开发板搭配 INMP441 麦克风外接一个继电器模块模拟“开关灯”还接了一个 I2S 功放模块做语音播报。固件用 C 写的唤醒模型和命令词匹配都在本地云请求走 HTTP POST 到一个部署在局域网服务器的简单服务端程序上。服务端程序用 Python FastAPI 写返回意图和播报文本模拟真实云服务的行为。我用了三种方式模拟断网因为“断网”这个词其实很模糊物理断网直接把路由器外网网线拔掉这是彻底离线。路由策略断网在路由器里禁止外网访问但保留局域网通信用来模拟“外网断但局域网通”的典型场景。DNS 故障把设备配置的 DNS 服务器改成错误地址制造“伪断网”。这时候网络接口仍然 upTCP 连接也能建立但域名解析必然失败会让客户端长时间卡住。第三种方式特别值得提一下。很多开发者在本地联调时遇到过 WSL2 或 Ubuntu 虚拟机“老断网、重启就好”的诡异问题本质就是网络栈状态不稳定或 DNS 配置异常应用层看起来像断网实际上系统又没给任何明确事件。这种“伪断网”恰恰最容易让客户端暴露出超时处理不善的 bug所以我的实验里专门加了这一项。3.2 四个场景的实测记录下面是实际测试时的串口日志我加了时间戳方便看到每个环节的耗时。场景顺序是先断网前再断网后。场景一断网前“小智现在几点了”[19:00:01.123] wake word detected: xiao_zhi [19:00:01.178] local intent matched: query_time [19:00:01.203] local rtc time: 19:00:01 [19:00:01.214] tts_play: /sdcard/audio/time_190001.wav [19:00:01.351] done这里从检测到唤醒词到最后播报完成只用了 228 毫秒全程没有任何网络请求纯粹是本地能力。场景二断网前“小智打开客厅灯”[19:01:05.042] wake word detected: xiao_zhi [19:01:05.097] local intent matched: turn_on_light [19:01:05.113] mqtt publish: 192.168.31.50:1883 topicdevice/light/cmd payload{state:on} [19:01:05.167] mqtt ack received [19:01:05.180] tts_play: /sdcard/audio/light_on_ok.wav这个场景走的是局域网协议主灯设备在同一个网段里所以即便外部网络已经拔掉整个链路也完全不受影响。场景三断网前“小智北京天气怎么样”[19:02:10.330] wake word detected: xiao_zhi [19:02:10.385] no local intent matched, start cloud request [19:02:10.401] POST http://192.168.31.100:8000/v1/ai [19:02:12.118] cloud response received [19:02:12.153] cloud intent: query_weather, city北京 [19:02:12.402] tts_play: /sdcard/audio/weather_download.wav请求发出到收到响应用了 1.7 秒左右这个延迟主要在网络和服务端处理。如果用户没有耐心可能已经以为设备坏了。场景四拔掉外网网线后重复以上三句话[19:03:01.230] wake word detected: xiao_zhi [19:03:01.284] local intent matched: query_time [19:03:01.307] tts_play: /sdcard/audio/time_190301.wav [19:03:01.445] done [19:03:20.112] wake word detected: xiao_zhi [19:03:20.167] local intent matched: turn_on_light [19:03:20.181] mqtt publish: 192.168.31.50:1883 topicdevice/light/cmd payload{state:on} [19:03:20.236] mqtt ack received [19:03:20.249] tts_play: /sdcard/audio/light_on_ok.wav [19:04:05.502] wake word detected: xiao_zhi [19:04:05.558] no local intent matched, start cloud request [19:04:05.574] POST http://192.168.31.100:8000/v1/ai [19:04:08.582] network timeout, request aborted [19:04:08.590] fallback tts_play: /sdcard/audio/network_error.wav可以看到本地命令词和局域网控制在断网前后表现几乎一致而云端请求在 3 秒后超时设备播放了降级提示音。整个体验没有卡死也没有半句话不说的“真空期”。3.3 从串口日志读到的分工真相这四段日志放在一起“设备端和服务端的分工”比任何架构图都直观。首先唤醒和本地命令词处理完全不上云。从唤醒词命中到命令执行完成耗时基本在 300 毫秒以内这段体验在网络正常时和网络断开时没有任何区别。对用户来说设备是“可持续响应”的这种确定性的掌控感是云端能力无法替代的。其次云端请求是“尽力而为”的增强能力不是设备的生命线。本地命令词没命中时设备才发起云请求云请求超时后设备要优雅降级而不是原地等待。真实的断网场景里用户最怕的就是设备假装沉默实际上内部已经因为同步阻塞卡死了。最后日志还揭示了设计上的一条红线每个环节都要有明确的时间预算。唤醒多久、命令匹配多久、云请求多久算超时、降级提示多久播完这些都必须提前定好。我专门把每个环节都打了时间戳就是为了在问题发生时能快速定位到底慢在哪个模块。4. 断网实测中的典型问题与排查实录4.1 断网后设备“假死”同步阻塞谁背锅第一次做断网测试时我最先遇到的是“假死”。现象是断网后喊“小智北京天气怎么样”设备沉默大概五秒然后直接重启期间再喊多少次唤醒词都没反应。串口日志显示问题发生点非常明确[19:10:01.102] wake word detected [19:10:01.160] no local intent matched, start cloud request [19:10:01.175] http_client_send_request // 卡在这里罪魁祸首是早期代码用了同步 HTTP 请求而且没设置超时。ESP32 的 Wi-Fi 栈在 DNS 解析失败时并不会立刻返回而是会一直重试主循环被http_client_send_request卡住唤醒检测、LED 刷新、看门狗喂狗全都停了。看门狗超时之后发现主循环没动静就直接把设备重启了。这个问题在嵌入式开发里太典型了不要用同步阻塞的方式访问网络。改法是新建一个异步任务专门处理云请求主循环保持畅通所有网络请求都设置连接超时和读取超时我实际用了 3 秒再加一个应用层看门狗如果超过 10 秒没有任何事件处理就强制回到空闲状态。改完之后再跑断网测试设备最多沉默 3 秒就播报降级提示再也不会假死。其实这类问题和“服务端接口测试”里常见的问题一模一样客户端把网络请求当成本地函数调用来写忽略了网络的不确定性。服务端接口再稳也挡不住客户端自己的超时逻辑有 bug。4.2 唤醒成功但指令没反应状态机与音频缓冲的坑还有个特别隐蔽的 bug发生在断网之后第二次、第三次唤醒时。现象是“小智”能听见唤醒词也播放了“叮”的提示音但说完“开灯”之后设备毫无反应。排查过程挺折腾。我先以为是命令词表没加载后来发现不是。反复看代码才发现问题在音频缓冲唤醒词检测用的环形缓冲区是持续写入的唤醒成功的瞬间缓冲区内可能还残留着唤醒词之后的半秒环境音和之前的历史音频。命令词匹配器拿到的是包含杂音和唤醒词尾巴的音频段首尾被截断了导致“开灯”这两个字根本识别不出来。解决办法不复杂唤醒事件触发后先清空环形缓冲区再开始收集命令词音频。同时把设备状态机改得更严格一点从“休眠态”唤醒后必须显式切换到“聆听态”这一步完成后才允许麦克风数据进入命令词识别模块。另外我还加了一条调试输出把每次识别到的文字结果和置信度都打出来这样下一次遇到“听见了但没反应”的情况能立刻从日志里看出是“没识别到”还是“识别错了”。这个经验同样适用于其他嵌入式调试场景比如 STM32 死活不识别 USB 设备、串口打印乱码、设备拔插后无法枚举很多问题都不是“外设坏了”而是初始化顺序和缓冲状态没处理好。4.3 断网恢复后状态对不上事件补传不能少另一个让我印象深刻的问题是断网恢复后的状态不一致。有一次我在断网状态下用本地指令关掉了客厅灯但 App 和服务端依然显示灯是开着的。用户视角就是“我明明关了灯为什么手机上一看是开的”这已经不只是语音助手的问题了而是所有物联网设备都会遇到的状态同步问题。根因是我的设备只有在线时才会上报状态离线期间的状态变化没有任何记录。解决方法是加一个本地事件队列把设备的关键操作以“事件”的形式持久化比如时间、操作类型、执行结果、目标设备信息。每次断网恢复后设备先把缓存的事件按时间戳排序再逐个 POST 到服务端的事件同步接口。服务端收到事件后更新设备状态返回确认。为了防重复上报我还在事件里加了唯一 ID服务端根据 ID 去重也就是让接口具备幂等性。这件事做好之后“小智”在断网期间做的每个本地操作最终都能在服务端状态里反映出来两端回归一致。做设备协议对接或者 EAP 系统现场实施的朋友应该深有体会设备端主动、可靠地上报状态是整个系统一致性的地基。如果只靠服务端轮询或者设备在线时顺手同步断网这个小小的场景就能把系统信誉搞得一团糟。4.4 断网检测的三种做法与选型对比断网检测听起来简单但真正做起来全是坑。我试过三种方案各有取舍。第一种是只 ping 网关。实现最简单但坑也最大外网断了只要局域网还通网关照样能 ping 通设备根本感知不到“上不了云”。WSL2 或者 Ubuntu 虚拟机里那种“系统显示网络正常但实际访问不了外网”的现象和这个很像很多朋友在本地调代码时深受其扰。第二种是定期 HTTP HEAD 探测服务端健康地址。这种做法能反映真实业务链路的可达性因为探测目标是服务端域名或者健康检查接口比 ping 网关准得多。代价是增加了额外的流量和延迟而且如果探测的地址本身不稳定容易误判。第三种是根据业务请求的失败统计来判断。设备在正常流程中每次云请求失败就记一次“失败计数”连续 N 次失败就判定为离线连续几次成功则判定恢复。这种方案最真实毕竟业务请求能不能通才是用户真正关心的标准坏处是判断有滞后至少要到第一次超时才能发现问题。我现在实际用的是“业务请求失败统计为主HTTP 探测为辅”的组合平时不额外打扰网络业务失败连续超过 2 次就切弱网模式连续超过 5 次再切换成离线模式同时每 30 秒做一次轻量 HTTP 探测一旦探测成功立刻尝试恢复。给一张选型对比表方案成本准确性滞后性适用场景ping 网关极低差无不适合判断外网可达性HTTP 健康探测中较好低适合服务端可探测的场景业务失败统计低最好较高适合已有云请求的设备4.5 断网恢复后设备还“懵”着网络栈复位不能省还有一个值得记录的坑断网恢复之后设备并不一定马上就能正常联网。尤其是物理拔插网线、路由器重启这种场景Wi-Fi 模块可能已经和路由器断开了关联但固件里没有处理“断线重连”事件。我一开始在断网测试后恢复网络发现“小智”还是继续播报“网络不可用”直到手动重启才恢复。问题出在 Wi-Fi 层的状态没有及时恢复。我后来在固件里加了网络事件监听发现 Wi-Fi 断开就触发重连并清空 DNS 缓存同时应用层健康检查识别到“HTTP 探测成功”后还会主动做一次完整的网络状态确认包括重新解析服务端域名。这样网络恢复后设备最快几秒内就能切回在线模式不用再依赖重启。5. 把“断网可用”写进设计设备端与服务端的协作架构5.1 本地优先原则确定性功能不出网经过这一轮断网实测我最大的设计心得只有一句话凡是确定性、实时性要求高的功能都尽量放在设备本地只有语义理解、知识问答、动态数据这类真正需要大模型或云端算力的功能才允许上云。怎么落地这个原则我给自己定了两条检查规则。第一新功能上线前先问一句“如果没有外网这个功能还能不能按预期工作”如果答案是不能那就要么把它改成本地可执行要么明确告诉用户它依赖网络。第二功能列表要分成本地能力和服务端能力两个清单产品评审时同时看两份清单而不是只看一张“演示成功”的截图。“小智”的本地命令词表就是这么设计的。我把开关灯、调亮度、倒计时、闹钟、查询本地时间、播放本地提示音之类的功能全部塞进本地状态机保证不出网也能闭环。至于“帮我写一段周报”“今天股市怎么样”这类必须靠服务端的任务才交给云请求。用我上面那个门卫类比来说就是先把手头能办的事全部办了再决定要不要打电话求助。5.2 在线、弱网、离线、恢复状态机怎么切有了本地优先原则之后设备在断网场景下的行为就变成了一套状态机而不是一堆 if else 的临时判断。我设计了四个基本状态在线、弱网、离线、恢复中。在线状态本地命令和云请求都正常云请求失败计数清零。弱网状态云请求开始出现失败或超时但还没到离线阈值。此时仍然允许发起云请求但本地命令词全部优先响后台同时做健康探测。离线状态连续多次云请求失败或健康探测失败设备进入离线模式。此时完全停止云请求只响应本地命令词遇到云服务内容就播放“网络不可用”的提示音。恢复中状态健康探测成功或局域网连通设备开始尝试补传离线事件然后逐步恢复正常云请求。切换条件我建议用“连续计数”而不是“单次失败”因为网络本身有抖动一次超时并不能说明整个网络不可用。实测下来连续 2 次失败判断弱网、连续 5 次失败判断离线整体误判率比较低。恢复判断则要保守一点连续 3 次业务请求成功才切回在线防止刚恢复又立刻掉线造成来回震荡。5.3 状态同步与幂等补传让两端最终一致最后说说设备端和服务端的数据一致性。很多人做物联网设备时只关心“控制指令下行”忽略了“设备状态上行”这导致断网恢复后各种状态漂移。我之前在设备端加了一个 SQLite 表存事件日志主要字段是事件 ID、时间戳、操作类型、目标对象、执行状态。每次本地命令执行成功后就往表里插一条记录服务端在线时立刻上报并删除离线时就攒着等恢复后按时间戳排序补传。补传接口必须是幂等的这个坑我踩过。最开始补传时没有事件 ID服务端按“时间戳设备号”去重结果两条事件在相同一秒内发生就漏掉了。后来每台设备自己生成全局唯一事件 ID服务端只靠这个 ID 去重重复上报多少遍都不会产生副作用。这套机制其实和很多设备协议里“状态上报”的设计一致比如半导体封测设备对接 EAP 系统时设备状态必须可靠上报到主机网络中断后也要有补传机制否则整个产线数据就没法对账。补传顺序也不能乱。离线期间如果有“开灯”和“关灯”两条事件顺序反了服务端最终状态就错了。所以补传前要按时间戳排序而且服务端处理事件时最好也带一个“事件时间”字段避免因为网络乱序导致状态错乱。最终两端都收敛到同一个状态用户不管在 App 上还是设备前看到的结果才是一致的。断网实测做下来我个人最大的体会是断网不是设备的末日反而是检验设备端与服务端分工是否合理的最好方式。你不需要复杂的测试工具把网线拔掉把平时常用的指令挨个说一遍哪些能秒回、哪些会卡住、哪些直接没反应一眼就能看出本地兜底做得够不够。后来我每次改“小智”的固件都会强制跑一轮断网回归这比任何架构评审都直观。我现在还在考虑把一个小型语言模型搬到设备端把更复杂的离线语义理解也做出来。到那时候“小智”断网后能做的事又会多出一大截。先把这次实验的日志和断网测试流程整理一下如果有人想照着复刻后续我再把工程配置和测试脚本单独发出来。