Scoped Permissions per Agent
共用 token 看起來省事,其實是把不同 agent 的 blast radius 疊成同一顆炸彈。
Hook
你有兩個 agent。 一個做 code review。 一個做 deploy。 前者每天讀 PR、 看 diff、 留 review comment。 後者負責把 release branch 推到 production。 你一開始圖方便, 讓兩個 agent 共用同一組 GitHub token。
那其實就是你自己的 personal token。 scope 很大。 因為這樣最快。 review agent 很快上線。 它會自己補 comment。 會自己幫忙 rebase。 某天它在整理 branch 的過程裡 判斷衝突太麻煩,
直接 force push 到 prod branch。 你事後沒有發現複雜攻擊。 也沒有發現奇怪 prompt。 你只發現一件很基本的事: review agent 跟 deploy agent 本來就不該拿一樣的權限。 它們的工作不同。 動作不同。
risk profile 不同。 但你給了它們同一把萬能鑰匙。 這一課要講的, 就是怎麼把權限拆到 agent 粒度。 不是拆到「整個 AI 平台」。 也不是拆到「所有人共用一組角色」。 而是: 每個 agent,
都應該有自己獨立的 capability set。
Learn
先記住這句話
有句話很值得直接背起來:
permissions ARE the security model 這不是修辭。 它的意思很直接。 你今天的安全邊界, 不是你寫了幾條 prompt。 不是你加了幾個 warning。 不是你在文件裡說
「這個 agent 不要亂來」。 真正決定邊界的, 是 runtime 裡 哪些 capability 真的能被用出來。 如果 review agent 手上有 deploy 權限, 那它就不是「一個只會 review 的 agent」。 它其實是一個帶著 deploy 能力的 agent,
只是平常沒用到而已。 這個差別非常重要。
為什麼一定要 per-agent
很多團隊在設計權限時, 會先想到 per-user。 再進一步一點, 會想到 per-service。 但 agent 系統還要再往下一層。 你需要 per-agent。 因為兩個 agent 就算都在同一個產品裡,
它們的目標、 輸入、 失誤模式, 也完全不同。 看一個最小例子:
| Agent | 應該有的能力 | 不該有的能力 |
|---|---|---|
code-review-agent | repo 讀取、PR comment 寫入 | force push、deploy、DB 寫入 |
deploy-agent | push 指定 release branch、建立 tag、觸發 deploy | 讀 HR 文件、改社群貼文 |
social-media-agent | 排程貼文、讀素材庫 | 讀 production DB、改 GitHub branch |
data-analysis-agent | DB 唯讀、匯出 report 到內部 bucket | delete table、send external email |
| 這張表的重點, | ||
| 不是它多完整。 | ||
| 而是它讓你看到一件事: | ||
| agent 身份本身 |
就應該是一個權限切分單位。 不是所有 AI worker 共用一個大角色。
Per-agent capability set 的核心價值是縮 blast radius
權限分開最直接的收益,
不是「概念比較乾淨」。
而是 blast radius 變小。
如果 code-review-agent
只拿得到:
-
github.readonrepo:org/app -
github.comment.writeonrepo:org/app那它就算被 prompt 帶偏, 最多也只是: -
留錯 comment
-
誤判 diff
-
在 review 流程裡製造噪音 它碰不到:
-
production deploy
-
customer email
-
DB delete 這就叫隔離。 隔離不是保證零錯誤。 隔離是在錯誤發生時, 把損害壓在一個比較小的房間裡。
權限邊界有三個維度
這一課你可以用三個問題來設計 capability。 不是只問「有或沒有」。 而是問:
what scopewhat timewhat target
一、what scope:它可以碰哪些資源
scope 是最直覺的一維。
同樣叫 github.write,
scope 不同,
風險就差很多。
例如:
repo:org/course-siterepo:org/prod-infrabranch:release/*branch:*如果 deploy agent 只需要推 release branch, 那branch:release/*
就比 branch:*
合理得多。
如果 social-media agent
只需要讀素材庫,
那它根本不該看到 DB scope。
所以 scope 不是 decoration。
它就是 capability 的一部分。
二、what time:它什麼時候有效
很多人設計權限時 只想到資源範圍, 忘了時間也是邊界。 例如:
- deploy token 只在 release window 有效
- incident-response agent 的 elevated cap 只開 30 分鐘
- contractor agent 的 access 到專案結束日自動失效 這些都不是附加功能。 它們是減少長期暴露面的方式。 一個「平常用不到但永久存在」的高權限,
本身就是風險。
三、what target:它是為誰做這件事
這一維很多團隊最晚想到。
但它非常重要。
同樣一個 comment.write
可以是:
- 只允許對自己的 PR 留言
- 只允許對指定 repo 留言
- 只允許 acting for 某個 principal 當 agent 有 ability 代人做事時, target boundary
就決定了它到底能替誰執行什麼。 這會直接接到下一課的 identity abuse。
用 capability table,而不是散落的 if-else
很多專案一開始沒有正式權限模型。 所以能力檢查會長成這樣:
-
某個 tool 裡寫一個
if agent_name == "...": -
另一個 API route 再補一段角色判斷
-
第三個地方靠 prompt 寫註解 這樣最後沒有模型, 只有碎片。 比較好的做法, 是先把權限整理成一個 capability table。 它可以長在:
-
dataclass
-
YAML
-
DB row
-
policy service 先看最小 dataclass 版本:
這段 code 很小。 但它做了一個很重要的事: 它把「某個 agent 可以做什麼」 從自然語言變成資料結構。 一旦變成資料結構,
你才能:
- review
- diff
- 測試
- audit
Runtime gate 要存在於 tool 入口
只有 capability table 還不夠。 你還要在 tool 執行的那一刻檢查。 也就是說, 判斷權限的地方, 應該盡量靠近 side effect。 這樣做的好處是: 就算上游 agent planner 判錯, 真正的 tool 仍然會擋。
最常見的實作方式, 是用 decorator 或 wrapper。 例如:
這裡先用最小邏輯示意。 真系統裡, 你通常還會補:
- scope wildcard
- expires_at
- acting_for principal
- audit log
但核心概念不變: tool 自己知道 呼叫它需要什麼 capability。
先在資料層宣告,再在執行層驗證
這一點值得強調。 很多團隊會問: 「我到底要把 required capability 寫在 agent 還是 tool?」 比較實際的答案是: 兩邊都要有, 但角色不同。 agent profile 上的 capability set 是在說:
「它目前持有哪些能力。」 tool declaration 或 task spec 上的 required caps 是在說: 「要做這一步需要哪些能力。」 一個是 possession。 一個是 requirement。 兩者比對之後, runtime 才能決定放不放行。
不該怎麼做:共用 token
這是最常見的反模式。 因為它看起來最省事。 但共用 token 的問題, 不只是不夠細。 它還會直接抹平責任邊界。 當 audit log 只看得到同一個 credential, 你很難回答:
- 到底是哪個 agent 做的
- 它本來應不應該能做
- 這是 policy 漏洞還是實作 bug
所以共用 token 的成本, 不只是安全。 也是可觀測性。
不該怎麼做:admin 全包
第二個常見反模式, 是每個 agent 起手式都給 admin。 理由通常是:
- demo 趕時間
- scope 太細不好設
- 怕之後少一個權限又壞掉 這三個理由都很現實。 但它們也都只是把設計債往後丟。 當你把 admin 當預設, 你其實是在說:
「我放棄用權限作為安全模型。」 之後任何 prompt 漏洞、 任何 tool bug、 任何人為誤判, 都會直接撞到最大權限面。
不該怎麼做:靠 prompt 提醒 LLM 不要亂來
這也是反模式。 因為 prompt 可以做的是偏好引導。 它不能代替 enforcement。 如果你的 deploy token 真的能 force push, 那你在 system prompt 寫: 「除非使用者明確要求,不要 force push」 並不構成邊界。
真正構成邊界的, 只有:
- token scope 根本不含 force push
- server 明確拒絕該 action
- workflow 需要人類 approval 其餘都只是建議。
你可以怎麼開始拆
如果你現在手上已經有一個 共用權限的大 agent 系統, 不用一次做完整 RBAC。 先做三步就夠有感:
- 先列出所有 agent 與所有 tool
- 為每個 agent 做一張 capability table
- 先把最明顯不需要的高權限拿掉 例如:
- review agent 拿掉 push
- analytics agent 拿掉 DB write
- social agent 拿掉 internal repo read 這種刪法通常不會破核心流程,
但能立即縮風險面。
一張可以帶去設計會議的檢查表
下次你 review agent 架構時, 先問這五題:
| 問題 | 你要逼自己講清楚的事 |
|---|---|
| 這個 agent 的任務是什麼? | 任務一句話定義,不要把整個平台都塞進去 |
| 它需要哪些 capability? | 用名單列出,不要只說「大概要 GitHub access」 |
| scope 有沒有縮到最小? | repo、branch、table、namespace、tenant |
| 有沒有時間邊界? | 是否短期有效、是否能自動失效 |
| 這個 capability 是否跟其他 agent 重複共用? | 若有,為什麼真的需要共用 |
| 答不清楚, | |
| 就表示你還沒有完成 permission design。 | |
| 不是只有程式還沒寫。 |
這一課你先帶走兩句話
第一句:
per-agent capability set 的目的,不是形式美觀,而是把 blast radius 壓小。 第二句: 權限不是附屬設定;permissions ARE the security model。 後面兩課, 我們會把這件事再往前推:
- 第三課談 trifecta 要優先拆哪一面
- 第四課談 tool misuse 跟 identity binding
- 第五課把 capability table 寫成可跑的 harness
AI 協作:學了這個,跟 AI 怎麼配合?
這個技能在 AI 協作中的定位, 是讓你要求 AI 幫你盤點每個 agent 的最小 capability set, 而不是請它幫你想一個「超強全能 agent」。 你的人類優勢:
- 只有你知道哪些 agent 在業務上真的需要跨 repo、跨環境、跨 principal,哪些其實是歷史方便留下來的過量權限。
- 只有你能判斷某個 capability 缺了會不會中斷關鍵流程,或其實只是少了一個可有可無的自動化捷徑。 可以這樣跟 AI 說:
我有 4 個 agent:review、deploy、social、analytics。請先不要提 prompt,先幫我為每個 agent 列一張 capability table,欄位包含 capability、scope、time bound、target bound、是否任務必要。最後指出哪些能力其實不該共用同一組 credential。
Do
互動示範
挑戰任務
請寫一個 @requires_capability("github.write") decorator。測試情境:review_agent 只有 {"github.read"},deploy_agent 有 {"github.read", "github.write"}。用 decorator 包住 push_release(agent) 後,讓 review_agent 呼叫它時被擋下,並且整支程式最後只印出 blocked。