dsh-voice-mode
DeepSeek Harness 语音双工对话模式:会话内一键进入 → 边说边出字的流式识别 → 停顿自动发送 → 最终答复按句流式朗读 + 实时字幕,开口即可打断(真 barge-in)。无需 API Key,识别模型在本地宿主端推理。
Full-duplex voice mode for DeepSeek Harness — streamed ASR to an editable draft, sentence-by-sentence read-aloud with live captions, and speaking interrupts playback and the running turn.
dsh-voice-mode 全双工语音对话
语音模式真实录制:流式转写 → 自动发送 → 按句朗读 + 实时字幕
全双工对话闭环:声音 → 文字 → 声音
运行时变更(最近一批:v0.7.10,2026-09-18):输出链路静默丢音根治——① 云端 TTS 单句重试耗尽后不再静默跳句(现在状态条提示「有一句朗读失败,已跳过」,此前表现为「AI 回复偶尔不朗读」且无从判断);② 浏览器挂起 AudioContext 后的「无声播放」(UI 显示朗读中、字幕照走)现在每次入队 + 任意点击/按键自动恢复;③ SSE 丢帧导致的坏句丢弃留诊断痕迹。其余同 v0.7.9:唤醒词链路全面修复(Issue #10 + 真机复测)——台架驱动真引擎逐项定位并修掉五处流程缺陷:① 朗读期 TTS 回声污染待机段(喊不醒);② 只上传超门限帧导致尾字不 flush(唤醒词只剩半截);③ 唤醒命中丢整段(连说「唤醒词+命令」只发出去尾部几个字);④ 命中晚于停口时命令悬挂不定稿;⑤ 只喊唤醒词后停顿会关掉命令窗口(命令丢失)。现在唤醒词可与命令连说、词头自动剥离不进消息,待机态实时显示「它听到了什么」,支持「唤醒词…停顿…命令」。其余同 v0.7.7:朗读默认 Edge 云端,本地 TTS(VITS / Kokoro)可选;静音断句默认 1500 毫秒。
🤝 Fork 增强(本仓库新增)
本仓库在上游 haoku123/dsh-voice 基础上加入了大量增强,核心如下(完整清单见 git 历史与 docs/rules/STATE.md):
- 朗读默认 Edge 云端;本地 TTS 可选(隐私优先):选本地则回复文本不出本机——
- 本地 VITS(
sherpa-onnx-vits-zh-ll,纯中文,5 说话人);
- 本地 Kokoro(中英混读,103 音色;
int8 默认约 109MB / 可选 fp32 约 311MB 音质更好),经 sherpa-onnx-node 原生 addon 运行(无 WASM 内存上限,连续合成不崩);
- Edge 云端朗读保留为可选(设置
ttsEngine: edge);设置面板「朗读引擎」热切换。
- Kokoro 音色全量 103 个(F0 实测标定性别),四个常用男声置顶带编号;音色面板用 下拉列表 + ◀▶ 步进切换;
- 增量传输:partial 只传新增 0.9 秒,长段按住说话松手秒出定稿(不再整段重传重解码);
- 交互增强:输入框旁模式切换按钮(持续聆听 ⇄ 按住说话,保存到设置);按住说模式下按住才录、不按住不打断;
- 长段支持:持续聆听连续多段自动拼成一条消息(内部按 30s 分块识别,跨块累积拼接),静音断句默认 1500 毫秒;按住说停顿不断句;
- 朗读稳定性:打断即终止在途合成释放 CPU;句间不再有 3-5 秒停顿;长朗读不触发空闲下线;
- 安全加固:会话存在性校验 / 回环+Origin 校验 / 全端点限流 / ASR+TTS 全模型 SHA256 固定 / 下载域名白名单 / 重定向守卫。
ℹ️ 顶部 demo-voice-flow.gif 是当前界面的真实录制(语音模式 → 边说边出字 → 停顿自动发送 → 按句朗读 + 实时字幕),由 screenshots/scripts/capture-demo.mjs 驱动真实链路产出。
⚠️ 仓库内 assets/demo.gif 与根目录 assets/ 下的旧截图仍为上游旧版界面(单按钮时期);当前界面在语音按钮旁多一颗「模式切换」按钮。
✨ 功能
- 语音模式:输入框工具排麦克风按钮或全局快捷键
Ctrl+Shift+V 进入/退出;全局单活(同一时刻仅一个会话处于语音模式,切换会话自动让出)
- 两种交互模式(输入框旁按钮或设置可切换,切换即持久化):
toggle(默认)持续聆听:RMS VAD 分段 → zipformer2 流式识别(边说边出字,实时字幕预览)→ 静音约 1500 毫秒断句进草稿,连续多段拼成一条消息,再静音约 1500 毫秒(合计约 3 秒)自动发送;按住 Ctrl 强制立即发送
hold 按住说话:短按进入/退出,按住麦克风按钮说话、松手即发(滑出取消、Esc/失焦放弃本段);按住期间停顿不断句(上限 10 分钟);Ctrl 按住即录、松开即发
- 唤醒词(可选,默认关):设置
wakeWord 后进入待机态,说出唤醒词才开始识别(如「你好小D」)。可直接与命令连说(「你好小D,帮我查天气」)——唤醒词会在定稿/字幕里自动剥掉、不进消息;匹配带容错(同音字替换「小莫→小墨」、首字错、前置语气词「呃/喂/我说」),建议 3-4 字;待机态状态条实时显示「说『x』开始 · 它听到的转写」,唤醒成败可自查。边界(重要):仅 toggle 模式生效(hold / 手动打断下唤醒词静默失效);说完整一句后回到待机态需重说唤醒词(只喊唤醒词、还没说命令时保持聆听,可停顿想好再说);朗读期说唤醒词不触发(打断由 VAD 开口即触发,与唤醒词无关)
- 字幕档位(批 3):
captionFontSize 4 档(0=12px / 1=14px / 2=18px / 3=24px)+ captionMaxWidth 3 档(0=50vw / 1=70vw / 2=90vw),窄屏自适应
- 让位语义(批 5 / ADR-0008):
backchannelYield 默认开(I10 豁免)—— 朗读期用户插话「嗯/对」自动让位 1.5s,真要说走硬打断;让 LLM 主动让出话轮(人格层让位);关掉恢复改造前行为
- 输出链路:只朗读最终答复的
text-delta(reasoning/工具调用不读),按句流式朗读(默认 Edge 云端;可切本地 VITS/Kokoro,中英混读选 Kokoro)+ 右下角实时字幕浮层;工具调用触发提示音;全文照常写入聊天记录;口语化提示词(设置 spokenFormat,默认开)让回复为自然短句、不带 Markdown 排版符号
- 开口打断(barge-in):服务端 Silero VAD 帧级检测 + 回声门控(echoGateDb)三档灵敏度 → 本地静音 + host 合成队列作废 + 正在运行的回合取消(保留半截并自然续入新消息);朗读中自动切超灵敏档。开启唤醒词后打断仍是开口即打断(打断门控是 VAD,不是唤醒词;打断后回待机态)
- 模型懒加载与进度:首次使用自动下载识别/合成模型(
.part 断点续传),状态条实时显示进度;可用 npm run prefetch 预下载
- 设置:设置 → Plugins → 插件配置 → 语音模式(voice-mode),可调朗读引擎/音色/语速/打断灵敏度/静音停顿/空闲超时/模型镜像/自动发送/交互模式/唤醒词/口语化提示词/字幕档位/让位语义;音色可试听(按当前音色+语速即时合成预览,自定义 ShortName 亦可)
- 界面语言:跟随浏览器语言(中文 / English;切换后刷新页面生效)
- 容错:麦克风被拒红点提示、模型下载失败可见提示、TTS 连接失败状态条提示(自动退避重试)、提交失败文字留在草稿、SSE 断线自动重连
- 空闲退出:5 分钟无活动自动退出并释放麦克风(正在朗读计为活动,长朗读不会中途下线;批 J 已微调默认 10→5)
🚀 5 分钟上手(Quick Start)
dsh plugin --profile web add dsh-voice-mode
bundle 插件安装后需重启 dsh 生效(Linux:systemctl restart dsh;其他平台重启 dsh 进程)。
第一次用:
- 进入任一会话,按
Ctrl+Shift+V(或点输入区麦克风按钮)进入语音模式,状态条显示「聆听中…」;
- 说一句完整的话(如「帮我看看今天的天气」)→ 实时字幕立即出现,停顿后自动发送;
- AI 回复开始朗读时,开口说话 → 朗读即刻停止,你的话被听见(这就是 barge-in)。
⌨️ 操作手势
| 手势 | 作用 |
|---|
Ctrl+Shift+V | 进入 / 退出语音模式 |
| 直接说话 | toggle:边说边出字,停顿约 1500 毫秒断句进草稿、再静音约 1500 毫秒(合计约 3 秒)自动发送;按住 Ctrl 强制立即发送 |
| 按住麦克风按钮 | hold:松手发送;短按退出;滑出 / Esc / 失焦放弃本段 |
| 点输入框旁模式按钮 | 在「持续聆听 ⇄ 按住说」间切换(保存到设置) |
| 说唤醒词 | 待机态激活识别(配置后) |
| AI 朗读时开口说话 | 打断朗读并取消当前回合 |
| 点状态条「退出」 | 退出语音模式 |
| 点字幕浮层「跳过」 | 跳过当前句朗读 |
⚙️ 设置(设置 → Plugins → 插件配置 → 语音模式)
| 键 | 默认 | 说明 |
|---|
ttsEngine | edge | 朗读引擎:edge 微软云端(默认,快)/ vits 本地中文 / kokoro 本地中英;即时生效 |
kokoroModel | int8 | Kokoro 模型精度:int8(默认,109MB,纯 CPU/低带宽推荐)/ fp32(311MB,音质更好,独显/大内存推荐);两档共用 103 音色,即时生效 |
voice | 按引擎 | 音色:VITS 五说话人;Kokoro 103 个(下拉+◀▶,62 深沉/68 浑厚/75 清亮/76 磁性置顶);Edge 进入时自动加载全量 322 个。行内「试听」可即时预览 |
rate | 1.1 | 朗读语速倍率(0.5 慢速 ~ 2.0 快速),即时生效(批 J 1.0→1.1) |
interruptLevel | 0 | 发声打断灵敏度(服务端 VAD 帧级检测 + 回声门控):0 高门槛(3 帧)/ 1 中(2 帧)/ 2 低(1 帧) |
bargeInMode | detect | 打断方式:detect 自动探测本机原生回声消除状态(默认;未生效时切为长按打断)/ auto 强制开口即打断(耳机、安静环境推荐)/ manual 长按打断(外放推荐)。批 7O 默认,I10 豁免(ADR-0006) |
echoGateDb | 6 | 回声门控阈值(dB):自动打断要求残差高于回声地板此值。原生 AEC 生效时此门控闲置(Safari / 耳机等无原生 AEC 环境才兜底生效);打不断降 3-4,噪音误打断升 8-10。不要为「打不断」调它(详见 ADR-0006) |
silenceMs | 1500 | 说完整一句的静音停顿毫秒数 |
idleTimeoutMinutes | 5 | 无活动自动退出语音模式的分钟数(朗读计为活动;批 J 10→5) |
modelHost | 默认源 | 模型下载源(国内网络填 https://hf-mirror.com) |
autoSend | true | 静音到点自动发送(连续多段拼成一条消息);关闭则只进草稿(按住 Ctrl / hold 松手仍会发送) |
autoResume | false | 切回上次语音会话时自动恢复语音模式(默认关)。开启后:下次进入语音会话即自动进入语音模式 + 恢复上次会话;关闭则需手动按 Ctrl+Shift+V 重新进入 |
mode | toggle | 交互模式:toggle 持续聆听 + 1500ms 静音断句;hold 按住说话、松手发送(短按退出) |
shortcut | |
生效范围:voice/rate/ttsEngine/kokoroModel/spokenFormat 立即生效;其余设置下次进入语音模式时生效。设置项默认值由插件配置(base 层)提供。
本地音色(VITS / Kokoro)
- VITS(纯中文):
suyingxue 素映雪·女 / gunian 顾念·男 / fushiyu 傅斯遇·女 / bingjiao 冰娇·男 / bazong 霸总·男
- Kokoro(中英混读均可):103 个音色全量入表,面板按编号 + 实测性别标注;四个常用男声置顶:
62 深沉 / 68 浑厚 / 75 清亮 / 76 磁性;中文女声 48 小北 / 49 小妮 / 50 小小 / 51 小艺。音色只是风格向量,语言能力与音色无关。
常用 Edge 音色(完整清单见 node scripts/list-voices.mjs)
| ShortName | 说明 |
|---|
zh-CN-XiaoxiaoNeural | 晓晓 · 女声(默认) |
zh-CN-XiaoyiNeural | 晓伊 · 女声 |
zh-CN-YunxiNeural | 云希 · 男声 |
zh-CN-YunjianNeural | 云健 · 男声 |
zh-CN-YunyangNeural | 云扬 · 男声 |
zh-CN-YunxiaNeural | 云夏 · 男声 |
zh-CN-liaoning-XiaobeiNeural | 小北 · 东北话 · 女声 |
zh-HK-HiuMaanNeural | 晓曼 · 粤语 · 女声 |
zh-TW-HsiaoYuNeural | 小雨 · 台湾腔 · 女声 |
en-US-AriaNeural | Aria · English · 女声 |
🔧 配置(bundle patch / settings.yaml)
也可以直接编辑 ~/.dsh/settings.yaml 的 voice-mode: 段(设置面板与 RPC 写的是同一份文档层):
- id: voice-mode
name: dsh-voice-mode
config:
enabled: true # false = 完全禁用语音模式(toggle 会被拒)
cacheDir: ~/.cache/dsh-voice-mode/models # 可覆盖;否则用平台默认
# 以下为设置项播种的默认值(设置面板优先级更高,面板是权威源):
voice: zh-CN-XiaoxiaoNeural
rate: 1.1 # 批 J 1.0→1.1
interruptLevel: 0
silenceMs: 1500
idleTimeoutMinutes: 5 # 批 J 10→5
modelHost: https://huggingface.co
注意:voice / rate / interruptLevel / silenceMs / idleTimeoutMinutes / modelHost / autoSend
的生效值来自设置面板;bundle 配置只负责为这些键播种默认值
(enabled / cacheDir 则仅由 bundle 配置决定)。
插件 HTTP 命名空间固定为 /voice-mode(与客户端打包契约一致,不可配置)。
🌐 API
| 路由 | 说明 |
|---|
GET /voice-mode/stream | SSE:event: audio({sessionId, seq, text, audio(base64 MP3)})、event: mode(全局单活归属)、event: tool(提示音)、event: asr-progress / asr-ready / asr-error / tts-error |
POST /voice-mode/toggle | {sessionId, on} 进入 / 退出语音模式(全局单活) |
POST /voice-mode/asr | 裸 f32 LE 16k PCM → {text}(流式 zipformer2);模型未就绪返回 202 {loading};?reset=1 丢弃在途段(唤醒词命中时使用) |
POST /voice-mode/cancel | {sessionId} 作废 TTS 队列并丢弃在途 ASR 段 |
POST /voice-mode/preview | {voice, rate?} 一次性合成试听 → audio/mpeg(400 缺 voice / voice 过长;502 合成失败,如无效 ShortName;403 插件 enabled=false)。不要求语音模式处于激活态;使用独立合成连接,不影响朗读队列 |
GET /voice-mode/config | 客户端启动参数(静音阈值 / 灵敏度 / 音色与语速等)——含 ASR 侧字段 senseITN / senseVoice / captionFontSize / captionMaxWidth / backchannelYield |
GET /voice-mode | 健康检查 {ok, name, enabled, active} |
💾 模型与缓存
- 识别模型:
csukuangfj/sherpa-onnx-streaming-zipformer-zh-int8-2025-06-30(encoder ≈154 MB / decoder / joiner / tokens,合计约 160 MB),宿主端经 sherpa-onnx(Node WASM,Apache-2.0,原生跨平台)运行
- 缓存目录的平台默认:
- Windows:
%LOCALAPPDATA%\dsh-voice-mode\models
- macOS / Linux:
~/.cache/dsh-voice-mode/models
- 两者都可用
cacheDir 覆盖
- 下载用
.part 断点续传;huggingface.co 失败时回退 hf-mirror.com(可用 modelHost 配置)
🏛️ 工作原理(Architecture)
flowchart LR
subgraph Client[浏览器 Client]
Mic[麦克风 16kHz<br/>AudioWorklet<br/>echoCancellation:true] --> VAD[客户端 VAD<br/>RMS 分段]
VAD -->|partial 0.9s| UI[状态条 + 字幕浮层]
end
subgraph Host[宿主 dsh.host]
ASR[zipformer2 流式识别<br/>host 端 WASM]
SV[SenseVoice 定稿<br/>+ ITN + 标点]
Tap[llm/stream tap<br/>仅观察·不阻塞]
Seg[sentence segmenter]
Q[TtsQueue<br/>epoch 打断]
TTS{引擎}
Edge[Edge 云端]
Vits[本地 VITS WASM]
Kokoro[本地 Kokoro<br/>原生 addon]
end
UI -->|audio f32 PCM<br/>POST /voice-mode/asr| ASR
ASR --> SV
SV --> Draft[composer draft<br/>autoSend]
Draft --> Tap
Tap --> Seg
Seg --> Q
Q --> TTS
TTS -->|edge| Edge
TTS -->|vits| Vits
TTS -->|kokoro| Kokoro
Edge -.->|SSE audio frame| UI
Vits -.->|SSE audio frame| UI
Kokoro -.->|SSE audio frame| UI
VAD -.->|唤醒词/打断| Q
- 识别在 host 端本地运行(zipformer2 中文 int8 WASM + SenseVoice 定稿,模型懒下载),音频不上传第三方;识别定稿由 SenseVoice 补标点;
- 朗读默认 Edge 云端;本地 VITS 纯中文 / Kokoro 原生中英(跑在独立子进程、崩溃自愈)可选(隐私优先);
- 同一时间仅一个会话处于语音模式(全局单活);LLM 流被无损观察(不阻塞)。
详细架构决策:见 docs/adr/ 8 个 ADR。
🔍 与 dsh 内置语音模式对比
| 维度 | dsh 内置 | dsh-voice-mode(本插件) |
|---|
| 识别模型 | 云端 API(需 key) | 本地 zipformer2 + SenseVoice(零 key) |
| 多语种 | 英文为主 | SenseVoice 自动识别(zh/en/ja/ko/yue)+ ITN |
| 朗读引擎 | 云端 TTS | Edge 云端 + 本地 VITS/Kokoro 三选一 |
| 打断检测 | 基础 VAD | 三档灵敏度 + 回声门控 + 让位语义 |
| 热词偏置 | 无 | 无(已移除,详见 v0.7.7 文档说明) |
| 字幕 a11y | 无 | 4 档字号 + 3 档宽度 + 主题跟随 |
| 唤醒词 | 无 | 轻量流式匹配 + 前缀语气词白名单 |
| 兼容 dsh | — | 0.1.1-rc.2 → 0.1.5-rc.2 全版本(+ 0.1.6-alpha.2 预览) |
🚧 已知限制
- 发声打断依赖浏览器回声消除(
echoCancellation);扬声器音量过大时可能漏声到麦克风
Ctrl+Shift+V 会覆盖浏览器「粘贴纯文本」快捷键(普通粘贴仍可用 Ctrl+V)
- 识别质量受环境噪声影响;zipformer2 中文流式 + SenseVoice 多语(中英日韩粤)定稿
- 浏览器自动播放策略:朗读需要页面已有用户交互(点击麦克风即满足);「试听」依赖
AbortSignal.timeout(Safari 16+ / Chrome 103+ / Firefox 100+;老浏览器点击试听会立即提示失败,属预期降级)
- 唤醒词为轻量实现(流式文本匹配,非专用 KWS 引擎):嘈杂环境可能延迟或误激活;唤醒词本身不会进入聊天
- hold 模式按住时切换窗口/标签页会放弃本段(防持续收音)
- hero(新会话空态)无语音入口:请先进入会话使用麦克风按钮
spokenFormat 提示词经官方 system-prompt/assemble 瀑布注入;若当前会话使用完整提示词配置(persona complete: true 的 agent preset),提示词不注入(官方 complete 契约优先)
- 本地 Kokoro 每次打断后下一句朗读前约有 1 秒引擎重建时间(打断即终止在途合成的代价)
- 苹果 Safari / iOS:
- 需 HTTPS 或 localhost(iOS/macOS Safari 强制安全上下文;
http:// 局域网 IP 下麦克风不可用)
- 首次进入需授权麦克风;被拒后到「设置 → Safari → 麦克风」开启(iOS)
- iOS 后台/锁屏时识别与朗读暂停,回前台自动恢复(可能丢句);建议语音模式期间保持前台
- 安全说明:插件 HTTP 面(
/voice-mode/*)遵循宿主安全模型——请勿将 dsh 端口直接暴露公网;经反向代理发布时由代理层(如 basic auth)鉴权;插件侧对敏感操作保留会话归属校验
🛠️ 故障排查
| 现象 | 处理 |
|---|
| 点麦克风无反应,状态条红字 | 浏览器拒绝麦克风:地址栏(iOS 为 设置 → Safari → 麦克风)开启后重试 |
| 状态条「正在加载模型… x%」卡住 | 检查网络;模型较大可先 npm run prefetch;国内网络 modelHost 配 https://hf-mirror.com |
| 朗读无声音/无字幕 | 本地引擎首次合成需加载模型;若持续失败查看状态条提示(自动退避重试);确认页面前台且未静音 |
| AI 回复偶尔不朗读(某句没声/整条没声) | 已定位三类原因并修(v0.7.10):① 云端 TTS 偶发超时——单句自动重试 3 次,仍失败会跳过该句并在状态条提示「有一句朗读失败,已跳过」(此前是静默丢句);② 浏览器挂起 AudioContext(切到后台标签页/长时间静音后回来)——播放被调度但无声、UI 却显示朗读中;现在每次入队与任意点击/按键都会自动恢复;③ SSE 断线丢帧导致整句不完整——按设计丢弃该句(避免播坏音频),可开 localStorage['dsh-voice-mode.telemetry']='1' 看 tts-drop-sentence 诊断 |
| 语音模式进不去 | 检查插件 enabled;多标签页时确认当前会话为活动会话 |
| 识别到但不是我要说的 | 环境噪声或唤醒词误判:降低音量、提高 interruptLevel(高门槛)或启用 wakeWord |
| 唤醒词唤不醒 | 先看待机态状态条的实时转写(它听到了什么):同音字 1 字内已自动容错;停顿约 1.5 秒(= 静音断句时长)再说,清掉待机段残留语音、从段首重新匹配;换一个转写稳定的词(建议 3-4 字) |
| 打断后说话没反应(开了唤醒词) | 唤醒词只管「开始识别」的门:打断/每句断句后回到待机态,需重说唤醒词再继续说;待机态状态条会显示「说『x』开始」提示 |
| 唤醒词完全没反应 | ① 确认交互模式是 toggle(hold 与手动打断下唤醒词不生效);② 确认 bargeInMode:manual(或 detect 在本机无原生回声消除时自动落 manual)会关闭常驻聆听 → 唤醒词失效,改回 auto 或按住麦克风/Ctrl;③ 朗读期说唤醒词不触发(先等 AI 说完);④ 看待机态实时转写,确认它听到了什么(同音字 1 字内已容错,建议 3-4 字词) |
| 按住说话松手后没反应 | 确认交互模式为「按住说」且按住期间按钮高亮;松手后识别定稿约 1 秒内进入草稿 |
| 打不断(朗读中开口无反应) | 调高 interruptLevel(更敏感档 = 0 或 1)或检查麦克风权限;若 VAD 持续不触发可临时切到「手动打断」(mode: hold / 唤醒词定时延后);不要调 echoGateDb——真机 3.1 分钟朗读期里 Silero 0/777 帧把回声判成语音,原生 AEC 生效时此阈值从未被执行(详见 ADR-0006) |
| 字幕被输入框挡住 | 默认 captionMaxWidth=1(70vw)+ captionFontSize=0(12px)在窄屏可能与底部输入框重叠;调高档位或点字幕浮层「×」收起 |
| 让位行为异常(朗读期说「嗯」不停 / 真话被打断) | 「嗯/对」类短词触发让位 1.5s 后继续朗读;继续说真话会走硬打断;不要时关 backchannelYield 即可恢复改造前行为(ADR-0008) |
| 朗读期说「嗯」没让位 | 确认 backchannelYield=true(默认开);hold 模式松手后让位 1.5s 内继续说话会变硬打断 |
| 空闲 5 分钟自动退出(不想退) | 调高 idleTimeoutMinutes(默认 5 分钟,朗读计为活动) |
🛣️ 路线图(Roadmap)
完整 backlog(43 项 P0-P3)见 docs/competitive/backlog.md。
- ✅ 已完成(v0.7.7):11 批次周全修复(字幕档位 / 让位语义 / 模型预热 / 默认值微调 / 死代码清理等)
- 🚧 P0(近期):ADR-0003 VAD 下沉 / ADR-0006 第一级探测接通 manual / F1 emotion DSL 全量上线
- 📋 P1(中期):MCP
voice_* 工具集 / 卡片表单 draft validate / 状态条 idle 优化
- 💡 P2(远期):声音克隆(用户已决定推迟)/ ADR-0004 WebSocket transport
- ⏸️ 已推迟:xAI fallback / C1 人格层(用户已决定推迟)
🛠️ 开发
pnpm install && pnpm build # esbuild:lib/index.js(host)+ lib/client.js(browser)
pnpm test # segmenter/wakeword 单测 + 发布前自检(无需网络)
systemctl restart dsh # 本机加载新 host 代码;其他平台重启 dsh 进程
注意:dsh 安装的是 pnpm file: 链接(目录拷贝),改完 node build.mjs 后需把 lib/client.js 同步到 <profile>/node_modules/dsh-voice-mode/lib/ 再刷新页面(lib/index.js 与工作区为同一文件自动同步)。集成探测脚本(test/hold-e2e.js、test/spoken-prompt-rpc.sh、test/spoken-toggle-ui-check.js)位于仓库根 test/,不在 npm 包内。
src/index.ts host:单活指针、llm/stream tap、SSE、settings 注册、口语化提示词注入
src/asr-host.ts host:zipformer2 流式识别 + SenseVoice 定稿 + 模型懒下载(.part 断点续传)+ 增量喂料
src/models.ts host:模型下载/校验(SHA256 固定 + 域名白名单)
src/security.ts host:限流器与安全守卫
src/tts-local.ts host:本地 TTS 引擎(VITS WASM / Kokoro 原生 addon,子进程管理)
src/tts-vits-worker.ts 子进程:合成执行(base64 IPC,空文本静音守卫)
src/tts-queue.ts host:逐会话 TTS 队列 + epoch 打断机制
src/segmenter.ts host:句子切分 + 文本消毒(markdown 剥离 + 噪声字符剔除)
src/asr.ts client:音频采集、VAD 分段、增量识别、唤醒词、按住说门控
src/client.tsx client:麦克风按钮 + 模式切换按钮 + 状态条 + 字幕浮层 + 打断
src/strings.ts client:中英文案字典(navigator.language)
📄 License
MIT
部分实现借鉴 haoku123/dsh-voice(派生声明见子包 LICENSE)。