前言¶
DeepSeek Harness(简称 dsh)的设计是「一切皆插件」:模型、工具、会话、UI 都可以往运行时上挂。终端里的智能体默认还是打字进出——离开键盘时没法口述一条指令,长任务跑完也只能回来盯一屏日志。社区里已经出现了不少视觉、浏览器类插件,语音这一侧仍然要单独装。
dsh-voice 冲着这两个日常缺口来:把口述音频转成用户消息,再让智能体把回复读出来。它不自己做一套音频管线,而是叠在 dsh 已有的 ctx.shell、ctx.jobs、ctx.settings、ctx.attachments、ctx.conversationEvents 上面。本文按社区目录页、GitHub 仓库 README / README.zh.md、package.json 与 cordis.patch.yml 交叉核对后整理。需要先说明两点:社区插件目录是独立站点,与 DeepSeek / 幻方没有官方从属关系;Harness 本身仍处于 developer preview,后续可能出现不兼容变更。GitHub 上还有其他同名 dsh-voice 仓库,能力边界并不相同,本文只写 Jesse-njx 维护的这一份。
这是什么¶
dsh-voice 是一款「工具与能力」类 DSH 插件,由 Jesse-njx 维护,许可证 MIT,主要语言 TypeScript。仓库 package.json 中的包名是 @dsh-voice/bundle,版本号 0.1.0,要求 Node.js >=20。2026-08-18 核实时,目录页与 GitHub API 均显示 1 星;仓库创建于 2026-08-13,社区目录收录日期 2026-08-14;topic 包括 deepseek-harness、dsh-plugin、stt、tts、voice。
一句话定位:语音笔记进去、朗读答案出来。口述音频会变成用户消息(转写),智能体也可以把回复读出声(合成);长任务、无界面运行还可以留下一句旁白。音频默认是 ~/.dsh/voice/ 下的普通文件,会话日志只保存引用和转写文本。
它解决的不是「对着麦克风实时聊天」。README 把 v0.1 的非目标写得很清楚:不做实时流式对话、不做外呼语音电话、不做唤醒词或常驻监听,也不把原始音频写进会话日志。录音和播放都要等模型显式调用工具,默认不会自己响。
核心功能¶
插件包注册两套工具、一个持久化事件、一个会话开关。Web 客户端再补上音频卡片。cordis.patch.yml 里的插件 id 是 dsh-voice,name 为 @dsh-voice/bundle。
transcribe:口述变成用户消息¶
transcribe({ source, to? }) 做语音转文字。source 必须二选一,不能两个一起传:
{ file: }:转写已经存在的音频文件。{ record: { seconds? } }:从麦克风录一段,默认 5 秒。录音路径要可用:ffmpeg,或 macOS 上捆绑的 swift shim。
转写结果插入为用户消息,不是工具输出。会话里会记一条 voice/note 事件,Web 端渲染成带播放/暂停、时长、后端徽标和转写字幕的音频卡片。工具本身返回紧凑句柄 { transcript, audioRef, backend, durationMs },方便 Code Mode 拿到结构化数据。
如果同时装了 dsh-crosstalk,还可以带 to:,把这条语音便签作为带标签的 peer 消息投到另一路本地会话,并附上音频路径。未安装 crosstalk 时,这个参数不会出现。
speak:后台朗读,不阻塞当前回合¶
speak({ text, voice?, rate? }) 做文字转语音。合成和播放走 ctx.jobs 上的后台任务,kind 为 voice-speak。工具立刻返回 { jobId, audioRef },不卡住当前回合;播放是异步的,失败会注入一条通知,而不是把错误抛进这一轮对话。
每个后端都会先在 audioDir 写下持久化文件,再尽力播放。因为只是 ctx.jobs 上的普通工具,routines 和无界面(headless)运行也可以调用。README 把它同时当作长任务旁白:例如构建结束时报一句「构建完成,0 失败」。旁白并不是另一套 API,就是在 job 上下文里调用 speak。
/voice 与 readReplies¶
会话级开关,用来自动朗读智能体的回复。配置项 readReplies 默认 false;会话里可以用命令即时改:
/voice on
/voice off
/voice status
/voice speak <text>
各条含义如下:
on:开始朗读助手回复。off:停止。status:查看当前开关、后端和audioDir。speak <text>:直接从输入框朗读一行,不必等模型调工具。
开关是当前会话生效,改完不用重装插件。readReplies 只朗读已有回复,不会去录音。
后端怎么选¶
语音转文字由 dsh-voice-backends 模块负责选择:
whisper-local:PATH 上的 whisper.cpp 二进制(也可在配置里指定),经ctx.shell调用,完全离线。openai:走标准凭据通道访问 OpenAI 兼容的whisper-1端点,密钥环境变量是OPENAI_API_KEY。这是唯一会把音频送出本机的 STT 路径,而且只有显式配置才会启用。macos:经捆绑的 swift shim 调用系统SFSpeechRecognizer,同样走ctx.shell。不额外装软件,也不需要网络配置。fake:文本到文本的测试夹具。文件内容是{"transcript": "…"},或文件名形如fixture-.m4a时,转写结果就是那段文本。给 CI 跑通整条工具链,不碰麦克风、不碰网络。
文字转语音:
say(默认):macOS 的say -o --file-format=m4af --data-format=aac,再用afplay播放。零安装,写出 Chrome / Safari 能播的 m4a。piper:本地 Piper 二进制,离线神经 TTS。edge-tts:云端;只有写进配置才会用。fake:写入固定 JSON,让 speak 的产物能再走一遍 fake STT。
选择规则是纯函数,仓库里有单测:配置了哪个后端就用哪个;没配则按离线回退,STT 是 whisper-local → macos,TTS 是 say → piper。云端后端永远不会被自动选中。本机既没有 whisper.cpp / macOS 语音识别,也没有 say / Piper 时,会给出明确报错,告诉你该配什么,而不是悄悄改走 OpenAI 或 edge-tts。
本地优先:文件在磁盘,日志只留引用¶
设计中心是本地优先:
- 除非显式设置
stt.backend: openai或tts.backend: edge-tts,音频不出本机。 - 每个产物都是
audioDir(默认~/.dsh/voice/)下可直接查看、可rm的文件。 - 会话日志只保存一条
voice/note:noteId、回合坐标、audioRef(路径 + mime + durationMs)、transcript、direction: 'in' | 'out'、backend。v0.1 没有更新事件。 - 音频文件缺了或被删掉时,Web 卡片降级成只显示转写文字。回放不需要再读一遍音频文件。
Web 客户端把入站便签(STT)画成用户回合,把出站 speak 画成智能体侧卡片。客户端 bundle 由 scripts/build-client.mjs 打成 lazy-CJS,安装到 web profile 后经 /plugins/@dsh-voice/bundle/client.js 提供。
安装与启用¶
社区目录页给出的安装命令如下,在 DeepSeek Harness 终端中运行即可:
dsh plugin add github:Jesse-njx/dsh-voice
需要可复现安装时,按目录页说明把 commit 哈希钉上:
dsh plugin add github:Jesse-njx/dsh-voice#<commit>
把 <commit> 换成仓库里实际的 commit SHA。
仓库 README 另外写了按 npm 包名、并指定 web profile 的安装方式:
dsh plugin --profile web add @dsh-voice/bundle
截至 2026-08-18,npm registry 上查不到 @dsh-voice/bundle(返回 404),不要把这条当成当前可用的安装方式。安装仍以目录页的 GitHub 命令为准。README 带 --profile web,是因为网页音频卡片要装进 web 客户端;目录页命令本身不带 profile。装完如果对话里看不到音频卡片,需要确认当前用的是带 Web UI 的 profile,而不是只装了 host 工具。
目录页的安全提示仍然适用:插件以当前 dsh 进程的权限运行,安装时可能执行代码。装之前请自己看源码和许可证。package.json 的 prepare / prepublishOnly 会执行 pnpm build。装完可用插件列表确认是否出现 dsh-voice;有的环境需要重启 Harness 后才生效。在模型调用工具之前,录音和播放都不会自己开始。
配置¶
所有字段都可选,写在 profile patch 或 cordis.patch.yml 里。README 给出的完整形状如下:
plugins:
dsh-voice:
stt:
backend: whisper-local | openai | macos | fake
model: whisper-1
whisperLocal: { bin: whisper-cli, model: tiny }
openai: { baseUrl: https://api.openai.com/v1, apiKeyEnv: OPENAI_API_KEY }
tts:
backend: say | piper | edge-tts | fake
voice: Samantha
rate: 180
piper: { bin: piper, model: /path/to/model.onnx }
edgeTts: { voice: en-US-GuyNeural }
readReplies: false
audioDir: ~/.dsh/voice
缺省行为:STT 自动选离线后端(whisper-local → macos),TTS 默认 say(自动回退时是 say → piper),readReplies 为 false,音频目录是 ~/.dsh/voice。openai 的密钥走标准凭据通道 OPENAI_API_KEY,并回退到启动环境变量。仓库自带的 cordis.patch.yml 只插入空的 stt / tts、readReplies: false 和默认 audioDir,具体后端留给使用者自己覆盖。
典型用法¶
仓库没有编造对话脚本,可复现的入口就是工具参数和 /voice 命令。
转写已有文件时,source 只带 file;从麦克风录几秒则只带 record:
{ "source": { "file": "/path/to/note.m4a" } }
{ "source": { "record": { "seconds": 5 } } }
让智能体朗读一段文本:
{ "text": "build finished, 0 failures" }
voice 和 rate 可选,不传就用配置里的默认音色和语速。
会话里要自动读回复,直接输入:
/voice on
看当前后端和目录:
/voice status
推荐顺序是:先确认本机有可用的离线后端(whisper.cpp 或 macOS 语音识别,以及 say 或 Piper),再用 /voice status 核对,最后才让模型调 transcribe / speak。不要一上来就把 stt.backend 设成 openai,除非你明确接受音频离开本机。
适用场景与注意事项¶
比较适合这些情况:
- 暂时离开键盘,想口述一条指令,让转写结果作为普通用户消息进入对话。
- 长构建、无界面任务跑完后,用
speak留一句旁白,不必盯着终端。 - 希望回复被读出来,但默认仍然是安静的:用
/voice on按会话打开。 - 在意音频落在哪:文件都在
~/.dsh/voice/,日志里只有引用和转写。
使用前建议把下面几条当作硬限制,而不是「以后可能会好」:
- 权限与供应链。插件以当前 dsh 进程权限运行,能调 shell、能读写
audioDir、在配置了云端后端时还能把音频送出本机。安装前检查 https://github.com/Jesse-njx/dsh-voice 源码和 MIT 许可证;需要可复现环境时固定 commit。 - 不是实时语音对话。v0.1 明确不做流式对讲、唤醒词、常驻监听、说话人分离、外呼电话,也不把原始音频写入会话日志。
- 默认后端偏 macOS。TTS 默认是系统
say;STT 自动回退链里有macos。Linux 上通常要自己准备 whisper.cpp 和 Piper,否则会得到「该配什么」的错误,而不会自动改走云端。 - 录音有前置条件。
{ record }依赖 ffmpeg,或 macOS 上的捆绑 swift shim。没有录音路径时,这条分支不可用,但仍可用{ file }转写已有音频。 - 云端是显式选择。
openai和edge-tts只有写进配置才会启用。装完插件不等于已经把语音送到了第三方。 - 网页卡片依赖 web profile。host 侧工具和
/voice命令与 Web 音频卡片不是同一半区。只在无 UI 的 profile 里装,看不到卡片是符合 README 描述的。 - 项目很新。仓库 2026-08-13 才创建,版本
0.1.0,星标很少。README 写测试套件有 46 个用例,覆盖参数 schema、后端选择、fake 端到端和卡片降级,但不要把它当成生产级语音套件。
小结¶
dsh-voice 给 DeepSeek Harness 补上了一层很薄的语音进出:transcribe 把口述变成用户消息,speak 在后台朗读且不阻塞回合,/voice 按会话决定要不要自动读回复。音频是磁盘上的普通文件,日志只留引用;云端后端不会被自动选中。它不是实时对讲机器人,也不是微信语音方案;边界写在 README 的非目标列表里,用的时候按「工具调用才出声、本地文件可删除」来预期,会比较稳。
- 目录页:https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-voice/
- GitHub:https://github.com/Jesse-njx/dsh-voice
- DeepSeek Harness:https://github.com/deepseek-ai/deepseek-harness