做 Agent 安全審查時,我們很容易把注意力放在單次工具呼叫:這次讀檔是否允許、這次 HTTP 請求能不能送、這條 shell 指令危不危險。每一關分開看都合理,也確實比完全沒有控制好很多。
但 Agent 的能力,偏偏來自它會把許多看似無害的小動作串起來。讀取一份資料、整理成摘要、交給另一個工具,再透過網路送出去;每一步單獨看都可能符合規則,合在一起卻已完成一次資料外洩。這讓我重新確認一件事:Agent 安全的審查單位不能只是一個 tool call,而必須是整個任務的資料流。
攻擊不一定會一次說完
傳統的權限檢查很習慣處理明確動作。某個身份能不能讀這張表、能不能寫這個目錄、能不能呼叫某支 API,答案通常可以落在一條 ACL 或 RBAC 規則上。
Agent 的執行方式不同。它會反覆觀察結果、調整計畫,再選下一個工具。惡意內容也不必在同一次互動中完整表達意圖。第一段只要求找出某個檔案,第二段要求摘要特定欄位,第三段把結果包裝成除錯資訊,最後才要求送到外部服務。
如果防線每次都只看當下參數,就會得到四個「似乎可以」:讀檔是工作所需、摘要是一般處理、格式化沒有副作用、HTTP 呼叫也在允許清單裡。問題是,安全風險不在任何單一步驟,而在資料從敏感來源一路流到外部目的地的組合。
這和資料庫權限不太一樣,反而更接近資訊流控制。系統不只要知道「誰能呼叫什麼」,還要知道「這份輸出帶著什麼來源與敏感性,接下來能流向哪裡」。
工具權限要分離,不能靠模型自律
我現在看 Agent 工具配置,會特別注意三類能力是否同時落在同一個執行身份上:讀取敏感資料、執行通用運算,以及向外傳送內容。
三者各自都有正當用途。檔案工具要讀設定與文件,shell 要跑測試與轉檔,網路工具要查詢 API 或回報結果。但把它們無條件放在一起,就等於給 Agent 一條完整的資料搬運管線。此時即使每個工具都有 allowlist,組合能力仍可能遠大於設計者原本想像。
比較務實的做法,不是把所有工具都關掉,而是依任務動態組裝能力。需要整理內部文件的任務,可以取得讀取與運算能力,但預設沒有任意連外;需要查外部資料的任務,可以使用網路查詢,卻不該同時讀取敏感目錄。真的需要跨越兩邊時,再透過明確的中介層、內容遮罩或人工核准完成。
這種分離應由 runtime、容器、服務端 scope 與網路政策落實,而不是在 prompt 裡提醒模型「不要洩漏資料」。Prompt 能描述意圖,不能充當資料防火牆。
Provenance 不能停在輸入端
很多團隊已經開始替外部網頁、郵件或文件標記「不可信來源」,這是好事。但如果標記只在內容第一次進入上下文時存在,經過摘要、轉檔或子代理交接後就消失,保護效果仍然有限。
資料的 provenance 應該跟著衍生物一起走。某段摘要雖然由內部模型生成,但它若源自外部網頁,仍應保留外部來源標記;某份報表若混入受限欄位,轉成 JSON、Markdown 或壓縮檔後,敏感等級也不該自動歸零。
這要求工具回傳不只是一段文字,而要帶有可供政策判斷的 metadata:來源、資料分類、產生方式、任務識別、轉換紀錄與允許用途。當下一個工具準備接收內容時,runtime 才能判斷這條資料流是否合理,而不是把決定全丟給模型。
稽核也要從 call log 升級成 run graph
只記錄「哪個工具在幾點被呼叫」已經不夠。事故發生後,真正需要回答的是:敏感資料從哪裡進來、經過哪些轉換、哪個決策讓它跨越信任邊界,以及最後去了哪裡。
因此,我會把一次 Agent 任務視為一張可追蹤的執行圖。節點包含模型判斷、工具呼叫、人工核准與資料轉換;邊則記錄輸入輸出的來源關係。政策檢查不只發生在每個節點,也要能針對一段路徑判斷,例如「來自受限目錄的內容,不得在未遮罩且未核准的情況下流向公開網路」。
這樣的紀錄不只是為了事後究責。它也能拿來做測試:準備一組會把惡意意圖拆成多步驟的情境,確認 runtime 是否能在資料真正越界前阻止,而不是只測一個明顯危險的指令會不會被拒絕。
Agent 的風險存在於組合,而不是清單
企業導入 Agent 時,常會做一份工具清單,逐項評估風險等級。這是必要的起點,卻不是終點。兩個低風險工具組合起來,可能產生高風險能力;四個合規的步驟串在一起,也可能完成一件不合規的事。
所以我現在更在意的,不只是 Agent 擁有哪些工具,而是資料能在這些工具之間怎麼走。身份與 RBAC 回答誰能開門,資料流政策則回答東西進門之後能被搬到哪裡。兩者缺一不可。
當 Agent 從一次問答走向長時間、多工具、多代理的執行系統,安全邊界也得跟著升級。別再只問「這一次呼叫可不可以」,還要問「如果這一步接在前面那些步驟之後,整件事還可不可以」。這才是面對 Agent 組合能力時,真正有用的安全問題。