2026 年 5 月 30 日 · 6 min read

做 Plaudio

開源

今天我上架了一個叫 Plaudio 的小工具。它是為 Plaud Note 系列錄音機而寫的「聲紋庫優先」說話人辨識器,用 Python 寫成,採 AGPL-3.0 授權,只跑在 macOS 上的 Apple Silicon。今天早上 v0.1.0 已經放上 PyPI。

這完全是個個人專案。我做它,是為了我自己平常開的那種會議。我用的錄音機是 Plaud Note Pro。我對逐字稿的要求高到一個程度:「誰說的」跟「說了什麼」一樣重要。我參與的會議通常有四到七個人,其中不少是同事,他們的聲音對一個從沒「認識」過他們的說話人分離模型來說,相當難以區分。

正是這最後一點,現有的工具在我這裡失靈了。

群集合併的問題

現行的本地說話人分離流程(pyannote-audio 3.1 的預設模式)會先把說話人分成幾群,再為每群貼上標籤。如果聲音差別夠大,這套方法很有效。但當會議裡有四個人的音高、口音、語氣都相近時,pyannote 經常會把這四人合併成同一個 SPEAKER 群。模型很有把握。輸出也篤定地錯了。所有發言都被歸到這四人中的某一個身上,其餘三人從逐字稿裡消失。

五月底某次會議裡,我正巧撞上這個失敗模式。四位資深同事、相近的語氣、節奏很快的對話。經過分離之後的逐字稿,其中三人完全消失了,他們說的話全部被歸到第四人身上。乍看之下沒有破綻。直到我憑當時的記憶回頭對讀,才意識到那些標籤一直在騙我。

錯得很有把握的逐字稿,比沒有逐字稿還糟。它是一份你可能會引用、轉述、或分享出去的東西。

你可能會把某個決定歸到一個並未做出該決定的人頭上。從隱私的角度來說沒問題;從錯誤歸屬的角度來說,就不行了。

另一條路

Plaudio 的做法不一樣。它不去先分群再把整群配對到 profile,而是逐窗匹配

你為每位經常出現的說話人「登錄」一次:給一段乾淨的 30 秒音檔,並徵得對方同意。登錄時 pyannote 的分離流程會吐出一段 256 維的嵌入向量。Plaudio 把它寫進本地的 JSON 檔,權限設為 0600。

處理新會議時,Plaudio 用 2 秒視窗、1 秒步幅在音訊上滑動。每個視窗交給 pyannote 算一段嵌入,再分別跟所有已登錄的 profile 算餘弦相似度。每個視窗自己挑出匹配的對象。連續同標籤的視窗合併成一段,再把這些段映射回原本的逐字稿區段。

相似的聲音再也不會被合併成同一群,因為這條流程裡根本沒有分群這一步。每個視窗各自決定。沒匹配上的視窗就停在 Unknown,這對於還沒登錄過的人來說,正是該有的答案。

實作不大,幾百行 Python,但實際效果很大。同樣那場四位同事的會議,現在標得對了,因為這四人早就在我的聲紋庫裡。

v0.1 包含什麼

從音訊出發的這半邊管線:

plaudio transcribe meeting.mp3
plaudio match meeting.mp3 meeting.plaud.json --threshold 0.55
plaudio label meeting.mp3 meeting.plaud.json --enrol      # 起步:聲紋庫還是空的時候用
plaudio enrol clean-30s-clip.mp3 --name "Alice Smith" --start 0 --end 30
plaudio db ingest meeting.plaud.json --meeting-id 2026-05-30-team
plaudio db search "deadline" --speaker "Alice Smith"

這幾個指令裡最容易被忽略的是 plaudio label。第一次跑 Plaudio 的時候,你的聲紋庫是空的,所以每個群最後都會被標成 Unknown,根本沒有 profile 可以拿去匹配。互動式標記介面就是為這個情境做的:它從每個未知的群裡挑出最長的一段乾淨獨白,背景播放給你聽,請你輸入名字,把標籤寫回逐字稿,並在你加上 --enrol 的時候,順手把這個聲紋也登錄進庫裡。大概跑過十場左右的會議之後,常出現的人就都進庫了,這個互動步驟也自然就不需要了。如果你早就知道誰是誰,還可以用 --batch-label "SPEAKER_00=Alice,SPEAKER_01=Bob" 這個形式,完全跳過音訊播放。

另外還有一個聲紋庫管理工具(plaudio voicebank list / export / import / migrate / remove),以及一個 doctor 指令,會直接告訴你缺哪一個依賴。

技術棧上,ASR 用的是 mlx-whisper(跑在 Neural Engine 與 GPU 上,在 M 系列晶片上大約有六倍即時的速度),嵌入模型來自 pyannote-audio 3.1,可搜尋的語料庫則是 SQLite 加上 FTS5 的 trigram 分詞器。trigram 分詞器是讓中英文混雜搜尋能順利運作的關鍵,這對我參與的會議很重要。

Plaud 雲端同步留在 v0.2 再做。完整的 README技術棧理由 都放在 GitHub 上。

從這個過程中我學到的

三個觀察。

第一,先為自己做,是一種紀律,不是退而求其次的選擇。我從一開始就有清楚的測試情境(我自己開的會議)。我知道什麼叫做成功(正確的名字出現在逐字稿裡)。我是第一個使用者、第一個 bug 回報者,也是第一個受益者。發佈開源專案最大的誘惑就是太早把範圍擴大。紀律就是先把窄窄的事情做好,要不要擴大,留給後續別人感不感興趣去回答。

第二,審查關卡比程式碼本身更重要。我從工作的 vault 裡自動生出一份私人詞表:同事的姓氏、計劃代號、內部縮寫。每一次 commit、每一次 CI build 都會把差異拿去比對這份詞表。pre-commit hook 是強制執行的;我給自己定了規矩:不准用 --no-verify 繞過。在我寫任何一行 Plaudio 程式碼之前,我就先把這套審查工具搭好了,因為程式碼一旦進了公開倉庫,就等於永久發佈了。洩漏比缺一個功能還難回復。

第三,垂直切片比水平分層出貨快。Plaudio 的每一個子指令都是端到端做完才開始下一個:測試、實作、CLI 全部包在同一個切片裡。把一個函式庫抽出來時,誘惑是先把所有核心演算法寫好,再去包所有的 CLI。紀律是先讓單一個指令從頭到尾跑得起來,再去做下一個。

接下來

v0.1 已經上線。v0.1.1 已知還有一份小型的待辦清單:有個預設參數設錯了、某條錯誤路徑直接吐原始的例外而不是友善訊息、兩個棄用警告需要清掉。v0.2 會加上 Plaud 雲端同步。

如果你手邊有一台 Apple Silicon 的 Mac,用的也是 Plaud Note Pro,並且遇到同樣的群集合併問題,請用 pip install plaudio 安裝。如果不是,那這個工具大概不適合你,這也是有意為之。

← 返回網誌