Bundle
@biliye/dsh-voice-call
个人语音通话助手:专属工作区/会话(自动创建、跨重启保持)、悬浮球通话面板、FunASR 流式/HTTP 语音识别、云端 TTS 语音回复、唤醒词通话模式(可配置唤醒词/休眠时长,建议本地 ASR)、子代理任务分发与进度跟踪。持久化安装,重启后仍在「设置 → 插件」中可见。
- Source
- biliye
- stars
- 2 stars
- License
- MIT
- Updated
- Updated 6 hours ago
Readme
# dsh-voice-call 语音通话助手
DSH Web GUI 的个人语音通话助手插件:悬浮球通话面板、FunASR 语音识别(流式/HTTP)、云端 TTS 语音回复、子代理任务分发与进度跟踪。
## ✨ 功能
- 🗂 **专属工作区与专属会话**:安装并打开后自动创建专属工作区「语音通话」(目录 `$DSH_HOME/voice-call`)与专属会话 `voice-call-main`;**所有语音通话固定保存在该会话,本体重启后依然延续同一会话**(自动 resume)
- ⚡ **快速回复**:专属会话自带「快速、简短回复」提示词,并限制重型工具(bash/pwsh/run_code/工作流/子代理等),需要执行任务时一律通过 `voice_task` 分发给独立子代理会话
- 🎙 **悬浮球通话面板**:页面最顶层悬浮球,可拖拽,点击展开通话/任务/设置面板
- 🗣 **语音识别**:FunASR Server HTTP(新版 v1.x,默认)/ FunASR 流式 ws://(旧版 2pass)/ 云端 API(OpenAI 兼容),设置可切换
- ⏹ **停止任务**:对执行中的任务提供面板「⏹ 停止」按钮、`voice_task`(`action=stop`)以及 HTTP `POST /api/voice-call/task-stop` 三种停止入口;停止后任务状态为「已停止」并语音播报
- 🔎 **任务会话可打开**:任务会话以普通工作区会话创建,可在「语音通话」工作区列表打开,实时查看执行过程(工具调用/中间输出),也可用会话页的停止控件中断任务
- 🔔 **唤醒词通话模式(可选)**:只有说出唤醒词(默认「小鲸鱼」,可改)才唤醒对话——仅唤醒词回「我在」,唤醒词后带指令(如「小鲸鱼帮我打开qq」)会去掉唤醒词后直接执行;沉默超过设定时长(默认 8 秒,可调)自动休眠;**建议使用本地部署的 ASR 进行语音识别**(语音不出本机、延迟低,唤醒更可靠)
- 🔊 **语音回复(TTS)**:MiniMax TTS 或 OpenAI 兼容 TTS,可开关;支持最大字数截断检测、语速调节;语音助手按语气在回复中加入情绪标记(如(笑)(叹气)),MiniMax speech-2.8 系列会以对应语气/插话朗读(默认模型 speech-2.8-hd,账号不支持时自动回退 speech-02-hd),OpenAI 兼容 TTS 与旧模型自动剥离标记只读正文;朗读前自动清理 emoji/颜文字等不可读符号,并整行剔除 `Route: …` 这类路由/流程元信息行(避免把声明念出来);没有对应实现的「朗读方式」标记(如(轻声)(温柔))一律剥掉——MiniMax 只有 `(laughs)/(sighs)` 这类插话标签,自创标记留着会被逐字念成"轻声";**每次 TTS 请求的文本、模型、是否回退都记在 `$DSH_HOME/logs/voice-call-tts.log`**(保留最近 200 行,供排障)
- 📋 **任务分发**:语音会话(主会话)通过 `voice_task` 工具或面板把任务分发给「语音通话」工作区下的**独立任务会话**执行(自带完整文件/代码/搜索工具;**上下文将满时自动新开一个任务会话续接**);主会话可查看进度、接收成功/失败汇报;**可随时停止任务**(面板「⏹ 停止」、语音说"停止任务"、`voice_task action=stop`,或打开任务会话后用 GUI 停止)
- 📢 **其他会话完成播报**:实时跟踪其他会话的任务完成情况(会话完成一轮 / 后台 job 完成),「动态」页签可查看记录,完成时自动**简要语音播报**(`「会话名」完成任务:内容`);跳过专属语音会话、子代理会话与你当前正在查看的会话;可在设置里关闭播报
- 🧠 **共享记忆**:任务子代理携带主会话上下文摘要;语音会话即主会话(共享历史)
- 👤 **人格设置**:可配置人格,留空用默认。**朗读听着发平、像念稿时**(常见于播报/总结这类书面语域),除插件自带的朗读约束(情绪标记不要只在句首给一个、转述用口语短句)外,还可以在人格里补一句,例如:「每句话都像跟人聊天,别用汇报腔;承载结论的结尾句也要带语气词或情绪标记,不要只在句首给一个」
- 🔔 **主动功能**:Host 定时检查任务进度,完成时注入主会话并推送
- 🔌 **联动**:提供 `voiceAssistant` 只读服务,其他插件(如桌宠)可读取任务/状态
## 📦 安装(持久化)
一条命令即可,**不需要手工编辑 `~/.dsh/profiles/web/package.json`**:本插件的 `package.json` 声明了 `dsh.bundle`,`dsh plugin add` 在 pnpm 装完后会把依赖自动追加到 profile 的 `dsh.profile.bundles` 层。
三条都可用(均已在 2026-09-12 实测;推荐 ① / ②,都不依赖 GitHub 连通性):
```bash
# ① npm(已发布 0.2.2;走 npm registry,可配国内镜像,不依赖 GitHub)
dsh plugin --profile web add @biliye/dsh-voice-call
# ② 预构建 tarball(不必拉取整仓 git 历史,也没有需要授权的 build 脚本)
# 每个 v* tag 由 .github/workflows/release.yml 自动产出该附件
dsh plugin --profile web add "https://github.com/biliye/dsh-voice-call/releases/latest/download/dsh-voice-call.tgz"
# ③ GitHub 源码直装(依赖 GitHub 连通性,国内可能需要代理)
dsh plugin --profile web add github:biliye/dsh-voice-call
```
装完重启 DSH(本次改动在 Host 半):`dsh plugin` 只装包,不会替你重启。
```bash
dsh --profile web --dump-config | grep voice-call # 应输出 - id: voice-call
dsh --profile web --dump-config | Select-String voice-call # Windows PowerShell
```
> 收录进插件市场后,用户也可以在「设置 → 插件市场」里搜索 `dsh-voice-call` 一键安装(安装来源同上,优先用 tarball)。投稿入口与步骤见 [`contrib/README.md`](contrib/README.md)。
### 本地开发安装
源码就在本机时直接用路径安装(pnpm 软链目录,改 `lib/client.js` 刷新浏览器即生效,改 `lib/index.js` 需重启 DSH):
```bash
cd /path/to/dsh-voice-call
dsh plugin --profile web add . # 相对路径按当前目录解析,不会误链到 profile 自身
```
### 升级 / 卸载
```bash
dsh plugin --profile web update @biliye/dsh-voice-call # 或 github:biliye/dsh-voice-call / tarball 地址
dsh plugin --profile web remove @biliye/dsh-voice-call
```
> 旧的手工安装方式(克隆到 `~/.dsh/voice-call-plugin`、手写 `file:` 依赖 + `bundles` 条目、再 `cd ~/.dsh/profiles/web && pnpm install`)已不再需要,也不再写进本文档:那三步里唯一真正必要的是 pnpm 装包,而 `dsh plugin add` 已经把它和 bundles 挂载一起做了。已在用旧方式安装的机器无需迁移,`dsh plugin --profile web update @biliye/dsh-voice-call` 即可切到新来源。
## 🚀 使用
0. **专属工作区自动就绪**:安装并重启 DSH 后,Host 会自动创建专属工作区「语音通话」与专属会话 `voice-call-main`(失败自动重试;会话被删除也会自动重建)。侧边栏可见该工作区,语音内容全部保存在此会话中,重启后依然延续
1. 点右下角 🎙 悬浮球(重启后自动出现)
2. 设置页配置语音识别引擎与 TTS(FunASR 模式 URL 填 `http://127.0.0.1:10095/v1/audio/transcriptions`,模型 `fun-asr-nano`),没有安装这个或者电脑性能不够依然可以使用绝大部分功能,该悬浮窗窗口也提供了文字对话的功能
3. 「▶ 开始通话」→ 直接说话 → **停顿 1.2 秒自动识别并发送**(VAD 实时监听,无需点结束)→ 助手回复自动语音朗读
4. 支持连续多轮对话:每说一句停顿一下即可,助手回复播放期间麦克风自动静音防回声
5. 「任务」页或语音说"帮我查一下…"分发子代理任务(专属会话会快速简短回复,耗时任务交给子代理执行);任务完成后会自动语音播报结果;需要中止时点任务卡「⏹ 停止」、在专属会话里说"停止任务",或在打开的任务会话页直接停止
6. 「⏹ 结束通话」停止监听(当前未说完的半句也会补发)
7. **唤醒词模式(可选)**:设置页开启「唤醒词通话模式」,配置唤醒词与「沉默多少秒后休眠」(默认 8 秒)后开始通话:休眠时只有听到唤醒词才唤醒对话——说「小鲸鱼」→ 回「我在」;说「小鲸鱼帮我打开qq」→ 去掉唤醒词后直接执行,回「好的」;沉默超时自动休眠。💡 **建议使用本地部署的 ASR 进行语音识别**(如本机 FunASR Server `http://127.0.0.1:10095`):语音不出本机、识别延迟低,唤醒更可靠;使用云端 ASR 时每段语音都会上传,唤醒响应也受网络影响
> **专属会话说明**:语音输入始终发送到专属会话(而非当前打开的会话),所有历史对话都保存在那里;面板可点「📂 打开会话」跳转到该会话查看完整记录。
### 前置依赖
| 组件 | 说明 |
|---|---|
| FunASR Server | 新版 v1.x:`funasr-server --port 10095`(OpenAI 兼容 HTTP,模型 fun-asr-nano / sensevoice;浏览器端 VAD 分段后整段上传识别) |
| node | ≥ 18(Host 端 TTS/ASR 网络桥需要 `node` 在 PATH 中) |
| TTS | MiniMax API Key + GroupId,或 OpenAI 兼容端点 + Key(可选) |
> **注意**:Host 半(`lib/index.js`)改动需重启 DSH 生效;Client 半(`lib/client.js`)改动会经 HMR 自动重载,刷新浏览器即可。
### 🛠 维护参考
- `FIX-2026-09-08.md`:任务生命周期修复档案(任务完成不播报 / 状态卡死 / 无法停止 / 任务会话无法完整打开)——含根因分析、修复内容、验证记录与维护注意事项。后续涉及任务分发/停止/会话模型或 DSH 运行时 Session API 升级时,先读该档案。
- `FIX-2026-09-09.md`:其他会话完成播报改造为「合并进主会话」后的修复档案(唤醒词休眠误跳过 / 任务结果截断与转述指令 / 多窗口双播排查)。涉及播报链路、唤醒词模式、多标签播报时先读该档案。
- `contrib/README.md`:发行与收录档案——投稿到 awesome-dsh-plugin 精选列表(=插件市场的唯一数据源)的条目文件、收录条件核对、tarball 链接防失效规则,以及 npm 发布与 `engines.dsh` 的可选项。改安装来源、发新版或补录 npm 之前先读该文件。
## 🏗 架构
```
lib/
├── index.js # Host 半:专属工作区/会话自动创建(workspaceRegistry + agents.create/resume,
│ # 固定 ID voice-call-main 跨重启保持)、任务分发(语音通话工作区下新建 va-task-*
│ # 任务会话,上下文满自动续接新会话,监听进程定时巡检完成/失败并汇报主会话)、
│ # 语音文本注入(agent.followup)、TTS/云端ASR 中转(subprocess node 桥,
│ # payload 走 stdin 避免命令行长度限制)、/api/voice-call/* 路由、
│ # voice_task 动态工具、voiceAssistant 服务、session/event 监听
└── client.js # Client 半:悬浮球(shell.overlay slot)、通话面板、VAD 实时监听(ScriptProcessor
# RMS 能量检测 + 停顿分段)、FunASR HTTP 整段识别、TTS 播放防自听、
# 唤醒词通话模式(handleRecognizedText 统一入口:休眠/唤醒状态机、
# 唤醒词匹配与剥离、沉默超时自动休眠、可配置唤醒词/休眠时长)、
# localStorage 设置、专属工作区/会话状态轮询与「打开会话」
```
### 设计要点
- **专属工作区/会话**:启动后自动创建 `$DSH_HOME/voice-call` 目录并在 workspaceRegistry 注册「语音通话」工作区;专属会话固定 ID `voice-call-main`——已存活直接复用、已持久化则 `agents.resume`、否则 `agents.create` 并固定标题、关联工作区。因此**无论本体重启多少次,语音通话始终落在同一会话**。会话被删除/卸载时由 30 秒巡检自动重建
- **快速回复**:专属会话通过 `setup` 注册 scoped systemPrompt 段落(快速简短回复 + 任务外派指引),每次 create/resume 都会重新注册,重启后依然生效;同时 `tools.restrict` 屏蔽 bash/pwsh/run_code/工作流/子代理等重型工具(工具名因部署而异,失败自动跳过)
- **实时监听(VAD)**:浏览器端 ScriptProcessor 采集 16kHz PCM,RMS 能量检测说话起止;静音达到 `vadSilenceMs`(默认 1200ms)自动把该段编码为 WAV 上传识别并发送——类似 hermes-voice-call 的 LISTENING→THINKING→SPEAKING 状态机,但完全在浏览器端实现
- **防回声自听**:TTS 回复播放期间(按文本长度估算时长)VAD 静默,`onended` 后恢复监听
- **任务分发**:每个任务在「语音通话」工作区下新建一个任务会话(`va-task-*`,命名「语音任务: 标题」),携带主会话最近摘要作为共享记忆;任务会话创建时加入 agent preset(优先继承父会话已加入的组合,否则挂载部署默认 preset,通常为 `standard`),因此具备完整的文件读写 / shell / 搜索 / 代码执行工具;任务会话完成后保留在工作区可查看。**任务会话以普通工作区会话身份创建**(不再标注 `origin=subagent`),因此可出现在工作区会话列表中,可打开、可实时查看过程、可被 GUI/面板/语音停止。宿主侧**监听进程**(默认每 15 秒,可配置 `taskPollMs`)定时巡检任务会话:上下文占用达到模型窗口约 80%(可配置 `taskContextLimit`,自动探测失败时回退 120k tokens)且任务未完成时,自动**新开一个任务会话续接**(携带进度摘要、标题加「(续接 N)」),并停止旧会话的当前轮;任务结算以**最后一次 `turn/end` 的 reason** 为准:`completed` → 成功、`aborted(user)` → 已停止、`error/interrupted/max-tokens/blocked` → 失败,均向主会话注入「已完成(成功)/ 已停止 / 失败:原因」汇报;空 idle 超时(默认 1 小时,可配置 `taskTimeoutMs`)/ 会话销毁 / 续接失败 → 判定**失败**;续接上限默认 4 次(`taskRolloverMax`)。停止由 `POST /api/voice-call/task-stop` 与 `voice_task action=stop` 触发:先标记 `stopped` 再 `agent.cancel`,避免被误判为完成
- **语音文本注入**:`agent.followup`(source:user)——与打字完全一致,会话历史可见
- **网络桥**:Host 无 fetch 全局,TTS/云端 ASR 通过 `subprocess` spawn node 脚本执行 POST;payload 经 **stdin** 传入(Windows 命令行 ~32KB 限制,argv 传音频 base64 会 `spawn ENAMETOOLONG`)
- **事件推送**:Host 维护事件队列(回复文本/任务状态),Client 轮询 `/api/voice-call/events`
- **不影响其他插件**:独立 slot id、工具 execute 内校验调用者、`voiceAssistant` 服务只读
## 📄 License
MIT
Install
dsh plugin --profile web add github:biliye/dsh-voice-call
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 biliye-dsh-voice-call from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.