從今天起進入生產環境實戰。前七天談的是心法,接下來這幾篇,是我跟 AI 協作時一手記錄下來的事故現場——AI 當場犯錯、被真實測試打臉、再一步步校正的完整過程。
第一個案例,是我跟 AI 協作至今繞得最久的一次:整整兩天。它之所以難,不是因為問題多深奧,而是因為每一個檢查工具都在說謊——它們全部回報「沒問題」,但東西就是不動。 而 AI 在這種「全部綠燈」的迷霧裡,一次次跳結論、一次次繞路,最後是靠人的一個直覺,才把它拉回正軌。
事情發生在一台新機器上架 Git 服務(Gitea)。需求很單純:開發者 git push 之後,要自動觸發後續的建置流程。但它完全不觸發。
如果只是「不觸發」,其實好查。難的是,所有你能想到的檢查,全都是綠燈:
git push 成功,程式碼確實進去了gitea doctor)26 項全部通過
ls 顯示權限 -rwxr-xr-x(完全正確)、擁有者正確每一個檢查都在說「沒問題」,但它就是不動。 這種「症狀自相矛盾」的情況,是最折磨人的——因為你不知道該相信哪一個。
一開始,AI 的表現談不上好。它一個一個假設丟出來,方向一直換:懷疑 hook 檔壞了、懷疑推送方式、懷疑內部佇列、懷疑 SELinux、懷疑 URL 帶了 .git 後綴……每一個聽起來都合理,但每一個查下去都不是。
其中有一段特別能說明問題。它一直叫我去檢查「webhook 的設定」。但我停下來反問了它一句:
為什麼是查 webhook?你不是說是有另外的 hook 去觸發 webhook,不是應該往前查嗎?手動傳送確定沒問題,那至少可以確認 webhook 也是好的。
它立刻承認:「你這個質疑完全正確,而且點出了我一直在繞的盲點。」——因為「手動傳送成功」這件事,邏輯上已經證明了 webhook 本身是好的,該往前查的是「為什麼 push 沒去觸發它」。AI 有它的推理能力,但它會被自己最先想到的方向黏住;把它從錯誤方向拔出來的,是人根據已知事實做的邏輯反推。 這就是 Day 02 講的「逼它驗證」,在除錯現場的樣子。
它甚至還幾次自己走進死巷又自己爬出來:一度斷言問題是「用 SSH 推送繞過了系統」,我把 push 紀錄貼給它,它馬上認錯「你說得對,我判斷錯了,你用的就是 HTTP」;還有一次它叫我手動去跑那個 hook,結果因為缺環境變數報了一個假錯誤,它也坦承「又是我的測試指令誤導了你」。
這些認錯都很誠實。但誠實不等於有效——繞了半天,我們還是沒找到真兇。
真正的轉折,來自一個 AI 一直沒有善用的線索。
我從頭就知道一件事:同樣的部署流程,我在另外兩個縣市(用的是 Ubuntu)的機器上跑,完全正常,只有這一台(CentOS)不正常。 到了某個節點,我把這句話明確丟出來:其他縣市用同一套流程都正常。
AI 這次接住了,它的反應是:「這是極其重要的資訊,直接推翻了『版本』和『共通流程』的方向……所以問題確實是這台特有的。」
這句「這台特有」,就是破案的鑰匙。它把搜索範圍從「所有可能性」瞬間縮小到「這台跟別台的差異」。這是 human-in-the-loop 最關鍵的一種貢獻:不是提供技術知識,是提供 AI 看不到的『對照組』。 它不知道你別台機器是正常的,除非你告訴它;而這個「正常 vs 異常」的對照,往往比任何單一的錯誤訊息都更有指向性。
方向對了之後,AI 給的方法就開始收斂了。它提議的做法很扎實:別再猜,直接在 hook 腳本裡插一行 debug,讓它在真正被觸發時,把現場狀況寫進一個檔案。
這一步很重要,因為它把「黑盒子」變成了「白盒子」——不再靠推測 hook 有沒有執行,而是讓 hook 自己報告。第一次插入後,log 出現了 HOOK RAN,證明主腳本確實有跑;但它裡面該呼叫的子腳本卻沒被執行。範圍又縮小了一圈。
然後是決定性的一擊。我們在腳本裡印出「執行時到底是誰、對那個子腳本檔做各種測試的結果」,跑出這樣一組數據:
whoami: git ← 執行者是 git
ls -la: -rwxr-xr-x git git gitea ← 檔案是 git 擁有的,權限完整
test -x: NO ← 但「能不能執行」的測試說:不行
同一個檔案,git 使用者是它的擁有者、權限明明是 -rwxr-xr-x、ls 也說有執行權限,但 test -x(測試能不能執行)卻回報「不行」。 這在正常的 Linux 上不可能發生。
為了確認不是眼花,我又補測了兩項:連 test -r(能不能讀)都回報「不行」,但 ./gitea(直接執行它)卻成功跑完、正常結束。
到這裡,矛盾被逼到了極致:三個原本應該一致的系統指令——ls(讀檔案狀態)、test(測試存取權限)、直接執行——給出了互相衝突的答案。這已經不是權限問題、不是使用者問題、不是路徑問題了。唯一能解釋「同一個檔案,不同的系統指令得到不同答案」的,只剩下最底層:作業系統核心本身,在回報檔案狀態時就是錯的。
最後 uname -r 一翻兩瞪眼:核心版本 3.10,也就是 CentOS 7——一個 2013 年的核心基礎,已經在 2024 年 6 月停止支援。它太舊,跑不動新版容器某些較新的系統呼叫,於是「測試檔案能不能執行」這個底層動作,回報了錯誤的結果。腳本因此以為那個子 hook「不可執行」,直接跳過,觸發鏈就斷在這裡——git push 成功、檔案權限正確、自我檢測全綠,但東西就是不動。
事後 AI 自己做了一段很誠實的復盤。它先肯定我該記取的教訓——「環境資訊(作業系統、核心、版本)應該一開始就給」,這點我認(這正是昨天 trixie 那篇的主軸)。但它接著說了句公道話,把自己的責任也攤開:
這次繞遠路,我的責任更大。我太早跳結論、方向一直換……而不是一開始就系統性地建立「已知事實 vs 未知」的清單。
它甚至點出,那個「權限正確、卻判定不能執行」的矛盾,其實中途就出現過一次,但它「想到一個可能性,查了沒有就放掉了」,沒有堅持追下去。它最後那句話,我覺得是整件事的註腳:
你早就說了其他機器正常,那是最強的線索……我應該一開始就抓著這條深挖,而不是繞了那麼多通用可能性。
把這兩天拆開來看,分工其實很清楚。AI 負責的是「方法」:怎麼把黑盒子變白盒子、怎麼設計那個印出 whoami / test -x 的 debug、怎麼從三個系統指令的矛盾推導到核心層。這些它做得到,而且做得漂亮。
人負責的是「方向」:擋下它「查 webhook」的錯誤路線、丟出「其他機器正常」這個它看不到的對照組、在它想放掉那個矛盾時堅持「這台一定不一樣」。這些它做不到,因為它沒有那些現場的、跨機器的、只存在於維運者腦中的脈絡。
一個很有力氣、但看不到全局的引擎,配上一個握著方向盤、看得到對照組的人——這就是為什麼那組「全部綠燈」的假象,最後沒能騙過我們。工具會說謊,doctor 會誤報,連 test -x 都會騙你。在一片綠燈裡還能問出「可是為什麼這台不一樣」的,只有人。
CentOS 7 這個坑,好歹還有一組矛盾的訊號可以追。但明天要講的更麻煩:AI 有些東西,是先天就產不準的——你叫它畫一張台灣地圖,它畫出來根本不像台灣;你問它一個檔案處理好了沒,它會一臉篤定地連錯四次。那時候,該怎麼辦?