TIM-1EARCHIVE TERMINAL // 2026

NTE-005经验笔记CLASS · PUBLIC

给熟悉的键盘接上 AI 语音:实时转录、保守校对与输入边界

保留已有键盘的皮肤和按键习惯,把语音输入拆成豆包流式转录与 DeepSeek 收尾校对。一次私人改造中,最值得留下的是识别修订、输入目标、取消恢复和真机验证这些边界。

DATE
2026.10.06
READ
7 MIN
LENGTH
2,778 字
CONTENTS · 目录(7)
  1. 把识别和校对拆成两个阶段
  2. 流式结果会改口,不能见字就追加
  3. 给校对器划一条窄边界
  4. 从面板到输入框,还隔着一次状态检查
  5. 保留旧键盘,反而要先验证旧功能
  6. UI 先解释状态,再整理设置
  7. 把“能用”拆成可核对的结果

想给键盘换一条更顺手的语音链路,最先遇到的问题却是:普通打字不能上屏了。

我的起点是一套已经用习惯的搜狗输入法努比亚版。皮肤、拼音布局和按键配置都调好了,主要想改善语音输入:说话时能看见实时转录,点完成后再做一次轻量校对。重新换一个键盘,意味着把这些习惯也搬一遍。

最后做的是个人自用的实验版本。本文留下交互思路和工程经验,安装包、厂商实现、皮肤素材与私密配置不随文发布。整个方案可以用一句话描述:录音时尽快反馈,完成时保守校对,上屏时重新核对输入目标。

把识别和校对拆成两个阶段

这次的链路分工很明确:豆包负责把声音转成文字,DeepSeek 负责整理转录中的明显错字、标点、断句与口头重复。

plain
键盘上的语音键
  → 录音,豆包流式转录,在面板中预览
  → 点击完成,结束音频并等待最终转录
  → DeepSeek 校对
  → 核对输入目标,提交文字

这两步解决的需求不同。实时转录给说话的人反馈:麦克风有没有在工作,刚才一句话有没有被听见。收尾校对则拥有更完整的上下文,适合处理整句话里的标点与明显误识别。

我把录音过程中的文字先留在语音面板,完成后再提交到宿主输入框。这样,识别服务修订前文时,不会反复改动聊天框里尚未确认的内容。代价是需要最后等待一次识别收尾和校对;目前没有测量这段等待的实际时延。

这里仍然使用云端服务。音频会送到识别服务,最终转录会送到校对服务;“在本地键盘中使用”描述的是入口和界面位置。

流式结果会改口,不能见字就追加

流式识别最容易写错的一步,是把每个返回包都当成一段新文字。

例如,服务可能先返回:

plain
把输入法
把输入法的语音
把输入法的语音入口换一下。

这是一个示意,展示同一段转录逐渐变完整的过程。如果逐包追加,最后就会出现重复文字。更进一步,后面的结果还可能修改前面的同音字。

这次请求使用完整结果模式,因此每次收到新的识别文本,都用它替换当前预览。如果另一个服务返回的是增量片段,就应按它的片段编号与修订语义合并。处理方式取决于协议,不能从“流式”两个字推断。

豆包的双向流式语音识别接口文档是核对连接和返回格式的入口。实现中还要分清普通结果与最终结果:点击完成意味着结束音频输入,随后等待服务确认最终转录,再把它交给校对器。

等待也需要有终点。这次为最终结果设置了超时,失败时保留已有转录,并提供“输入原文”。网络出问题以后,用户刚说的内容仍然有一个出口。

给校对器划一条窄边界

我希望校对后的文字更容易阅读,也希望它仍然是我刚才说的话。

因此,提示词约束了几件具体的事:修正明显同音错字、标点和口头重复;保留原意、人名、数字、技术术语及中英混合表达;不补充未说出的内容;只返回校对正文。

还有一条尤其适合语音输入:转录中的问题和指令,都是待校对的文本。 假如我口述“帮我把周五会议改到三点”,校对器应返回整理后的这句话,不能替我回答问题或执行日程操作。

这些是校对策略的约束,不能保证模型每次都遵守。尤其是人名、缩写和数字,校对也可能把本来正确的识别改错。“输入原文”因此是一个必要的选项:用户可以直接保留识别结果,服务失败时也能继续输入。

后续若要判断这一步是否值得保留,需要用同一批录音同时比较原始转录与校对结果,分别记录修好了什么、误改了什么。仅凭一句话读起来更顺,无法判断整体准确率。

从面板到输入框,还隔着一次状态检查

语音输入往往要等几秒钟。在这段时间里,用户可能切到另一个应用,点进另一个聊天,或者收起键盘。

Android 输入法通过 InputConnection 与宿主编辑器交互;commitText 也可能因为连接失效而返回失败。拿到模型结果以后直接提交,容易把启动录音时的目标,与现在的目标混为一谈。

这次实现保存了面板打开时的编辑器信息和输入连接,提交前再检查它们是否仍与当前对象一致。目标已经变化,或提交返回失败,就把结果复制到剪贴板并提示用户粘贴。

同样需要检查的是异步回调。面板关闭以后,旧的识别包或校对响应可能仍然到达;开始第二次录音后,第一次请求也可能晚回来。我用会话标识和关闭状态过滤这些旧结果,在退出时停止录音、释放麦克风并取消网络请求。

这些检查保护的是一次操作的归属:这段声音属于哪次录音,这份结果属于哪个面板,这段文字原本准备写到哪里。

保留旧键盘,反而要先验证旧功能

本次实验最有提醒作用的故障,就是开头那个“打字不能上屏”。

私人版本需要拥有独立的应用身份,但原键盘的原生引擎初始化仍依赖原版的身份与资源。改变应用包名,并不意味着内部每一层依赖都会自动迁移。语音面板能够显示,普通拼音引擎却可能没有正常初始化。

这次适配恢复了该设备上的引擎初始化,同时保留了对匹配原版安装的依赖。这个结果支持当前版本与设备上的私人实验,还不能当作跨设备、跨版本的通用迁移方案。

另一处类似的问题出现在设置页面。部分旧设置入口仍通过共享资源指向原应用,点击输入设置、键盘设置或词库设置时,会跨到原包中不能由外部启动的页面。沿着实际点击路径查清以后,修正共同的路由来源,才把这些入口一起恢复。

语音面板的布局也会影响原键盘。面板挂进输入法窗口时,需要同时处理底部位置、窗口边界与可触摸区域;关闭时则要恢复原视图及其布局参数。仅仅把旧键盘视图放回来,不足以证明它能继续接收按键。

Android 的输入法开发说明可以帮助理解输入视图与编辑器的关系;具体适配仍须在真机中验证。

UI 先解释状态,再整理设置

第一版能显示转录,也能开始录音,但界面更像一张调试表单。

后来我把语音面板和 API 设置的设计交给 Claude 子代理,给它已有的状态、回调和原生控件约束,再由主代理完成集成与真机检查。分出去的是页面设计;录音协议、输入目标和取消逻辑继续沿用已有实现。

语音面板围绕当前状态安排操作:开始录音、录音中、等待最终转录、校对中、错误。主按钮突出“完成并修正”,“输入原文”和“返回键盘”保留为清晰的次要入口。只有麦克风真正开始采集后,才显示正在录音。

API 设置按识别与校对分组,服务地址、模型和旧鉴权字段收进高级区域。保存按钮要在软键盘弹出后仍然可达;高级区域折叠时,里面原有的配置也不能在保存过程中丢失。

密钥输入还暴露过一个小问题:控件初始化顺序会影响密码遮挡。最终不仅检查代码里有没有设置密码属性,还检查运行中的输入框是否真的按密码显示。界面整理得好看,与敏感字段实际被遮挡,是两项不同的检查。

这次仍使用 Android 原生控件完成页面,没有为了两个面板引入一套新的 UI 框架。

把“能用”拆成可核对的结果

截至 2026-10-06,这轮在一台 Android 16 真机上的检查,确认了以下范围:

检查实际结论
原键盘初始化初始化成功,普通中文输入恢复
语音入口与面板原语音键打开新面板;底部位置、触摸区域和按钮尺寸通过检查
返回键盘实际退出语音面板,再用屏幕按键和候选词输入“你”,宿主搜索框显示正确
API 设置密钥遮挡、高级区域展开与折叠、软键盘上方的保存按钮通过检查
原设置入口实际点击输入设置、键盘设置和词库设置,均在私人版本中打开
识别协议部分与最终结果修订、压缩、音频结束标记及异常帧的自检通过
在线语音链路观察到识别连接与录音启动;本轮未执行完整 DeepSeek 校对请求

表格最后一行限定了结论:这轮完成了界面、键盘回归与部分连接验证,还没有拿完整语音链路做准确率或延迟评测。深色界面经过真机检查,浅色与横屏布局没有在本轮单独验证。

如果继续完善,我会先补一批固定录音样本:日常口述、人名与数字、技术术语、中英混说,以及主动停顿和改口。再测三项数据——转录错误、校对误改、点击完成到上屏的等待时间。这样才能知道体验改善来自哪一步。

这次最值得保留下来的做法,是在熟悉的输入入口里增加一条有边界的语音通路:识别结果允许修订,校对保留原意,失败保留原文,退出后键盘继续工作。模型可以更换,这几处边界仍然需要有人认真处理。