语音输入法(dudu)

MVP 前后端设计方案 · 大模型识别 · 流式实时上屏 · 全平台

版本 v0.3 日期 2026-06-11 状态:已确认

变更记录

版本日期变更
v0.22026-06-11 引入 design/ 官方设计系统后修订:① 计费模型由"订阅制 + 每日免费 10 分钟"改为时长包(不过期)+ 每日免费试用 3 分钟;② 第八章 mockup 已被设计系统高保真稿取代,正式以 design/ui_kits/ 为准;③ 数据表 plans/subscriptions 废弃,改时长账本。详细设计见配套文档:前端实现设计 v1.0 · 后端与数据架构 v1.0
v0.32026-06-11 新增两项能力并更新配套文档至 v1.1:① 客户端打点(响应时间等指标采集与批量上报,监控首字延迟等体验目标);② 反馈问题(设置内提交文字 + 图片 + 诊断信息)。详见 前端实现设计 v1.1 第八/九章 · 后端与数据架构 v1.1 3.5/3.6
v0.12026-06-11初版 MVP 前后端设计方案

目录

一、产品概述与 MVP 需求

一款付费语音输入法,核心卖点:大模型识别准确度 + 流式实时上屏 + 极简交互

MVP 需求说明
全平台覆盖Mac / Windows / iPhone / iPad / Android
大模型识别使用云厂商大模型 ASR,识别准确率显著优于传统方案
极简交互桌面端:焦点在输入框时按住全局快捷键说话;移动端:键盘上按住按键说话
流式识别说话过程中识别结果实时显示,不等说完
商业化付费软件:微信登录 + 微信支付购买时长包
性能识别快、无可感知卡顿(首字延迟 P95 < 500ms)

二、关键技术决策

决策点选择理由
ASR 引擎 国内云厂商大模型流式 ASR
首选:阿里云百炼 gummy-realtime-v1
备选:火山引擎豆包流式 ASR
原生 WebSocket 流式、国内延迟低、自带标点/ITN/中英混说、按量计费免运维。通过 Provider 抽象接口接入,可随时切换
后端语言 Go 本质是 WS 流式中继 + 业务 CRUD,瓶颈在云端 ASR 而非自身代码,Rust 性能优势体现不出来;微信支付有官方 Go SDK(wechatpay-go);goroutine 天然适合"每连接一个转发循环";开发速度快
桌面端 Tauri 2 + Rust 体积小(~10MB)、常驻内存低;Rust 侧做全局快捷键/音频采集/文字注入等系统层;一套代码出 Mac + Windows
移动端形态 系统第三方输入法(键盘扩展) iOS Keyboard Extension / Android InputMethodService,按住说话直接上屏到任意 App,体验最佳。iOS 麦克风限制为最高风险项(见风险章节)
移动端技术 Swift / Kotlin 各自原生 协议简单(WS + JSON + PCM),不值得引入跨端框架的复杂度;输入法扩展本身必须原生

三、总体架构

┌─────────────┐   ┌─────────────┐   ┌──────────────────┐
│ 桌面端 Tauri │   │ iOS/Android │   │                  │
│ Mac/Windows │   │  键盘扩展    │   │   微信开放平台    │
└──────┬──────┘   └──────┬──────┘   │ (OAuth/支付回调)  │
       │  WSS 音频流      │          └────────┬─────────┘
       ▼                 ▼                   │
┌──────────────────────────────────────────────────────┐
│                  Go 后端(单体起步)                    │
│  ┌─────────┐ ┌───────────┐ ┌─────────┐ ┌──────────┐  │
│  │  auth   │ │asr-gateway│ │ billing │ │  quota   │  │
│  │ 微信登录 │ │ WS 流式中继│ │ 微信支付 │ │ 用量/配额 │  │
│  └─────────┘ └─────┬─────┘ └─────────┘ └──────────┘  │
└────────────────────┼─────────────────────────────────┘
                     │ WSS(同区域部署,内网级延迟)
                     ▼
          ┌─────────────────────┐     ┌──────────────┐
          │ 阿里云百炼 Gummy     │     │ PostgreSQL   │
          │ (流式大模型 ASR)   │     │ + Redis      │
          └─────────────────────┘     └──────────────┘
为什么音频走后端中继而不是客户端直连云 ASR:
  1. ASR 密钥不能下发到客户端;
  2. 计费基于识别秒数,必须在服务端计量,否则配额可被绕过;
  3. Provider 可切换、可灰度,客户端协议永远不变。
中继增加的延迟约 10–50ms(后端与 ASR 同区域部署),对体验无感。

四、后端设计(Go)

4.1 技术栈与部署

4.2 模块划分

server/
  cmd/server/main.go
  internal/
    auth/        微信 OAuth(扫码 + App 登录)、JWT 签发与中间件
    asr/         Provider 接口 + gummy / volcano 两个实现
    gateway/     WS /v1/asr/stream 处理器(核心)
    billing/     微信支付下单、回调验签、订单状态机
    quota/       用量计量(Redis INCR + 异步落库)、试用/余额校验
    user/        用户、设备 CRUD、时长包查询
    store/       gorm models + migrations
  pkg/protocol/  客户端协议常量与消息结构(5 端对齐的唯一真相源)

4.3 ASR Provider 抽象(可切换的关键)

type Provider interface {
    // 建立一次流式识别会话
    StartSession(ctx context.Context, cfg SessionConfig) (Session, error)
}
type Session interface {
    SendAudio(pcm []byte) error   // 推 100ms 音频帧
    Results() <-chan Result       // {Text, IsFinal, BeginTime, EndTime}
    Close() error
}

MVP 实现 gummy provider(DashScope WebSocket API,原生流式、自带标点和 ITN、中英混说)。

4.4 客户端 ↔ 后端流式协议(WSS /v1/asr/stream

方向 / 类型内容
上行 · 二进制帧16kHz / 16bit / mono PCM,每 100ms 一帧(3200 字节)
上行 · 文本帧{"type":"start","sample_rate":16000} / {"type":"stop"}
下行 · partial{"type":"partial","text":"今天天气"} — 增量结果,客户端实时刷新浮层
下行 · final{"type":"final","text":"今天天气不错。"} — 句子定稿,客户端执行注入
下行 · error{"type":"error","code":"QUOTA_EXCEEDED","message":"..."}

鉴权:连接时带 Authorization: Bearer <JWT>。连接为长连接 + ping/pong 心跳,客户端启动时即预连接,按下快捷键时零握手开销。

4.5 微信登录

方式微信平台应用类型
桌面端扫码登录:后端生成带 state 的二维码页 → 手机微信扫码授权 → 回调 → 客户端轮询 GET /v1/auth/qr/:state 拿 JWT网站应用
移动端主 App 集成微信 OpenSDK 拉起授权 → code 换 token移动应用

unionid 关联同一用户的多端身份(桌面扫码与手机 App 登录是两个不同 openid)。

4.6 微信支付与计费

4.7 核心数据表

userswechat_identitiesduration_packsordersbalance_ledger(时长账本)、trial_usage(每日试用)、asr_sessionsdevices

五、存储选型说明

一句话总结:PostgreSQL 管"钱和账"(强一致、持久),Redis 管"快和临时"(计数器、带过期时间的状态)。

PostgreSQL — 持久化业务数据(不能丢的数据)

用途
users / wechat_identities用户账号,微信 openid/unionid 与用户的关联
duration_packs / orders时长包定义(服务端可配)、微信支付订单——支付回调验签后改状态,钱相关数据必须强一致、可审计
balance_ledger / trial_usage时长账本(只追加,余额 = 合计)与每日免费试用用量(180 秒/天,自然日重置)
asr_sessions每次识别会话的用量明细(时长、试用/余额扣减拆分),用于对账与数据分析

特点:写入频率低、绝对不能丢,且需要事务("回调到账 → 订单置为已支付 → 时长入账"必须原子完成)。

Redis — 高频、短生命周期的热数据

  1. 实时配额计数:识别过程中每隔几秒累计用量并校验是否超额。每帧都查 PostgreSQL 既扛不住也拖慢链路,故用 Redis INCR 实时计数,后台异步批量落库到 balance_ledger / asr_sessions;
  2. 扫码登录轮询状态:桌面端展示二维码后轮询"扫了没",state 只活 2 分钟,天然适合带 TTL 的 key;
  3. JWT 黑名单 / 会话踢出:退出登录、账号踢出立即生效。
MVP 若想再简化可暂不引入 Redis(配额放进程内存 + 定时落库),但多实例部署时会算不准,且阿里云托管 Redis 运维成本接近零,故直接采用。

六、桌面端设计(Tauri 2 + Rust)

6.1 核心交互流程(push-to-talk)

按下快捷键 → 浮层弹出(光标附近)→ 采集音频流式上传
→ partial 结果实时显示在浮层 → 松开快捷键
→ final 注入焦点输入框 → 浮层消失

6.2 Rust 侧模块

模块方案
全局快捷键tauri-plugin-global-shortcut;默认按住 Cmd/Ctrl + Shift + Space 说话(press 开始 / release 结束)。macOS 监听 Fn 键需 CGEventTap,二期再做
音频采集cpal 采集 + 重采样到 16kHz mono PCM,100ms 分帧
WebSockettokio-tungstenite,启动即预连接、断线指数退避重连
文字注入 macOS写剪贴板 → CGEvent 模拟 Cmd+V → 恢复剪贴板(兼容性最好);需引导用户授予辅助功能权限,首次启动做权限引导页
文字注入 WindowsSendInput + KEYEVENTF_UNICODE 直接注入 Unicode,无需剪贴板
浮层窗口Tauri 无边框 / 置顶 / 透明 / 不抢焦点(关键:focusable: false,否则焦点离开目标输入框)小窗,显示实时文本 + 音量波形
上屏策略(MVP):partial 只显示在浮层不注入,final 句子级一次性注入。避免"增量注入 + 退格修正"与目标应用输入状态冲突的复杂性,二期可选讯飞式逐字上屏。

6.3 前端 UI(Web 技术)

七、移动端设计(系统输入法扩展)

7.1 iOS / iPadOS(同一套代码)

⚠️ 最高风险项:iOS 键盘扩展内麦克风录音受系统限制(历史上 App Extension 不可用 AVAudioSession 录音;讯飞/搜狗有键盘内语音但实现路径需验证)。M0 第一周必须做技术 spike;若不可行,降级方案为键盘按钮经 deep link 跳主 App 录音 → App Group 回传 → 键盘上屏(体验打折但可发布)。

7.2 Android

实施顺序:先 Android 后 iOS(风险低、验证快)。两端不引入跨端框架,Swift / Kotlin 各自原生实现协议(对齐 server/pkg/protocol)。

八、各终端简要设计(界面与交互)

v0.2 注:本章 mockup 为 v0.1 规划草稿,已被 design/ 官方设计系统的高保真稿取代(计费已改时长包、视觉禁用渐变/emoji)。正式界面以 design/ui_kits/ 演示与前端实现设计 v1.0 为准,本章仅作历史记录保留。

8.1 桌面端(Mac / Windows)

界面清单:① 识别浮层② 系统托盘菜单③ 设置窗口④ 登录/购买窗口

① 识别浮层(核心界面,两态)

按下快捷键瞬间弹出在光标附近,不抢焦点;松开快捷键注入文字后淡出。

🎙️ 聆听中…
松开 ⌘⇧Space 完成输入
态 A:按下快捷键瞬间(不等首个结果,先给反馈)
帮我把这份周报整理一下,重点突出本周的
松开 ⌘⇧Space 完成输入
态 B:流式识别中(深色为 final,浅色为 partial 实时刷新)

② 系统托盘菜单 + ③ 设置窗口

🎙️ dudu 语音输入已就绪
启用快捷键
设置…
本月已用128 / 600 分钟
账号:wang***(已订阅)
退出
托盘菜单:状态一目了然,入口极简
dudu 设置
说话快捷键 Space(按住说话)
麦克风MacBook Pro 麦克风 ▾
开机自启
登录状态微信已登录 · wang***
当前套餐年度会员(2027-06-10 到期)续费
设置窗口:单页搞定全部配置

④ 登录 / 购买窗口

8.2 移动端键盘扩展(iOS / Android 布局一致)

界面清单:① 键盘面板(核心)② 候选/partial 文本条③ 未登录/超额提示态

MVP 键盘策略:以语音为主键盘——大麦克风按键 + 一行常用功能(切回系统键盘、退格、回车、标点),不做完整 26 键(降低开发量,用户在语音和系统键盘间一键切换)。

微信 · 与张三聊天中
说点什么…
dudu 语音输入已订阅 ✓
🌐 切换键盘
⌫ 退格
↵ 发送
🎙️ 按住说话
态 A:默认态(大按键 + 一行功能键)
微信 · 与张三聊天中
好的,我马上把文件发给你
dudu 语音输入00:04
好的,我马上把文件发给你,大概十分钟之
🟢 松开完成 · 上滑取消
态 B:按住说话中(partial 实时显示,final 即时上屏)

键盘交互细节

8.3 移动端主 App(iOS / Android)

界面清单:① 启用引导页② 登录页③ 套餐购买页④ 我的(用量/订阅管理)

页面内容与交互
① 启用引导页三步卡片式引导:添加键盘 →(iOS)开启完全访问 → 试一试(内置输入框练习)。每步带系统设置深链与动图示意
② 登录页单按钮"微信一键登录"(拉起微信 OpenSDK),登录后 token 写入 App Group 供键盘扩展使用
③ 套餐购买页免费版 vs 会员对比表 + 月/年价格卡片,点击拉起微信 APP 支付,支付回调后即时解锁
④ 我的本月用量进度条、订阅到期时间、麦克风/键盘权限自检(异常时红点提示并跳设置)

8.4 iPad 适配要点

九、延迟优化清单(贯穿各端)

  1. 客户端 WS 启动即预连接 + 心跳保活,按键时零握手;
  2. 100ms 帧直推,客户端不做缓冲聚合;
  3. 后端与 ASR 同区域部署,中继 goroutine 零拷贝转发;
  4. 浮层/键盘 UI 在按键瞬间先进入"聆听中"态,不等首个结果;
  5. 埋点指标:按键 → 首个 partial(目标 P95 < 500ms)、松开 → final 注入(目标 < 300ms)。

十、仓库结构(monorepo)

dudu/
  server/      Go 后端
  desktop/     Tauri 2(Mac + Windows)
  android/     Kotlin:主 App + IME Service
  ios/         Swift:主 App + Keyboard Extension
  docs/        协议文档、架构决策记录

十一、里程碑

阶段内容周期
M0 风险验证 ① iOS 键盘扩展录音 spike;② Gummy 流式接入 demo;③ macOS 注入 + 不抢焦点浮层 demo;④ 立即启动微信开放平台(网站应用 + 移动应用)和商户号申请(周期数周,是关键路径;输入法上架需软著) 1 周
M1 核心链路 后端 auth / gateway / quota + 桌面端完整链路(快捷键 → 采集 → 流式 → 浮层 → 注入) 2–3 周
M2 商业化 微信登录(扫码 + App)、微信支付、时长包与配额 2 周
M3 移动端 先 Android IME,后 iOS 键盘扩展 3–4 周
M4 打磨 断网重连、错误兜底、延迟调优、内测分发 持续

十二、风险与验证

主要风险

风险等级缓解措施
iOS 键盘扩展麦克风限制M0 第一周 spike 验证;降级方案:deep link 跳主 App 录音回传
微信开放平台 / 商户号申请周期长第一天就启动申请(需企业资质、软著),与开发并行
macOS 文字注入在部分应用不兼容剪贴板 + 粘贴方案兼容面最广;M0 在微信/浏览器/IDE 实测
ASR 厂商限流 / 故障Provider 抽象保证可热切到火山引擎备选

验证方式