dsh-go-local-voice:讓 dsh-go 手機 App 原生界面用 MiMo / 豆包念回覆

前言

DSH 生態裏的 TTS 插件大多跑在 Harness 網頁版,聲音靠瀏覽器的 Web Audio 播。如果目標是手機 App 的 Flutter 原生界面,這些插件根本不會觸發——播放能力和瀏覽器綁在一起了。

27Synthesis/dsh-go-local-voice(中文名「鯨鯨朗讀」)解決的就是這一件事:給 DeepSeek Harness / dsh-go 手機 App 原生界面加語音朗讀,用小米 MiMo TTS 或字節豆包 Seed TTS 合成回覆內容,直接在 App 裏播放。分類屬於模型推理,由 27Synthesis 維護。下面按功能、安裝、配置、用法的順序介紹。

核心能力

先明確它管哪些事,都是 README 裏寫明的可落地能力:

  1. 長按任意一條助手回覆,菜單出現「讀出來」和「存成音頻」;前者直接播放,後者導出 MP3 到手機存儲。
  2. 小米 MiMo 走 mimo-v2.5-tts,內置 8 個音色(冰糖 / 茉莉 / 蘇打 / 白樺 / Mia / Chloe / Milo / Dean),支持語音指令(語氣、方言、情緒)。
  3. 豆包 Seed TTS 走 seed-tts-2.0,聲音復刻走 seed-icl-2.0,使用 HTTP v3 單向流。
  4. 長文一次唸完:回合結束後臺分段預取,點一次交出去的是一整段音頻,不是「點一次念 120 字」。
  5. 「停止」按鈕真的停得住,靠固定播放實例加穩定的 lease。
  6. 輸入框上方有狀態條(正在合成語音… / 朗讀中 0:12 / 1:40),面板裏有進度條。
  7. 導出過的文件在面板裏列出,點一下複製路徑。
  8. 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 後,操作順序只有三步:

  1. 打開 App,發起一次對話。
  2. 回合結束後,插件已在後臺按 140 字切段、併發 3 合成,寫入 data/cache/<sha1>/sNNN.mp3,全齊後拼成 all.mp3。
  3. 長按最新那條回覆,選「讀出來」開始播放,或選「存成音頻」導出。

冷緩存時插件最多等 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「已知限制」列出的:

  1. 不能「回覆結束自動開口」:協議只允許用戶點按觸發播放。
  2. 小米目前沒有語速參數;豆包有 speech_rate,但面板還沒暴露。
  3. 暫停 / 繼續:橋接的 pause 實測不可靠,已移除。
  4. 單次播放音頻上限 16 MiB(約 5.5 分鐘語音),超長回覆會在後臺繼續合成,下次點按接着念。
  5. 豆包那一路沒有 Key 實測過(按 v3 單向流接口寫的),首次使用請先短句試聽。

另外插件在踩坑裏提到幾處實現約束:播放的 instance 必須固定(這裏用 read),每次換實例名會讓 stop 找不到 lease;面板的 select 下拉框不可靠,音色面板改成 list 一行一個,點一下立刻寫設置並刷新;狀態必須走數據綁定({"$": "data.info.status"}),視圖裏寫死文字的話數據每秒推界面也不變;主面板只用 v1 組件,需要輸入框的項放在「朗讀設置」面板(v2)。

排查與自檢

出問題時從這三處入手:

  1. data/debug.log:記錄點按、媒體字節數與耗時、audio.ended/stopped/error、預取情況,不記 Key、不記正文。
  2. data/settings.json:當前配置(權限 0600)。
  3. 插件結構自檢:
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,功能邊界也在已知限制裏寫清楚了,照着做就能用。

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜