状态:未来候选方案,不进入当前版本开发
日期:2026-06-07
关联基线:v2.1.30-build20260607.1127-xasr-live-caption
真实实时上屏可以做,但不建议放进当前版本。下一次如要继续推进,应作为 X-ASR 专属实验功能开发,默认关闭,不影响 SenseVoice、Qwen3-ASR、Moonshine、Parakeet 的现有“录完后最终粘贴”路径。
当前已经具备做实验版的基础:
StreamingEngine / StreamingSession 持续产出 partial 文本。仍不具备直接做成默认功能的原因:
实现 X-ASR 专属的实验模式:
按下热键 -> 开始录音 -> X-ASR 持续输出 partial -> partial 实时写入当前输入框
松开热键 -> X-ASR final 校准 -> 输入框留下最终文本
异常发生 -> 停止实时上屏 -> 回退到当前最终粘贴流程
用户可感知价值:
第一版不做:
真实实时上屏必须同时满足以下条件才启用:
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 都稳定兼容。
第一版不要删除现有最终粘贴逻辑。真实实时上屏失败时必须能回到当前已验证路径。
当前基线:
privatevoice.src/internal/engine/engine.go 已有 StreamingEngine 和 StreamingSession。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
建议新增包:
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
}
第一版只要求“对自己插入的文本负责”,不要尝试理解用户输入框里已有内容。
优先采用增量 diff,不每次全删全贴。
规则:
lastPartial 和 nextPartial 求最长公共前缀。nextPartial 只是追加,直接输入新增部分。建议阈值:
单次删除字符数 <= 24
累计删除字符数 <= 80
单次 partial 长度 <= 300
更新时间间隔 >= 120 ms
示例:
last: 我想去苹果市场
next: 我想去 App Store
公共前缀: 我想去
删除: 苹果市场
输入: App Store
第一版建议只用最保守的模拟输入动作:
DeleteBackward / Backspace 指定次数。不要第一版就引入复杂剪贴板替换和选区控制混合策略,否则 QA 面会扩大。
需要记录:
本次实时上屏插入了多少字符
删除了多少字符
最后一次 partial 是什么
最终 final 是什么
是否触发 fallback
开始录音时记录当前目标:
frontmost app bundle id
frontmost window title / pid
每次准备写入前检查目标是否仍一致。
如果目标变化:
停止实时上屏
不再修改输入框
继续录音和最终识别
松开后是否最终粘贴需谨慎:默认不粘贴,或提示/记录 fallback
第一版可以采用更保守策略:
焦点变化 -> 取消本次实时上屏 -> 不做 final 粘贴
这样可以避免文字进入错误 App。
松开热键后:
Finish() 或现有 final Recognize()。如果 final 和 partial 差异过大:
不做大规模删除
保留已上屏内容
记录错误
必要时显示“请检查文本”
不同 App 对模拟输入的处理不同:
影响范围只在 X-ASR 实时上屏开启时,不影响其他模型。
流式模型可能修正前文。小幅尾部修正可处理,大幅回改风险高。
风险例子:
partial 1: 今天下午开会讨论苹果市场
partial 2: 今天下午开会讨论 App Store 上架材料
如果删除跨度大,模拟删除容易误删用户原有文本或删不干净。
用户说话时可能切换窗口、点击别处、系统弹窗抢焦点。实时上屏如果继续写入,会进入错误位置。
必须有焦点检查和快速停止。
如果实时上屏依赖粘贴:
第一版应优先复用现有剪贴板保护逻辑,并记录是否能够恢复剪贴板。
实时上屏依赖辅助功能权限。权限失效时:
实时上屏比“录完识别”多了持续 decode 和持续 UI/input 操作。
需要观察:
第一版必须具备以下保护:
x-asr-zh-en-960ms 生效。推荐 fallback reason:
focus_changed
large_partial_rewrite
delete_failed
paste_failed
permission_missing
session_timeout
target_app_unsupported
第一轮至少覆盖:
| 类型 | App / 场景 | 目的 |
|---|---|---|
| 系统纯文本 | TextEdit 纯文本模式 | 基础输入、删除、final 校准 |
| 系统富文本 | Notes / TextEdit 富文本 | 富文本输入稳定性 |
| 浏览器 | Safari / Chrome textarea | 网页输入框 |
| Electron | VS Code / 飞书 / Slack | 高频工作 App |
| 聊天 | 微信 / 飞书输入框 | 中文即时通讯 |
| 开发者工具 | VS Code editor | 英文和符号混输 |
第二轮再扩展:
必须覆盖:
进入下一阶段前至少满足:
RealtimeInsertExperimental 配置,默认 false。realtimeinput 包和纯单元测试。X-ASR 的价值不是替代现有模型,而是提供另一种输入体验:
SenseVoice / Qwen3-ASR / Moonshine / Parakeet:
更适合录完后最终识别
X-ASR:
更适合探索实时反馈、实时字幕、实时上屏
因此,真实实时上屏应当是 X-ASR 的实验性增强,不应成为所有模型的共享默认行为。
StreamingEngine / StreamingSession。v2.1.30-build20260607.1127-xasr-live-caption 可作为回滚基线。