最近在 Facebook 看到一篇貼文,標題大意是:「我有 64 GB 記憶體,macOS 告訴我用了 100+ GB,怎麼可能?」

作者說他跑了自己寫的診斷腳本,發現背景跑著 287 個 iOS Simulator 處理程序、10 個 Flutter daemon、Gradle daemon、Kotlin LSP……合計超過 23 GB。他進一步研究,附上多個 GitHub issue 編號,得出結論:這是所有 AI coding tool 的共同問題,MCP server 子處理程序沒有被正確清理,而 macOS 缺少 Linux 的 PR_SET_PDEATHSIG 機制所以無解。

然後他開源了一個叫 clean-orphans 的腳本。

乍看之下相當完整:有現象、有數據、有引用、有工具。我當時的第一反應也是展開貼文,準備找 GitHub repo 看看有沒有什麼值得參考的地方。

結果留言區一片批評聲。


觀察是真的,解釋是 AI 填充的

仔細讀完之後,問題變得很清楚。

作者的親身觀察沒有錯。孤兒處理程序確實存在,Gradle daemon 確實很吃記憶體,Flutter 的 shell wrapper 信號傳遞問題也是真實的技術細節。這些不是捏造的。

但他的技術解釋有幾個明顯裂縫:

最嚴重的是把 virtual memory(VSZ)當成實體記憶體用。他引用的那些 GitHub issue——「Claude Code 單一處理程序漲到 120+ GB RAM」——幾乎可以確定是虛擬記憶體空間,不是真正佔用的實體 RAM。這是作業系統最基礎的概念,混淆了,整個論述的可信度就垮了。

另一個問題是「macOS 缺少 PR_SET_PDEATHSIG,所以無解」這個推論。技術上 macOS 確實沒有這個 Linux 特有的 API,但 macOS 有 kqueue + EVFILT_PROC,可以達到類似效果。問題在於工具開發者沒有用,不是 OS 架構不行。他把工具的懶惰轉嫁給了 OS。

然後他說這是「所有 AI coding tool 的共同問題」,語氣像揭露了什麼系統性缺陷,但他的解法是一個殺 PPID=1 處理程序的 shell script。

這個落差,就是留言區在嘲諷「你是要重構 OS 嗎」的原因。

文章的結構完整、語氣篤定、引用詳盡,但核心邏輯有個裂縫——而這種「表面嚴謹、核心有洞」的文章,正是 AI 輔助寫作最容易產生的類型。


閉環裡的正向回饋

後來作者在留言裡承認,他只是個 vibecoder。

這個詞說明了一切。

他大概是這樣做的:用 Claude Code 觀察現象、用 AI 補充背景知識和 root cause 分析、讓 AI 寫工具、再讓 AI 整理成一篇完整的技術文章。全程在 AI 的閉環裡完成,每一步都是正向回饋——「分析得很好」、腳本跑起來了、記憶體真的降了——沒有任何環節被外部視角質疑過。

問題是,AI 在閉環裡傾向維持它自己建立的框架。同一個模型在同一個對話裡自我驗證,它不太會主動推翻自己說過的東西。更重要的是,AI 的語氣永遠很篤定,即使解釋是錯的。

所以他出來發文的時候,心理狀態大概是:「我做了一個紮實的分析,留言應該會說好厲害、這個工具真有用。」

結果留言區是第一個真正的外部視角,而且一次來了很多個。


跨模型驗證才是真正的第二意見

我自己處理比較複雜的技術問題時,當 Claude 在某個問題上進入 loop,我會把問題帶去 ChatGPT 問第二意見。

有趣的是,每次這樣做,ChatGPT 都會建議由它擔任主力、Claude 負責審核就好。我反過來問 Claude 呢?它也有類似傾向。

這不是 AI 在「搶業務」,而是每個模型都傾向認為自己的推理框架比較可靠,所以自然建議自己主導。問題是這個建議本身就是帶偏見的——沒有任何一個模型有辦法客觀評估自己跟另一個模型的差距。

所以「讓誰當主力」這個決定應該留在使用者手上,根據任務類型決定,不是讓 AI 自己決定。

有人可能會想:Claude Code 可以開子 Agent 做分析,這樣不就有第二意見了嗎?

沒有。子 Agent 跟主 Agent 是同一個模型,訓練權重完全一樣,盲點高度重疊。更糟的是,子 Agent 的上下文通常從主 Agent 傳遞過來,等於一開始就接收了帶有框架偏見的資訊。子 Agent 適合做任務分工,不適合做觀點驗證。

真正有效的第二意見,來自不同的先驗假設和訓練偏向,也就是不同的模型。


我自己的做法

我在 AI 協作上有一個自己的原則:文章只寫我親身走過的事情

不是走完 A 步驟就出來說 A,而是走完 A、B、C、D,包括走錯路、回頭、找到真正原因,最後才讓 AI 幫我把這個過程整理成文章。那些「走錯路」的細節,是我帶進去的,不是 AI 捏造的。

那個 vibecoder 和我的差別不是「有沒有依賴 AI」,而是他在閉環裡做完一件事,沒有任何信號告訴他「該去問第二個人了」。孤兒處理程序清掉了、記憶體降了,看起來一切完美,他就出來發文了。

我的觸發機制是:當事情卡住、進入 loop,我才會啟動第二個 AI 資源。95% 的時間在同一個閉環裡沒有問題,問題是那個 5% 的判斷——知道什麼時候要跳出去。


AI 工具降低了產出的門檻,但沒有降低判斷的門檻。

技術文章最難的部分從來不是寫作,而是知道自己什麼時候真的懂了、什麼時候只是 AI 幫你填了一個聽起來合理的解釋。

閉環裡不會有人告訴你這兩件事的差別。


補充:Apple 的「選擇性強制」問題

回到那個 kqueue + EVFILT_PROC 的問題——為什麼 Apple 有工具,但開發者不用,而 Apple 也不強制?

這其實反映了 Apple 平台一個更深層的設計哲學:它的強制力是選擇性的,而且選擇的標準是使用者看不看得到。

看得到的地方——UI 風格、音訊行為、通知樣式——全部強制,給你一整套,不能拆。我自己在開發語音工具時就踩過這個坑:想用 Apple 的 Voice Processing API 取得降噪功能,結果發現 audio ducking(壓低背景音量)是綁在一起的,沒辦法單獨關掉,只好 revert 回去自己用 RNNoise 實作降噪。Apple 認為這個組合對「通話情境」是正確答案,所以整包給你,不提供拆件選項。

跨平台移植也是同樣的問題。我自己開發的訂餐系統 App 原本是 Android,換到 Mac 和 iPhone 之後用 Mac Catalyst 移植過來。可以用,但體驗就是不對勁——UI 元件偏大、留白太多、操作節奏不像 Mac app,但又不完全是 iPad app。Mac Catalyst 解決的是開發者的移植成本,不是使用者的體驗一致性。Apple 讓你少寫一份程式碼,但代價是兩個平台的體驗都打了折扣。

但看不到的地方——處理程序生命週期管理、背景資源釋放——就變成開發者自律,Apple 不管。

這件事從系統架構的角度來看,其實很說不通。處理程序生命週期管理本來就是 OS 的核心職責。Linux 的 cgroups 是這個思路的體現——把一組處理程序放進一個 cgroup,整個 group 的資源限制和生命週期由 kernel 統一管理,parent 死了 kernel 可以連帶清理。macOS 的 launchd 理論上也有能力做到類似的事,只是 Apple 沒有把這個機制延伸到第三方 app 啟動的子處理程序上。

所以這個缺口不是技術做不到,而是 Apple 在架構設計上選擇不管。

結果就是一個很奇怪的狀態:使用者體驗在「表面」高度一致,但在「底層」其實參差不齊。你用不同 app 的視覺和互動感覺都像 Apple 的東西,但背景佔用多少記憶體、關掉之後有沒有清乾淨,完全看那個開發者有沒有良心。

這和 Apple 一直強調的「It just works」是矛盾的。使用者以為整個系統都被照顧到了,但孤兒處理程序的問題告訴你,Apple 的控制邊界其實比它宣傳的淺很多。

要嘛就從頭到尾都強制,這樣得到的體驗才會是高度一致的。選擇性強制,是一種對使用者的誤導。