iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0

第 3 層:工具層強制。

前面三層都是 advisory——AI 願意配合才有效。

這一層不一樣。這一層是 AI 物理上做不到違反。

而那個失敗的案子,這一層完全沒有設置。

講一下那是什麼案子:一個四個月的技術升級案,舊系統是 Web Forms,最後客戶要求重做。

它在「規定」這件事上其實很用力。12 月 26 日,「DB Schema 不可動」第一次被寫成方框,放在一份專門的關鍵規則文件裡。主規範文件也用方框重申了同一件事。粗體、驚嘆號、「絕對」「必須」「嚴禁」,該用的都用了。

然後測試問題單裡出現這一行:

AI 創建了新的資料表

兩份文件,擋不住一個 CreateTable

因為那些字全部停在第 0 層——它們是講給 AI 聽的,不是擋在 AI 前面的。

一個對照

先看兩種寫法,做的是同一件事。

第 0 層的寫法:

## 🔴 絕對禁止事項

⚠️【極其重要】**嚴禁**異動資料庫 Schema!
**這不是建議,是強制要求!!!**
任何 Migration 指令都必須先經過審核!

第 3 層的寫法:

#!/bin/bash
# PreToolUse hook:輸入是 stdin 的 JSON,不是環境變數
input=$(cat)
# 看「要寫進去的內容」,不是看磁碟上的檔案
body=$(jq -r '[.tool_input.content, .tool_input.new_string] | map(select(.!=null)) | .[]' <<<"$input")
grep -qE 'migrationBuilder\.(AddColumn|DropColumn|CreateTable)' <<<"$body" \
  && { echo "❌ 偵測到 Migration 結構異動指令" >&2; exit 2; }
exit 0

這裡有一個很容易寫錯的地方,我第一版就寫錯了: 我原本抓 .tool_input.file_path 然後去 grep 那個檔案。但 PreToolUse 是在寫入之前觸發的。新建一支 migration 的時候那個檔還不存在,grep 回錯誤、&& 短路、exit 0 放行。

它只擋得住「編輯一支本來就已經違規的既有檔案」,而那不是主要情境。 要看的是即將被寫進去的內容,不是磁碟上的東西。

(另外這條 pattern 只涵蓋三個動詞,它不完備RenameColumnAlterColumnSql() 都沒擋到,換行寫法也繞得過。完備的清單要你自己補,這裡只示範「第 3 層長什麼樣」。)

第二種沒有任何形容詞。沒有「極其重要」、沒有驚嘆號、沒有「嚴禁」。

但它是唯一真的擋得住的那個。

而且它有一個第一種永遠達不到的性質:腳本本身不佔 context。那五行 shell 不會被讀進 AI 的注意力,它在外面等著。(但這句話要打一個折,明天會講。它擋下來的時候,錯誤訊息還是會回餵給模型。)

這一層有哪些工具

實務上可用的手段,按介入時機排:

手段 介入時機 擋什麼
PreToolUse hook AI 寫檔之前 直接 return error,檔案根本不會被寫出去
PostToolUse hook AI 寫完檔案之後 自動跑檢查,違規立刻回報
自訂 Linter 規則 編輯/編譯時 禁止特定 API、特定寫法
Git hook(pre-commit) 提交之前 擋住不該進版控的東西
唯讀連線 執行時 物理層封鎖——AI 想改資料庫,連線層直接拒絕
CI gate PR 合併之前 不一致就 block

最上面那個 PreToolUse 是最徹底的。它的差別在於「錯誤根本沒有機會存在」。不是寫出來再抓,是寫不出來。最下面那個唯讀連線我特別喜歡,因為它不需要任何規則、任何檢查、任何提示詞。開發環境連到唯讀副本,「不可以改資料庫」這條規則就從一句話變成了一個物理事實。

能用物理封鎖的,就不要用規則。

https://ithelp.ithome.com.tw/upload/images/20260914/20178262WuFAOQ3Rhx.png

三個實際跑起來的例子

講理論比較虛,我拿三個不同案子的實際做法來看。

例一:結構檢查掛在寫檔之後一個進行中的專案,設定檔裡是這樣:

每次 Write / Edit 之後  →  自動跑結構檢查腳本
每次工作結束時          →  自動跑文件一致性檢查

結構檢查很便宜(毫秒級),所以每次寫檔都跑。文件一致性稍貴,結束時跑一次。關鍵在於:這件事不是 AI 決定要不要做的。 它是外部執行的,不在 AI 的自主範圍內。例二:把「貼警語」升級為「機器擋」

Day 12 提過的那條迭代紀錄。原本的做法是在文件旁邊貼一段警語(第 0 層),後來把檢查腳本的掃描範圍擴及文件內的 SQL,違反就擋。

同一條規則,從第 0 層搬到第 3 層。

例三:擋數字漂移——而且它處理的不是程式碼一個文件產出的工作區:建議書、簡報、報價。一組承重數字散在十幾份文件裡,改一個地方沒同步就是自相矛盾。做法是建一份數字母體當單一真相,配一支檢查腳本掛在 hook 上,每次改檔就跑。而最關鍵的設計是這個參數:

--quiet   無漂移時靜默,有漂移才中斷出聲

平常完全不吵,出事才叫。 如果每次改檔都印一堆綠色的 PASS,三天之後就沒有人會看了。那份說明文件開頭那句話,我認為是整個系列可以直接搬走的一句:

文件「數字漂移」= runtime bug,必須自動擋。

(後面會單獨講那支腳本實際檢查什麼。包括我後來回頭去查,發現它有幾項根本沒在跑。)

為什麼那個案子沒做

回到那個失敗的案子。這一層完全沒有設置,為什麼?

我認為有三個原因,而且每個都很常見。

一、它不像「開發工作」。 寫一支 hook 腳本不會產出功能,在進度報表上是空白的。趕進度的時候第一個被砍。

二、它需要先知道要擋什麼。 而「要擋什麼」通常要踩過才知道。所以第 3 層天然是滯後的。這也是為什麼 Day 11 講的「95% 事後補鍋」不完全是壞事,補鍋補在第 3 層是對的,補在第 0 層才是白費。

三、多數人不知道有這些工具。 hook、MCP、CI gate 這幾種掛載點。很多團隊的認知還停在「寫 prompt」。

一個判準

我現在檢視任何一條 advisory 規則,都會問同一句話:

這一條,能不能用工具強制?

能的就升級。不能的,再問下一句:「那它能不能被自動檢測?」——那是第 4 層。

兩個都不行的,才留給人。

而實務上你會發現:**大部分你以為只能靠自覺的規則,其實都能用三行 grep 擋掉。**命名規範、目錄結構、禁用 API、引用路徑存在性、版本一致性、必填欄位。這些全部是確定性問題,全部可以被機器判斷。那 591 行的命名規範裡,我猜有八成可以變成一條 linter 規則。

小結

  • 第 3 層是唯一真正的 preventive,AI 物理上做不到違反
  • 手段按介入時機排:PreToolUse → PostToolUse → linter → git hook → 唯讀環境 → CI gate
  • 能用物理封鎖的,就不要用規則(唯讀連線 vs「請不要改資料庫」)
  • 好的第 3 層檢查應該平常靜默、出事才叫
  • 判準:每條 advisory 規則都問「這條能不能用工具強制?」

明天

理論講完了。明天全部是可以複製的東西。三個階梯,由零風險到真的會擋,包含我自己第一支 hook 裝完之後什麼都沒發生的那個坑。


上一篇
Day 15 - 第 2 層 流程閘道:配置都對,順序錯了
下一篇
Day 17 - 三個階梯:把「請不要改」變成「順手改不了」
系列文
綠燈不等於做對:AI 開發的驗收工程 ——從兩個失敗的案子,到驗收交付23
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言