尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

圆屏语音设备不跑模型?ESP32-S3语音客户端架构实战

圆屏语音设备不跑模型?ESP32-S3语音客户端架构实战 这个糖球系列做到第三期正好可以聊聊一个很多人容易误解的点为什么这代圆屏方案我没让ESP端去跑模型而是老老实实当后台的语音客户端。说实话刚立项那会儿我也天真过想着ESP32-S3毕竟带向量加速指令多少能塞个轻量模型进去结果一跑就发现根本不是那么回事。你真把模型塞进去之后唤醒、识别、对话刷新、屏幕渲染这堆活儿全挤在同一个MCU上体验基本是灾难级的。所以这一篇我就把整个设计逻辑和落地过程完整拆一遍尤其是“不跑模型”这个取舍背后的原因以及ESP端到底该负责些什么活才最合理。先说说这套东西的整体定位。糖球是一个圆形屏幕的桌面语音交互设备核心交互方式是语音对话加屏幕反馈背后的大语言模型能力全部由后台提供。ESP32-S3在整套系统里承担的职责是收音、播放、显示、按键与状态管理另外负责和后台的语音识别、大模型和语音合成服务通信。说得直白点它就是一个带屏幕的语音终端模型推理放在后台PC服务器上ESP端只做客户端的活。正因为把模型推理从端侧拿走了整个系统的开发效率和升级灵活性才有了质的提升而圆屏上的交互效果也能做到足够流畅。1. 整体架构为什么让ESP圆屏当“语音客户端”而不是“模型终端”1.1 端侧算力与需求之间的真实差距先算一笔账。ESP32-S3主频最高240MHz带向量指令内存最大的模组也就8MB左右的外部PSRAM可用Flash一般是8MB或16MB。看起来好像能跑点东西但常识判断一下一个参数规模在1B左右的量化对话模型光权重就至少占500MB以上存储空间运行时还要加载进内存MCU上根本没地方放。即便是小得多的语音识别模型或者唤醒词模型用ESP-SR这类专门优化过的方案跑起来也需要占用大量CPU周期和内存带宽尤其是进入连续监听状态时系统几乎干不了别的活。我做第一版原型的时候试着在上面同时跑唤醒词模型和一个轻量的命令词分类模型结果屏幕刷新掉帧、音频采样出现断流、网络请求超时。最直观的现象是你说一句话屏幕要卡半天才反应。这就是典型的任务挤占效应MCU的算力是固定的模型推理一旦开始就会周期性地抢占CPU时间片音频DMA传输、LVGL渲染、WIFI协议栈这些实时性要求高的活会全部受影响。所以后来我想明白一个道理不是所有设备都非得跑模型才是智能硬件。模型在端侧的价值是离线可用、低延迟、隐私更好但代价是算力、内存、功耗、flash空间的极大消耗。对于桌面型语音设备来说后台有PC或局域网服务器的情况下让ESP只做采集和渲染既能把硬件成本压下来又不会因为模型能力有限导致对话效果拉胯。1.2 前后端分离从“单机逻辑”到“端云一体”的思路转变这套系统的核心思路可以概括成一句话端侧管输入输出后台管理解与生成。ESP32-S3端只做四件事音频采集、音频播放、屏幕渲染、用户交互事件采集。后台统一处理语音识别、语义理解、对话生成、语音合成。这种架构本质上和Web前后端分离是同一个套路。这种分离带来的第一个好处是升级方便。模型迭代这种事放在后台做就够了服务器上换个模型文件重启一下服务所有设备自动就具备新能力。不需要OTA刷固件不需要担心Flash写坏不需要挨个设备升级。我做第一版的时候后台接的开源本地模型后来觉得效果不理想换了好几个模型每次都是后台几分钟搞定ESP端一行代码没改这种爽感只有经历过“端侧模型升级地狱”的人才懂。第二个好处是并行开发。做硬件和做算法的人可以互不干扰。后端同学专心在服务端处理音频流、接大模型API、调TTS音色嵌入式这边的活就是音频格式、网络协议、屏幕UI、交互状态机。两边只要定好通信协议各自调试各自的联调的时候很少出大问题。1.3 这个方案的天花板在哪里但“端侧不跑模型”不代表没有短板。最明显的是断网不能用网络一断糖球就直接变成摆设了。第二个问题是延迟音频要上传、处理、再下传和端侧本地推理相比网络传输加后台推理的时间开销不小。实测下来在局域网环境下从用户说完话到屏幕出现反馈文字一般需要500毫秒到1秒左右如果走公网API延迟更高。所以做体验优化的时候必须在交互流程上加“已听到”之类的即时反馈否则用户会觉得设备没反应。还有一个点是隐私。所有语音内容都会经过后台如果是局域网部署的本地模型隐私风险还可控如果用的是云服务API那就要考虑数据安全策略。好在现在很多本地模型服务框架已经很成熟可以完全离线跑在PC或者开发板上隐私问题就有了更多选择空间。2. 核心细节圆屏显示、语音采集与客户端协议设计2.1 圆屏驱动和UI设计上的几个关键选择糖球用的是GC9A01这颗圆形LCD驱动芯片分辨率240x240接口是SPI。这颗屏在市面上很常见驱动代码也相对成熟但真正把它用好的前提是别忽略刷新方式。SPI刷一帧全屏RGB565数据大概需要115200字节如果按30fps算每秒要传3.4MB以上数据普通SPI不加DMA根本扛不住。我实测用SPI 40MHz加DMA双缓冲LVGL刷新能稳定在25到30fps左右基本够用。圆屏UI设计和方屏区别很大。四个角的信息没法放布局天然就是中心聚焦式。菜品、时钟、对话气泡这类内容居中最合适边缘区域适合放状态图标和滚动文字。LVGL本身提供了圆形的样式裁剪能力但要注意在样式中设置圆角半径足够大否则控件边缘会露出方形的底。色深方面这块屏是RGB565显示渐变和阴影效果时会有明显色阶断层所以UI审美要走扁平风减少大面积渐变。还有一个特别容易踩的坑是屏幕初始化序列。市面上GC9A01模组来自不同厂家初始化命令可能略有差别有些屏幕上电后颜色偏紫大概率是初始化序列里的Gamma设置不对或者RGB/BGR顺序设反了。我用的初始化序列是厂家提供的版本跑起来颜色正常但如果你买的屏幕有偏色问题优先检查SPI模式Mode 0还是Mode 3和RGB字节序这两个是最常见的偏色来源。2.2 语音采集I2S麦克风与音频前处理音频采集用的是一款I2S接口的模拟麦克风加外部ADC还是数字PDM麦克风取决于硬件选型。第一版我用的PDM麦克风接线简单但PDM对时钟抖动和电源噪声比较敏感布线不好容易有沙沙声。后来改成I2S数字麦克风之后噪声问题好了很多代码上也更可控。采样率方面后台语音识别一般用16kHz或更高我统一用的16kHz、16bit、单声道既能保证识别准确率又不会让上传数据量过大。按这个参数每秒音频数据是32KB一段10秒的语音也就320KB在局域网环境完全能接受。采集流程上ESP端要做的第一件事是检测用户开始说话。最简单的方案是按键触发按住说话、松开发送这种模式最可靠不会出现误触发。进阶方案是用ESP-SR的唤醒词做免提唤醒识别到“你好糖球”之类的关键词后开始录音。再进一步是做VAD语音活动检测自动检测到人声结束后停止录音并上传。我实际产品里做的是唤醒词触发开始VAD检测结束。三件事分层处理每一层都不会占用过多CPU。值得注意的坑是I2S采集的DMA缓冲区大小设置要匹配网络发送的节奏。如果缓冲区太小中断太频繁CPU占用高太大则录音延迟高导致一句话说完后要等很久才开始上传。我调试下来DMA缓冲区设置为480样本约30ms比较合适配合FreeRTOS的流缓冲把音频数据按100ms一个包推送出去整体体验最顺。另一个血泪教训麦克风的参考电压和供电纹波直接影响底噪。模拟麦克风必须用低纹波的LDO单独供电千万别和屏幕背光共用电源否则屏幕上亮度变化时扩音器里全是滋滋的电流声。2.3 网络选型WiFi连接与通信协议通信方式上ESP32-S3通过WiFi连接局域网后台服务跑在同一局域网内的PC或服务器上。采用HTTP还是WebSocket主要看对实时性的要求。如果只是“录完音上传拿结果返回”HTTP短连接就够了。但做对话型语音助手时用户说完话后希望尽快看到反馈最好是“边说边传”的流式模式这时候WebSocket就合适得多。我最终用的是WebSocket做双向通信通道JSON封装消息。从ESP到后台发音频数据时用WebSocket是二进制帧后台按流式处理后台返回结果时按文本帧推送包含状态码、文字内容、播放指令、表情动画指令等。如果后台配置了流式输出就是大模型一边生成一边吐字还可以做成“打字机效果”屏幕上文字逐个出现体验比等全部生成完再一次性显示强太多了。网络上额外要考虑的是断线重连和心跳机制。WebSocket连接如果长时间没有数据传输可能会被路由器或系统断开所以客户端需要定期发ping服务器回pong。另外ESP端的WiFi省电模式默认会影响延迟我直接关掉了WiFi Modem Sleep功耗大了点但延迟从几秒降到几百毫秒代价完全值得。2.4 与后台模型对接从语音到文字再到语音的完整链路后台框架我用的Python实现。WebSocket服务接收音频帧先做语音识别ASR把音频转成文本文本交给大语言模型服务比如本地模型框架生成回复回复文本再交给语音合成TTS生成音频最后把音频和文本一起推回ESP端。这整条链路里最值得注意的点是ASR、LLM、TTS都是独立服务不要把它们写死在同一个进程里要让它们可以独立重启。我给这三个环节分别设计了端口和接口ASR用WebSocket接收音频、返回文本LLM用HTTP或SDK方式调用TTS用HTTP上传文本、返回音频二进制。这样哪个环节挂了、哪个模型效果不好都可以单独替换和调试。像TTS这块我试过好几套方案有的音色自然但延迟高有的速度快但机械感强最后还是选了可以在本地运行、延迟相对可控的方案具体到项目上可以根据自己的硬件和网络条件权衡。协议设计上我定义了一条比较简单的消息规范。后台返回给ESP的每条消息都包含一个type字段用来区分是纯文本显示、是音频播放、还是UI动画指令。有的消息带文本字段有的带音频数据字段有的两者都有。比如用户问完天气后后台返回文本“今天晴25度”加一段TTS音频ESP先把音频解码播放同时屏幕上把文本显示出来再根据type字段触发一个天气动画。这样协议保持简单端侧的解析逻辑也清晰。3. 实操过程从环境搭建到端侧代码落地3.1 ESP-IDF开发环境搭建与编译问题排查开发框架我用的乐鑫官方ESP-IDF版本是v5.x。Windows下安装的时候IDE自带的安装工具会自动把工具链、Ninja、CMake这些装好听起来省心但实际坑不少。最常见的就是安装路径问题如果路径有中文或者空格Ninja在编译的时候会报各种莫名其妙的错误比如找不到头文件或者无法生成构建文件。我自己遇到过的典型问题是这个报错终端进程“c:\app\esp\espressif\tools\ninja\1.12.1\ninja.exe”已终止退出代码 1这种问题我会按四个方向排查。一是检查项目路径和ESP-IDF路径有没有中文、空格有就改成纯英文路径二是看内存是不是不够Windows下同时开着IDE、浏览器、微信再加上编译内存很容易吃紧Ninja分配不到内存就直接挂三是删除build目录重新编译排除CMake缓存损坏的干扰四是暂时关闭杀毒软件有些杀毒软件会对ninja生成的可执行文件做实时扫描导致进程被误杀。编译配置也需要注意。第一次编译时我直接用默认的sdkconfig结果WiFi吞吐率特别低后来才发现是默认开启了省电模式导致WiFi模块频繁休眠。正确的做法是在menuconfig里把WiFi省电关掉同时把TCP/IP的收发缓冲区调大一点这样音频数据传输才稳定。3.2 ESP端代码结构音频采集、屏幕刷新与状态机端侧代码我按模块划分main目录下放了几个核心文件。音频采集模块负责初始化I2S接口把麦克风数据读取到环形缓冲区网络模块管理WiFi连接和WebSocket的建立、重连UI模块用LVGL实现屏幕上的所有界面状态机模块是整个设备的主干负责协调音频、网络和UI之间的切换。状态机的设计非常关键。糖球的典型状态包括“待机”“唤醒中”“录音中”“等待后台回复”“语音播报中”“网络异常”等。每一种状态下用户交互和屏幕显示内容都不一样。比如待机状态屏幕显示表盘或者待机动画听到唤醒词后进入唤醒中状态屏幕显示聆听动画同时开始录音。录音结束后自动切换到等待回复状态屏幕上显示正在打字的效果。收到后台回复后切换成语音播报状态屏幕同步显示文字。状态管理最怕的是状态错乱比如用户还在说话后台结果就返回了或者网络断开后录音还停在录音状态。所以我的状态机只在主循环和事件回调中切换加了一个专门的锁机制保护共享数据同时给网络断线设置了超时检测超过三秒没有心跳响应就强制回退到网络异常状态提示用户检查网络。代码结构大致是这样// 状态机核心数据结构 typedef enum { STATE_IDLE, STATE_WAKEUP, STATE_LISTENING, STATE_WAITING_REPLY, STATE_SPEAKING, STATE_NETWORK_ERROR } sys_state_t; // 主循环中持续运行状态机 void app_main(void) { // 初始化各模块 audio_init(); wifi_init(); ws_client_init(); ui_init(); // 状态机主循环 while (1) { state_machine_tick(); vTaskDelay(pdMS_TO_TICKS(10)); } }3.3 后台语音客户端VAD、ASR、LLM、TTS的串联后台部分我写了一个Python脚本直接串起整条链路。这个脚本监听来自多个ESP客户端的连接每收到一段音频帧先缓存到临时缓冲区当检测到语音停止通过VAD判断或者客户端主动发送结束标志时调用ASR接口识别文字将识别结果送入大语言模型服务生成回答回答文本再送入TTS服务生成语音最后把文本和语音一起推回给ESP端。VAD我用的WebRTC VAD库它专门检测人声和非人声对于静音、噪音的容忍度还可以。这里有个经验单纯的VAD容易把气流声、键盘声误判为人声所以实际处理时要结合能量阈值和时间长度做双重判断只有持续超过一定时间且能量超过阈值才认为是有效语音。后台代码的一个核心片段# 收到完整音频后串联ASR/LLM/TTS def handle_full_audio(audio_bytes): # 1. 语音识别 text asr_engine.recognize(audio_bytes) if not text: send_to_esp({type: hint, text: 没有听清请再说一遍}) return # 2. 交给大模型获取回复 reply llm_client.chat(text) # 3. 文本转语音 tts_audio tts_engine.synthesize(reply) # 4. 推给ESP显示和播放 send_to_esp({type: reply, text: reply, audio: tts_audio})链路里最影响体验的是TTS延迟所以我把TTS做了缓存常用回复短句提前生成好音频命中缓存就直接播放能省几百毫秒。3.4 屏幕反馈优化让用户始终知道设备在“听”和“想”屏幕反馈是整个交互体验的灵魂。圆屏虽然小但反馈及时性直接决定用户觉得这个设备“灵不灵”。我做了几层优化第一是录音时屏幕有个动态的音量波动动画用LVGL画一圈圆弧根据音频RMS值实时调整圆弧长度让用户一眼就知道设备正在收音。第二是等待后台回复期间屏幕中央显示一个呼吸灯效果的小圆点同时旁边显示“思考中...”之类的文字。这个动画虽然简单但能显著降低用户因等待产生的焦虑感。实测下来加了这层反馈后用户普遍觉得设备“反应变快了”其实后台速度没变只是用户在等待期有事可看感知延迟降低了。第三是文字显示采用逐字弹入效果模拟打字机。收到后台完整文本后不一次性刷出来而是按每帧显示一部分的方式逐步出现。配合LVGL的textarea控件用定时器每50ms追加几个字符实现类似ChatGPT打字流的效果。一点小技巧追加字符时如果遇到标点符号可以多停顿一两帧读起来更像真人说话节奏。3.5 语音播报与文字显示同步后台返回的TTS音频格式我用的MP3还是WAV需要根据ESP端的解码能力决定。ESP32-S3要解码MP3一般需要软件解码比较吃CPUWAV的话直接I2S就能播放几乎不占额外资源。为了控制延迟和降低CPU占用我在TTS服务端直接把合成结果转成16kHz单声道WAVESP端拿到WAV数据直接进DMA播放一路畅通。音频播放和文字显示同步这一步要做到“说了哪段字就高亮显示到哪段”。最简单的实现方式是后台TTS返回时同时返回每个句子的时间戳ESP播放音频时根据时间戳动态更新文字高亮位置。但做完整对齐工作量不小我在第一版里偷了个懒采取了一个近似方案先等TTS音频全部播完然后在播完的同时把全文一次性显示出来。实际体验还行但缺少逐句对应感。后来优化成把文本按中文标点切分为短句每播放一句话前先显示该句话的文字看起来也接近逐句高亮的效果代码却简单得多。4. 常见问题与排查技巧实录4.1 ESP-IDF编译环境反复出问题这一节先说编译。很多初学者卡在环境上最典型的几个表现Ninja进程崩溃、找不到工具链、编译到一半报内存不足。我总结了一套固定排查流程遇到就按顺序检查基本都能解决。第一步确认路径。项目目录、ESP-IDF目录、用户文件夹这三个路径都要是纯英文不能有空格。Windows用户名如果是中文也会引发很多奇怪问题最简单的办法是换个英文路径重新clone项目。第二步确认内存。编译ESP-IDF工程尤其是首次全量编译时建议保证系统有至少8GB可用内存IDE加其他应用一起跑很容易不够。第三步删掉build目录和sdkconfig重新配置有些时候是配置缓存和当前环境不匹配。第四步关杀毒软件这一步听着玄幻但真的有效。另外还要注意ESP-IDF自带的espressif.exe installer一键安装工具在Windows下有时会用到旧版本工具链导致编译错误。如果遇到新版本组件编译失败建议去乐鑫官网下载最新版的ESP-IDF Tools Installer完全重装一遍不要用老版本升级上来。我自己重装后很多诡异问题就消失了。4.2 收音和识别效果不佳的调优方向如果你做好之后发现后台识别准确率低先说结论问题八成出在音频采集端而不是模型不够好。常见的原因有麦克风采样率设置错误、数据溢出导致削波、背景噪声太大等。采样率必须和后台ASR要求的输入一致我用的16kHz后台ASR也要求16kHz如果两端不一致识别效果会断崖式下降。麦克风增益也一样太大会导致削波声音破音太小则信噪比不足安静环境下都识别不好。我调试的时候先通过串口把采集到的原始音频导出到PC用Audacity打开看波形和频谱就能直观发现问题。特别是模拟麦克风的电路设计电源一定要干净。我之前用ESP32-S3开发板的3.3V给模拟麦克风供电屏幕一亮音频底噪立刻明显增加。后来换成一棵独立的低噪声LDO给麦克风供电电源纹波从几十毫伏降到几毫伏底噪问题明显改善。另有几个方向可以尝试拾音孔开孔位置如果太靠内人声反射会造成梳状滤波效应声音发闷麦克风尽量靠近设备正面开孔周围不要有遮挡。4.3 后台服务状态异常或者连接不稳定设备偶尔连不上后台服务网络上用的名词叫做“后台有进程但面板不显示”其实也是后台服务没有正常监听端口。我排查这个问题的思路是先看后台进程是否真的启动成功然后在同一局域网内用手机或电脑的浏览器访问后台HTTP端口能不能通再查看防火墙有没有拦截端口。Windows防火墙经常默认拦截Python进程监听外部端口需要在入站规则里放行对应端口。另外如果后台服务是多进程模式比如线程池开了多个工作进程要注意每个进程是否正确绑定了端口。用netstat -ano | findstr 端口号可以快速确认端口监听状态。如果是WebSocket服务还可以用在线的WebSocket测试工具做连接测试确认不是客户端代码的问题。设备端问题多见于WiFi信号强度弱、路由器开启了客户端隔离功能导致设备无法访问局域网内其他设备。遇到连不上后台时首选排查方法是在ESP端串口日志里看WebSocket连接错误码是DNS解析失败、TCP连接失败还是服务端主动断开然后对照排查。还有一个坑是无线路由器开启了AP隔离或访客网络模式导致同一WiFi下的设备之间不能互通这种情况下无论如何调代码都白搭优先换网络环境验证。4.4 圆屏显示卡顿、刷新率低圆屏卡顿的原因排在第一位的永远是SPI刷新方式不对。如果不用DMACPU光刷屏就占用大量时间音频和WiFi全都要受影响。所以第一步是确认SPI是否启用了DMA传输LVGL的刷新回调里是否直接用了spi_device_polling_transmit这种阻塞式接口如果用了改成spi_device_queue_trans异步发送。第二是LVGL的缓冲区大小。LVGL实际使用时会从内部buffer读取像素绘制。Buffer太小会导致频繁刷屏和撕裂太大则内存紧张。我这边开了双缓冲每个buffer分配了屏幕总像素的1/10大概240x240x2除以10等于11520字节两个缓冲区总共23KB左右在ESP32-S3的PSRAM加持下完全不是问题。开启PSRAM后可以把LVGL的buffer放在PSRAM里不占用内部SRAM进一步降低内存压力。第三是屏幕SPI时钟频率。GC9A01这个屏理论上可以跑到80MHz但实测超过40MHz后部分模组会出现花屏和颜色错位特别是杜邦线连接的时候信号完整性差更容易出问题。所以我最终锁定了40MHz稳定性和速度兼顾。如果用了PCB软排线连接可以尝试60MHz到80MHz但需要做长时间稳定性测试。第四是渲染耗时LVGL画圆弧、画图片、文字抗锯齿都是CPU密集操作。如果渲染函数占用太长时间会阻塞主循环导致音频、网络处理不及时。针对这个我做了两个优化一是把常用背景图和图标做成C数组避免从Flash读取时因为SD卡或文件系统IO慢而拖慢渲染二是尽量少用透明度混合效果圆屏小纯色和简单边框就足够美观。4.5 后台模型返回内容过长导致截断这个问题很常见大模型默认输出长度可能超过实际需要后台和客户端又没有做处理于是屏幕上显示到一半就截断了或者TTS播报时只说了一部分。之前热搜里有人提到“达到输出token上限回答被截断”本质就是这个问题。我的处理方式是对话提示词里直接限定回复长度要求模型用两到三句话、不超过50个字回答适合口语播放后台侧再设置一个最大token数从默认的2048收紧到256这样即使模型“话痨”也不会返回一大段长文TTS合成前还要做一次文本长度检查超过设定长度就截断到最近的标点符号避免播放时话说到一半突然中断。另外客户端收到超长文本时可以设计一个分页或滚动显示机制但语音播报一定要截断否则用户体验极差。4.6 关于“后台有进程但面板不显示”的补充这一类问题在Windows上格外常见。我调试后台服务时也遇到过几次进程列表里明明有python.exe在跑但控制台窗口或者自带Web面板打不开。排查的顺序我建议是先确认端口是否真的在监听。netstat -ano | findstr 8000如果没有任何输出说明服务根本没绑定端口进程可能在启动初始化阶段卡死了此时去看日志文件最直接。如果端口有监听但浏览器访问不通那大概率是防火墙拦截或者服务绑定的是127.0.0.1而不是0.0.0.0。绑定地址是127开头时只有本机能访问局域网内其他设备肯定连不上。解决办法是把服务启动的主机地址改成0.0.0.0也就是监听所有网卡地址这样局域网内的ESP设备就能正常访问了。这个问题我在不看文档的时候踩过好几次查出来之后感觉特别基础但就是容易忽略。5. 一些体会和小技巧这套“ESP圆屏做后台语音客户端”的方案做下来最大的心得是先想清楚“端侧到底应该做什么”再动手。很多人一听说语音助手直觉就是设备端要跑模型但实际上对桌面设备来说后台有算力、有成熟模型生态端侧把采集、显示、交互做好系统的上限会高很多。还有两个细节想特别强调。一个是音频数据格式统一从麦克风采集到ASR识别再到TTS播放整条链路里的采样率、位深、声道数必须完全统一我统一成16kHz/16bit/单声道后出问题的概率大幅下降。另一个是日志系统一定要从一开始就建好端侧用串口日志后台用文件日志每次出问题先看日志再猜原因排查效率天差地别。最后再分享一个实际优化技巧为了让语音对话的“回合感”更自然我在ESP端加了一个“播放结束”的回调事件TTS音频播放完成之后设备会主动进入待机状态并且屏幕回到待机界面。这个细节看似不起眼但实际体验中非常关键不然设备会一直停留在“播报完”的状态用户再说话时状态机可能还卡在语音播报里导致下一轮对话无法触发。加上这个回落后整套交互的连贯性算是基本到位了糖球这个项目到这里也就算是不跑模型但足够好用的一个完整语音客户端了。
返回列表