終端機裡開著一個 CLI 工具,游標停在輸入列。你按下 fn,講一句話,語音輸入工具辨識完,呼叫 macOS 的輔助使用 API 把文字寫進焦點元素,API 回報成功。終端機裡什麼都沒有。

這個場景是我從一段原始碼註解讀出來的,不是我自己碰到的。註解寫在 Hyday Talk 的 TextInsertion.swift 第 132 到 133 行:

Always use the editor’s paste command. AX setters can report success on a terminal’s hidden textarea without sending an input/paste event.

終端機的輸入區背後常常是一個看不見的 textarea。用 Accessibility 的 setter 直接改它的值,系統說好,可是終端機沒收到任何輸入事件,所以畫面上不會出現字。作者的結論寫在第一句:一律走編輯器自己的貼上指令。這條路被程式碼明確封掉了,想自己寫同類工具的人可以直接跳過它。

Hyday Talk 是一個 Swift 寫的 macOS 語音輸入工具,只支援 Apple Silicon、macOS 14 以上,按 fn 說話,辨識完把文字插回你正在用的 App。repo 10 月 13 日才開,兩天內從 v0.1.0 一路發到 v0.1.5,共六個 release。貢獻者只有作者一人,2026 年 10 月 15 日查 GitHub API 是 4 顆星、2 個 fork。

先講清楚範圍:這篇讀的是 README 和 commit 2216d08 的原始碼,我沒有安裝它,沒下載 DMG,也沒執行過,實際手感與速度我不知道。2216d08 之後,10 月 15 日晚上發布的 v0.1.5 又加了 TEA-ASR 1.1 這個本機辨識模型,和透過 Claude Code 整理文字的選項,下面講「三個模型」「三種整理方式」時不含這兩項。

用這種工具要決定兩件事:聲音交給誰辨識,辨識完的字要不要再讓模型整理一次。Hyday 把它們拆成兩個開關,辨識有三條路,整理有兩條路加一個不用 AI 的基礎選項。但有一筆帳跟這兩個開關無關,怎麼選都要付。

不管選哪條路都要付的帳

那筆帳是輔助使用權限,外加你的剪貼簿。

先講剪貼簿。既然不能直接寫值,就只能假裝使用者按了 Cmd+V。TextInsertion.swift 第 134 到 158 行的做法是這樣:先把 NSPasteboard.general 裡每個 item 的每一種 type 的資料存起來,清空,寫入辨識好的文字,然後用 CGEventSource(stateID: .hidSystemState) 造一個鍵盤事件,virtualKey 9 就是 V,加上 .maskCommand,送進 .cgAnnotatedSessionEventTap。0.8 秒之後再把原本的剪貼簿內容放回去。

放回去之前還有一道檢查:如果剪貼簿的 changeCount 在這 0.8 秒裡被別人改過,它就不還原。這個判斷很誠實。你剛好在那段時間複製了別的東西,它寧可讓原內容回不來,也不蓋掉你新複製的那一份。代價是每次聽寫,剪貼簿都會被借走一下,借用期間它不屬於你。

貼上之前還有一串防呆。焦點元素必須跟開始錄音時是同一個,游標位置與選取範圍不能變,選取的文字也不能變,任何一項對不上就丟錯,結果留在面板上讓你自己複製。密碼欄位直接拒絕插入。講話講到一半切去別的視窗,字不會貼錯地方,代價是你要手動處理一次。

再講 fn 鍵。一般 App 註冊全域快捷鍵,用的是 Carbon 那套 hotkey API。FnShortcut.swift 第 5 行的註解一句話講完為什麼不行:

Fn is a modifier, so Carbon’s normal hotkey registration cannot represent it.

fn 跟 Shift、Option 一樣是修飾鍵,Carbon 沒辦法把「單按一個修飾鍵」表示成快捷鍵。剩下的路是 CGEventTap,在系統層攔截所有鍵盤事件。GlobalShortcuts.swift 第 19 到 31 行用 CGEvent.tapCreate 建一個 .cgSessionEventTap、.headInsertEventTap、.defaultTap 的 tap,而且在 AXIsProcessTrusted() 回 false 時乾脆不建。也就是說,沒給輔助使用權限,fn 鍵根本不會有反應。

純 fn 的判定也有講究。註解寫「A tap is committed on release; chords never also trigger the plain Fn action.」放開時才算數,按著 fn 期間碰過別的鍵,就當成組合鍵,不會觸發聽寫。你平常用 fn 加方向鍵翻頁,不會不小心開始錄音。

所以 Hyday 要的權限只有三種:麥克風、輔助使用,以及選了系統辨識才需要的語音辨識。entitlements 檔裡只有 com.apple.security.device.audio-input 一項,沒有 app-sandbox。輔助使用這個權限的範圍很大,拿到它的程式能讀寫其他 App 的介面元素。Hyday 攔 fn 鍵要它,貼上前比對焦點元素也要它。如果你的公司電腦不准給第三方 App 輔助使用權限,後面三條辨識路都不用比了。

聲音要不要離開這台 Mac

過了權限這關,才輪到辨識來源。README 第 59 到 67 行列了三條路,我把它們和程式碼裡的硬性條件排在一起:

辨識來源 README 怎麼說音訊去向 單次錄音上限 要先準備什麼
系統辨識 由 Apple Speech 處理,部分語言可能使用 Apple 伺服器;可設定僅使用離線辨識 55 秒 系統的語音辨識權限
本機模型 以內建的 whisper.cpp 或 transcribe.cpp 在本機辨識,錄音不會上傳 180 秒 下載模型,574,041,195 到 1,692,554,208 bytes
API 音訊傳送至指定的 OpenAI 相容服務 180 秒 一組 API Key

55 秒那條是寫死的:AppModel.swift 第 299 到 302 行是 let limit = snapshot.speechBackend == .system ? 55.0 : 180.0。習慣一口氣口述一整段需求的人,系統辨識會在一分鐘前就把你切掉。

系統辨識的「僅離線」也有一條死路。AudioCapture.swift 第 55 到 59 行把這個設定接到 requiresOnDeviceRecognition,可是如果你的語言在這台 Mac 上不支援離線,它不會偷偷改走伺服器,而是直接報錯:「這台 Mac 的系統辨識不支援目前語言離線使用。請關閉「僅使用離線辨識」或選擇本機模型。」兩個出口,一個是讓聲音可能出門,一個是換條路。我比較欣賞的是它不替你做這個決定。

另外要分清楚一件事。Apple 的 Mac 聽寫說明頁講的是 macOS 內建的「聽寫」,Hyday 用的是 Speech framework。兩者是不是同一套處理,我沒查到,所以不能拿那一頁替系統辨識的隱私背書。

本機模型內建三個選項,寫在 LocalSpeechModels.swift 第 24 到 37 行:Whisper Large v3 Turbo q5_0 是 574,041,195 bytes,Qwen3-ASR 0.6B Q8_0 是 850,423,456 bytes,Qwen3-ASR 1.7B Q6_K 是 1,692,554,208 bytes。程式碼把 0.6B 標成 recommended,說明文字是「中文口語與中英夾雜辨識較準,速度快。」這是作者自己的描述,不是獨立評測,我沒跑過,沒辦法背書也沒辦法反駁。延遲也一樣,沒有能引用的數字。

非系統辨識的音訊會先錄成 capture.caf 放在暫存目錄,再用 /usr/bin/afconvert 轉成 16 kHz 單聲道 16-bit 的 WAV,做完或取消後刪掉暫存目錄。系統辨識走串流,不寫音訊檔。辨識語言在介面上只有 zh-TW、en-US、ja-JP、zh-CN 四個。

字整理壞了,就退回原文

辨識出來的字可以直接貼,也可以再交給模型整理。選項有三個:不叫 AI 的基礎整理、本機 Ollama、OpenAI 相容的 /chat/completions API。README 推薦的本機模型是 qwen3.5:9b,Ollama 的模型頁對這個 tag 標的大小是 6.6GB 到 7.6GB(2026 年 10 月 15 日查)。光這一項就比最大的辨識模型多出好幾倍,硬碟吃緊的人要先算一下。

這一層最值得看的是防呆。TextProcessing.swift 第 53 到 61 行對模型輸出做了三個檢查:輸出長度超過原文字數乘 2 再加 80,丟錯;輸出裡有 <think>,丟錯;原文裡的數字和英文字面值只要有一個在輸出裡找不到,丟錯,訊息是「文字整理改動了數字或專有名詞。」丟錯之後退回原始辨識文字。

模型可以幫你刪贅詞、順句子,但只要它碰了數字或 useEffect 這種英文詞,Hyday 寧可給你粗糙的原文。對口述技術內容的人來說,這比整理得漂亮重要得多。代價是翻譯和提問這兩個功能必須有文字 AI,基礎整理用不了。

還有一個容易忽略的輸入:螢幕上下文。開始錄音時,它會讀游標前 800 字、後 200 字和視窗標題,從裡面挑出含大寫字母的英文詞,最多 40 個,交給 AI 當參考。預設是本機 AI 開、API 關,排除清單裡有 1Password、鑰匙圈存取、Keychain Access、Passwords、密碼這幾個 App。如果你改用 API 又手動打開這個選項,送出去的不只是你的聲音或逐字稿,還有你畫面上的英文詞。API Key 本身放在 macOS 鑰匙圈,其他設定與紀錄放 ~/Library/Application Support/HydayTalk/。

我會怎麼選,以及什麼情況不該裝

我的選擇是本機模型加基礎整理。聲音不出門,錄音上限 180 秒,要付的只有一次模型下載。Ollama 那 6.6GB 到 7.6GB,我會等到確定基礎整理的結果真的不夠用再補;反正整理層一動到數字或英文詞就會退回原文,先用基礎整理看看原文夠不夠乾淨,比較省事。API 那條,只在你手上已經有一個信得過的 OpenAI 相容服務、而且不介意聲音離開機器時才考慮,它的費用要看你接的是哪家,Hyday 自己沒有寫。

會讓我改變想法的條件有兩個。如果你每次只講一兩句話、都在 55 秒內,而且你的語言在這台 Mac 上支援離線辨識,系統辨識開「僅離線」就夠了,連模型都不用下載。反過來,如果你的 Mac 是 Intel 或還在 macOS 13,這個工具一開始就不在選項裡。

最後一個條件比辨識來源更優先:這個 repo 沒有授權條款。GitHub API 查 license 回 404,根目錄也沒有 LICENSE 檔。照 GitHub 的說明,沒有授權就適用預設的著作權,作者保留所有權利,你能在 GitHub 上看、能 fork,但沒有取得複製、修改、再散布的許可。拿它的原始碼改一改放進自己的產品,現在是不行的。再加上專案建立才兩天、版本號停在 0.1.x、只有一個人維護,安裝方式也只有 GitHub Releases 的 DMG,沒有 Homebrew。

所以,想找一個每天都要依賴的語音輸入工具,現在先別裝 Hyday Talk;想知道 Hyday 為什麼非要輔助使用權限、為什麼寧可借你的剪貼簿也不直接寫值,就去讀它的 TextInsertion.swift 和 FnShortcut.swift。