2016 年,Google 出版了《Site Reliability Engineering》,把他們維運大型系統的做法整理成書。其中一章談 postmortem,也就是事故發生後的檢討報告,核心原則是不究責:檢討要找出造成事故的各種原因,但不指控任何個人或團隊做了不當的事。
理由很實際。一旦檢討變成找戰犯,大家就會開始隱瞞,問題被藏起來,下一次照樣發生。書裡寫道,當檢討不再忙著分配責任,轉而追查是什麼系統性的原因,讓這個人或團隊當時拿到的資訊不完整或不正確,才有辦法訂出真正有效的預防措施。
書裡也提到,這種文化源自醫療和航空,這兩個領域的錯誤可能致命。研究人為錯誤的心理學家 James Reason,在 1990 年代提出了一個比喻,2000 年他在《英國醫學期刊》的一篇文章裡把它帶進醫療界,後來廣為流傳:每一層防護都像一片瑞士起司,上面有洞,只有當好幾片起司的洞剛好對齊,錯誤才會一路穿過去。他的結論是:我們改變不了人性,但能改變人工作的條件。
假設某天 agent 推錯了分支,或刪掉了一批不該刪的檔案。第一個反應通常是:AI 又亂來了。
怪 agent 其實沒什麼用。agent 不會因為怕被罵而隱瞞,被罵了也不會因此記得,除非教訓被寫進記憶或設定。真正會因為被究責而開始隱瞞的,是坐在 agent 旁邊的那個人:按下確認放行的人,或是為了省事開了 bypassPermissions 的人。
不究責的檢討,在這裡保護的是人。要問的不是「誰按了同意」,而是「那個確認畫面為什麼沒讓人看出危險」。Day 8 提過,使用者在確認提示出現時有九成以上會直接按同意,一個一天要按很多次的確認框,本身就是一個會讓人出錯的工作條件。
接著是 Reason 的起司。這次的錯誤穿過了哪幾片?權限規則有沒有讓推送不經確認就能執行,sandbox 有沒有開,這段改動有沒有經過 review,測試有沒有覆蓋到。這個系列前面談過的每一層防護,出事之後都是一片要回頭檢查的起司。
事故剛發生時,順序很重要。先讓損害停下來:中斷還在跑的 session,把錯誤的改動 revert 回去。接著,趁紀錄還在的時候把它保存下來。
Claude Code 會把每一段對話完整存在本機,包括每一次工具呼叫和它的結果。用 /export 可以把整段紀錄匯出成文字檔,用 /resume 可以回到過去的對話。要注意的是,用 claude -p 或 SDK 跑的 session,不會出現在 /resume 的選單裡,得拿著 session ID 用 claude --resume 才叫得回來,而且只能在留著紀錄的那台機器上。偏偏 agent 出事,常常就發生在 CI 這類自動化流程裡,而 CI 的機器用完就銷毀,紀錄也跟著消失,所以要在流程裡把紀錄另外存成產出物保留下來。
這份紀錄預設只保留三十天,過期的會被自動清掉。事故如果一個多月後才被發現,最關鍵的那段可能已經不在了。可以在設定裡把保留期限調長,或用一個在對話結束時觸發的 hook,把紀錄另外備份。官方文件也提醒,紀錄檔的內部格式會隨版本改變,要用程式處理的話,應該透過匯出功能或官方提供的介面。
還有一件事要分清楚:事後問 agent「你剛剛做了什麼」,拿到的是它整理出來的摘要。Day 17 提過,agent 自己的說法不能當成證據,檢討時要看紀錄裡實際的工具呼叫。
一個人用 Claude Code 時,本機的紀錄大概夠用。團隊一起用,問題就變成「上週誰的 session 跑過這個指令」「這個月哪些操作被拒絕過」,這時候需要集中的紀錄。
Claude Code 支援 OpenTelemetry,一套業界通用、用來收集監控資料的開放格式,可以把資料送到 Grafana 這類監控工具。這個功能預設是關的,要先開啟;團隊也可以用統一的組織設定,替所有成員一次開好。開啟之後,它會送出用量、費用、修改的程式碼行數這類指標;再另外設定事件的輸出位置,還能送出事件,例如每次權限決定是同意還是拒絕、每次工具執行是否成功、花了多久。
但照預設設定,前面那個問題其實查不到。為了保護隱私,使用者輸入的 prompt 內容預設不記錄;工具的詳細參數,包括 Bash 實際跑了什麼指令,也要另外開啟才會送出,否則只知道「跑過一次 Bash」,不知道跑了什麼。工具輸出的內容則是第三個開關,還得搭配仍在測試階段的追蹤功能才看得到。
記得越完整,出事時越好查,也代表有更多原本只在個人電腦上的內容,被集中到了另一個系統。團隊要事先決定記到什麼程度,否則到了出事的那天,才會發現當初那個開關沒開。
SRE 的 postmortem 會列出一份後續行動清單,記錄要做哪些改變來避免再犯。少了這一步,檢討就只是一份事故描述。
對 agent 來說,補洞的地方都在前面談過的那幾片起司上。少了一條規則,就寫進 CLAUDE.md,只是它屬於提醒,模型不一定每次都照做。真正要擋住的動作,例如推送到受保護的分支,就交給不靠人判斷、也不靠模型自覺的機制:權限規則直接禁止,遠端再加上分支保護,讓確認框不再是最後一道防線。同類的錯誤反覆出現,就寫成一個 eval 案例,往後每次調整都拿它驗證一遍。
這些改變寫進專案層級的設定和檔案,就會跟著專案走,下一個人、下一個 session、下一個模型都會受到同樣的保護;只寫在個人設定裡的,就只保護得了自己。
Reason 那句話原本講的是醫院:與其要求醫護人員永遠不犯錯,不如改變讓錯誤有機會發生的環境。
把這句話放到 agent 身上,意思幾乎不用改。模型會出錯,按確認的人會疲乏,這些都很難靠要求來改變。能改的,是它們工作的條件:哪些動作要確認、哪些牆要先蓋好、哪些紀錄要先留下來。
延伸閱讀