lijiawei255/dsh-status-chime ↗★ 0
dsh-status-chime
提供任务状态的语音警报与提示功能 适合需要在后台运行任务、等待批准或完成时获得声音提醒的用户。
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:lijiawei255/dsh-status-chime说明文档
阅读完整 README ↗运行时配置(配置不在上面那个目录里,是同级的一个文件)
Remove-Item -Force "$env:USERPROFILE.dsh\voice-alerts.config.json"
卸载后需要**完全重启 DSH Desktop** 才生效。
⚠️ 质检报告**不在** `$DSH_HOME` 下,也**不随包发布**:`qa/report.md` 由开发脚本 `scripts/qa.mjs` 写进**仓库检出目录**里的 `qa/`。那个目录在 `.gitignore` 里、也不在 `package.json` 的 `files` 中,所以用 `dsh plugin add` 装出来的用户**没有这个文件**(要跑质检得先克隆仓库)。
## 它怎么工作
DSH 事件 ──▶ lib/index.js ──▶ 聚合 / 节流 / 优先级 ──▶ 播放器 ──▶ 音频文件
**事件来源**(都是宿主进程里的公开事件):
| 事件 | 用途 |
|---|---|
| `session/event` → `turn/start` / `user/message` / `turn/end` | 判断这一轮是不是你发起的,以及它是怎么结束的 |
| `session/event` → `goal/change` | 目标的 `complete` 与 `block` |
| `jobs.onJobDone` | 后台任务的 `completed` 与 `failed` |
| `tools/pre-execute` | 工具名命中 `waitingTools` 时,说明代理要停下来等你 |
| `session/event` → `approval/asked` | **主路径**:审批策略为 `ask` 时必定派发 |
| `approval/request` | **兜底**:作用域瀑布,同样只在 `ask` 下可能派发;两条路播同一条音 |
**播放规则**:同一 400 毫秒窗口内的多个事件只播**优先级最高**的那条;同一场景 1.5 秒内不重复;新的提示会打断正在播的那条。
**运行时零网络**:插件只读本地音频文件、只启动本地播放器。不联网、不上传、不校验。
## 技术栈:用大模型给声音做「预审」
这部分是我觉得最值得分享的地方。
给提示音挑一个合适的音色,传统做法是**一个个盲听**:试 10 个音色、改 20 次风格描述、听到耳朵疲劳,最后凭模糊印象拍板。这个项目换了个做法——**让大模型先做初筛和排序,人只在筛出来的少数里做最终决定**。
具体是三件事:
**1. 用全模态模型当评审(核心)**
`tools/qw_local_omni.py` 把**多段候选音频一次性**交给 Qwen-Omni,让它横向比较并按维度打分:
```powershell
python tools/qw_local_omni.py preview/flash-mary-en.mp3 preview/flash-eva-en.mp3 preview/flash-yuanfei-en.mp3 `
--message "横向比较这几段录音,按清晰度/自然度/音色/干净度打分并排序"
拿到的是一个排序而不是一堆孤立分数——「A 比 B 更贴目标」这种相对判断,比「A 得 7 分」有用得多。实测给出的排序和理由相当具体:
A4 是唯一一个在保持清晰稳重的基础上,做到了低沉与气声质感的音色。A2 虽然成熟但不够有辨识度,A5 略显平淡,A1、A3、A6 则因音色过亮或过嫩而被排除。
2. 用 ASR 转写回读做客观校验
光听着顺耳不够——TTS 可能吞字、念错、把缩写拆开读。tools/qw_local_asr.py 把音频转回文字,与预期文案做字符级相似度比对:
python tools/qw_local_asr.py assets/clips/turn-error.mp3 --lang zh
这条是可以当硬门槛的客观指标:文案是已知的,转写对不对是机械可判的。本项目中英各 8 条、共 16 条音频的相似度都是 1.000。
3. 时长梯度作为信息编码
上面表格里的时长不是随手定的,而是把「严重程度」编码进了音频自身:长 = 需要你出手。这样即使手机在旁边、屏幕没看,也能靠声音长短判断该不该放下手里的事。
由此得到的完整流程(scripts/ 里两个脚本,可直接复现):
scripts/build.mjs audition 出多个音色候选
↓
scripts/qa.mjs rank 全模态模型横向排序(不做 ASR;回读在下一步)
↓
人听筛出来的前 2-3 个,拍板 ← 决策量被压缩到很少
↓
scripts/build.mjs build 批量生成 + 响度归一
↓
scripts/qa.mjs clips 全部质检过关
一点经验:不要把大模型的主观打分当硬门槛。实测同一段音频两次评分能从「自然 6 / 音色 4」跳到「自然 9 / 音色 7」,四段明显不同的音频甚至拿到过完全一样的分数。所以本项目把指标分了两层——客观项(ASR 相似度、有无削波、清晰度、干净度)当门槛,主观项(自然度、音色、成熟度)只作参考,最终由耳朵决定。这个分工是这套流程能稳定跑起来的关键。
后来补上的三条检查,都是先发现了具体漏洞才加的:
| 检查 | 性质 | 为什么加 |
|---|---|---|
| 静音下限(峰值 ≥ −30 dB、均值 ≥ −35 dB) | 硬门槛 | 原来的峰值检查只抓削波,所以「时长正常但内容全静音」这种经典 TTS 失效能全项通过。实测成品峰值 −4.2…−1.9 dB、均值 −20.6…−16.6 dB,而全静音是 −91 dB。两条线的余量不一样:峰值门槛离最差成品 25.8 dB(−4.2 vs −30),均值门槛 14.4 dB(−20.6 vs −35) |
| 净语速(去掉停顿后的单位/秒) | 参考项 | 中文刻意放慢(rate 0.95),套用人类播报那个 3.2–5.5 字/秒会把八条里的七条判失败。对这个项目真正有意义的是条与条之间的一致性(因为时长承载紧急度),所以跟同语言中位数比;少于 5 个单位的短句豁免,否则两个词的 clip 会假报警 |
| UTMOS(MOS 预测器) | 参考项 | 给自然度一个可复现的数字,补上「大模型打分不稳定」那一环。必须按语言分别比:它给英文八条的分全部高于中文八条(4.38–4.51 vs 3.75–4.28),跨语言比会读成中文那套有缺陷 |
这三条都有反向验证(scripts/qa-negative-control.mjs,离线运行、不花 API 额度):注入全静音、极安静、语速拉长三类缺陷,断言门槛确实报出来,同时确认健康音频不会被误伤。一个只会说「通过」的检查器没有价值。
顺带记一个坑:加静音下限之前,先用
ttsproof(一个现成的 TTS QA 工具)试过。它能抓削波和截断,但抓不到全静音 —— 它源码里的empty_audio只判断「文件不存在或 ≤44 字节」,不看音频内容。所以那条下限是自己加的,不是从工具里白拿的。
配置
配置文件:$DSH_HOME/voice-alerts.config.json($DSH_HOME 通常是 ~/.dsh)。
找不到这个文件时使用内置默认值,所以不配置也能正常用。文件按修改时间热读取,改完立即生效,不需要重启。
完整模板见 assets/voice-alerts.config.json。常用项:
| 配置项 | 默认 | 说明 |
|---|---|---|
enabled | true | 总开关 |
volume | 85 | 0-100,只对 ffplay 生效 |
minIntervalMs | 1500 | 同一场景的重复触发抑制窗口 |
coalesceMs | 400 | 事件聚合窗口,窗口内只播最高优先级那条 |
interrupt | true | 新提示是否打断正在播的 |
scenes..enabled | true | 单独关掉某个场景 |
player | "auto" | auto / ffplay / powershell |
ffplayPath | null | 显式指定 ffplay 路径(不在 PATH 上时用) |
playPs1Path | null | 显式指定回退脚本 play.ps1 的路径 |
commandName | "voice-alerts" | 斜杠命令名。与别的插件重名时只丢命令,不影响出声 |
waitingTools | ["ask_user_question","exit_plan_mode"] | 命中即视为「在等你回答」;DSH 若改工具名可在此覆盖 |
language | "zh" | 语音语言:zh / en;无法识别的值回退到 zh(不会静默) |
watchApprovals | true | 审批请求是否出声(只在策略为 ask 时可能触发) |
clipsDir | null | 自定义音频目录,优先级最高 |
命令
| 命令 | 作用 |
|---|---|
/voice-alerts | 依次播放全部八条(当前语言) |
/voice-alerts on / off | 立即开关(会写回配置文件) |
/voice-alerts lang | 切换语音语言,写回配置 |
/voice-alerts status | 看播放器探测结果、各语言各场景音频是否齐备 |
/voice-alerts test | 只播一条,用来排查某类事件有没有触发 |
换成你自己的声音
音频不是必须用仓库里这 8 条(中英各 8 条)。完整流程:
## 配置
配置文件:`$DSH_HOME/voice-alerts.config.json`(`$DSH_HOME` 通常是 `~/.dsh`)。
找不到这个文件时使用内置默认值,**所以不配置也能正常用**。文件按修改时间热读取,改完立即生效,不需要重启。
完整模板见 [`assets/voice-alerts.config.json`](https://github.com/lijiawei255/dsh-status-chime/blob/3c5a005819d2b85cebcf2777d896b0bfc1007fa6/assets/voice-alerts.config.json)。常用项:
| 配置项 | 默认 | 说明 |
|---|---|---|
| `enabled` | `true` | 总开关 |
| `volume` | `85` | 0-100,**只对 ffplay 生效** |
| `minIntervalMs` | `1500` | 同一场景的重复触发抑制窗口 |
| `coalesceMs` | `400` | 事件聚合窗口,窗口内只播最高优先级那条 |
| `interrupt` | `true` | 新提示是否打断正在播的 |
| `scenes..enabled` | `true` | 单独关掉某个场景 |
| `player` | `"auto"` | `auto` / `ffplay` / `powershell` |
| `ffplayPath` | `null` | 显式指定 ffplay 路径(不在 PATH 上时用) |
| `playPs1Path` | `null` | 显式指定回退脚本 `play.ps1` 的路径 |
| `commandName` | `"voice-alerts"` | 斜杠命令名。与别的插件重名时只丢命令,不影响出声 |
| `waitingTools` | `["ask_user_question","exit_plan_mode"]` | 命中即视为「在等你回答」;DSH 若改工具名可在此覆盖 |
| `language` | `"zh"` | 语音语言:`zh` / `en`;无法识别的值回退到 `zh`(不会静默) |
| `watchApprovals` | `true` | 审批请求是否出声(只在策略为 `ask` 时可能触发) |
| `clipsDir` | `null` | 自定义音频目录,优先级最高 |