dsh-voice-hotkey:按住 F9 本地识别并插入当前输入框

前言

在 DeepSeek Harness(DSH)里做对话或回答 Agent 提问时,语音输入往往比长段打字更顺手。DSH 自带实验性语音输入插件会把麦克风挂在固定 UI 槽位上;当 ask_user_question 弹出提问卡片时,输入栏槽位会被提问 UI 顶替,麦克风按钮消失,主观作答框就无法继续语音输入。社区插件 dsh-voice-hotkey(维护者 caly250,分类:模型推理)用全局快捷键代替槽位绑定:按住 F9 说话,松开后走本机 SenseVoice 识别,把文字写进当前获得焦点的输入框——对话 composer、提问卡片里的 textarea,或页面上任意 input / textarea / contenteditable。

这是什么

dsh-voice-hotkey 是 DSH 的 Host + Client 双半侧插件:浏览器侧在 window 捕获阶段监听快捷键并录音;Host 侧暴露 HTTP 接口,把 WAV 交给 DSH 内置语音 Bundle 的识别器。它不占用 conversation.input.activity 等槽位,因此在提问卡片接管 composer 时仍可对着作答框语音输入。

社区插件目录为独立站点,与 DeepSeek / 幻方无官方从属关系;安装前请自行审阅源码与许可证。插件随当前 DSH 进程权限运行。

与内置语音输入的差异

内置 @deepseek-ai/dsh-experimental-client-ui-voice-input 依赖 UI 槽位。本插件不占用任何槽位,在哪个输入框获得焦点、就在哪里插入识别结果。内置能力仍可作为对比参考;需要「提问卡片里也能说」时,更适合用快捷键方案。

前置条件

识别依赖 DSH 实验性语音 Bundle,而非插件自带模型:

  1. Profile 的 dsh.profile.bundles 中需包含 @deepseek-ai/dsh-experimental-voice-input-bundle。
  2. 模型 SenseVoiceSmall INT8(约 230 MB)需已在 $DSH_HOME/speech-to-text(首次启用语音 Bundle 时通常会自动下载)。

未配置语音 Bundle 时插件仍可加载,但 GET /api/dsh-voice-hotkey/status 会报服务不可用,POST /api/dsh-voice-hotkey/transcribe 返回 503。

核心功能

  • 按住说话:默认按住 F9 录音,松开触发识别(可改为 toggle 模式)。
  • 本地识别:通过 Host 路由把 PCM WAV 交给 ctx.get('speechToText')(SenseVoice)。
  • 焦点插入:按下快捷键时的 document.activeElement 优先;另有提问框、composer 等回退链。
  • 状态浮标:录音(红点 + 音量)、识别中(黄点)、成功/失败提示;平时 pointer-events: none,避免挡住底部输入栏。
  • 安全边界:两条 API 仅接受回环地址,并校验 Host 头;因 exact 路由会绕过浏览器 cookie 鉴权,必须自行做 localhost 校验。
  • 刻意不做:自动发送、常开麦、唤醒词、流式字幕;页面失焦时快捷键不生效。

安装与启用

README 给出的安装方式如下(请将 <your-user> 换成实际 GitHub 用户名或组织):

# 从 GitHub 安装
dsh plugin --profile desktop add github:<your-user>/dsh-voice-hotkey

# 或从本地目录
dsh plugin --profile desktop add link:/path/to/dsh-voice-hotkey

启用后:

  1. Host 半侧随 profile 的 patchReload: live 即时生效。
  2. 浏览器侧需刷新页面一次才会加载 Client 脚本。

配置

DSH loader 对客户端插件只传 { name: id },读不到 profile 里的 config,因此选项写在 localStorage['dsh-voice-hotkey']:

localStorage.setItem('dsh-voice-hotkey', JSON.stringify({
  key: 'F9',            // KeyboardEvent.code
  modifiers: [],        // 例如 ['ctrl','shift']
  holdToTalk: true,     // false = 按一下开始、再按一下结束
  maxSeconds: 120,
  showIndicator: true
}))

缺省即上表。若 F9 被其他软件全局占用,把 key 改为 F8、KeyG 等(使用 event.code 命名)。

典型用法

  1. 确认语音 Bundle 与模型已就绪(必要时先看 /status)。
  2. 在 DSH 桌面 profile 安装插件并刷新浏览器。
  3. 光标放入要输入的位置(对话框、提问作答框或普通表单)。
  4. 按住 F9 说话,松开后等待识别;成功时文字插入当前焦点控件,顶部浮标约 1.5 秒后消失。
  5. 录音过程中若窗口失焦、页面隐藏或按 Escape,会取消录音,避免麦克风常开。

Host 侧诊断可用:

方法 路径 说明
GET /api/dsh-voice-hotkey/status 识别器状态
POST /api/dsh-voice-hotkey/transcribe body {audioBase64, providerId?, language?} → {ok, text}

/transcribe 要求规范 44 字节头的 16 kHz 单声道 PCM16 WAV,body 上限 6 MB / 120 秒;浏览器侧会用 OfflineAudioContext 重采样并编码,以兼容识别器校验。

适用场景与注意

适合:希望在 DSH 任意输入场景(含提问卡片)用同一套快捷键做本地语音输入、且已启用实验性语音 Bundle 的开发者。

注意:

  • 需浏览器麦克风权限;被拒时会给出专门提示。
  • 输入法组合态(isComposing 或 keyCode === 229)不会抢键。
  • 空结果分两类:音频短于 0.5 秒与「有音频但未识别到语音」,提示不同。
  • 出问题时查看 localStorage['dsh-voice-hotkey-probe'],常见 phase 从 listeners-installed 到 inserted;停在 failed / getusermedia-failed 时看 extra 字段原因。
JSON.parse(localStorage.getItem('dsh-voice-hotkey-probe'))

Lexical composer 等编辑器对 execCommand('insertText') 异步提交 DOM;插件用「内容是否变化 + 宏任务宽限」避免同一句话插两遍,属于集成细节,一般只需知道「成功时只应出现一次文本」即可。

结尾

dsh-voice-hotkey 把「按住说话、松开识别、插入焦点」做成与 UI 槽位无关的快捷键流程,补上提问卡片等场景下内置麦克风不可用的问题。安装命令与配置以 README 为准;进一步实现细节(WAV 形态、回环鉴权、插入优先级)见 GitHub 文档。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye