什麼必須寫在程式碼裡
置信度門控那一頁講的是「閾值交給程式碼」。這一頁講得更強一層: 有一批檢查根本不該問模型。
最強的一句邊界宣告
公開的做法裡最硬的一條來自一個 agent 迴圈的實現:
風險分高的工具呼叫強制執行人工授權,任何機率都不能覆蓋它。
「任何機率都不能覆蓋」這句話值得單獨拿出來。它說的不是「置信度要很高」, 而是這條規則不在機率空間裡。區分這兩者很重要:
- 「≥ 0.95 才自動放行」是一條閾值規則 —— 理論上存在一個足夠高的機率能讓它通過。
- 「高風險必須人工批准」是一條結構性規則 —— 無論模型說什麼都成立。
兩者的可靠性不在一個量級上。你的系統裡應該明確區分哪些檢查屬於哪一類, 而結構性規則要儘量多。
確定性規則先跑,模型只看灰區
同一個模式的另一種表述:
先跑確定性規則:能判清楚的直接 block 或放行,模型只是一個可選後端。 常規命令在本地就決定了,根本不發給模型。
先做規則有一個容易被忽略的好處:它把攻擊面縮小了。一個純粹依賴模型的 判定器,面對提示注入時的行為是不可預測的;而一個先跑規則的判定器, 注入只能影響「規則判不了的那一小撮」。
有一份公開的注入測試報告值 300 次注入嘗試,並且照實說明門擋住了什麼、 什麼又溜過去了。這種報告比一句「防注入」有用得多。
金鑰與敏感資料:本地先攔
一個金鑰檢測器的設計把順序講得很清楚:
| 情況 | 處理 |
|---|---|
| 已知格式的金鑰 | 本地攔截,根本不發給模型 |
| 未知的高熵字串 | 先遮罩,再把遮罩後的文本發給模型 |
| 模型判定 ≥ 0.80 | 阻斷 |
| 模型判定 ≥ 0.30 | 問人 |
| 模型不可用 | 也問人 |
兩條設計決定值得記住:
- 遮罩在前。 你不能為了判斷「這是不是一個金鑰」而把一個可能是金鑰的東西 發到外部服務上去。判斷本身也有洩露風險。
- 模型掛了也問人,而不是放行。 這是 fail-closed 的一個具體形態 —— 服務不可用不是一個可以悄悄降低安全等級的理由。
實測:6/6 個金鑰被攔住,0/6 個良性輸入被誤攔。
預算上限、簽名回執與審計
有些檢查與「模型說什麼」完全無關,但必須在同一個系統裡:
- 一個執行時把危險命令阻斷、預算上限交給確定性程式碼, 並用 Ed25519 簽名回執鏈記錄每一次裁決。
- 另一個把風險分限制成程式碼控制的 0–100 分,並對每次裁決做 ES256 簽名背書 (540 次呼叫,閾值 65–75 時 99.76%、0 誤報)。
簽名的意義不是防模型,是防事後說不清。當一個自動裁決導致了事故, 「當時模型說了什麼、程式碼怎麼處理的」必須能被獨立驗證,而不是隻能相信日誌。
注入要在兩個位置篩
一個防護層的做法值得單獨列出來,因為它指出了兩個容易漏掉的位置:
在工具呼叫執行之前篩一次,在工具結果被 agent 讀到之前再篩一次。
第二次是關鍵。只篩輸入的話,攻擊可以藏在工具返回的內容裡 —— 一個被 agent 抓取的網頁、一段讀進來的檔案內容,都會進入上下文, 而此時它已經繞過了輸入側的檢查。
同類的另一條規則來自一個檢索增強的實現: 引用來源裡沒有的數字,一律不輸出。
沙箱與許可權
除了篩內容,另一層是限制影響面:受保護的路徑永遠走人工確認; agent 的執行放在隔離的工作區裡(有實現用 Docker); 子 agent 的許可權與父級分開。
一張對照表
| 檢查 | 屬於哪一類 | 出錯時倒向哪邊 |
|---|---|---|
| 已知金鑰格式 | 確定性規則 | 攔 |
| 高風險操作授權 | 結構性規則 | 拒絕(人批准才行) |
| 只讀命令自動放行 | 閾值規則 | 退回人工提示 |
| 工具輸出裡的注入 | 確定性過濾 + 模型 | 攔 |
| 「agent 做完沒有」 | 效率門 | 放行(fail-open) |
| 格式與風格檢查 | 效率門 | 放行 |
表的第二列是關鍵:越靠近第一行的,越不該讓模型經手。
一句話
能被機率覆蓋的檢查,最終都會被某個機率覆蓋。 先決定哪些檢查不允許被覆蓋,剩下的才輪到調閾值。