對應原題(Common ‧ Agents and Tool Use)
- How do you make an agent’s actions reversible, or at least auditable, in a production system? — Asked at: Palantir
- Design human-in-the-loop approval for an agent that takes consequential actions. — Asked at: OpenAI, Palantir
🎯 什麼情境該想到我
當你「要把 agent 放進會產生真實後果的系統」的時候——它能發信、能改資料、能付錢、能部署。
★ 這一頁的核心命題:不可逆的動作只能靠事前把關,事後補救不存在。所以設計的第一步是「把動作分級」。
⚙️ Part 1:可逆性
★ 動作分級(所有設計的起點)
| 級別 | 例子 | 策略 |
|---|---|---|
| L0 唯讀 | 查詢、搜尋、讀檔 | 自動執行,只記錄 |
| L1 可逆寫入 | 建草稿、加標籤、寫暫存檔 | 自動執行 + undo 能力 |
| L2 難逆 | 改生產資料、合併 PR、調整設定 | 需要 dry-run + 核准,並備妥補償動作 |
| L3 不可逆 | 寄出 email、轉帳、刪除、對外發布、呼叫第三方 API | 必須人工核准,且要有延遲視窗 |
★ 面試的第一句話應該是「我會先把工具按可逆性分級」,而不是直接講技術。
沒有分級,你要嘛什麼都擋(agent 沒用)、要嘛什麼都放(遲早出事)。
讓動作變可逆的七種技術
| 技術 | 說明 |
|---|---|
| ★ Plan / Apply 兩段式 | 先產出具體的變更計畫(像 terraform plan),核准後才 apply。最好用的一招——把「模型的意圖」變成「可審查的具體 diff」 |
| 沙箱 / 影子執行 | 在資料副本或隔離環境執行,驗證無誤再套用到真實環境 |
| 軟刪除 + 版本 | 永遠不硬刪。標記 deleted_at,保留版本鏈 |
| 事務包裹 | 一組動作在同一個交易內,要嘛全成要嘛全退 |
| 補償交易(Saga) | 本質不可逆的,預先定義補償動作(退款、發更正信) |
| ★ 延遲執行 + 撤回視窗 | email 排程 5 分鐘後才真的送出,期間可撤回。成本極低、效果極好,把 L3 降成準 L2 |
| 事件溯源 | 所有變更是 append-only 事件,可重放、可回捲到任一時點 |
設計技巧:想辦法把 L3 降級成 L2。
「寄信」不可逆,但「排程 5 分鐘後寄信」在那 5 分鐘內是可逆的。很多不可逆性是可以用時間換掉的。
⚙️ Part 2:審計
審計要能回答六個問題
誰 · 什麼時候 · 以什麼身分 · 做了什麼 · 為什麼 · 依據什麼
所以每個動作至少要記:
trace_id: 關聯整條 agent 軌跡
timestamp: 時間
actor:
agent_id: 哪個 agent
agent_version: 版本(程式碼 + prompt + 工具集)
model: 模型與版本
on_behalf_of: ★ 代表哪個使用者在行動
action:
tool: 工具名
args: 完整參數
result: 完整回傳(或指標)
status: 成功 / 失敗 / 被拒絕
rationale: ★ 模型當時的推理輸出
inputs: 完整 prompt(含檢索到的文件 ID 與版本)
approval: 核准人、核准時間、核准時看到的內容三個容易漏掉但關鍵的欄位:
rationale(模型的推理) —— 事後要回答「它為什麼覺得該這麼做」,沒有這欄就只能猜inputs含檢索文件的 ID 與版本 —— 錯誤答案的根因常常在「它讀到了什麼」approval要記「核准人當時看到什麼」 —— 而不只是「有人按了同意」。否則出事時無法判斷是人看錯還是介面顯示錯
不可篡改與身分
- Append-only,不可編輯不可刪除;高合規環境要加簽章或雜湊鏈
- ★ Agent 必須有自己的身分,而不是共用一把 admin key
- agent 的權限是代理使用者的權限(on-behalf-of / 委派憑證),不是 superuser
- agent 能做的,不應該超過發起它的那個人能做的 —— 這一條是權限設計的基石
- 每個 agent 實例/每次任務發短期憑證,範圍最小化
- 日誌本身要分級與保護 —— 它包含了所有原始資料(見 權限感知檢索與引用歸因)
⚙️ Part 3:人類把關(HITL)
★ 分級核准矩陣
不要「每件事都問」。 這是這題最重要的設計判斷。
| 模型信心高 | 模型信心低 | |
|---|---|---|
| L0–L1 低風險 | 自動執行 | 自動執行 + 事後通知 |
| L2 中風險 | 自動執行 + 事後通知(可撤銷) | 事前核准 |
| L3 高風險 | 事前核准 | 事前核准 + 升級到更高層 |
| 超出範圍 | 拒絕 | 拒絕 |
其他該納入的維度:金額門檻、影響筆數、對象是否為外部、是否在營業時間。
好的核准介面要件
| 要件 | ❌ 反例 | ✅ 正例 |
|---|---|---|
| 顯示具體變更,不是意圖 | 「agent 想更新客戶資料,同意嗎?」 | 顯示 diff:客戶 #4821 的 email: a@x.com → b@y.com |
| 顯示依據 | — | 「因為工單 #993 中客戶要求更改」+ 可點開原文 |
| 顯示影響範圍 | 「批次更新」 | 「將影響 1,247 筆記錄」 ← 這個數字常常就是救命的那一眼 |
| 預設拒絕 | 逾時自動放行 | ★ 逾時 = 拒絕(fail-closed) |
| 可修改後執行 | 只有「同意 / 拒絕」 | 允許人類調整參數再執行 |
| 可追問 | — | 「為什麼你認為要這樣改?」 |
★ 核准疲勞:最大的失敗模式
低風險的核准請求太多 → 人類開始盲按 → 真正高風險的那一個也被盲按。
一個會送出 200 個核准請求的系統,等於沒有核准。
對策:
- 提高自動化門檻 —— 把低風險的改成事後通知,不要事前打斷
- ★ 聚合相似請求 —— 但聚合不等於批次盲核。聚合要顯示「共 12 筆,全部都是同一類型,這是其中的代表樣本與完整清單」
- 量核准指標 —— ★ 追蹤「拒絕率」和「核准所花的秒數」
- 拒絕率趨近 0% + 核准秒數趨近 0 → 人類已經在盲按了,這是系統失效的訊號
- 逐步放寬 —— 新 agent 先全部核准,累積出可信的拒絕率統計後再自動化低風險類別
升級與逾時
- 升級路徑:一般核准者 → 主管 → 安全團隊。依風險等級決定起點
- 逾時行為必須明確:L3 逾時 = 拒絕並回報;L2 逾時 = 拒絕或降級為草稿
- 離線/半夜怎麼辦 —— 設計時就要決定,不要讓它變成生產事故
⚠️ 注意 / 什麼時候不適用
- ★ 人類核准不是萬靈丹。人會累、會信任 agent、會被說服。核准是最後一道防線,不是第一道——前面還要有權限最小化、工具分級、輸入驗證。
- agent 可能繞過核准。用一連串「個別看起來低風險」的動作達成高風險結果(例如分 100 次各轉一小筆)。→ 要有累積額度與速率限制,不只單次判斷。
- 模型的「信心」不可靠。用它當自動化門檻要先校準,或改用可客觀判定的規則(金額、筆數、對象類型)。
- 資料外洩是另一條路。能讀資料 + 能對外呼叫 = 可外洩,即使每個動作都「合法」。見 工具集設計 與批次 3 的提示注入。
- 審計日誌會很大。要有保存期限政策與成本規劃;但高風險動作的日誌不能因為省錢而縮短保存。
- 別事後才加。可逆性與審計要在工具設計時就決定,否則之後每個工具都要改。
🧪 我實際套用的紀錄
- 2026-09-20:從面試題庫展開。待辦:把現有 agent 的工具照 L0–L3 分級一次,並確認 L3 有沒有延遲視窗。
🔗 相關
- 工具集設計 —— 分級要在工具設計時就做
- ReAct 與 Agent Loop —— 冪等性與重試
- 多 Agent 編排與 Agent Drift —— 檢查點設計
- 權限感知檢索與引用歸因 —— on-behalf-of 與資料存取
- Agent 記憶設計 —— 記憶污染會繞過事前把關
- 工具-會花錢的skill該怎麼設計 —— 成本核准的同類設計
- 工具-AI-Agent設計 —— 決策層