跳到主要內容
邊界實驗室 · Boundary Lab
正在啟動 Python 環境(首次約 15 秒)...

Lab:Capability-based agent harness

前三課都在拆觀念。這一課把 capability、tool gate、audit log 接成一段真的能跑的最小骨架。

Hook

到這裡, 你手上其實已經有幾塊拼圖:

  • Lesson 02 的 per-agent capability set
  • Lesson 03 的拆 autonomy / data / tools 心智模型
  • Lesson 04 的 tool wrapper 與 audit log 欄位 如果這些東西一直分開看, 你很容易覺得: 「概念我懂, 但落地時到底長什麼樣子?」 所以這堂 lab 的目標很直接。 我們不做完整平台。 不接真 GitHub。 不接真資料庫。 不做完整 RBAC service。 我們只做一個 大約 80 行等級的 最小 capability-based harness。 它要證明四件事:
  1. agent 啟動時有自己的 required_caps
  2. runtime 真的會檢查 capability
  3. read-only agent 在 write 步驟真的會被擋下
  4. audit log 真的會留下 blocked 與 success 的差異

換句話說, 這不是展示語法。 這是一個最小可執行證明。 你可以把它抄進自己的內部工具裡, 再慢慢把 mock tool 換成真實 side effect。

Learn

這個 lab 在驗什麼

先把成功標準講清楚。 這個 lab 不是在證明:

  • 你已經做出完整權限平台
  • 你已經解完 multi-tenant RBAC
  • 你有 production-ready policy engine 它要驗的其實更小, 但也更關鍵: | 驗證點 | 你要看到什麼 | |---|---| | per-agent caps | 三個 agent 拿到不同 capability set | | runtime gate | tool call 進來時會比對 capability | | blocked write | read-only agent 讀得到,但寫不到 | | audit visibility | 成功與阻擋都會留下 audit entry | 做到這四件事, 你就已經把: 「權限邊界很重要」 變成一段可 review、 可測試、 可重複執行的程式。

我們只需要兩個 dataclass 跟三個核心 function

這份 lab 的骨架非常小。 只有:

Capability
Agent
check_capability(...)
audit_log(...)
run_tool(...)

其中:

  • Capability 描述一項能力
  • Agent 描述誰持有哪些能力
  • check_capability(...) 判斷某個需求是否被滿足
  • audit_log(...) 記錄每次工具呼叫的 outcome
  • run_tool(...) 把檢查與執行包在一起 你可以把它想成: 最小 policy engine 加上 最小 tool executor。

啟動時宣告 required_caps

這一點特別重要。 很多團隊的 tool gate 只有在執行瞬間才硬編碼需求。 但較好的做法是: 任務一開始就把自己需要的 capability 宣告成資料。 也就是: required_caps 不是散在各處的字串。 而是一份可以被檢視的需求表。 這樣做的好處是:

  • 任務缺權限時提早看得出來
  • review 時能直接比對「持有」與「需求」
  • 後續想接 approval 或 policy report 比較容易

Reference code:最小 capability harness

下面這段 code 是完整可跑的。 只用 Python 標準函式庫。 沒有假 import。 沒有外部相依。 你會看到:

  1. 三個 agent:read-only / write / full-access
  2. 同一個 task:先 github.read,再 github.write
  3. read-only agent 在 write 被擋下
  4. write 與 full-access agent 成功通過
  5. audit log 最後統計出 blocked 與 success

如果一切正常, 你應該看到:

== agent-read-only ==
audit = {'agent_id': 'agent-read-only', 'tool': 'github.read', 'args_hash': 'cd235278fb6b', 'outcome': 'success'}
read = README snapshot
audit = {'agent_id': 'agent-read-only', 'tool': 'github.write', 'args_hash': 'b10688d5d592', 'outcome': 'blocked'}
write_error = agent-read-only lacks github.write on repo:abd-ids-class

== agent-write ==
audit = {'agent_id': 'agent-write', 'tool': 'github.read', 'args_hash': 'cd235278fb6b', 'outcome': 'success'}
read = README snapshot
audit = {'agent_id': 'agent-write', 'tool': 'github.write', 'args_hash': 'b10688d5d592', 'outcome': 'success'}
write = comment-created

== agent-full-access ==
audit = {'agent_id': 'agent-full-access', 'tool': 'github.read', 'args_hash': 'cd235278fb6b', 'outcome': 'success'}
read = README snapshot
audit = {'agent_id': 'agent-full-access', 'tool': 'github.write', 'args_hash': 'b10688d5d592', 'outcome': 'success'}
write = comment-created

audit_summary = {'blocked': 1, 'success': 5}
comments = [{'repo': 'repo:abd-ids-class', 'text': 'LGTM after capability check'}, {'repo': 'repo:abd-ids-class', 'text': 'LGTM after capability check'}]

這就是這堂 lab 的最小證明。 不是用口頭說 read-only agent 不能寫。 而是讓它真的在 runtime 被擋掉。

check_capability(...) 真正做了什麼

這個 function 很小。 但它做了整個權限模型最核心的一件事: 把「持有的能力」 和 「要求的能力」 做比對。 這份 demo 的規則只有兩條:

  1. name 必須相同
  2. scope 必須相同,或持有的是 * 真系統裡, 你還可以再長出:
  • branch prefix match
  • namespace hierarchy
  • expires_at
  • environment bound 但這個最小版本已經夠表達重點。

audit_log(...) 的價值,不只是除錯

很多人看到 audit_log 第一反應是除錯方便。 但在權限邊界裡, 它其實也是 policy 可觀測性。 如果沒有 audit, 你很難回答:

  • 哪個 agent 最常被 block
  • 哪個 tool 常被誤用
  • 哪種 capability 其實配置錯了 所以 audit 不是最後才補。 而是 capability harness 本來就該帶著的東西。

為什麼 run_tool(...) 比把檢查散在各 tool 裡更好

你當然也可以 在每個 tool function 裡 自己寫 permission check。 但那樣很容易變成:

  • A tool 有記 audit
  • B tool 沒記
  • C tool 有 scope check
  • D tool 忘了 blocked outcome 把這些邏輯收進 run_tool(...) 的好處是: 所有 tool 都走同一條 enforcement path。 這就是 harness 的意義。 它不是多一層抽象而已。 它是把邊界收斂成一個可重用入口。

為什麼 full-access agent 仍然值得保留在 demo

有些人會問: 既然 write_agent 已經能成功寫了, 為什麼還要 full_access_agent? 因為它提醒你另一個重要現象: 同一個 task 在高權限 agent 身上看起來也能正常通過。 這正是高權限最危險的地方。 你平常看不到問題。 功能都會成功。 直到某一天, 它碰到另一個更危險的 tool, 或被帶偏到另一條路徑。 所以 full_access_agent 不是拿來證明「它也會成功」。 而是提醒你:

成功執行 不代表權限設計合理。

你帶回真系統時,最先該升級哪三件事

這份 lab 刻意做得小。 所以它還沒處理很多現實問題。 如果你要把它帶回專案, 我會建議先升級這三個地方:

  1. Capability 增加時間邊界,例如 expires_at
  2. audit_log(...) 加上 principalrequest_id
  3. check_capability(...) 把 scope 比對做成真正的 resource matcher 這三步做完, 你就會從 demo 跨到一個相當像樣的內部 harness。

這堂 lab 還沒做哪些事

這裡也要誠實。 這份 code 還沒做:

  • 真正的 identity binding
  • 真的 external side-effect tool
  • capability source of truth 來自 DB 或 YAML
  • rich policy reason code
  • denied event 的告警或自動分析 但這不影響它的教學價值。 因為它的目標很明確: 先把 capability check 做成一個最小可執行單位。

這一課你應該帶走的三個名字

如果只能記三個名字, 就記:

  • check_capability(...)
  • audit_log(...)
  • run_tool(...) 第一個負責判斷。 第二個負責留下痕跡。 第三個把兩者包進實際執行。 這三個一起工作, 才叫 capability-based harness。 不是因為你在 README 裡 寫了「我們遵循 least privilege」。

AI 協作:學了這個,跟 AI 怎麼配合?

這個技能在 AI 協作中的定位, 是讓你可以要求 AI 幫你生成一個最小可跑的權限骨架, 然後你再用真實 capability 與真實 policy 把它補上。 你的人類優勢:

  • 只有你知道哪些 capability 在你的系統裡需要 wildcard,哪些其實必須綁到單一 repo、單一 branch、單一 namespace。
  • 只有你能判斷 audit log 裡哪些欄位足夠追責,哪些欄位若記原文反而會造成新的資料暴露面。 可以這樣跟 AI 說:

請幫我做一個最小 capability-based harness。需要有 CapabilityAgentcheck_capabilityaudit_logrun_tool。請用三個 agent 測同一個 task:read-only、write、full-access。read-only 要在 write 被擋下,其他兩個成功,最後輸出 blocked / success 的 audit summary。只用 Python 標準函式庫。

Do

互動示範

DEMO 1可以修改程式碼試玩

挑戰任務

Task 1

請修改這堂 lab 的 Capability,加上一個 expires_at: datetime 欄位,並讓 check_capability(...) 額外檢查 capability 尚未過期。測試情境:建立一個 github.write capability,但 expires_at 是昨天;對它做檢查時,程式最後只印出 False

BackTake the Exam →