avatar
语音输入里,哪些体验不能丢

2026-08-30 | 体验工程

语音输入里,哪些体验不能丢

Helen

从 ai-ask 与 med-agent 两套语音输入方案出发,看一次典型的体验工程取舍:实时字幕、真音波、弱网稳定性都想要时,究竟什么必须保留,什么可以降级。


体验工程不是把所有效果都实现出来,而是先判断哪些反馈承担了用户的控制感、确定感和信任感。核心体验不能丢,反馈形式可以降级。

一开始的问题:实时 ASR 为什么总在吞字?

ai-ask 和 med-agent 最早其实是同一个起点:都用了实时 ASR。

按下
→ 开始录音
→ 建立 ASR 连接
→ PCM 帧实时上传
→ 边说边出字

体验看起来很好,问题也很直接:录音和网络连接绑在了一起。

最典型的是两种吞字:

  • 头部吞字:用户已经开始说话,但连接 / 凭证还没准备好
  • 尾部吞字:用户已经松手,但最后几帧还没完整送进 ASR

本质上不是识别模型不准,而是:

用户说话的时间线,与网络链路准备完成的时间线产生了竞争。


ai-ask 的选择:继续保留实时

ai-ask 没有放弃实时字幕,而是继续把链路补完整。

包括:

  • RecorderManager PCM 帧级录音
  • WebSocket 实时 ASR
  • recorder 预热
  • PrewarmSession 状态管理
  • sessionId 防止旧会话污染
  • 触摸状态保护
  • SentenceEnd 收尾
  • 各种异常与设备冷却处理

最终换回来的是一个很明确的体验:

用户说话
↓
文字实时出现

也就是:实时字幕。

但它的代价也很明确。

只要识别输入依赖 WebSocket,那么弱网下就会出现一个结构性问题:

Recorder 已经产帧
↓
WebSocket 还没 ready
↓
这一段帧怎么办?

如果没有完整的本地 buffer + 补发机制,就可能真的丢。

所以 ai-ask 的失败模式是:

弱网不仅会慢,还有可能丢。


med-agent 走了相反的方向

med-agent 后来直接把录音轨和识别轨解耦

先不考虑 ASR:

按住
↓
RecorderManager 本地完整录音
↓
松手
↓
整包 POST /speechToText
↓
一次性识别

这样最重要的一件事情发生了变化:

用户的声音先完整落在本地,识别只是后续处理。

网络慢:

上传慢一点

ASR 慢:

结果晚一点

但声音本身已经在。

失败模式从:

慢 + 可能丢

变成了:

只慢,不丢

对于短句语音输入来说,这是一个很大的交换。


但去掉实时字幕之后,又冒出一个问题

原来实时字幕其实偷偷承担了很多反馈。

用户看到文字不断出现,会自然得到几个判断:

麦克风工作了
系统听见我了
识别链路正常
它大概听对了

所以把实时字幕删掉,不是简单地少一个 UI。

真正的问题是:

这些确定感,要由谁来补?

这时候才发现,本地录音反而打开了另一个能力:主包现在能拿到 PCM 帧了。

于是可以直接算 RMS:

PCM Frame
↓
RMS / Power Level
↓
真实音量
↓
波形

原来 CSS 假动画的波形,终于可以变成跟真实声音联动的反馈。


真音波又把格式问题带了出来

原本本地整包上传时用的是:

mp3 48kbps

体积很小。

onFrameRecorded 的 mp3 帧是压缩数据,并不能直接拿来算 RMS。

如果要真音波,最直接的方案就是:

PCM 录
PCM 画
PCM 传

问题也很明显:

PCM 文件比 mp3 大很多,松手后的上传会不会变慢?

于是中间试过不少方案:

方案想解决什么最后为什么没走
mp3 帧直接算音量不改录音格式压缩块无法真实反映振幅
mp3 + WASM 解码小文件 + 真音波小程序帧边界与稳定性有问题
PCM + lamejs 边录边转 mp3两边都要增依赖、编码开销和录音栈复杂度
PCM 分块预上传降低松手等待为体积引入新的网络状态管理
mp3 服务端算 RMS前端不解码波形反馈延迟,失去实时意义

其实所有方案都在回答同一个问题:

为了保住 mp3 这个工程优化,值不值得重新增加一整套复杂度?


最后还是靠实测做决定

真正需要验证的是:

PCM 整包上传,在真实场景里到底慢多少?

真机测试:

语音长度文件大小网络上传 + 识别
~2s64KBWiFi~181ms
~9.5s298KBWiFi~547ms
~10s5G~678ms

结果比一开始按带宽估算乐观很多。

对于日常几秒到十几秒的语音:

松手
↓
200~700ms
↓
结果

仍然处于比较自然的「转一下文字」感受。

因此最终没有为了几百毫秒,再引入额外的转码 / 解码链路。

最后变成:

按住
↓
本地 PCM 完整录音
├─ PCM Frame → RMS → 真音波
└─ 本地文件完整保存

松手
↓
整段 PCM → speechToText
↓
直接发送

PCM 录、PCM 画、PCM 传。


回过头看,真正被舍弃的其实只有一个东西

med-agent 放弃的是:

实时字幕。

换回来的是:

  • 录音完整性
  • 弱网稳定性
  • 真音波
  • 更简单的状态模型
  • 更容易处理取消 / 后台 / 异常
  • 更低的工程维护成本

所以这不是「功能做少了」。

而是一次很明确的体验工程 Trade-off:

把增强体验降级,换核心任务更可靠。


哪些体验是绝对不能丢的?

把整个语音输入拆开,会发现反馈其实有不同优先级。

第一层:任务正确性

用户说的话必须真的被录进去。

Correctness

这个不能为了任何动画、字幕或者实时效果牺牲。


第二层:状态可感知

用户必须知道:

现在有没有在录?
系统有没有收到声音?
现在是不是正在识别?
刚才到底有没有发送?

所以:

  • Recording 状态
  • 输入反馈
  • Processing 状态
  • Success / Failure

都不能丢。

但具体长什么样,可以变化。


第三层:控制权

用户必须能:

取消
中断
重新说
从失败状态恢复

比如「上滑取消」。

这个 UI 可以简单,但行为不能不可靠。


哪些东西可以降级?

真正可以根据工程成本降级的,反而是表现形式。

例如波形:

28 根真实柱
↓
12 根
↓
5 根
↓
一个跟随音量变化的形状

都可以。

用户真正需要确认的是:

系统有没有收到我的声音。

而不是必须看到 28 根柱子。


实时字幕也是一样:

逐字实时
↓
分句刷新
↓
延迟 Preview
↓
完全不显示

如果它只是用来告诉用户"系统听到了",那完全可以被真音波替代。

只有当语音本身就是一个文字编辑任务时,例如语音写邮件、写文档、填写内容,实时字幕才会从「增强反馈」变成「核心任务对象」。


可以形成一个简单的体验优先级

L0  Correctness
    核心任务必须成功

L1  Observability
    系统状态必须可信

L2  Control
    用户必须能取消、干预、恢复

L3  Responsiveness
    尽量即时、稳定、跟手

L4  Enhancement
    实时字幕、复杂波形、精细动画

遇到:

  • 弱网
  • 低端机
  • 小程序 Runtime 限制
  • 工程成本过高

应该优先从 L4 往上砍

而不是为了保住一段漂亮动画,牺牲 L0 / L1。


一个更通用的判断方式

以后再遇到复杂交互,可以先问四个问题。

1. 这个反馈是在告诉用户真实系统状态吗?

是的话,尽量保留。

2. 用户会根据这个反馈改变下一步操作吗?

是的话,不能轻易删除。

3. 如果这个反馈是假的,会不会制造错误信心?

会的话,就必须和真实 Runtime 数据绑定。

4. 它只是让体验更实时、更精致、更"爽"吗?

是的话,可以降级。

所以:

录音状态       不能丢
取消状态       不能丢
识别状态       不能丢
失败恢复       不能丢
真实输入反馈   不能丢

实时字幕       可以降级
波形精度       可以降级
动画复杂度     可以降级
逐字刷新       可以降级

还能继续走一步:Preview 和 Commit 分离

ai-ask 与 med-agent 其实还存在一个中间路线。

不是:

流式
vs
整包

二选一。

而是:

                    ┌→ RMS → 真音波
                    │
Local PCM ──────────┼→ Streaming ASR → Preview
                    │
                    └→ 完整 PCM
                           ↓
                         松手
                           ↓
                       Final ASR
                           ↓
                         Commit

关键在于:

实时 ASR 不再作为最终结果的 Source of Truth。

它只是 Preview。

这样:

WS 正常
→ 有实时字幕

WS 慢
→ 字幕晚一点

WS 挂
→ 不显示字幕

本地 PCM
→ 始终完整

Final ASR
→ 始终以完整文件为准

增强体验可以失败,但核心任务不会一起失败。


小结

这次语音输入真正值得留下来的,不只是 PCM、RMS 或 WebSocket 这些实现细节。

更重要的是一次体验优先级的判断:

核心任务不降级,反馈形式可以降级;系统状态不能骗人,增强能力允许失败。

实时字幕、真音波、漂亮动画都是手段。

真正不能丢的是:

用户知道发生了什么、知道自己的操作有没有成功、始终拥有控制权,并且系统失败后还能回来。

这可能才是体验工程和单纯「把设计效果实现出来」之间最大的区别。

© 2026 Hailin. All rights reserved.