一次代行 Action 應如何同時保留 User intent、Agent executor 與部署方責任?
想像公司裡有一個「專案助理 Agent」。Alice 對它說:
整理客戶今天的回覆,寄給專案團隊,提醒大家明早以前確認。
Agent 讀取 CRM、產生摘要、從通訊錄找出它認為的「專案團隊」,最後呼叫寄信 Tool。信件成功寄出,但其中一個群組其實包含外部顧問,客戶尚未公開的報價因此外流。
事故調查時,大家很快會遇到一個看似簡單、實際上答不完的問題:
這些答案都只說對一部分。一次 Agent Action 通常同時存在「誰發起」、「以誰的權限為基礎」、「誰實際送出請求」、「誰核准」、「誰營運系統」與「結果發生後由誰處理」等不同角色。若把它們壓成 audit log 裡的一個 user_id,事後就只能找一個人背鍋,無法還原責任鏈。
本篇談的是 accountability architecture:系統要留下哪些 identity、delegation、decision 與 outcome evidence,才能回答誰在什麼角色下影響了這次 Action。它不是法律責任判定;法律與勞動、契約、產業監管的歸責仍須由組織依適用規範處理。
傳統的 delegated application 也會代表使用者呼叫 API,但 Agent 又多了一段會改變結果的執行鏈:自然語言 intent 先被模型解讀,再由 Agent 規劃步驟、選 Tool、組參數,甚至根據中途結果改寫後續 Action。
在剛才的例子裡,Alice 並沒有逐字指定:
send_email(
recipients=["project-all@example.com"],
attachment="customer-summary.md",
classification="confidential"
)
她提供的是目標,不是完整 API request。真正跨越安全邊界的是 Agent 後來具體化的 Action。因此即使 Alice 已通過登入、也確實有寄信權限,仍有三個傳統 User → Application → API 圖很容易藏起來的落差:
所以「User 通過 authentication」只回答 User 是誰;「Agent 有 access token」只證明某個 caller 帶著 credential。兩者都沒有單獨回答:這個 Agent 現在代表誰、這個具體 Action 為何被允許,以及實際結果由誰承接。
我們把事故現場的 audit log 分成三種常見設計。
{
"user": "alice",
"operation": "send_email",
"result": "success"
}
這筆紀錄讓人誤以為 Alice 親自選擇收件者並送信。它無法回答是哪個 Agent deployment、哪個 runtime instance、哪個版本的 planner 產生 Action,也無法區分 Alice 的原始 intent 和 Agent 補出的參數。
{
"service_account": "project-assistant",
"operation": "send_email",
"result": "success"
}
這次看得到 workload,卻不知道它是否代表 Alice、執行背景排程,或使用平台自己的 app-only authority。也看不出 Alice 是否真的委派過寄信,還是 Agent 拿先前殘留的 credential 自行重試。
{
"user": "alice",
"agent": "project-assistant",
"approval_id": "ap-2048",
"operation": "send_email",
"result": "success"
}
如果 approval 只表示「有人按過同意」,沒有綁定收件者、內容摘要、資料分類、期限與 Action digest,就無法證明核准的是最後送出的那封信。Agent 在核准後改了參數,log 看起來仍然一切合法。
這三種設計的共同問題不是 log 太少,而是把不同責任角色壓成單一 principal,並把「API 成功」誤當成「行為合理」。
W3C PROV 的 provenance model 提供了一個很好的語意起點:一個 Agent 可以 actedOnBehalfOf 另一個 Agent,而被代表的一方仍保留某種 outcome responsibility。它也允許同一 Activity 與多個 Agent 建立關聯。這正適合提醒我們:delegation 不是把責任從 A 搬走、全部丟給 B。W3C PROV-O
在企業 Agent Action 中,至少應區分以下六個角色:
| 角色 | 本篇工作定義 | 寄信情境中的例子 | 必須回答的問題 |
|---|---|---|---|
| Initiator | 發起 Task 或提供原始 intent 的人或系統 | Alice | 誰要求開始這個任務?原始要求是什麼? |
| Subject | 其身分、權益或資源關係構成此次 delegated authority 基礎的 principal | Alice | Agent 是基於誰的 authority 存取 CRM 與寄信? |
| Actor | 實際向 Tool / API 發出 Action request 的 workload 或 Agent instance | project-assistant/instance-7f3a |
哪個可驗證 runtime 真正動手? |
| Approver | 在政策要求下核准特定 Action 的人 | 專案經理 Bob | 他核准的確切內容、限制與有效期是什麼? |
| Operator | 部署、設定、監控 Agent 與其 policy 的組織角色 | AI Platform Team | 誰選定版本、Tool、policy 與運行控制? |
| Outcome accountability | 組織對不同結果預先指定的承接與升級責任;它是一套 governance mapping,不是另一個 token subject | Data Owner、Security Incident Owner | 外流、誤刪或錯寄發生後,誰必須處理、通報與改善? |
同一個人可能同時扮演兩個角色,例如 Alice 既是 initiator 也是 subject;但 schema 仍應分欄記錄,不能因這次值相同就合併概念。反過來,approver 也不會因按下核准就自動變成 actor 或 operator。
RFC 8693 OAuth 2.0 Token Exchange 將 subject_token 描述為「代表被代行一方身分的 token」,將 actor_token 描述為「代表 acting party 身分的 token」。同一份規格的 act claim 則能在 JWT 中表達 authority 已發生 delegation,並識別目前 actor;巢狀 act 還可記下先前的 delegation chain。
套到 Agent:
{
"sub": "user:alice",
"act": {
"sub": "agent:project-assistant/instance-7f3a"
},
"aud": "tool:mail-gateway",
"scope": "mail.send"
}
這段只是示意,不是宣告一套新的標準 claims profile。重點是 Tool 同時看得到:
sub:被代表的 subject;act.sub:目前真正呼叫 Tool 的 actor;aud:這份 token 預定交給哪個服務處理;還要注意 RFC 8693 的限制:較早的 nested actors 是歷史資訊,不應直接拿來作當前 access-control decision。Delegation history 能協助調查,不等於鏈上每一個 principal 都自動取得權限。
JWT 的 sub、act 或 client_id 可以傳遞身分語意,卻不會回答以下問題:
換句話說,token 是 decision input 與 evidence 的一部分,不是「責任全歸 sub」的機器可讀免責條款。NIST AI RMF 也把清楚的 organizational roles、responsibilities 與 accountability mechanisms 視為治理要求;不同 AI lifecycle actors 會因角色而有不同責任,而不是由單一技術元件承擔一切。NIST AI RMF 1.0
Accountability chain 的目的不是讓每次事件都平均分責任,而是避免只憑最後結果倒推「一定是 User」或「一定是 AI」。同一組角色在不同 failure mode 下,應接受檢查的控制點不同:
| 情境 | 應先檢查的 evidence | 可能失效的責任控制 |
|---|---|---|
| Action 符合 intent、policy 與 approval,結果也正確 | User request、allow decision、Tool outcome | 合法執行;保留正常稽核與 operator 監控,不必把成功假裝成無人負責 |
| User intent 合法,Agent 卻擴大收件者 | Intent、plan、Action diff、actor version | Agent planning guardrail、PDP parameter policy、PEP enforcement 或 operator 測試 |
| User 明確要求越權 Action | 原始 request、deny / allow decision、subject entitlement | Initiator 行為與 authorization control;安全設計應讓 Tool 即使面對惡意 intent 仍能拒絕 |
| Approver 看過 A,Agent 最後執行 B | Approval digest、executed Action digest、時間 | Approval binding、TOCTOU 防護與 PEP 比對 |
| Tool 回 success,Resource outcome 卻不符 | Tool response、Resource change record、idempotency / retry evidence | Tool implementation、下游 Resource 或 operator 的補償機制 |
這張表仍不直接宣告個人的法律過失;它把調查從「誰最後碰到系統」轉成「哪個角色應控制哪個環節、證據顯示哪裡偏離」。
要能事後回答責任問題,evidence 至少要覆蓋下面七個 checkpoint:
| Lifecycle checkpoint | 核心 evidence | 它回答的責任問題 |
|---|---|---|
| Request | task_id、initiator、原始 intent 或受控摘要、時間 |
誰要求做什麼? |
| Delegation | subject、actor、scope、purpose、期限、audience | 誰把哪些 authority 交給哪個 Agent? |
| Planning | Agent instance、版本、所選 Tool、結構化 Action | 誰把目標具體化成什麼操作? |
| Authorization | PDP decision、policy ID / version、inputs、constraints | 根據哪一版規則允許或拒絕? |
| Approval | approver、Action digest、核准條件、expiry | 人核准的是否就是將要執行的 Action? |
| Execution | PEP、Tool、request ID、實際 parameters digest | 真正送出的請求是什麼? |
| Outcome | success / failure、Resource side effect、資料分類、補償狀態 | 最後發生了什麼,而不只是 API 回了什麼? |
NIST SP 800-53 Rev. 5 的 AU-3 要求 audit record 至少能建立事件類型、時間、位置、來源、結果,以及相關 user / subject 身分;對 Agent 系統而言,這是最低線而非完整答案,因為我們還要加上 actor、delegation、policy 與 Action binding。NIST SP 800-53 Rev. 5
User Token ──> Agent ──> Tool
│
└── log: user=alice, result=success
這個設計讓 Agent 直接重用 User credential,Tool 只看 sub=alice。其後果是:

這個設計沒有假裝一張圖可以決定「誰負全部責任」。它做的是讓每一個角色在 Action lifecycle 中留下可關聯、不可任意互換的 evidence。
圖上至少有四個邊界:
任何 authority 或資料跨越邊界,都應帶著接收方能獨立驗證的 context;不能只相信 Agent 自報「Alice 叫我做的」。
一條可歸責的寄信 flow 可以是:
task_id。subject=Alice、actor=Agent instance、允許的目的與期限。task_id、action_id、trace_id 關聯回 evidence plane。OpenTelemetry Logs specification 說明了用 TraceId、SpanId 把分散元件的 log 與 trace 關聯起來的價值。不過 correlation 只代表「這些事件屬於同一次 execution context」,不等於已證明授權正確;policy decision、Action digest 與 outcome evidence 仍要由應用層明確記錄。
PDP 不應只問 subject=Alice 是否有 mail.send。它至少要評估:
subject = user:alice
actor = agent:project-assistant/instance-7f3a
delegation = task-2026-0042, purpose=project-update, expires=...
action = mail.send, recipients=[...], content_digest=...
resource = mailbox:alice, data_classification=confidential
approval = approver:bob, action_digest=..., expires=...
environment = time, network zone, risk signals
決策結果也不該只有 boolean。它可以是 deny、allow_with_constraints 或 approval_required,並附上可執行的 obligations,例如只允許公司網域收件者、移除 confidential attachment、限制寄送一次,或要求重新核准。
更重要的是:PDP 的 allow 表示當時輸入符合某版 policy,不表示所有相關人員從此免責。 若 operator 配錯 policy、Agent 執行的 Action 和送審版本不同,或 Tool 忽略 constraint,仍可沿 evidence chain 找到各自的控制失效點。
這個 PoC 不啟動 Identity Provider,也不新增 dependency。它只用 Python standard library 比較三種寄信事件,檢查事故調查所需欄位是否齊全。
from hashlib import sha256
import json
def digest(value):
canonical = json.dumps(value, sort_keys=True, separators=(",", ":"))
return sha256(canonical.encode()).hexdigest()
action = {
"operation": "mail.send",
"recipients": ["project-team@example.com"],
"content_digest": "sha256:summary-v3",
"classification": "confidential",
}
user_only = {
"task_id": "task-2026-0042",
"initiator": "user:alice",
"subject": "user:alice",
"action": action,
"outcome": {"status": "success"},
}
agent_only = {
"task_id": "task-2026-0042",
"actor": "agent:project-assistant/instance-7f3a",
"action": action,
"outcome": {"status": "success"},
}
dual_subject_actor = {
"task_id": "task-2026-0042",
"action_id": "action-0007",
"trace_id": "4fd0c8c9d5e64dd69c4a2b66e24c45e2",
"initiator": "user:alice",
"subject": "user:alice",
"actor": "agent:project-assistant/instance-7f3a",
"operator": "team:ai-platform",
"delegation": {
"purpose": "project-update",
"expires_at": "2026-08-12T10:05:00Z",
},
"action": action,
"authorization": {
"decision": "allow",
"policy": "mail-policy@42",
},
"approval": {
"approver": "user:bob",
"action_digest": digest(action),
},
"outcome": {
"status": "success",
"provider_message_id": "msg-9918",
"actual_recipient_count": 1,
},
"outcome_accountability": {
"business_owner": "team:project-office",
"incident_owner": "team:security-response",
},
}
required = {
"initiator", "subject", "actor", "operator", "delegation",
"action", "authorization", "approval", "outcome",
"outcome_accountability",
}
for name, event in {
"user_only": user_only,
"agent_only": agent_only,
"dual_subject_actor": dual_subject_actor,
}.items():
missing = sorted(required - event.keys())
approval_matches = (
event.get("approval", {}).get("action_digest")
== digest(event["action"])
)
print(name, "missing=", missing, "approval_matches=", approval_matches)
預期輸出:
user_only missing= ['actor', 'approval', 'authorization', 'delegation', 'operator', 'outcome_accountability'] approval_matches= False
agent_only missing= ['approval', 'authorization', 'delegation', 'initiator', 'operator', 'outcome_accountability', 'subject'] approval_matches= False
dual_subject_actor missing= [] approval_matches= True
這個測試證明的不是 JSON 越長越安全,而是 investigation question 需要對應欄位:只記 User 無法找出 executor,只記 Agent 無法證明 delegation,只有 approval ID 也無法證明核准內容與執行內容相同。
此 PoC 還不是 production evidence system。正式環境仍須補上可信時間來源、append-only 或 tamper-evident storage、敏感資料遮罩、retention、schema version、credential verification,以及 decision / approval 的完整性保護。完整的 evidence 設計會在 Day 26、27 再深入。
user_id。今天我們把一次 Agent Action 裡的責任角色拆開了,但還沒有系統性盤點:攻擊者能從哪裡進來、哪些資產會受影響、authority 又會跨過哪些 trust boundary。
下一篇 Day 5,我們會替一個會自主選 Tool、組參數並執行多步計畫的 Agent 畫 Threat Model,讓這條 accountability chain 變成可以逐點檢查的安全模型。