talkback — 聽取而非閱讀 是做什麼的?
從任何專案目錄中朗讀文字、儲存音訊,且絕不無聲耗盡計量配額。
預設為按需啟用。 它只在被要求時才會說話——使用者說「大聲解釋一下」,或直接執行腳本。它不會主動敘述,直到有人開啟對話模式,而對話模式一次只屬於一個工作階段。
如何使用
S=~/.claude/skills/talkback/scripts
$S/talkback.py "以下是變更的內容及其重要性。" # ElevenLabs(預設)
$S/talkback.py --fast "隨意一句話。" # 本地、即時、免費
$S/talkback.py --quota # 查詢剩餘配額
$S/talkback.py --voices # 列出可用語音
$S/talkback.py --dry-run "..." # 查看將朗讀的文字,不合成
$S/talkback.py --help # 所有參數
$S/talkback-hook.py --on # 開啟對話模式(僅限此工作階段)
$S/talkback-hook.py --off # 停止說話
$S/talkback-hook.py --status # 查看誰在說話、掛鉤是否已註冊
$S/talkback-hook.py --outputs # 列出此主機的音訊輸出裝置
文字也可以透過管道傳入:git log -1 --format=%B | $S/talkback.py。
代理應如何使用
此技能包含兩個不同的功能。單次朗讀是將組合好的散文傳遞給 talkback.py。對話模式是一個持續設定,透過 talkback-hook.py 切換;你無需組合其文字,掛鉤會讀取當前回合的內容。
單次朗讀
當使用者要求聽取解釋時,為聽覺而寫,然後朗讀出來。 不要將原始的 Markdown 或 diff 直接傳遞給工具。撰寫兩到五句簡潔的口語散文——講述變更的內容、原因以及對他們的意義——然後將其傳入。聽眾無法回滾內容,因此請先說明結論。
預設使用優質語音——方案通常負擔得起。當文字為隨意內容或希望零網路延遲時,使用 --fast。如果發生後備,請說明使用了哪個後端。
驅動對話模式
使用者說的話對應一個命令。直接執行它;不要手動敘述變更,因為掛鉤會朗讀你正在寫的回合內容。
| 使用者說 | 執行 |
|---|---|
| "開啟對話模式"、"繼續說"、"朗讀這個工作階段" | talkback-hook.py --on |
| "停止說話"、"靜音"、"安靜" | talkback-hook.py --off |
| "只要重點"、"少一點細節" | talkback-hook.py --on brief |
| "只告訴我重要部分" | talkback-hook.py --on marker |
| "使用我的 AirPods"、"在 X 上播放" | talkback-hook.py --outputs,然後 --on --output "<名稱>" |
| "使用便宜的語音"、"停止消耗配額" | talkback-hook.py --on --backend say |
| "它開啟了嗎?"、"為什麼我聽不到聲音?" | talkback-hook.py --status |
| "有其他東西在說話嗎?" | talkback-hook.py --sessions |
| "讓所有東西靜音"(所有工作階段) | talkback-hook.py --off --all |
在告訴使用者它已生效之前,需要檢查三件事:
--status會報告掛鉤是否已註冊。 如果未註冊,請執行--install並告知他們需要重新啟動。- 如果
--status警告掛鉤是在此工作階段開始後才註冊的,則對話模式已開啟,但在此工作階段中不會發出聲音。請明確告知——不要讓使用者發現異常的靜默。 - 對話模式不會跨工作階段保留。 重新啟動或執行
/clear後會再次關閉,需要--on。這是設計特性而非錯誤;請說一次,不要讓他們重複詢問。
在 marker 模式下——以及任何你希望回讀內容是為聽覺撰寫的摘要而非回合本身時——請以標記結束訊息:
<!-- talkback: 重構了認證層,三個呼叫點,測試通過。 -->
後端
| 後端 | 費用 | 品質 | 備註 |
|---|---|---|---|
elevenlabs(預設) | 計量 | 最佳 | 配額保護,自動後備至 say |
say(--fast) | 免費、無限 | 尚可 | macOS 原生,約即時,無網路需求 |
omnivoice | 免費、無限 | 良好 | 本地 + 私人;需後端啟動。未驗證——詳見下方 |
備援順序
elevenlabs → omnivoice → say,最優優先。無法處理任務的層級——無金鑰、配額已達保留額度、本地伺服器離線、合成錯誤——會將任務傳遞給下一層,因此語音品質會逐漸下降,而非完全無聲。TALKBACK_CHAIN 可重新排序此順序。
明確指定某個層級時,會從該層級開始並僅向下搜尋:--fast 表示「使用本地」,且絕不向上回到計量後端。每次後備都會在 stderr 上列印原因,且所選後端會記錄到帳本中,因此品質下降的情況絕不會無聲地被忽略。
--strict 會將任何後備行為轉為硬性失敗(exit 1),適用於不允許品質無聲下降的腳本。
配額
帳戶為 Creator 方案:每月 130,958 字元。兩分鐘的口語解釋約為 1,500 字元,因此每月約可進行 87 次——這使得優質語音可以成為預設選項,而非偶爾為之的獎勵。
在使用點即時驗證,絕不依賴記憶——talkback.py --quota 會即時讀取配額。方案等級已經變更過一次,文件中記錄的數字在方案變動後即過時。
在合成之前,工具會讀取即時配額並保留 250 字元的緩衝,因此一次較長的解釋絕不會完全耗盡餘額。如果請求無法滿足,它會在 stderr 上發出警告,並改用 say。
憑證解析順序:$ELEVENLABS_API_KEY → ~/.elevenlabs/api_key(由 elevenlabs auth login 寫入)→ ~/broomva/.env.local 中的 ELEVENLABS_API_KEY。此機器上有兩組不同的金鑰,但它們解析到同一個帳戶,因此檢查其中一個即等同於檢查另一個。
Creator 方案還解鎖了即時與專業語音克隆(30 個語音槽位,1 個專業)。--voices 可列出帳戶目前可使用的語音。
@elevenlabs/cli套件不在執行時使用,也無法執行此功能——它的所有功能都是auth · agents · tools · tests · components,用於管理託管的 ConvAI 代理專案。它沒有合成命令。此 CLI 在此僅用於auth login,該命令會寫入金鑰檔案。
OmniVoice 層級未驗證
omnivoise 後端是根據文件描述的結構實作的,但從未實際測試過——此功能發佈時本地後端處於關閉狀態。它會乾淨地降級(後備至 say,或在 --strict 下失敗)。若要啟用它,請參閱 omnivoise 技能;程式碼庫已位於 ~/broomva/external/OmniVoice-Studio。
口語文字處理
代理的散文並非為聽取而寫,因此會先進行文字預處理:
- 程式碼區塊 →
(程式碼省略);URL →(連結) - 標題和項目符號轉換為句子斷點,因此清單不會連在一起
- 深層路徑縮短為基本名稱(
src/lib/engine.py→engine.py);使用--keep-paths可聽到完整路徑 - Markdown 強調僅在界定範圍時被移除——在
backend_chain內部的裸底線會保留,因為移除它會將一個聽眾可能搜尋的符號轉變為無法搜尋的內容
儲存的音訊
每段語音都會儲存至 ~/.talkback/audio/,格式為 mp3(當 ffmpeg 存在時轉換),檔案名稱包含時間戳記和 Slug,並附加到 ~/.talkback/ledger.jsonl。使用 --no-save 可丟棄音訊,使用 --out-dir 可重定向儲存目錄。
對話模式——隨工作階段進行朗讀
對話模式會讓代理在每個回合結束時朗讀簡短的回讀內容,持續整個工作階段。它預設為關閉,需要手動開啟,且屬於單一工作階段。
$S/talkback-hook.py --on # 完整細節,每個回合,此工作階段
$S/talkback-hook.py --on brief # 僅開頭幾行
$S/talkback-hook.py --on marker # 僅包含標記的回合
$S/talkback-hook.py --on --backend elevenlabs # 優質語音,按量計費
$S/talkback-hook.py --on --output "AirPods Pro" # 此工作階段使用的揚聲器
$S/talkback-hook.py --off # 停止說話(此工作階段)
$S/talkback-hook.py --off --all # 全部靜音,所有位置
$S/talkback-hook.py --status # 模式、後端、還有誰在說話
$S/talkback-hook.py --sessions # 每個目前正在說話的工作階段
為什麼以工作階段為範圍
Stop 掛鉤是全局且永久註冊的——一個在工作階段中無法切換的開關不是真正的開關。因此掛鉤會在每個工作階段中觸發,而旗標才是範圍界定:對話模式是工作階段的屬性,而非機器的屬性。
旗標是 ~/.talkback/sessions/<工作階段ID>;從未選擇加入的工作階段沒有此檔案,因此掛鉤會以 exit 0 退出,不發出任何聲音。這正是六個平行代理在六個工作目錄中保持靜默,而只有你正在觀看的那個工作階段說話的原因。0.3.0 之前的設計使用單一的機器範圍旗標,這也是它從未被開啟的原因:啟用它會使盒子上的所有代理同時發出聲音。
工作階段 ID 從 CLAUDE_CODE_SESSION_ID 或 CLAUDE_SESSION_ID 讀取;--session <id> 可明確設定。當兩者都未回應時,命令會印出 pass --session <id> 並以 exit 2 退出,而非猜測——之前它會後備到工作目錄中最新的記錄檔,這意味著如果一個目錄中有兩個工作階段,--on 會啟用最近被存取的那個工作階段,而非發出請求的那個。
在掛鉤端,有效負載中的 session_id 是權威的:當它存在時,它是閘門授權的唯一金鑰。僅在該欄位不存在時才使用記錄檔名稱的主幹。之前兩者會同時被接受,這使得一個命名工作階段 A 卻使用工作階段 B 記錄檔的有效負載會解析 B 的選擇加入,並在 A 中說話。
--session <id> 針對另一個工作階段是故意的,且這不是安全邊界。 工作階段 A 執行 talkback-hook.py --session B --off 可以在未經 B 同意的情況下啟用或停用 B 的對話模式——沒有所有權檢查來將旗標與設定它的工作階段綁定。這是一個特性,刻意保留而非還原:盒子上的每個工作階段都以相同的作業系統使用者執行,因此任何工作階段都可以直接在 ~/.talkback/sessions/ 下寫入另一個工作階段的旗標檔案。CLI 內部的所有權檢查只會阻止透過旗標的禮貌路徑,而非直接路徑——相同主體的邊界無法阻止共享該主體的寫入者。旗標的作用範圍是音訊,因此平行代理預設保持靜默;它並不作用於控制,此處的任何內容都不應被如此解讀。
細節層級
| 模式 | 朗讀內容 |
|---|---|
full(工作階段預設) | 整個回合,每個回合——當代理撰寫了標記時,從標記開始 |
brief | 長度超過 TALKBACK_HOOK_MIN_CHARS(80)的回合的開頭 TALKBACK_HOOK_CHARS(320);標記存在時優先使用標記 |
marker | 僅包含 <!-- talkback: … --> 標記的回合,且僅朗讀標記文字 |
off | 永不——由 --off 寫入,當全局旗標會重新啟用該工作階段時 |
full 是預設模式,因為回讀的重點是能夠離開並回來知道發生了什麼。截取的摘要是答案的預覽,而非答案本身——你仍然需要閱讀螢幕,而這正是對話模式要避免的事情。被省略的只有完全無法聽到的內容(程式碼區塊、URL、深層路徑——請參閱口語文字處理),而非僅僅是較長的內容。full 也沒有長度下限:「完成,測試通過」是你離開時想要聽到的結果,而非噪音。
brief 和 marker 適用於長時間無人值守的流程,你希望了解進度的輪廓而非完整記錄。
TALKBACK_FULL_MAX_CHARS 為 full 設定上限,預設為關閉——上限會將 full 在恰好值得完整聽取的回合處恢復為 brief。
回讀使用完整的後端備援順序——首先是 ElevenLabs,僅在某一層級無法使用時才下降。因此每個回合的完整細節會消耗實際的配額:一個 1,500 字元的回合約為 Creator 方案每月餘額的 1%,因此長時間的工作階段會達到保留配額,屆時它會繼續使用下一層級說話,而非保持靜默。--on --backend say 會將其固定在低成本層級,--on brief 則會縮短內容。
當一次回讀與下一次重疊時
一次完整的回讀可能在下一回合結束時仍在播放。TALKBACK_ON_OVERLAP 決定發生什麼:
| 值 | 行為 |
|---|---|
interrupt(預設) | 新回讀會中斷舊回讀——最新的狀態是最值得聽取的 |
queue | 它們按順序播放,因此較長的詳細回讀不會被截斷;你可能會落後,但會聽到所有內容 |
queue 適用於你打算之後完整回聽的無人值守流程;interrupt 則適用於你正在機器旁並希望獲得最新資訊的情況。
標記
要選擇單一回合加入——在 marker 模式下,或要朗讀為聽覺撰寫的摘要而非訊息的開頭幾行——請以以下內容結束訊息:
<!-- 對話回饋:重構了認證層,三個呼叫點,測試通過。 -->
在 full 模式下,標記會成為頭條:它先被朗讀,然後是整個回合,因此回讀會先說明結論,同時保留細節。在 brief 模式下,標記取代摘要,而沒有標記的回合會回到開頭句子,並在句子邊界處截斷。
使用哪個揚聲器
音訊從執行 Claude Code 的機器發出。你從手機、平板或另一台筆記型電腦驅動的工作階段仍會在主機上發出聲音,因此重要的槓桿是選擇它要從主機的哪個輸出裝置播放——與 Mac 配對的 AirPods、AirPlay 揚聲器、顯示器、內建揚聲器。
$S/talkback-hook.py --outputs # 查看此主機可播放的裝置
$S/talkback-hook.py --on --output "AirPods Pro"
$S/talkback.py -d "MacBook Pro Speakers" "在指定裝置上播放的一行內容"
輸出設定以工作階段為單位儲存...