edit | blame | history | raw

X-ASR 真实实时上屏技术路径与风险报告

状态:未来候选方案,不进入当前版本开发
日期:2026-06-07
关联基线:v2.1.30-build20260607.1127-xasr-live-caption

1. 结论

真实实时上屏可以做,但不建议放进当前版本。下一次如要继续推进,应作为 X-ASR 专属实验功能开发,默认关闭,不影响 SenseVoice、Qwen3-ASR、Moonshine、Parakeet 的现有“录完后最终粘贴”路径。

当前已经具备做实验版的基础:

  • X-ASR 已通过 StreamingEngine / StreamingSession 持续产出 partial 文本。
  • Recorder 已支持录音中增量读取 PCM 样本。
  • 浮窗实时字幕已经验证为基本可用。
  • X-ASR 最终识别已加入尾部静音 padding 和最后非空结果保护。
  • 现有最终粘贴流程仍可作为失败兜底。

仍不具备直接做成默认功能的原因:

  • 真实实时上屏需要持续修改当前目标 App 的文本内容,不只是识别模型问题。
  • 不同目标 App 对粘贴、删除、选区、撤销栈、焦点变化和安全权限的处理不同。
  • 一旦 partial 回改处理不稳,可能把用户正在编辑的文本改乱。

2. 目标与非目标

2.1 目标

实现 X-ASR 专属的实验模式:

按下热键 -> 开始录音 -> X-ASR 持续输出 partial -> partial 实时写入当前输入框
松开热键 -> X-ASR final 校准 -> 输入框留下最终文本
异常发生 -> 停止实时上屏 -> 回退到当前最终粘贴流程

用户可感知价值:

  • 边说边看到文字进入输入框。
  • 长句输入等待感降低。
  • 中英混输时用户能更早发现识别方向是否正确。
  • 让 X-ASR 的流式能力区别于 SenseVoice/Qwen3-ASR 的传统听写体验。

2.2 非目标

第一版不做:

  • 不影响 SenseVoice、Qwen3-ASR、Moonshine、Parakeet。
  • 不把实时上屏设为默认。
  • 不做所有 App 的完美兼容。
  • 不做复杂文本编辑器协议适配。
  • 不在 App Store 正式发布线启用。

3. 隔离策略

真实实时上屏必须同时满足以下条件才启用:

SelectedModelID == x-asr-zh-en-960ms
Engine implements StreamingEngine
Config.RealtimeInsertExperimental == true
当前录音模式允许实验上屏
辅助功能权限可用

任何条件不满足,走原路径:

录音 -> StopAndGetSamples -> Recognize -> PostProcess -> userdict -> Paste

建议新增配置:

type Config struct {
	RealtimeInsertExperimental bool `json:"RealtimeInsertExperimental"`
}

建议前端展示为实验开关,不放在普通模型卡片主流程里:

X-ASR 实验功能
[ ] 边说边写入当前输入框

开关说明只表达风险,不应承诺所有 App 都稳定兼容。

4. 技术路径

4.1 保留现有最终粘贴路径

第一版不要删除现有最终粘贴逻辑。真实实时上屏失败时必须能回到当前已验证路径。

当前基线:

  • privatevoice.src/internal/engine/engine.go 已有 StreamingEngineStreamingSession
  • privatevoice.src/internal/audio/recorder.go 已有 ReadSamplesSince()
  • privatevoice.src/app.go 已有录音中 live caption loop。
  • privatevoice.src/internal/engine/engine_darwin.go 的 X-ASR final 已做 padding。

下一步应新增一个独立组件,而不是把实时输入逻辑塞进 engine:

app.go
  -> live streaming loop
  -> RealtimeInserter
  -> input backend

4.2 新增 RealtimeInserter

建议新增包:

privatevoice.src/internal/realtimeinput/

核心接口:

type Inserter interface {
	Start(target Target) error
	Update(partial string) error
	Finish(final string) error
	Cancel() error
}

内部状态:

type Session struct {
	targetAppID      string
	targetWindowID   string
	insertedText     string
	lastPartial      string
	startedAt        time.Time
	totalEdits       int
	fallbackRequired bool
}

第一版只要求“对自己插入的文本负责”,不要尝试理解用户输入框里已有内容。

4.3 partial 更新策略

优先采用增量 diff,不每次全删全贴。

规则:

  1. lastPartialnextPartial 求最长公共前缀。
  2. 如果 nextPartial 只是追加,直接输入新增部分。
  3. 如果尾部回改,删除上一段尾部,再输入新尾部。
  4. 如果回改跨度超过阈值,停止实时上屏,回退最终粘贴。

建议阈值:

单次删除字符数 <= 24
累计删除字符数 <= 80
单次 partial 长度 <= 300
更新时间间隔 >= 120 ms

示例:

last: 我想去苹果市场
next: 我想去 App Store

公共前缀: 我想去
删除: 苹果市场
输入: App Store

4.4 输入动作策略

第一版建议只用最保守的模拟输入动作:

  • 插入文本:复用现有 paste/type backend。
  • 删除文本:优先模拟 DeleteBackward / Backspace 指定次数。
  • final 校准:按 diff 修正尾部,不直接清空整段。

不要第一版就引入复杂剪贴板替换和选区控制混合策略,否则 QA 面会扩大。

需要记录:

本次实时上屏插入了多少字符
删除了多少字符
最后一次 partial 是什么
最终 final 是什么
是否触发 fallback

4.5 焦点与目标 App 保护

开始录音时记录当前目标:

frontmost app bundle id
frontmost window title / pid

每次准备写入前检查目标是否仍一致。

如果目标变化:

停止实时上屏
不再修改输入框
继续录音和最终识别
松开后是否最终粘贴需谨慎:默认不粘贴,或提示/记录 fallback

第一版可以采用更保守策略:

焦点变化 -> 取消本次实时上屏 -> 不做 final 粘贴

这样可以避免文字进入错误 App。

4.6 final 校准

松开热键后:

  1. 停止接受新的 partial。
  2. 调用 X-ASR Finish() 或现有 final Recognize()
  3. 对输入框中已插入文本做 final diff。
  4. 只修正本次 session 插入的尾部。
  5. 完成后清理 session 状态。

如果 final 和 partial 差异过大:

不做大规模删除
保留已上屏内容
记录错误
必要时显示“请检查文本”

5. 主要风险

5.1 目标 App 行为差异

不同 App 对模拟输入的处理不同:

  • 有的 App 对粘贴稳定,但对多次退格慢。
  • 有的 App 会把每次 partial 更新写入撤销栈。
  • 有的 App 在富文本输入框中会自动改格式。
  • 有的网页输入框会拦截粘贴事件。
  • 有的 Electron App 对焦点和输入事件处理延迟明显。

影响范围只在 X-ASR 实时上屏开启时,不影响其他模型。

5.2 partial 回改导致文本错乱

流式模型可能修正前文。小幅尾部修正可处理,大幅回改风险高。

风险例子:

partial 1: 今天下午开会讨论苹果市场
partial 2: 今天下午开会讨论 App Store 上架材料

如果删除跨度大,模拟删除容易误删用户原有文本或删不干净。

5.3 焦点变化

用户说话时可能切换窗口、点击别处、系统弹窗抢焦点。实时上屏如果继续写入,会进入错误位置。

必须有焦点检查和快速停止。

5.4 剪贴板和撤销栈

如果实时上屏依赖粘贴:

  • 可能短暂覆盖用户剪贴板。
  • 目标 App 的撤销栈可能变成多次小操作。
  • 用户按 Cmd+Z 的结果可能不符合预期。

第一版应优先复用现有剪贴板保护逻辑,并记录是否能够恢复剪贴板。

5.5 权限与安全

实时上屏依赖辅助功能权限。权限失效时:

  • 不能继续模拟输入。
  • 必须停止实时上屏。
  • 不能影响录音和最终识别。

5.6 性能与功耗

实时上屏比“录完识别”多了持续 decode 和持续 UI/input 操作。

需要观察:

  • CPU 占用
  • 内存增长
  • 长句 3-5 分钟输入稳定性
  • 浮窗动画是否卡顿
  • 目标 App 是否出现输入延迟

6. 降风险设计

第一版必须具备以下保护:

  • 默认关闭。
  • 只对 x-asr-zh-en-960ms 生效。
  • 单 session 最大实时插入长度限制。
  • partial 大幅回改时停止实时上屏。
  • 焦点变化时停止实时上屏。
  • 删除失败或权限失败时停止实时上屏。
  • 保留最终识别兜底。
  • 日志记录每次 fallback 原因。

推荐 fallback reason:

focus_changed
large_partial_rewrite
delete_failed
paste_failed
permission_missing
session_timeout
target_app_unsupported

7. QA 计划

7.1 目标 App 矩阵

第一轮至少覆盖:

类型 App / 场景 目的
系统纯文本 TextEdit 纯文本模式 基础输入、删除、final 校准
系统富文本 Notes / TextEdit 富文本 富文本输入稳定性
浏览器 Safari / Chrome textarea 网页输入框
Electron VS Code / 飞书 / Slack 高频工作 App
聊天 微信 / 飞书输入框 中文即时通讯
开发者工具 VS Code editor 英文和符号混输

第二轮再扩展:

  • Word
  • Pages
  • App Store Connect 网页表单
  • 终端
  • 全屏和 Split View

7.2 用例矩阵

必须覆盖:

  • 按住热键短句输入。
  • 按住热键长句输入。
  • 点按模式开始/停止。
  • 录音中立即取消。
  • 录音中切换焦点。
  • partial 小幅追加。
  • partial 小幅尾部回改。
  • partial 大幅回改触发 fallback。
  • 中英混输。
  • 英文技术词。
  • 中文长句标点。
  • 连续快速触发两次。
  • 辅助功能权限缺失。

7.3 验收标准

进入下一阶段前至少满足:

  • 其他模型路径完全不变。
  • X-ASR 实验开关关闭时行为完全等同当前版本。
  • X-ASR 开关打开时,基础纯文本 App 连续 20 次输入不乱序、不误删。
  • 焦点变化不会把文字写到错误 App。
  • fallback 后不会继续实时修改目标输入框。
  • final 校准不会删除用户原本已有文本。
  • QA 日志能明确每次 fallback 原因。

8. 建议实施顺序

Phase 1:不可见底座

  • 新增 RealtimeInsertExperimental 配置,默认 false。
  • 新增 realtimeinput 包和纯单元测试。
  • 实现 diff 算法、删除/插入计划生成。
  • 不接真实 App 输入。

Phase 2:受控输入实验

  • 只在 TextEdit 纯文本模式手工开启。
  • 只支持追加,不支持回改删除。
  • 验证焦点检查和 session 生命周期。

Phase 3:尾部回改和 final 校准

  • 加入有限删除。
  • 加入 final diff。
  • 大幅回改 fallback。

Phase 4:App 矩阵 QA

  • 扩展到浏览器、聊天、Electron。
  • 补 QA 记录和兼容表。
  • 决定是否进入产品 UI。

9. 需要保留的产品判断

X-ASR 的价值不是替代现有模型,而是提供另一种输入体验:

SenseVoice / Qwen3-ASR / Moonshine / Parakeet:
  更适合录完后最终识别

X-ASR:
  更适合探索实时反馈、实时字幕、实时上屏

因此,真实实时上屏应当是 X-ASR 的实验性增强,不应成为所有模型的共享默认行为。

10. 下一次开工检查清单

  • [ ] 当前代码仍保留 StreamingEngine / StreamingSession
  • [ ] X-ASR profile 仍为实验模型,未进入正式 App Store 线。
  • [ ] v2.1.30-build20260607.1127-xasr-live-caption 可作为回滚基线。
  • [ ] 新增配置默认关闭,并可在 UI 或调试入口打开。
  • [ ] 先完成 diff 纯单元测试,再接真实 input backend。
  • [ ] 先做 TextEdit 纯文本 QA,再扩展目标 App。