未在播放

MustDo 语音转文字模块优化方案


MustDo 语音转文字模块优化方案

当前状态

架构概述

MustDo 后端(FastAPI + SQLite,部署在 2C2G VPS)通过 WebSocket 代理模式对接讯飞语音听写 API:

浏览器/小程序                   FastAPI 后端                      讯飞云
─────────────                  ────────────                    ────────
WebSocket ──────> voice.py ────> voice_stream.py ────> iflytek.py ────> wss://iat-api.xfyun.cn
 PCM chunk               认证/校验          编排/事件流          协议封装

数据流

1. 前端按住说话 → 浏览器采集 PCM(16kHz/16bit/mono)
2. 前端先发 WebSocket 给后端完成认证
3. 后端连接讯飞 WS,成功后向前端发送 ready 事件
4. 前端收到 ready 后开始发送 PCM chunk
5. 后端按讯飞协议拆成 1280B/40ms 音频帧代理转发
6. 讯飞返回 interim/partial 结果 → 后端即时推给前端
7. 前端松手发 end 事件 → 后端发结束帧 → 等待 final 结果
8. final 结果返回前端 → POST /api/todos/ai → DeepSeek 解析 → 入库

模块边界

代码抽象清晰,ASR 供应商被完全隔离在 services/iflytek.py 中:

模块职责对外接口
routers/voice.pyWebSocket/HTTP 边界,认证,响应FastAPI endpoint
services/voice_stream.py编排:音频流 → ASR → 事件流transcribe_pcm_stream()
services/iflytek.py纯讯飞协议:鉴权 URL、音频帧、结果解析IflytekIatClient / IflytekIatSession

IflytekIatSession 暴露的核心接口:

await session.send_pcm(chunk)              # 喂音频字节
await session.finish()                     # 结束标记
async for event in session.recognition_events():
    # event.text        → 当前 chunk 新增的文本
    # event.transcript  → 累积完整文本
    # event.is_final    → 是否为最终结果

前端只认识三种事件类型:readypartialfinal,不关心后端用哪个 ASR 供应商。

存在的问题

讯飞免费 API 延迟过高。 免费层存在:

  • 并发限制和低优先级队列
  • WebSocket 连接排队
  • 转写请求排队

实测用户说话结束后 2-5 秒才能拿到最终结果,加上 DeepSeek 解析的 1-2 秒,用户感知延迟达到 4-7 秒。对于语音待办这种短交互场景,体验明显不够理想。


候选方案对比

约束条件

  • 当前 VPS 为 2C2G,跑着 FastAPI + SQLite + 前端静态托管
  • 不能在同一台 VPS 上部署模型(内存不足)
  • 语音平均时长 <10 秒,对话场景,对响应速度敏感
  • 后端 WebSocket 代理架构已成熟,前端无 ASR 耦合

方案一:本地部署 SenseVoice-Small

维度评估
模型大小~65MB(ONNX Int8 量化)
运行所需内存~500-800MB(模型 + Python + ONNX Runtime)
10 秒音频处理耗时~0.6s(CPU)/ ~0.06s(GPU)
流式支持✅ FunASR WebSocket,端到端延迟 <200ms
中文准确率~93%(日常口语场景)
单次成本几乎为 0
运维需单独一台 2C4G VPS(月费约 ¥30-60)或升级现有 VPS

结论:不适合。 2C2G VPS 跑不动,必须加机器。在 MustDo 当前体量下,加一台服务器的月费已经超过云 API 的全部开销。

方案二:豆包 ASR(火山引擎)

维度评估
部署方式云 API,支持 WebSocket 流式
延迟~500ms(端到端,推测)
中文准确率⭐ 评测中优于讯飞(4.8 vs 1)
单次成本(5s 语音)~0.007 元
每月 1000 次~7 元
新用户额度企业认证 20 小时免费
需要改代码量~100-150 行

结论:可选。 中文效果最好,但延迟比 Deepgram 高约 200ms。

方案三:Deepgram Nova-3(推荐)

维度评估
部署方式云 API,支持 WebSocket 流式
延迟<300ms(业界最快)
端到端延迟(含网络)~300ms(官宣 sub-300ms)
中文准确率中等偏上,日常口语够用
流式中间结果✅ interim_results 实时推送
单次成本(5s 语音)~0.0004 元
每月 1000 次~0.4 元
新用户额度$200(够日常使用数年)
需要改代码量~100-150 行

结论:推荐。 延迟最低,成本最低,改动最小。

综合对比

讯飞免费(现状)SenseVoice 自部署豆包 ASRDeepgram
延迟2-5s<200ms~500ms<300ms
中文准确率~91%~93%最高中等
月成本(1000 次)0 元~50 元(加服务器)~7 元~0.4 元
加服务器不需要需要不需要不需要
代码改动0(基准)~200 行 + 部署~100-150 行~100-150 行
运维0需要管模型服务00
免费额度5 小时020 小时$200

推荐方案:切换到 Deepgram

为什么是 Deepgram

  1. 延迟最低:<300ms 端到端,比讯飞免费 API 快 10-15 倍
  2. 成本趋零:MustDo 的用量下每月不到 1 元
  3. 无需加服务器:2C2G VPS 继续用,只负责 WebSocket 转发
  4. 改动最小:替换一层协议实现,架构不变,前端不变
  5. $200 免费额度:在 MustDo 的量级下基本等于永久免费

预期效果

用户说一句 5 秒的话:

阶段现状(讯飞免费)优化后(Deepgram)
WebSocket 连接1-2s(排队)~200ms
中间结果出现~300ms(边说话边出字)
最终结果返回2-5s<300ms(说话结束即返回)
+ DeepSeek 解析1-2s1-2s
总延迟(用户感知)4-7s~1.5s

如果利用 Deepgram 的流式中间结果,在用户说话过程中就把 partial transcript 喂给 DeepSeek 预热分析,总延迟还能进一步压缩到 1 秒以内。


实施计划

代码改动范围

backend/app/services/
  deepgram.py         ← 新增:Deepgram WebSocket 客户端(~120 行)
  voice_stream.py     ← 改动:import 替换 + 参数适配(~5 行)
  iflytek.py          ← 保留:兼容回退或直接删除

backend/.env.example  ← 改动:新增 Deepgram 配置项
backend/.env          ← 改动:填写 Deepgram API Key

Deepgram 协议适配

讯飞和 Deepgram 的核心差异:

讯飞Deepgram
连接方式wss + HMAC-SHA256 签名 URLwss + API Key header
音频格式1280B/40ms 固定帧任意 chunk,直接转发
协议帧JSON frame(含 base64 音频)二进制音频 + JSON 控制消息
结果格式{code, data: {result, status}}{type: "Results", is_final: bool, channel: {alternatives}}
连接成功信号自定义 readyDeepgram 连接即 ready

需要新写的 DeepgramSession 接口保持一致:

class DeepgramSession:
    async def send_pcm(self, chunk: bytes) -> None: ...
    async def finish(self) -> None: ...
    async def recognition_events(self) -> AsyncIterator[RecognitionEvent]: ...

voice_stream.pytranscribe_pcm_stream 只需改 IflytekIatClient → DeepgramClient,其余编排逻辑不变。

需要新增的配置项

# .env
DEEPGRAM_API_KEY=xxx
DEEPGRAM_MODEL=nova-3            # 可选
DEEPGRAM_LANGUAGE=zh-CN          # 可选,中文优化

实施步骤

  1. 注册 Deepgramhttps://deepgram.com,获取 API Key,领取 $200 免费额度
  2. 编写 deepgram.py:实现 Deepgram WebSocket 协议封装,接口对齐 iflytek.py
  3. 修改 voice_stream.py:替换 import,适配参数
  4. 本地测试:用测试音频验证链路
  5. 部署上线:更新 .env 配置,重启服务
  6. 观察:保留讯飞代码和配置项,通过环境变量切换,便于回退

回退方案

config.py 中新增 ASR_PROVIDER 配置项:

ASR_PROVIDER=deepgram  # 可选 iflytek / deepgram

voice_stream.py 根据配置动态选择 provider,万一 Deepgram 出问题,切回讯飞只需要改一个环境变量重启。


成本核算

当前(讯飞免费)

  • 免费,但延迟 2-5 秒
  • 超过免费额度后:2-4.95 元/小时 ≈ 每次 0.03-0.07 元(按 5 秒语音)

优化后(Deepgram)

  • 单次 5 秒语音:~0.0004 元
  • 每天 200 次:~0.08 元
  • 每月:~2.5 元
  • $200 免费额度下:等于免费

为什么不做本地部署

方案月成本延迟
SenseVoice + 一台 2C4G VPS~50 元<200ms
升级到 4C8G VPS 跑模型+后端~100 元<200ms
Deepgram 云 API<3 元<300ms

在 MustDo 这个体量下,多花 50 元/月来省 100ms 延迟的性价比极低。200ms vs 300ms 的端到端延迟差异在用户体验上是不可感知的。


其他优化建议

1. 利用流式中间结果预热 AI

当前架构是拿到 final transcript 之后再调 DeepSeek。Deepgram 可以在用户说话过程中持续返回 interim results:

Time ──────────────────────────────────────────>
用户:     [明天] [下午] [三点] [提醒] [我] [开会]
Deepgram:   [明天] [明天下午] [明天下午三点]...
DeepSeek:              [预热分析中...]
说话结束:                                        [final: "明天下午三点提醒我开会"]
DeepSeek:                                        [确认,0.3s 出结果]

前端 UI 可以在用户松手前就展示 “正在分析…”,松手后几乎即时出结果。这个优化只需要在 voice_stream.py 中加入 DeepSeek 预热逻辑,前端不需要改。

2. 10 秒限制收紧

当前 MAX_AUDIO_SECONDS=30,可以收紧到 10 秒。待办语音不需要 30 秒那么长。更短的超时也意味着更短的 DeepSeek prompt,解析速度更快。

3. 超时兜底逻辑保留

当前 voice_stream.py 中已有的超时兜底逻辑(final 超时降级使用 partial)体现了很好的容错意识,替换 Deepgram 后保留。


总结

  • 不需要小模型,也不需要加服务器
  • 换 Deepgram API:改动 ~150 行代码,延迟从 2-5s 降到 <300ms,月成本 <3 元
  • 代码结构天然支持替换:iflytek.py → deepgram.py,接口不变,架构不动,前端无感
  • $200 免费额度:在 MustDo 的量级下,几乎等于终身免费
2026-07-26T00:00:00.000Z