前言¶
DeepSeek Harness(DSH)的理念是一切皆插件,agent 能做什麼取決於裝了哪些插件。但跑長任務時有個常見的不便:agent 的產出都在屏幕上——任務完成了、卡在某個決定上了、或者它覺得有件事值得說,你並不知道,除非一直盯着窗口。
常規的 TTS 方案是單向朗讀:打開開關,agent 的輸出被念出來,人是被動接收的一方。dsh-voice-call 把這個關係倒了過來:agent 擁有一個自己的聲音,它決定什麼時候開口,用 offer_call 給你打一個電話;你握着接聽鍵——接聽、拒接、稍後再說,不接就絕不播放。下面介紹它的能力、安裝步驟和用法。
這是什麼¶
dsh-voice-call 是 PandaPolo 維護的 DSH 插件,MIT 許可證,Fork 自 Jesse-njx/dsh-voice,在其基礎上新增了通話域、crispasr 合成後端與本地播放,並針對 rc.6 的插件事件與後臺任務限制做了修復。
兩個設計點先說清楚:
1、本地優先,可完全離線。語音合成跑在本機 CrispASR + Qwen3-TTS GGUF 引擎上,產物是 ~/.dsh/voice/ 目錄下的普通音頻文件。
2、任何音頻行爲都不會自動運行。要出聲,要麼是模型自己調用了工具,要麼是你接聽了一次來電。
核心功能¶
offer_call:由 agent 發起的通話¶
工具簽名是 offer_call({ text, voice? })。調用後來電開始振鈴,人類應答:接聽,則由後臺任務合成並播放;拒接或選擇稍後再說,工具會把決定作爲結果返回給 agent,由它決定接下來怎麼處理。
說與不說、說什麼、用哪個音色,由 agent 決定;是否真的有聲音從揚聲器出來,由你決定。
來電卡片(v0.2)¶
配置 callMode: card 後,來電以浮層卡片形式振鈴:雙環脈衝動畫、來電者身份、想說的話預覽;振鈴超時自動記爲 missed,不會無限掛着。沒有網頁客戶端連接時,自動回落爲彈窗詢問。
speak:直接朗讀¶
speak({ text, voice?, rate? }) 由後臺任務直接朗讀,真實本地播放:Windows 用 PowerShell SoundPlayer,macOS 用 afplay,Linux 用 aplay。語速用 rate 控制。
transcribe:語音轉文字¶
transcribe({ source, to? }) 把語音轉成文字,作爲用戶消息進入會話。後端可選 whisper-local / openai / macOS 原生。to 參數可以把轉寫結果跨會話投遞,需要 dsh-crosstalk 插件。
限制:錄音入口 transcribe({ record }) 僅 macOS 支持,Windows 與 Linux 調用會明確報錯。
/voice 命令¶
會話內輸入 /voice 可以查詢狀態、用 on|off 切換朗讀回覆開關、用 speak <text> 讓它立刻說一句。朗讀回覆默認關閉。
內置音色與合成後端¶
crispasr 後端內置 9 個 Qwen3-TTS CustomVoice 音色,其中 2 個是中文方言:aiden、dylan(北京話)、eric(四川話)、ono_anna、ryan、serena、sohee、uncle_fu、vivian。
合成後端不止一個:edge-tts 只合成不播放;fake 用於沒有模型時的聯調。
安裝與啓用¶
前置要求:
- Node.js ≥ 20(插件運行要求;運行本項目測試需要 22.18+)
- pnpm 9+
- dsh CLI:
@deepseek-ai/dsh0.1.2-rc.1 - CrispASR ≥ 0.8.28 與 Qwen3-TTS GGUF 模型——沒有這組引擎和模型,就沒有本地合成音色
- 已配置可用的 LLM API 憑據(agent 本身依賴)
1、安裝插件:
dsh plugin --profile web add dsh-voice-call
插件已發佈到 npm(dsh-voice-call@0.2.0),上述命令從 npm 拉取。
2、寫配置。在 profile(默認 web)目錄的 cordis.patch.yml 中,按 id 更新 dsh-voice-call 這一項。注意:同一個 id 只能出現一次,重複 insert 會導致啓動崩潰。
Windows 示例(來自插件 README,路徑按實際目錄改):
- id: dsh-voice-call
config:
tts:
backend: crispasr # 本地神經 TTS 引擎
voice: dylan # 默認音色,取內置 9 音色之一
crispasr:
bin: D:\crispasr\crispasr.exe
model: D:\crispasr\models\qwen3-tts-12hz-0.6b-customvoice-q8_0.gguf
codec: D:\crispasr\models\qwen3-tts-tokenizer-12hz-q8_0.gguf
callMode: card
readReplies: false # 朗讀回覆開關
durableEvents: false # rc.6 上必須保持 false
audioDir: ~/.dsh/voice
tts.crispasr 下的 bin、model、codec 分別是引擎可執行文件與兩個 GGUF 模型的絕對路徑;durableEvents 控制會話事件日誌,默認關閉,在 rc.6 上必須保持 false,否則會話歷史可能無法繼續加載;audioDir 是音頻輸出目錄,默認 ~/.dsh/voice。
3、重啓 dsh web。經過上面的步驟,打開一個會話就可以驗證了。
典型用法¶
1、查狀態。會話內輸入:
/voice
應顯示 stt / tts / readReplies / audioDir 狀態。
2、朗讀測試。讓 agent:「用 speak 工具說‘你好’。」聽到聲音即成功。
3、通話測試。告訴 agent:「你有 offer_call 工具——有什麼值得說的就打電話給我。」來電後點接聽,聲音從揚聲器播出。
4、排查配置。執行:
dsh --profile web --dump-config
查看合成後的完整配置樹,確認插件的配置是否真的生效。
適用場景與注意事項¶
適合誰:
- 跑長任務、希望 agent 在值得開口時主動打給你的人;
- 想要本地、可離線的 TTS 鏈路,產物是普通音頻文件的人;
- 在 macOS 上想用語音給 agent 發消息的人——轉寫結果會作爲用戶消息進入會話。
注意事項:
1、插件以當前 dsh 進程的權限運行。安裝任何第三方插件前,建議先檢查源碼與許可證;本項目爲 MIT。
2、rc.6 上 durableEvents 必須保持 false。
3、cordis.patch.yml 中同一個 id 只能出現一次,重複插入會導致啓動崩潰。
4、沒有 CrispASR + Qwen3-TTS GGUF 模型就沒有本地合成音色;還沒有模型時可以先用 fake 後端聯調。
5、錄音(transcribe({ record }))僅 macOS 支持。
小結¶
dsh-voice-call 把「agent 發聲」做成了雙方各有控制權的交互:agent 有撥號權,自己決定何時來電、說什麼;你有接聽權,不接就沒有聲音。加上本地合成、離線可用、產物是普通音頻文件,整條鏈路都留在自己的機器上。安裝一條命令,配置集中在 cordis.patch.yml,按上文步驟即可跑通。