前言¶
DSH 生态里的 TTS 插件大多跑在 Harness 网页版,声音靠浏览器的 Web Audio 播。如果目标是手机 App 的 Flutter 原生界面,这些插件根本不会触发——播放能力和浏览器绑在一起了。
27Synthesis/dsh-go-local-voice(中文名「鲸鲸朗读」)解决的就是这一件事:给 DeepSeek Harness / dsh-go 手机 App 原生界面加语音朗读,用小米 MiMo TTS 或字节豆包 Seed TTS 合成回复内容,直接在 App 里播放。分类属于模型推理,由 27Synthesis 维护。下面按功能、安装、配置、用法的顺序介绍。
核心能力¶
先明确它管哪些事,都是 README 里写明的可落地能力:
- 长按任意一条助手回复,菜单出现「读出来」和「存成音频」;前者直接播放,后者导出 MP3 到手机存储。
- 小米 MiMo 走
mimo-v2.5-tts,内置 8 个音色(冰糖 / 茉莉 / 苏打 / 白桦 / Mia / Chloe / Milo / Dean),支持语音指令(语气、方言、情绪)。 - 豆包 Seed TTS 走
seed-tts-2.0,声音复刻走seed-icl-2.0,使用 HTTP v3 单向流。 - 长文一次念完:回合结束后台分段预取,点一次交出去的是一整段音频,不是「点一次念 120 字」。
- 「停止」按钮真的停得住,靠固定播放实例加稳定的 lease。
- 输入框上方有状态条(
正在合成语音…/朗读中 0:12 / 1:40),面板里有进度条。 - 导出过的文件在面板里列出,点一下复制路径。
- Markdown 清洗:代码块、链接、表格、行内代码都不会被念出来,链接读作「链接」。
安装与启用¶
插件目录放在 $DSH_HOME/dsh-go/plugins/ 下(DSH_HOME 默认 ~/.dsh)。下面两条命令分别克隆目录、注册插件。
git clone https://github.com/27Synthesis/dsh-go-local-voice.git \
"$HOME/.dsh/dsh-go/plugins/local-voice"
dsh plugin --profile web add "$HOME/.dsh/dsh-go/plugins/local-voice"
装完需要在 App 里重启一次引擎(设置 → 引擎 → 重启引擎),之后才能看到入口:
- 长按助手消息,菜单里出现「读出来」「存成音频」;
- 输入框上方「插件」胶囊里点「鲸鲸朗读」。
卸载则反过来:
dsh plugin --profile web remove local-voice
再删掉本地目录即可。
配置¶
第一次使用要先在主面板点「打开设置」,填入 API Key。各字段如下表。
| 项 | 说明 |
|---|---|
| 小米 Key | 在小米 MiMo 控制台申请,形如 sk-... |
| 豆包 Key | 在豆包语音控制台申请,标准音色与 Agent Plan 都支持 |
| 豆包音色 ID | 形如 zh_female_...;用声音复刻时填复刻音色 ID |
| 每段字数 | 默认 140。约 0.08 秒/字,140 字≈11 秒,稳在单次 30 秒的生产预算内 |
| 自动预取 | 默认开启。回复一结束就在后台把整条合成好,之后点「读出来」几乎秒播 |
Key 只保存在本机 data/settings.json(权限 0600),不会进仓库、不会出现在界面、不会发给模型;data/ 已在 .gitignore 中。
典型用法¶
装好并配好 Key 后,操作顺序只有三步:
- 打开 App,发起一次对话。
- 回合结束后,插件已在后台按 140 字切段、并发 3 合成,写入
data/cache/<sha1>/sNNN.mp3,全齐后拼成all.mp3。 - 长按最新那条回复,选「读出来」开始播放,或选「存成音频」导出。
冷缓存时插件最多等 15 秒,先交已经合成好的前几段(约 30~60 秒语音),剩下的后台继续;10 分钟内再点一次会从上次停的地方接着念。状态条和面板进度条会同步显示进度。
实现要点¶
给想改代码的人:dsh-go 原生插件的桥接协议(v1.2)有三条硬约束决定了实现方式。一是 media 路由必须在 30 秒内产出字节(CAP_LIMITS.mediaMs),这是生产预算,不是音频时长上限;二是音频本身可以很长(≤16 MiB),App 拿到整段自己播,play / stop 作用在同一个 instance 上;三是插件不能主动推播放效果,onCapability 的返回值不会被执行成效果,所以「播完自动接下一段」做不到。
据此,插件把工作拆成预取和播放两段:session/event 的 turn/end(completed)时切段并发合成到缓存目录;用户点按后 read 只建任务并立刻返回 {type:'audio', op:'play', instance:'read', src:'media:speech', key},App 回来 GET 媒体路由时,插件把缓存拼成一个整段 MP3(MP3 裸帧直拼合法,不需要 ID3 头)。
关于段长取 140:README 给出实测数据(小米 MiMo v2.5 TTS,非流式 MP3,手机网络),15 字≈2.4 s、60 字≈4.7 s、120 字≈8.3 s、240 字≈38 s(超预算)、400 字≈32.6 s、800 字≈54.9 s。为保证单段稳定落在 30 秒预算内,才把默认段长定为 140 字。
已知限制¶
使用前需要知道这些边界,都是 README「已知限制」列出的:
- 不能「回复结束自动开口」:协议只允许用户点按触发播放。
- 小米目前没有语速参数;豆包有
speech_rate,但面板还没暴露。 - 暂停 / 继续:桥接的
pause实测不可靠,已移除。 - 单次播放音频上限 16 MiB(约 5.5 分钟语音),超长回复会在后台继续合成,下次点按接着念。
- 豆包那一路没有 Key 实测过(按 v3 单向流接口写的),首次使用请先短句试听。
另外插件在踩坑里提到几处实现约束:播放的 instance 必须固定(这里用 read),每次换实例名会让 stop 找不到 lease;面板的 select 下拉框不可靠,音色面板改成 list 一行一个,点一下立刻写设置并刷新;状态必须走数据绑定({"$": "data.info.status"}),视图里写死文字的话数据每秒推界面也不变;主面板只用 v1 组件,需要输入框的项放在「朗读设置」面板(v2)。
排查与自检¶
出问题时从这三处入手:
data/debug.log:记录点按、媒体字节数与耗时、audio.ended/stopped/error、预取情况,不记 Key、不记正文。data/settings.json:当前配置(权限 0600)。- 插件结构自检:
node $DSH_HOME/skills/dsh-go-plugin/kit/bin/check.mjs \
<插件目录> --load
适用场景与注意¶
适合想在 dsh-go 手机原生界面里给助手回复加语音、不希望依赖浏览器 Web Audio 的人;不适合需要自动朗读或暂停继续的场景。
两点提醒:这个插件会以当前 dsh 进程的权限运行,安装前应先读一遍源码(仓库地址见下)并确认许可证;插件格式与桥接能力来自 deepseek-harness 的 dsh-go 插件协议,思路和小米/豆包接口细节参考了网页版项目 dsh-xiaomi-tts(MIT),本仓库是面向 dsh-go 原生界面的独立实现。
目录页:https://www.skillhub.cn/plugins/27Synthesis/dsh-go-local-voice
源码:https://github.com/27Synthesis/dsh-go-local-voice
许可证:MIT(仓库版本 1.0.0,engines.dsh 要求 >=0.2.1-rc.2 <0.3.0-0)。
小结¶
dsh-go-local-voice 的价值很集中:在 DSH 手机原生界面里提供一条真正出声的 TTS 路径,覆盖小米 MiMo 和豆包两路,且把长文、停止、状态、导出和 Markdown 清洗这些细节都处理掉了。安装命令和配置字段都来自 README,功能边界也在已知限制里写清楚了,照着做就能用。