Bundle
@harness-flow/dsh-voice
dsh-voice — turn-based voice loop for DeepSeek Harness: pluggable Qwen / MiMo / local ASR+TTS engines, agent-driven speak/listen tools and browser PTT UI, built for interviewer presets
- Source
- Harzva
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-voice
回合制语音通道 for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)——为「面向面试者的 preset」而生的语音插件。
Agent 通过三个工具获得语音能力,浏览器端自动配合录音/播放,形成完整的一问一答闭环:
| 工具 | 方向 | 作用 |
|---|---|---|
| `voice_speak` | Agent → 人 | 把提问/反馈合成语音,浏览器自动播放 |
| `voice_listen` | 人 → Agent | 弹出录音面板,候选人作答后自动转写回传 |
| `voice_status` | — | 引擎健康检查(TTS/ASR 可用性、队列、录音状态) |
## 三引擎 provider 抽象
TTS 与 ASR 各自独立选择引擎,`auto` 模式按 `qwen → mimo → local` 顺序探测,取第一个可用者:
| 引擎 | TTS | ASR | 依赖 |
|---|---|---|---|
| **qwen**(首发) | `qwen3-tts-flash`(DashScope 原生 multimodal-generation) | `qwen3-asr-flash`(OpenAI 兼容 `/audio/transcriptions`) | 环境变量 `DASHSCOPE_API_KEY` |
| **mimo** | `MiMo-V2.5-TTS`(`POST /audio/speech`) | `MiMo-V2.5-ASR`(`POST /audio/transcriptions`) | 环境变量 `MIMO_API_KEY`;baseUrl/model 可配(本地 MiMo 网关同理) |
| **local** | sherpa-onnx / piper 二进制 / macOS `say` | sherpa-onnx(paraformer/zipformerCtc 等离线模型)/ whisper.cpp 二进制 | 本地模型与二进制,零 API 依赖 |
> **本地 ASR(sherpa-onnx)已验证闭环**:macOS `say` 合成中文 → 16 kHz WAV →
> `sherpa-onnx-paraformer-zh-small-2024-03-09`(int8,约 82 MB,hf-mirror 可下载)
> 转写,552 ms、逐字命中。插件将 `sherpa-onnx-node` 声明为 optionalDependency,
> 本地引擎生效只需:模型目录就位 + 配置 `asr.local.{kind, modelType, modelDir, model}`。
音频落盘在 `~/.dsh/voice`(可配),由本机 loopback 路由(支持 Range)服务给浏览器;浏览器端以 16 kHz 单声道 WAV 录音(对所有 ASR 后端通用)。云引擎只收到需要转写的音频本身。
## 安装
```sh
dsh plugin --profile web add @harness-flow/dsh-voice
```
> 安装后重启对应 profile 生效。插件行的默认配置是中性值;在你的 profile 的
> `cordis.patch.yml` 里按行 id `dsh-voice` 覆盖(不要重复 insert 同一 id)。
## 配置
```yaml
- id: dsh-voice
config:
asr:
engine: auto # auto | qwen | mimo | local
language: zh
# qwen: { apiKeyEnv: DASHSCOPE_API_KEY, model: qwen3-asr-flash, baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1 }
# mimo: { apiKeyEnv: MIMO_API_KEY, model: MiMo-V2.5-ASR, baseUrl: https://api.mimo.mi.com/v1 }
# local: { kind: sherpa-onnx | whisper-bin, modelType: paraformer, modelDir: ~/.dsh/voice-models/paraformer-zh-small, model: model.int8.onnx, bin: ... }
tts:
engine: auto # auto | qwen | mimo | local
voice: Cherry
# qwen: { apiKeyEnv: DASHSCOPE_API_KEY, model: qwen3-tts-flash, baseUrl: https://dashscope.aliyuncs.com/api/v1 }
# mimo: { apiKeyEnv: MIMO_API_KEY, model: MiMo-V2.5-TTS, baseUrl: https://api.mimo.mi.com/v1 }
# local: { kind: sherpa-onnx | piper-bin | say, modelDir: ..., bin: ..., model: ... }
audioDir: ~/.dsh/voice
listenTimeoutSec: 120
```
- 云引擎密钥只在调用时从引用的环境变量读取,配置里只写变量名,永不写密钥。
- `sherpa-onnx` 需要 profile 里安装 `sherpa-onnx-node` 并配置模型目录;`whisper-bin`/`piper-bin` 指向 whisper.cpp / piper 的可执行文件与模型。
- 本地 MiMo(MiMo-Audio-7B 经 vLLM-Omni 等 OpenAI 兼容网关)通过 `tts.mimo.baseUrl` / `asr.mimo.baseUrl` 指向本机地址即可复用同一实现。
## 面试官 preset(推荐用法)
preset 与插件各司其职:插件负责语音链路(host 侧服务 + 浏览器 UI),preset 只负责「面试官」这个角色与流程。给面试 preset 的 persona 建议:
```yaml
- id: persona
name: '@deepseek-ai/dsh-persona'
config:
text: >-
你是一位专业的面试官,正在通过语音进行一场结构化面试。
一次只提一个问题:用 voice_speak 朗读问题,随后立刻调用 voice_listen
等待候选人作答;根据 transcript 追问或进入下一题。
流程:开场寒暄 → 自我介绍 → 技术/项目深挖 → 行为面 → 候选人提问 →
结束语与后续安排。全程专业、中立、鼓励,不做主观臆断;
面试结束后输出评分表(各维度 1–5 分 + 一句话依据)与录用建议。
```
## 开发
```sh
pnpm install
pnpm check # build + 22 项单元测试
pnpm run verify:dsh-offline # 一次性 DSH_HOME 隔离 profile 启动验证(不碰用户 profile)
```
- Host:`src/index.ts`(TypeScript,tsc 直出 ESM 到 `lib/`)
- Client:`src/client/*.tsx`(esbuild 打成 `window.__ModuleLoader__.load` 懒加载 CJS 包)
- 测试:`node --test test/*.test.mjs`(fake/mock 引擎,不访问网络)
- 浏览器验收:`scripts/browser-accept.mjs`(Playwright,需 `DSH_VOICE_BASE_URL` + `PLAYWRIGHT_ENTRY` 环境变量)
## 发布与市场
- GitHub:`Harzva/dsh-voice`(topic: `dsh-plugin`),Release tarball 即安装包:
`dsh plugin --profile web add github:Harzva/dsh-voice`
- awesome-dsh-plugin.com 收录后自动辐射 dsh-market、dsh-find-plugin、
dsh-webui-market-plugin 与内置 dsh-market 的桌面客户端;
带 `dsh-plugin` topic 的仓库会在话题驱动市场(DSH-Plugins-Marketplace、
DSH-Plugin-Market 等)自动收录。
## 边界与设计取舍(v0.1)
- **回合制优先**:`voice_speak` 阻塞到合成完成(2–5s),保证「提问 → 收听」顺序;全双工打断(barge-in)留给后续版本。
- **单客户端桥接**:listen 会话与播放队列在内存中,假设 DSH web UI 一个浏览器;页面关闭后队列自动丢弃。
- **不做会话事件持久化**:v0.1 不写 `voice/*` 会话事件(历史加载兼容风险),聊天记录里保留文本转写。
- **失败局部化**:引擎探测失败只影响该引擎;浏览器播放被自动播放策略拦截时,等下一次用户手势重试。
Install
dsh plugin --profile web add github:Harzva/dsh-voice#476969c996b986b142a9ac12b1bfd56a059ec64d
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install harness-flow-dsh-voice from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.