资讯动态

ESP32-S3 AI陪伴设备实战:从端侧唤醒到云端大脑的端云架构设计

发布时间:2026/9/6 10:00:30 来源:尧图企业网站定制
去年年底朋友丢给我一块 ESP32-S3 开发板说想做一个能陪孩子聊天的 AI 小摆件。市面上类似产品不少但拆开看无非两种路线要么是“离线语音板 手机 App 中转”要么干脆就是个塞了安卓系统的平板套壳。前者能力有限后者功耗、成本和隐私都压不住。我们最后决定从这块板子开始自己搭一套可持续演进的端云架构——设备端只做唤醒、采集和播放真正的 AI 大脑放在云端但又不让“端”变成哑巴终端。整个过程踩了不少坑也总结出一些可以复用的设计思路这篇就按实际开发顺序拆开聊聊。这套架构适合谁参考如果你也想做 AI 陪伴硬件、桌面机器人、语音交互摆件或者只是好奇一块 ESP32-S3 到底能不能扛住“始终在线、随时唤醒、断网可用”的场景那这篇文章应该能帮你少走很多弯路。1. 为什么是 ESP32-S3硬件选型背后的真实约束1.1 算力、内存与麦克风阵列的取舍做 AI 陪伴设备第一反应往往是“上树莓派”或者“上手机方案”。但在实际产品里成本和功耗会立刻把你拉回现实。树莓派加摄像头加屏幕整套物料可能要几百块待机功耗随随便便三五瓦还得配主动散热。手机方案更夸张光是屏幕、电池、主板就足够把一个小摆件撑成一台“半手机”但用户体验却远不如手机原生 App。所以我们的目标非常明确这应该是一个“安静的桌面设备”不是一台迷你电脑。ESP32-S3 恰好卡在这个需求点上。它有一颗 240MHz 双核 Xtensa LX7 处理器带向量指令理论上可以跑一些轻量级 KWS唤醒词模型芯片内置 512KB SRAM但它通常配合外部 PSRAM 使用比如 N16R8 模组就是 8MB flash 8MB PSRAM。这个内存规模跑不了大语言模型但跑音频采集、Opus 编码、显示缓冲、轻量唤醒模型完全够用。更重要的是它自带 Wi-Fi 和 BLE不需要外挂网络芯片布线和射频成本都低很多。麦克风方面我们最开始用 INMP441 这样的 I2S 数字麦克风后来换成 MSM261S4030H 这类 PDM 麦克风。两种方案都能跑但 PDM 麦克风在走线和物料上更省一个引脚就能出数据I2S 麦克风胜在生态成熟ESP32-S3 的 I2S 外设文档和示例都很多。实际选型时还是看整机布局如果麦克风要远离主板I2S 的抗干扰会好一些PDM 走线短则没问题。这里多说一句不要指望在 ESP32-S3 上做端侧大模型推理。虽然社区有人能跑 2B 模型量化但那是演示性质不是产品级方案。AI 陪伴设备的“端”主要是感知和执行推理放在云端。端云架构不是为了炫技而是让“端”保持低成本低功耗同时让“脑”可以随时升级。1.2 从开发板原型到准产品的供电与音频硬件开发板阶段大家都是 USB 供电直接跑。但真把它当陪伴设备就得认真对待供电和功放。我们用的是 MAX98357A 之类的 I2S D 类功放直接输出给 3W 小喇叭。这类功放芯片成本很低但增益设置很关键默认增益过高时小喇叭会削波人声听起来像“炸麦”。硬件上建议把增益引脚配置到中等档位软件上再做峰值限制。供电上一块 18650 电池加充放电管理模块就够了。注意 ESP32-S3 的 Wi-Fi 瞬态电流能达到 300~500mA电池放电能力不够就会掉压重启。所以电池选型时别只看容量得看最大放电电流最好并联一个大电容或者选用支持 1A 以上放电的锂电保护板。音频硬件最大的坑是回声。设备播放 TTS 时麦克风会清清楚楚地采到喇叭声音然后把这部分音频传到云端导致 AI 像在“自言自语”。ESP32-S3 本身没有硬件 AEC回声消除我们要么在软件里跑 SpeexDSP 的 AEC要么用乐鑫的 ESP-ADF 音频框架把麦克风、功放、音频处理节点串联成 pipeline。ESP-ADF 对 ESP32-S3 支持得不错但学习曲线有点陡后面我们也是在项目做到一半时才把 AEC 真正调明白。这块放到最后“踩坑”部分再细讲。1.3 显示屏和交互反馈GC9A01 圆形屏接入AI 陪伴设备不能只有一个喇叭至少要有一个“表情”。我们选了 GC9A01 圆形 LCD240x240 分辨率SPI 接口便宜又好买。接法和常见方屏差不多SCLK、MOSI、DC、CS、RST、BLK 对应开发板的 SPI 引脚用 ESP-IDF 的 SPI Master 驱动LVGL 在上面画表情包。因为分辨率不高一张卡通表情大概只有几十 KB扔到 PSRAM 里做帧缓冲完全没问题。但要注意SPI 刷新屏幕会占用 CPU 时间不能把 UI 刷新和音频采集放在同一个核心上。ESP32-S3 是双核我们会把音频、网络、唤醒词放在 core0UI、LED 灯效、按键扫描放在 core1。这里需要一点 FreeRTOS 的任务绑核经验但也不是很难关键是别让高优先级音频任务被 UI 刷新卡住。圆形屏的常见应用是画眼睛、嘴型和情绪动画。唤醒时眼睛睁开聆听时嘴巴动说话时眼睛变亮断网时显示云朵断开。这套状态映射其实和后面的设备状态机是配套的UI 只是状态的“显示器”。2. 端侧不是“哑终端”唤醒、采集、播放与状态机2.1 麦克风驱动与唤醒词工程化设备上电后并不是一直在录音上传那样既费流量又费电还会把大量无用背景音送到云端增加隐私风险。正确做法是端侧先跑一个唤醒词引擎只有听到指定唤醒词才进入“聆听”状态才开始上传音频。乐鑫官方提供了 ESP-SR 唤醒词方案默认支持“你好小智”之类的唤醒词也可以用 ESP-WakeNet 自定义训练。我们定制了“小贝小贝”作为唤醒词训练数据主要是中文环境下的真实语音正样本要覆盖不同年龄、语速、背景噪声负样本则要包含电视声、音乐声、非目标人声等。实测下来唤醒率可以达到 95% 以上误唤醒率需要靠阈值调参控制不能一味追求唤醒率高否则半夜电视里一句“小贝小贝”就把设备唤醒了。麦克风驱动上ESP32-S3 的 I2S 可以配置成 16kHz 单声道16bit 采样。这个规格是 ASR 和 TTS 的标准配置太低会损失识别率太高则浪费带宽。如果用的是 PDM 麦克风要注意 PDM 时钟频率和抽取滤波一般配置成 2.048MHz 时钟输出 16kHz PCM。代码里不要在主循环里阻塞读取 I2S而要用 DMA 双缓冲一个 buffer 在填数据另一个 buffer 交给 DSP 任务处理这样才不会丢 audio frame。2.2 音频上传VAD 分段与 OPUS 编码唤醒后设备采集到的是 16kHz PCM 裸流直接上传会很浪费。16kHz、16bit、单声道一秒就有 32KB一分钟接近 2MB在移动热点或家庭 Wi-Fi 下虽然能跑但没必要。我们在端侧先做 VAD语音活动检测检测到人声才开始编码上传静音时就本地丢弃。VAD 可以用 ESP-SR 里的 VAD 组件也可以自己实现简单的能量阈值加过零率判断。后者的问题是容易把掌声、咳嗽也当成语音但作为第一版已经够用。编码器选择上实测下来 Opus 是最合适的。16kHz 单声道码率设到 16~24kbps语音质量依然不错而且编解码延迟很低。ESP32-S3 上有现成的 Opus 移植库在 240MHz 主频下实时编解码只占一个核很小一部分 CPU。每一段有效语音会被封装成一个音频帧加上“开始”“结束”标记通过 WebSocket 二进制帧发给云端。这里引出一个重要设计端侧必须有一个明确的状态机。我们定义了四个基本状态IDLE待唤醒、LISTENING上传中、WAIT_RESPONSE等待云端回复、PLAYING播放 TTS。状态之间靠事件迁移比如唤醒词触发、VAD 超时、asr_result 到达、tts_chunk 播放完成。为什么要这么细因为 AI 对话是半双工的如果播放 TTS 时麦克风还开着回声问题会不可控。我们的策略是进入PLAYING后立即关闭音频上传直到 TTS 播完再回到IDLE等待下一次唤醒或用户打断。状态机也是后续“功能扩展”的骨架。后来我们加了“长途旅行模式”“夜间勿扰”“儿童锁”都是往状态机上挂几个新状态和迁移条件而不是在业务代码里到处打补丁。2.3 设备端显示与交互反馈“交互反馈”不等于“显示表情”。除了 GC9A01 圆形屏我们还加了 RGB LED 灯环和一颗物理按键。物理按键很重要短按静音、长按配网、双击报时。为什么不用全触控因为触摸屏在这类小摆件上误触率太高而且增加整机成本。一颗按键配合状态灯反而更可靠。屏幕显示由 core1 上的 LVGL 任务负责。我们维护一个全局ui_state结构体里面包含当前设备状态、当前音量、Wi-Fi 信号、云端是否连接、电池电量等信息。audio 任务和 network 任务只负责更新这个结构体UI 任务每 100ms 拉一次并刷新屏幕。这样 UI 不会阻塞音频链路表情切换也不会有撕裂感。关于 GC9A01 的驱动我想补充一个小细节它的 SPI 时钟不要一上来就拉满 80MHz很多普通杜邦线在 40MHz 以上就会花屏。我们最后稳定在 26.7MHz刷新率完全够用。PSRAM 做 framebuffer 时要注意 cache 一致性如果发现画面偶尔出现花块多半是 PSRAM 写入和 SPI DMA 读取之间没有做 cache 同步。这个坑不踩一次很难记住。3. 云端“大脑”从一句聊天到可持续演进的 Agent3.1 云端服务边界划分设备端上传了一眼望到头的音频流云端这边不能也一眼望到头地只接一个“大模型接口”。我们一开始就按领域把云端拆成了六个小服务接入网关、ASR 网关、对话编排、TTS 服务、记忆服务、设备管理。小团队搞微服务容易被架构反噬所以这里说的“服务”更多是逻辑边界不是物理进程必须拆开。我们实际部署时先全部放在一个 Python 后端进程里但每个模块之间的接口从一开始就是独立定义的。这样做的原因是“可持续演进”ASR 供应商可以随时换对话编排可以改 PromptTTS 声音可以升级记忆库可以加字段。这些变化互不干扰。比如换 TTS 引擎时设备端完全无感因为设备只认tts_start、tts_chunk、tts_end这三个消息不关心具体是哪个云厂商合成的。接入网关负责管理设备连接、鉴权和消息路由。设备连接用 WebSocket网关收到audio_chunk后转给 ASR 网关收到设备心跳后更新在线状态。我们用 Go 写网关因为并发连接处理和内存占用比 Python 有优势但对话编排和 Agent 逻辑用 Python因为 AI 生态最顺手。Go 和 Python 之间通过内部 HTTP/Redis 队列通信对这个小项目来说足够稳。3.2 ASR 与 LLM 选型的实际体验语音识别做陪伴场景有两个关键指标识别延迟和口音容忍度。小孩子说话经常吞字、调音不准成年人带方言的普通话也很多。我们最开始接的是现成云 ASR识别率不错但延迟波动大而且隐私上有点不放心。后来换成了开源模型在 GPU 上自托管经过量化后一段 3 秒的语音大约 0.5 秒能出结果。如果你的用户群体目标明确可以考虑用较大模型做 off-line 蒸馏成小模型但第一版直接上成熟开源模型就行。LLM 的选型比 ASR 更依赖场景。我们要的是“会陪伴”而不是“会答题”所以要求模型能保持固定人设能记住上下文还能被 function calling 驱动。开源社区的中小尺寸模型经过 Prompt 调教后足够用部署成本也比较可控。有人可能会问为什么不直接用大厂的通用 API答案是孩子的话里可能涉及姓名、学校、喜好等个人信息端云架构里我们更希望能控制数据流通路径。自托管模型可以把数据留在自己的服务里再通过隐私策略约束日志和存储。当然自托管 LLM 也有代价需要 GPU 资源、需要做并发控制、需要监控幻觉问题。我们的折中方案是“默认走自托管高峰期溢出到厂商 API”。架构上把 LLM 调用封在对话编排服务里上游完全不知道后端是本地还是远端这就为未来算力调度留了余地。3.3 让 AI“记得住”记忆系统与人格一致性AI 陪伴设备最大的体验瓶颈不是识别率而是“记性”。用户发现 AI 昨天记住他喜欢恐龙今天再聊恐龙时AI 能自然接上话这个“陪伴感”才真正立起来。所以我们在云端做了一个三层记忆系统。第一层是短期对话上下文。就是一个滑动窗口保留最近 20 轮对话摘要超过窗口就把旧对话压缩成一条摘要继续保留。第二层是长期事实记忆。对话编排服务会在每轮用户说完后用一个小模型做关键信息抽取格式类似{type: hobby, value: 恐龙}或{type: family, value: 妹妹叫小禾}然后写入结构化存储同时做 embedding 存入向量库。第三层是用户画像由长期记忆按类型聚合生成比如年龄段、兴趣标签、语言风格偏好。每次用户说话时对话编排服务会先从向量库里检索和当前话题相关的记忆条目拼接到 system prompt 里。比如用户说“我想听故事”检索结果可能命中“喜欢恐龙、喜欢冒险故事”然后模型生成的就是有恐龙元素、有冒险情节的故事而不是随机一个童话。这套机制跑通后设备从“问答机”变成了“认识你的小伙伴”。这一节的工程要点不要把所有记忆一股脑塞进 Prompt否则 token 成本扛不住模型也会被无关信息干扰。我们限制最多检索 5 条记忆每条不超过 50 个 token同时用重排rerank把最相关的那几条排到前面。这个优化对陪伴体验的提升比换更大模型还明显。3.4 对话编排中的“技能”扩展陪伴设备不能只会聊天还得会“做事情”。我们给对话编排服务加了一套 function calling 机制云端模型在收到用户请求后决定要不要调用某个“技能”。初期只做了三个技能播放儿歌、讲百科、设闹钟。后来逐步扩展控制智能家居、查天气、播放白噪音。每次扩展技能只需要在代码里注册一个 plugin填写参数描述和回调地址对话编排的 Prompt 里会自动加入该技能的 function schema。设备端不需要知道技能的具体实现。比如用户说“放一首儿歌”云端对话编排调用音乐插件插件返回一个音频 URL 和标题然后对话编排让 TTS 播报“好的来听这首《小星星》”同时设备端通过一个media_play消息拿到 URL 开始播放。整个过程是端云两边协作TTS 播报走 WebSocket 二进制流长音频走 HTTP 流式播放。设备端状态机需要增加一个MEDIA_PLAYING状态和 TTS 播放状态区分开这样用户语音打断时可以暂停儿歌再回应。技能机制的另一个价值是产品迭代自由度。我们后来想让设备具备“睡前安抚”模式就做了一个专门的bedtime_story插件根据时间、孩子年龄、最近兴趣生成睡前故事。整个功能从开发到上线只花了两天因为端侧只是加了一个指令映射真正的逻辑都在云端插件里。4. 端云联调BLE 配网、可靠传输与离线降级4.1 BLE 配网体验优化开发阶段用串口配 Wi-Fi 没问题但给目标用户用就太离谱了。我们必须做到手机扫码或打开小程序就能配网。ESP32-S3 自带 BLE我们用 GATT 实现了一套配网服务设备上电后如果检测不到 Wi-Fi 凭据就进入配网模式广播 BLE 服务手机端通过小程序或 App 扫描附近设备输入 Wi-Fi 密码通过 BLE 写特征值发给设备设备收到后尝试连接 Wi-Fi成功则把凭据写入 NVS并通过 BLE 通知手机“配网成功”。这个过程里有两个容易踩的坑。第一BLE 配网和 Wi-Fi 共存时2.4G 频段会有互相干扰如果发现配网过程中 Wi-Fi 连接不稳定可以把 BLE 连接间隔调大或者先断开 BLE 再连接 Wi-Fi。第二配网超时后设备必须自动回到休眠状态不能整夜开着 BLE 广播不然第二天就没电了。我们的设定是 10 分钟无成功配网就 deep sleep按一下按键再唤醒。另外NVS 里保存的 Wi-Fi 密码是明文虽然 ESP32 的 flash 读取门槛不低但为了安全还是建议用 NVS 加密或者只保存一个 token不要让设备端长期保存明文口令。后面做得更细时给每台设备颁发一个设备证书WebSocket 连接时用 TLS 证书鉴权。4.2 音频流与消息协议设备与云端的通信采用 WebSocket而不是 HTTP 轮询。原因很直接音频流是上行实时数据TTS 和指令是下行实时数据HTTP 的请求-响应模型会让延迟和实现复杂度都变高。WebSocket 天然支持双向二进制帧非常适合这类语音交互。我们设计了一套简单的消息协议。上行消息包括hello设备启动后上报固件版本、设备 ID、能力集wake端侧唤醒后通知云端开启一轮对话audio_chunk二进制帧携带 Opus 编码的音频数据audio_end当前这句话结束告诉云端可以开始 ASR 解码event比如按键事件、低电量事件、错误事件下行消息包括hello_ack云端确认设备上线下发配置asr_result语音识别中间结果或最终结果agent_reply对话编排返回的文本回复tts_start/tts_chunk/tts_endTTS 语音流media_play/media_stop长音频播放控制ota_available/config_updateOTA 和配置下发每个消息都带一个session_id表示这是哪一次唤醒对话。因为上行音频和下行 TTS 会交叉没有 session 就会乱套。同时音频二进制帧带一个递增序号云端如果发现丢帧可以要求重传设备端则维护一个发送缓冲区等待 ACK 后滑动删除。这里还要考虑断线重连。ESP32-S3 的 Wi-Fi 偶尔会掉线WebSocket 也会被运营商切断我们必须实现指数退避重连1 秒、2 秒、4 秒、8 秒……最大 60 秒。重连成功后设备上报hello云端会检查是否有未下发的状态比如用户之前请求的儿歌还没播完云端会重新下发media_play。4.3 离线降级与本地小能力端云架构最怕的就是“断网变砖”。陪伴设备的主要功能依赖云端但如果家里 Wi-Fi 临时出问题设备至少要能“体面地待着”。我们在架构里引入了一个降级层网络断开时播放本地提示音“网络好像走丢了我还在哦”本地保存 3 个离线故事和 2 首白噪音通过按键触发播放本地时钟、闹钟、倒计时这类基础功能完全离线可用唤醒词仍然激活但识别到语音后只会闪灯并提示“等我连上网再陪你聊天”这些本地能力用的都是 ESP32-S3 的 SPIFFS/LittleFS 存储。我们把离线音频资源放在单独的分区后续可以通过 OTA 更新。降级层看起来不起眼但在用户真实使用中特别重要。很多 AI 硬件产品口碑崩掉就是因为断一次网用户觉得“这玩意儿是砖头”。设备可以在能力上妥协但不能在基本可用性上装死。4.4 安全与隐私底线做 AI 陪伴设备尤其可能面对儿童用户隐私安全是必须正面回答的问题。我们在设备和云端都做了硬性约束设备端不存储用户语音原文只存必要的唤醒词和离线资源所有端云通信走 WSS/TLS禁止明文音频流云端 ASR 处理完的音频原始文件默认保留 7 天超过自动删除除非用户明确开启“语音记录”功能对话记录和记忆画像分开存储记忆画像只存事实与偏好不存可回溯到具体身份的敏感组合设备上有一颗物理按键可以一键关闭麦克风关闭后设备不再做任何录音状态灯显示红色这些规则不是写完设计文档就结束而是在代码评审里一条条核对过的。AI 陪伴这类产品信任是最大的隐形资产。与其等出事再补不如在一开始就把数据最小化原则落到代码里。哪怕是原型机也不要在云端打日志时顺手打印用户说过的每句话。5. 可持续演进OTA、可插拔技能与灰度发布5.1 OTA 升级链路让几台设备也能滚动发布开发板阶段固件用串口烧一次就完事。但设备一旦分发出去就必然面临固件升级问题。ESP32-S3 支持双分区 OTAapp0 和 app1 两个分区升级时写到非当前启动分区校验成功后切换启动标志。一旦新固件启动后连续崩溃几次设备会自动回滚到旧分区避免“升级即变砖”。我们给 OTA 加了三层控制。第一层设备定期比如每 6 小时向设备管理服务请求版本信息如果云端有新版本下发 OTA 任务。第二层固件通过 HTTPS 下载下载完做 SHA256 校验再写入 OTA 分区。第三层支持“灰度通道”测试设备默认订阅 beta 通道普通设备订阅 stable 通道。云端可以先给 5 台测试设备推 beta运行一天没有异常再按 10%、50%、100% 逐步推 stable。OTA 不只是升级主固件还需要升级几个独立资源分区唤醒词模型、UI 资源包、离线音频包。我们把这些资源做成“资源包”形式版本号独立于固件设备启动时检查资源包版本需要更新则从云端下载并写入对应的分区。这样做的好处是想换一个唤醒词或加一组离线故事完全不需要动主固件发布风险小很多。5.2 云端配置下发与功能开关设备数量少的时候改参数可以直接改代码重新烧录。但做产品化哪怕是十台设备也不能每次改音量都发一版固件。我们在设备管理服务里维护每台设备的 JSON 配置比如{ volume: 70, wake_word: 小贝小贝, child_mode: true, max_response_length: 120, language: zh-CN, plugins: [music, story, alarm, home_control] }云端通过 WebSocket 下行一条config_update消息设备收到后先做 schema 校验再写进 NVS。部分配置可以热生效比如音量、灯光亮度部分配置需要重启比如唤醒词切换我们就标记一个reboot_required字段设备在空闲时自动重启应用。功能开关是“可持续演进”里很实用的一环。新技能上线时并不需要所有设备立刻启用。我们可以在配置里控制某个插件是enabled还是beta只有 beta 设备才在对话编排里挂载该技能。万一新技能出了 bug关掉开关就回滚了不用改设备固件。5.3 日志回传与远程调试真实设备上的问题靠串口日志很难复现尤其是在用户家里。我们做了一个轻量日志回传机制设备端把结构化日志时间、模块、级别、消息、上下文打包成 JSON周期性地通过一个独立 WebSocket topic 回传到云端。日志级别可以远程调整平时只回传 WARN 以上排查问题时动态改成 DEBUG。更重要的一个能力是会话回放。每一轮对话云端都会保存 session_id、唤醒时间、音频时长、ASR 文本、Agent 回复、TTS 首包延迟等关键字段。如果用户反馈“有时候叫它没反应”我们就可以按设备 ID 和时间段拉出这条会话链路看是唤醒没触发还是音频上传断了还是 ASR 超时。这个“可观测性”比任何日志都直观。崩溃处理我们也做了。ESP32-S3 上发生 panic可以先把 core dump 保存到 flash 的专门分区重启后通过 OTA 链路把 core dump 上报到云端。解析 core dump 需要 ESP-IDF 的espcoredump.py在 DevOps 流程里自动解析并关联到对应固件版本。这个小系统救了我们好几次尤其是一些内存越界导致的随机重启问题没有 core dump 根本定位不到。6. 实测中的那些坑从 demo 到稳定陪伴设备6.1 回声、底噪和“炸麦”demo 阶段声音能出来、语音能识别就心满意足了。但真放到桌面上用第一个让你崩溃的问题就是回声。孩子对着设备说话设备播放 TTS 的同一瞬间麦克风会把自己播放的声音采回去云端 ASR 识别出“你在说什么”然后回复一些莫名其妙的话。这种现象在语音设备里叫“自我唤醒 自打断”非常破坏体验。解决回声有几个层面。硬件上喇叭和麦克风的距离尽量拉开中间加隔音泡棉喇叭朝下或朝后麦克风朝前。软件上播放 TTS 时强制关闭音频上传但关闭的时机有讲究要在 TTS 真正播完并加一点余韵后再恢复否则末尾几个字会被截断。再进一步是 AEC我们后来用 ESS-ADF 的 AEC 组件把远端参考信号也就是喇叭播放的数据接入回声消除器效果比单纯关麦克风好很多也支持打断交互。底噪问题则是另一类。MAX98357A 的增益调到最高时即使没有播放音频喇叭也有明显嘶嘶声。我们通过测量待机电流和输出波形发现功放在无输入信号时存在本底噪声后来将增益调到中档并做了静音控制声音干净多了。麦克风侧还会遇到射频干扰Wi-Fi 天线离 I2S 走线太近时音箱里偶发“滋啦”声这个只能靠改板子布局或者加屏蔽罩解决。6.2 延迟预算与体验优化AI 语音交互最怕“迟钝”。我们把一次完整对话拆成几个阶段本地唤醒0.2s、音频上传0.3s、云端 ASR0.5s、LLM 首 token1s、TTS 首包0.5s。加起来超过 2.5 秒用户主观感受就是“喊完等很久它才说话”。最开始这个数字更高因为我们是等 ASR 识别完一整句再一次性送 LLMLLM 也等生成完整回答才合成 TTS用户会觉得极其拖沓。优化动作有三个。第一ASR 改成流式识别用户还没说完云端已经开始出中间结果等到audio_end后立即合并最终结果。第二LLM 用流式输出对话编排拿到第一段完整句子就立刻送去 TTS不必等整段回答结束。第三TTS 本身改用流式合成边合成边推送设备端边接收边播放。三者叠加端到端延迟能从原来的 3.5s 压到 1.5s 左右。如果还想更快可以在用户这边优化 VAD 的静音尾长把“说完到触发”的等待从 800ms 缩短到 400ms。延迟优化不能盲目压阈值尤其 VAD。尾长设太短用户在句间停顿 500ms 就被当成话说完了ASR 会识别出一句残缺的话。我们通过大量真机测试最终把尾长定在 450~600ms 之间并且根据用户年龄段动态调整小朋友说话停顿多尾长要稍微大一点。6.3 让“可持续”落地看板、文档和自动化架构可以演进但如果团队不维持设备、云端、移动端三部分的版本对应关系演进很快就会变成灾难。我们把设备固件版本、云端服务版本、资源包版本、对话编排 Prompt 版本全部纳入一个版本矩阵每次发布都记录“固件 A 资源包 B 云端 C”的组合是否经过验证。这个小团队也能做到其实就是一张表的事。设备在线率和唤醒成功率是最核心的两个指标。我们搭了一个非常简单的看板每天展示在线设备数、平均在线时长、请求成功率、端到端延迟 P50/P95、TTS 播放失败次数。这些数据来自日志系统的基础打点不复杂但能让你在功能迭代时知道到底有没有变好。“可持续演进”不是事后补文档而是结构上允许你低成本地替换模块。我们在项目开始时就约定设备端不依赖云端的实现细节云端不依赖设备端的硬件型号。所有能力都通过消息协议暴露所有接口都有版本号。这让我们后来加麦克风阵列、换屏幕、加摄像头时设备端改动都只是新增“能力”和“消息”而不需要动整个架构。这个体会是这轮开发最值钱的收获。最后再分享一个落地过程中的小技巧在开发早期把设备端状态机画成一张状态迁移表挂在工位前面。每次遇到“设备为什么没反应”的 bug先查状态机停在哪个状态而不是直接看代码。绝大多数语音交互问题都是状态迁移没有考虑全导致的。这张表我到现在还在维护每次加新功能都会先改它再写代码。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价