dsh-talk
按下麦克风说话,回复被念出来——你一开始说话,播报就停。
DeepSeek Harness 的 voice-first 会话闭环:语音转写回填输入框,speak 工具让 agent 朗读回答,回合完成/待审批/出错都有语音播报;语音引擎可全部留在本机。
安装 · 配置 · FAQ · 简体中文 · English · Español · Português · हिन्दी
dsh plugin --profile <name> add dsh-talk
社区插件,与 DeepSeek 官方无关。唯一仓库:https://github.com/PerryLink/dsh-talk(Gitee 为镜像)。
它是什么
dsh-talk 给 DeepSeek Harness 补上声音的两端。输入端:输入框旁的麦克风按钮录音,浏览器 Web Speech、FunASR HTTP 服务或本地 whisper.cpp 把话转成文字,回填草稿或直接提交。输出端:agent 调用 speak 工具朗读回复,浏览器语音、edge-tts 或 piper 把文字变成声音。回合完成、待审批、出错这三类事件也能自动播报,而你一开口说话就会打断正在播放的语音——整个过程不需要云端账号,本地引擎可以全程离线。
结构:一条浏览器 / 宿主边界、两段反向通道 —— 上行话音经 talk/transcribe 进入宿主引擎阶梯(FunASR → whisper.cpp → 浏览器 Web Speech),转写回填草稿;中段 agent turn 铰链把消息交给 speak() 与三类事件播报;下行沿 TTS 阶梯(piper → edge-tts → 浏览器语音)落进缓存与有界导出目录,再由 talk/audio 回到播放;左缘是 talk/interrupt 回边,底部是设置写回。
三步上手
安装(三选一):
# git 通道(最新 main;等价于 git+https://github.com/PerryLink/dsh-talk.git) dsh plugin --profile web add github:PerryLink/dsh-talk # npm 通道(已发布版本) dsh plugin --profile web add dsh-talk # tarball 通道:本仓库 pnpm pack 后 dsh plugin --profile web add ./dsh-talk-<version>.tgzgit 通道的
prepare脚本仅用生产依赖构建,不需要额外工具链。最小配置(默认零配置即可用):
开箱即走浏览器语音:麦克风按钮用 Web Speech 转写,
speak用 SpeechSynthesis 播放。要用本地引擎时,按下面的形状补模型路径:- insert: - id: talk name: dsh-talk config: stt: engine: whisper whisper: modelPath: ~/models/whisper/ggml-small.bin tts: engine: piper piper: modelPath: ~/models/piper/zh_CN-huayan-medium.onnx验证已生效:
dsh --profile web --dump-config | grep -A2 'id: talk'然后按下输入框旁的麦克风说话;也可以让 agent 朗读回复:
> 用 speak 工具说“你好”。
卸载:dsh plugin --profile web remove dsh-talk(或从 profile patch 删掉该行)。
从 DSH Desktop 市场安装如果 pnpm 对本包报
ERR_PNPM_IGNORED_BUILDS(esbuild 的平台二进制无害校验),在你的pnpm-workspace.yaml中加入allowBuilds: { esbuild: true }——dshCLI 会打印确切片段。
所有 PerryLink 插件均可在 DSH Desktop 内置市场中浏览:市场 → 来源 → 添加来源 → 粘贴 https://perrylink-dsh-catalog.perrylink.workers.dev/catalog-source.json → 选中。安装仍需通过市场的 npm 身份校验与你的确认。
远程浏览器需要安全上下文
浏览器麦克风(getUserMedia / Web Speech / MediaRecorder)只在安全上下文中可用:
https://… 或 http://localhost / http://127.0.0.1。通过局域网 IP 打开的
http://<LAN-IP>:3081(包括另一台机器上的 Safari)不会暴露麦克风,即使 Talk 设置正确。
远程浏览器请使用:
- 通过 HTTPS 反向代理或隧道打开 Web UI;
- 用 SSH 转发端口,在客户端打开
http://localhost:<port>; - 让 UI 与浏览器在同一台机器上。
stt.engine: funasr / whisper 改变的是转写在哪里运行,不是浏览器采集权限;
录音仍然需要安全上下文。
核心能力
dsh-talk 双向闭合语音环路:
speak工具 —— agent 朗读自己的回复。TTS 引擎:浏览器语音、edge-tts(网络神经语音)或piper(本地)。音频在浏览器播放;使用本地引擎时还会落盘为文件,工具结果携带其绝对路径(audioPath)供其它工具使用;在能够承载该事件的宿主上,会话日志记录脱敏后的朗读内容(见安全边界)。- 输入框麦克风按钮 —— 按下说话,转写文本自动回填输入框(或直接提交)。STT 引擎:浏览器 Web Speech(含中间结果)、FunASR HTTP 服务或本地
whisper.cpp。 - 说话即打断 —— 开始说话即停止当前播报(经 client→host 的
talkRemote 通道)。 - 事件播报 —— 回合完成、待审批(waterfall 安全:绝不阻塞门禁)与出错,带静音开关与可配话术。
- 设置页签 —— 引擎/语言选择与播报开关,以追加式 profile patch 操作保存(带备份)。
浏览器 宿主
🎙 按下 ──▶ 打断 ────────────────────▶ talk/interrupt
录音(MediaRecorder / Web Speech)
转写(浏览器)或 talk/transcribe ────▶ FunASR / whisper.cpp
回填草稿或提交 ◀── talk:speech 投影 ── speak 工具 / 事件播报
▶ 播放音频(talk/audio 或 speechSynthesis)
工具与界面
| 界面 | 类型 | 说明 |
|---|---|---|
speak |
工具 | 朗读文本(browser/edge-tts/piper);支持单次引擎/音色覆盖;规范 JSON 结果 |
| 麦克风按钮 | conversation.input.left 槽位 |
录音 → 转写 → 回填草稿(或提交);按下即打断 |
| 设置页签 | settings.plugins.tab(id talk) |
引擎/语言/播报开关;追加式保存 |
talk:* |
Typert Remote | status、audio、transcribe、applySettings、interrupt(宿主命名空间) |
示例
以下为真实接口;设置页与麦克风按钮是 UI 入口。
详细示例 —— 命令 speak,输入:
{"text":"hello","engine":"browser"}
输出:
Spoke hello aloud with the browser engine. No audio file: the browser engine synthesizes in the browser.
Observed from plugins/dsh-talk/tests/lifecycle.spec.ts:124-137; renderer at src/tool.ts:47-64
失败分支 —— 输入:
{"text":" "}
输出:
Error: dsh-talk: speak text must not be empty
Observed from plugins/dsh-talk/src/tool.ts:124; plugins/dsh-talk/tests/tool.spec.ts:41-44
简短示例:talk:status · talk:transcribe
配置
所有可调项都是 Schemastery Config 字段(可在 cordis.yml 中修改)。cordis.patch.yml 内联说明了每个键。
最常用键:
| 键 | 默认值 | 含义 |
|---|---|---|
record.enabled |
true |
显示输入框麦克风按钮 |
record.autoSubmit |
false |
转写结果直接作为用户消息提交(false = 回填草稿) |
stt.engine |
auto |
auto / web / funasr / whisper;auto 优先已配置的本地引擎,其次 Web Speech |
tts.engine |
auto |
auto / browser / edge-tts / piper;auto 优先 piper,其次 edge-tts,最后浏览器语音 |
tts.export.dir |
<home>/.dsh-talk/audio |
导出目录;~ = 家目录,相对路径按家目录解析(绝不落在工作区) |
announce.enabled |
true |
事件播报总开关 |
interrupt |
true |
开始说话即停止当前播放 |
maxSpeakChars |
20000 |
speak 工具文本上限(1..100000) |
| 键 | 默认值 | 含义 |
|---|---|---|
record.enabled |
true |
显示输入框麦克风按钮 |
record.hotkey |
(无) | 可选切换快捷键,如 "alt+r" |
record.maxSeconds |
60 |
录音上限(秒,1..600) |
record.autoSubmit |
false |
转写结果直接作为用户消息提交(false = 回填草稿) |
record.vad.enabled / silenceMs / energyThreshold |
true / 1500 / 0.01 |
语音活动检测:静音自动结束录音(缺 AudioContext 时降级为手动停止) |
stt.engine |
auto |
auto / web / funasr / whisper;auto 优先已配置的本地引擎,其次 Web Speech |
stt.language |
auto |
BCP-47 语言或 auto |
stt.interim |
true |
显示中间转写(Web Speech) |
stt.silenceFinaliseMs |
4000 |
连续 Web Speech 在没有语音活动达到此毫秒数后停止(500..15000) |
stt.funasr.url |
(无) | FunASR 推理端点;引擎为 funasr 时必填 |
stt.whisper.modelPath |
(无) | whisper.cpp 模型;引擎为 whisper 时必填 |
tts.engine |
auto |
auto / browser / edge-tts / piper;auto 优先 piper,其次 edge-tts,最后浏览器语音 |
tts.rate |
0 |
edge-tts/piper 语速偏移(百分比,-50..50) |
tts.fallbackToBrowser |
true |
本地引擎失败时回退浏览器语音 |
tts.browser.voiceName |
(无) | 首选浏览器语音名称;未找到时使用平台默认语音 |
tts.browser.rate |
1 |
浏览器 SpeechSynthesis 语速(0.1..10) |
tts.browser.pitch |
1 |
浏览器 SpeechSynthesis 音高(0..2) |
tts.piper.modelPath |
(无) | piper 语音模型;引擎为 piper 时必填 |
tts.export.enabled |
true |
把本地合成的音频写入文件,并以 audioPath 返回路径 |
tts.export.dir |
<home>/.dsh-talk/audio |
导出目录;~ = 家目录,相对路径按家目录解析(绝不落在工作区) |
tts.export.maxFiles |
20 |
导出目录保留的文件数,含刚写入的那个(1..1000) |
tts.export.maxAgeMinutes |
1440 |
超过此分钟数的导出文件会在下次写入时清理(1..10080) |
announce.enabled |
true |
事件播报总开关 |
announce.onTurnEnd / onApproval / onError |
true |
播报哪些事件 |
announce.messages.* |
"Turn complete." 等 | 播报话术 |
interrupt |
true |
开始说话即停止当前播放 |
maxSpeakChars |
20000 |
speak 工具文本上限(1..100000) |
maxAudioCacheBytes |
8388608 |
内存合成音频缓存上限(1 MiB..64 MiB) |
stt.silenceFinaliseMs 与 record.vad.silenceMs 是两套独立机制:前者在连续 Web Speech 识别听不到语音时定稿转写文本;后者是 MediaRecorder 的能量检测器,用于结束录音(record.autoSubmit 开启时随即提交)。两者处于不同流水线,不共享任何状态。
音频文件导出
由本地引擎(edge-tts、piper)合成的每次朗读,除了进入浏览器播放用的内存缓存,还会额外写入一个文件。浏览器引擎在客户端合成,因此不导出任何文件 —— 这一状态会明确报告,绝不静默。
- 位置:
tts.export.dir,默认<home>/.dsh-talk/audio(本插件自己的目录)。它刻意不用宿主的工作目录,因此绝不会污染代码仓;相对路径同样按家目录解析。 - 命名:
speech-<YYYYMMDD>T<HHMMSS>Z-<8 位十六进制>.mp3(edge-tts)或.wav(piper)。时间戳为 UTC,故按名排序即按时间排序;后缀取自 utterance id,可回溯到对应的dsh-talk/speech日志条目。朗读文本绝不进入文件名。 - 清理:每次写入都重新收敛,先按时间、再按数量。早于
tts.export.maxAgeMinutes(默认 1440 = 24 小时)的文件被删除,随后从最旧的开始删除,直到目录内不超过tts.export.maxFiles个(默认 20,含刚写入的那个)。清理只删除匹配本插件speech-…命名的文件,共享目录中的其它文件绝不触碰。按默认值,目录最多 20 个文件(数 MB),且不会有超过一天的旧文件。 - 失败:导出是朗读的附加动作,绝不是前提。目录无法创建或写入时,朗读照常播放,结果里报告
audioExport: "failed"及原因;清理失败被忽略,下次写入时重试。 - 模型可见的路径:
speak工具的规范值携带audioPath(以及audioExport,无文件时还有exportNote),渲染给模型的结果文本里也有同一路径 —— 因此 agent 可以直接把它交给需要文件路径的其它工具,例如某个消息插件的文件参数。事件播报(回合完成、审批、出错)走同一条流水线,也同样导出,并受同样的上限约束。 - 把文件交给其它插件:只接受「自己工作区内文件」的消费方 —— 例如
dsh-wechat的send_wechat,除非它自己的allowAnyFilePath打开,否则会拒绝调用会话工作区之外的file_path—— 会拒绝你主目录下的路径。要让这条链路成立,请把tts.export.dir指到该工作区内的目录(例如<workspace>/.dsh-talk-audio);默认值刻意留在所有代码仓之外。
profile patch 覆盖示例:
- insert:
- id: talk
name: dsh-talk
config:
tts:
rate: 10
fallbackToBrowser: false
announce:
onApproval: false
FAQ
音频和转写文本存在哪?会被写进会话日志吗?音频字节只进内存缓存(maxAudioCacheBytes,默认 8 MiB)和本地引擎的导出目录(tts.export.*,默认最多 20 个文件、24 小时),绝不进入模型上下文或会话日志。dsh-talk/speech 事件只在宿主词汇表接受时写入,且只带 utterance id、引擎、原因、大小、脱敏文本与浏览器音色参数。唯一的持久文件路径是 audioPath——它有意交给其它工具复用,因此会出现在 speak 的工具结果里。
不会静默。在 0.1.1-rc.2、0.1.2-alpha 线、0.1.2-rc.1 与 0.1.6-alpha.2 上,宿主词汇表不认识该事件,门控不写入任何内容,客户端的会话级播放列表保持为空;但宿主按会话保留「已追加/已跳过」计数,可由 talk/latest(sessionId) 读回,speak 工具结果仍是可重建的审计痕迹。朗读、麦克风、设置页签与工具本身均不受影响。
不会。工具全部带命名空间、只提供 talk 一个服务键(非内置 seam,也不被任何调研过的高星插件提供)、不注册客户端 slot key、不注册 webServer 前缀、不改全局对象。顺序敏感的 approval/request 监听器一律经 next() 委托,绝不短路链条。逐条论证见「与其他 DSH 插件的互操作性」。
没有额外的模型调用。转写在浏览器或你配置的引擎侧完成;本地 TTS 每次朗读起一个子进程,音频进有界内存缓存,导出写入走二维清理(先按时间、再按数量)。事件播报是 fire-and-forget,approval/request 上只做一次异步朗读、绝不阻塞审批。
安全边界
- 模型可见 ⟺ 已记录 —— 模型只看到 speak 工具的规范值与渲染文本。
dsh-talk/speech事件只在宿主能够承载它时写入(见宿主兼容性);tool/call+tool/result事件始终是可重建的痕迹。 - 审批播报绝不阻塞 ——
approval/request监听器总是调用next()。 - 输出脱敏 —— 凭据与临时音频路径绝不上日志或展示面。唯一的刻意例外是 speak 结果里的导出音频路径:它是插件自有目录中的持久文件(仍经密钥脱敏),用途正是交给其它工具。
- 宿主兼容性 ——
dsh-talk/speech事件经「是/否」门写入:已知词汇表覆盖该事件的宿主直接追加,且调用方可知追加已发生;其余宿主——截至0.1.1-rc.2的全部已发布线、0.1.2-alpha线、0.1.2-rc.1,以及0.1.6-alpha.2(其Session.append只能盖章 surface intent,无法盖章ignorable信封,该字段仅保留用于存量日志读取兼容)——完全不追加,因此语音在这些宿主线上永远不会污染会话日志。跳过不再无声:宿主按会话保留「已追加/已跳过」计数,可由talk/latest(sessionId)读回;客户端会话级播放列表保持为空,speak 工具结果仍是可重建的审计痕迹。 - 失败响亮 —— 非法引擎、越界数值、缺模型/端点的引擎配置在挂载时即报错。
权限与数据
- 权限:插件保留一份内存中、字节受限的音频缓存用于播放,并在使用本地引擎时把合成音频写入自己的受限目录(
tts.export.*,默认<home>/.dsh-talk/audio,默认最多 20 个文件、保留 24 小时);麦克风权限由浏览器管理。设置页签只向 profile 追加 patch 片段(带时间戳备份)——绝不重写文件。 - 数据:音频字节绝不进入模型上下文或会话日志。在宿主会话词表接受的宿主上,
dsh-talk/speech事件携带 utterance id、引擎、原因、大小、脱敏文本,以及适用时的浏览器音色/语速/音高;在无 envelope 的宿主上该事件完全不写。speak工具结果携带导出文件的路径(插件自有目录中的持久产物),其余所有展示/日志面都经过脱敏,凭证、JWT、bearer 头与临时路径都不会出现。 - 网络:只联系你配置的引擎。
edge-tts执行网络合成,FunASR 使用其配置的端点,而 Chrome 的webkitSpeechRecognition会将麦克风音频发送到 Google 服务器进行转写;浏览器的speechSynthesis播放仍在本机完成。
已知限制
- 浏览器支持:Web Speech 与 MediaRecorder 均特性检测;不可用时麦克风按钮自动禁用,配置了宿主引擎(FunASR/whisper.cpp)仍可转写。
- 本地引擎需自行安装:
edge-tts、piper、whisper.cpp可执行文件与模型需单独安装。 - 音频文件需要本地引擎:浏览器语音在客户端合成,因此那里
speak不返回audioPath(结果中会明确说明);本地引擎失败并回退到浏览器时同样没有文件。导出文件位于插件自有的受限目录 —— 位置、命名与清理规则见「音频文件导出」。 - 录音格式:浏览器按其原生 MediaRecorder 编解码器录音;whisper.cpp 可能要求 WAV 录音配置或服务端转换。
- 设置重载生效:设置页签追加到 profile patch;重载 profile(或重启 Web 应用)后生效。
- 无该词汇表的宿主上实时播放历史为空:在
0.1.1-rc.2、0.1.2-alpha线、0.1.2-rc.1与0.1.6-alpha.2上,宿主词汇表不认识dsh-talk/speech,因此门不写入任何内容,客户端的会话级播放列表保持为空;talk/latest(sessionId)仍返回最新一条朗读与「已跳过」计数。朗读本身、麦克风、设置页签与工具均不受影响。 - dsh-talk ≤ 0.2.1 写下的存量日志可能需要在冷加载前修复:版本 0.2.1 及更早会追加未标记的
dsh-talk/speech事件。在0.1.0-rc.7及更新的宿主上,日志已含这些事件的会话,其下一次冷加载会以SessionFormatUnsupportedError失败。修复:停止宿主,备份该会话的.jsonl日志,为每一行"type"为"dsh-talk/speech"的 JSON 记录加上顶层成员"ignorable":true(例如在起始的{之后插入"ignorable":true,),然后重新打开会话。其余内容不变,不会丢失任何数据;本版本的新追加永远不会产生未标记事件。
与其他 DSH 插件的互操作性
已对照 DSH 0.2.0-rc.2(本 README 面向的运行时)与 2026-10-05 实测的高星插件集验证。
本插件不干扰其他插件,包括广泛安装的高星插件:
- 无工具名冲突。 所有工具都带命名空间,不占用任何已被内置工具或其他插件持有的裸名。
- 无服务键冲突。 只提供
talk;该键既不是内置 seam,也没有被任何已调研的高星插件提供。 - 无 slot 冲突。 不注册客户端 slot key,因此不参与
shadows-shipped-ui座位争抢。 - 无 HTTP 路由冲突。 不注册任何
webServer前缀。 - 无 patch 层冲突。 组合包 patch 只
insert自己那一行,从不覆写内置行的config。 - 无全局改写。 不改原型、不改写
process.env、不替换全局 fetch dispatcher。
共享事件监听器在构造上就不互相干扰。 它用 ctx.on() 监听顺序敏感事件 approval/request —— Cordis 的广播语义:每个监听器都会运行,任何一个都无法饿死其他监听器。此处每个监听器都通过 next() 委托,因此链条绝不会被短路;改写作用在 next() 产出的值上,而不是用它顶替返回:
静态证据:dsh-plugin-doctor 的 K10–K13 在本仓全部为 pass。
兼容性
| 方面 | 状态 |
|---|---|
| Harness | DeepSeek Harness dsh-v0.2.1-alpha.1(2026-09-24 适配:peer 第三段 + engines.dsh + manifestVersion: 1,月度 Compat workflow 已锚定该线);门禁链于 2026-09-24 全绿(双 typecheck 尺子、86 个测试、build、self-contained、artifacts、pack)。npm 开发/测试线 0.1.7-rc.2,peers `>=0.1.2-rc.1 <0.2.0 |
| Node | ^22.19.0 || >=24.9.0 |
| 浏览器 | Web Speech + MediaRecorder(Chrome/Edge 最佳);其余场景用宿主侧转写/TTS 引擎 |
生态与质量
- Gitee 镜像(同步镜像,以 GitHub 为准)
- dshfind 插件页(安装量与排名)
- OpenSSF Scorecard 报告(由 .github/workflows/scorecard.yml 驱动)
- dsh-plugin-doctor 验证报告(K10–K13 全
pass) - DSH Market(已上架)
- 1024 商店渠道:先
npm i -g dsh1024,再dsh1024 plugin --profile web add dsh-talk(计入 deepseek1024.com 安装排行) - 生态实测知识库:插件开发指南 · 选型实测数据 · 维护取舍判据
开发
pnpm install # node ^22.19 || >=24.9
pnpm run typecheck # tsc:src + tests,对照本地 harness checkout
pnpm run typecheck:ci # tsc:对照已发布的 0.1.7-alpha.2 类型(无 paths)
pnpm test # vitest:108 个测试、16 个套件
pnpm run build # tsc 声明 + tsdown bundle(lib/)
pnpm run verify:self-contained # 依赖声明全部来自 registry
pnpm run verify:artifacts # 构建产物 ESM 面 + client ModuleLoader 握手
pnpm run check:readmes # 五语 README 一致性
pnpm pack # 发布用 tarball
配图由 scripts/gen-readme-assets.py 程序化生成(Python + Pillow 画 hero,手写 SVG 画结构图),产出 docs/assets/dsh-talk-hero-{zh,en}-{light,dark}.webp 与 docs/assets/dsh-talk-flow-v5-{light,dark}.svg,可重复执行。
Topics:dsh, dsh-plugin, deepseek-harness, deepseek, cordis, voice, speech, tts, stt, speech-to-text, text-to-speech, microphone
家族与致谢
本项目是 PerryLink 维护的 DeepSeek Harness 插件家族成员(95 个,全部 Apache-2.0)。如果这个插件帮到了你,给个 star——它不解锁任何功能,但会让下一个人更容易找到它。
- @PerryLink —— 创建者与维护者:朗读管线、语音引擎、麦克风录音、事件播报、投影单元与五语文档。
- @Mo-Mia —— Web Speech 隐私/兼容性、输入框动作、语言回退、连续听写、
talk:speech投影与浏览器 TTS 设计建议(#2–#9);其中若干条影响了当前的设置面。
License
LICENSE(Apache License 2.0)© 2026 dsh-talk contributors
Apache-2.0 · 社区插件,与 DeepSeek 无关。
No comments yet. Be the first to write one.