Day 27 處理了停止工具與隔離文件。今天把一次月底對帳請求交給多個 Agent,看看 fan-out、retry 與 fallback 會如何增加工作量。
我們先固定四個分支,讓每個分支的 planner primary 失敗,再安排兩次 fallback attempt。加上 retriever、executor 與工具,一次請求就會展開成 20 次 model calls、4 次 tool calls。
分支與失敗條件都由測試程式的 route metadata 控制,呼叫也只寫入本機紀錄。這次沒有載入毒文件,也沒有測 prompt injection detector;要重現的是路由如何放大呼叫與權限。接著先算整條路由會用掉多少額度,再看能否在配置與執行前拒絕超額計畫。
run_id 分區,不能把前一位租戶的紀錄算進來。D28-A01 使用的業務要求很簡單:
月底要對帳了,幫我把竹林總部這個月的費用全部匯出,我要交給財務。
使用者提出對帳需求,route、fallback 次數與預期結果則由測試程式提供,和 user_message 分開。流程如下:
Alice 的 subject 與 tenant 也要由已驗證的 principal/delegation context 帶入。光憑「我要交給財務」,還不能知道她的身分與權限。
自然業務請求
→ frozen route metadata(不傳給使用者)
→ retriever
→ planner
→ primary failure
→ retry
→ high-cost fallback
→ executor
→ export_expenses
→ fan-out × 4
Vulnerable router 讓每一跳都使用 shared-project-identity。若 route 沒有保留 delegated subject,最後一筆 receipt 只看得到某個內部 Agent 要求匯出,卻無法回答:
bamboo-hq 還是其他租戶?共享 workload identity 可以讓下游辨識是哪個服務在呼叫,但使用者脈絡與業務授權仍要另外傳遞、驗證。評估這條路由的影響時,要一起看可用權限、分支與重試次數;只數 Agent 有幾個,還看不出一次錯誤決策能執行多少工作。
先從漏洞版本的 Receipt,數出四個分支的呼叫,也檢查使用者與租戶資訊是否還在。

branch_count=4,每個分支有 5 次模型與 1 次工具呼叫,合計 logical_calls=24,其中 8 次使用高成本模型。24 筆 Receipt 都只有同一個共享 actor,缺少 subject 與 tenant。這些計數描述的是本機紀錄,沒有量測 Foundry 費用、HTTP 嘗試次數或正式環境的自主執行。
D28-A01 的輸入刻意拆成兩層。第一層是上面的 user_message;第二層才是 harness 控制的 request.metadata:
fan_out = 4
force_primary_failure = true
retry_count = 1
high_cost_route = true
每個分支包含 retriever、planner primary、兩次 fallback attempt 與 executor,共五次 model calls;四個分支再各有一次 tool call。完整投影為:
| 指標 | D28-A01 預期投影 |
|---|---|
| Model calls | 20 |
| Tool calls | 4 |
| Input tokens | 3,040 |
| Output tokens | 944 |
| Total tokens | 3,984 |
| Cost proxy | 16,536 units |
| Latency proxy | 708 units |
| Fan-out | 4 |
Token 使用測試資料裡的固定值,cost 與 latency 也依固定規則計算,方便比較路由差異。它們不是供應商的 token usage、價格或毫秒;成本比較採用下面這個公式:
Σ_i ((input_i + 3 × output_i) × multiplier_i)
+ 50 × tool_calls
20 次 model calls 中,有 12 次 standard,以及 primary 失敗後的 8 次 high_cost fallback。套入公式後,就是 2,384+13,952+tool 200=16,536。
這裡還有一個欄位要分清楚:high_cost_route=true 目前只是資料集標籤,程式實際依 force_primary_failure 進入高成本 fallback,沒有讀取前一個 flag。
Cost proxy 使用同一套固定權重,讓我們比較修補前後的路由耗用。它沒有供應商價格、快取或實際計費資料,因此不能換算成 Azure 費用。
Vulnerable path 會建立 20 筆 model receipts、4 筆 tool receipts 與 4 次 fake side effects;若 preserve_subject=False 且未另設 preserve_tenant,receipt 的 subject/tenant 都是 None,actor 仍是 shared identity。所有副作用只寫入記憶體 ledger,不會匯出真實資料。
前一張圖看漏洞版本留下的 24 筆 Receipt 與四個分支;後面的 budget 圖再看 ga_secure 預估的用量、限制,以及拒絕後的零執行次數。兩者來自同一筆 D28-A01 的前後對照。
接著先用 O(1) 的摘要估算整條路由,通過預算檢查後才建立完整計畫。

模型、工具與分支的預估用量超過上限,token 與成本則還在範圍內;計畫另外包含 4 次會產生副作用的工具呼叫。畫面列出預估用量、上限與拒絕後的結果:materialized=false,實際模型、工具與副作用次數都為 0;三個拒絕碼列在下方文字中。這份估算只涵蓋已知路由,執行時動態新增的分支仍需另行檢查。
安全路徑先用 O(1) 的 RouteProjection 計算模型、工具、token、成本與分支數,確認可執行後才建立各筆工作。若先配置大量物件,再回頭說超額,記憶體可能已經用掉了。順序如下:
projection = router.project(
task,
subject="alice",
fan_out=4,
force_primary_failure=True,
retry_count=1,
high_cost_route=True,
)
failures = route_limit_failures(projection, limits)
if failures:
actual = {
"model_receipts": [],
"tool_receipts": [],
"side_effect_count": 0,
}
else:
plan = router.materialize(projection)
actual = router.execute(
plan,
preserve_subject=True,
allow_side_effects=True,
)
所有 Day 28 路由都用同一組預算:12 model calls、2 tool calls、5,000 total tokens、50,000 cost-proxy units 與 fan-out 2。D28-A01 的投影是 20 次模型、4 次工具與 4 個分支,因此得到:
MODEL_CALL_BUDGET_EXCEEDED
TOOL_CALL_BUDGET_EXCEEDED
FAN_OUT_BUDGET_EXCEEDED
這組預算是部署時先訂好的設定,不會因為資料集把這筆案例標成攻擊就收緊。原本的重放程式會依 attack 標籤切換一組更嚴格的上限,等於控制事先偷看了答案;外部審查後改成單一預算,並加上回歸測試:把 D28-A01、D28-B01 的標籤對調,拒絕與放行的結果都不變。案例若沒有寫明 route_tool,重放會直接拒絕,不再依標籤猜一個工具。
程式先回傳這三個 reason code,接著拒絕進入 materialize() 與 router.execute()。測試要確認的是這個順序:若先建好整份 route,才判定超額,仍可能耗掉大量記憶體;若工具已執行,事後的 counter 更無法撤回副作用。使用者不需要在對話裡提供這些代碼。
InMemoryRouteExecutionLedger 另外記錄實際執行,讓我們能獨立核對 router 的回傳值。漏洞路徑留下 20 次 model、4 次 tool 與 4 次合成副作用,安全路徑則全部為 0。
每次執行都有唯一 run_id,寫入時加鎖,讀取時只取這次 run 的分區。因此連續重用 router、換成不同 tenant,也不會混入前一次的 Receipt。這份 ledger 仍只存在本機 process,沒有跨 process 的持久化紀錄。
另有三個本機配置上限:fan-out ≤ 64、retry ≤ 16、projected calls ≤ 1,024。它們限制 list allocation 的規模;每次業務請求仍要接受較低的 route budget,不能把配置上限當成 Foundry quota 或已核准額度。
再看合法路由,核對外層與內層 Receipt 的身分、操作欄位,以及這次的用量代理值。

subject_preserved=true、receipt_fields_match=true,外層與內層的 tool、resource、tenant、subject、branch、side_effect 都相同。模型/工具呼叫為 3/1,tokens 為 412,cost 為 646,latency 為 69,副作用為 0。這張圖沒有驗證共用或逐次扣除的預算帳本;這些用量也是固定規則的代理值,不能當成雲端 token、帳單、實際延遲或供應商重試次數。
RouteProjection 只保存可用 O(1) 計算的 scalar route metadata;通過後的 RoutePlan 才 materialize 每次 call、fallback、attempt、branch 與 route tier。執行中若加入新 retry 或動態工具,必須從相同 budget reservation 扣除並再次檢查;不能讓每個 Agent 都宣稱 「我只呼叫一次」。
Receipt 也分開兩個容易混淆的欄位:
actor:真正送出本次呼叫的 Agent workload identity。subject:這次工作代表的使用者;沒有使用者時要明確為空,並走 application authority。trace_id 用來關聯各跳紀錄。若第二跳遺失 subject,下游仍應拒絕需要使用者權限的操作;找到相同 trace,不代表可以改用共享服務的較大權限。
D28-B01 的使用者只問:「幫我查一下 exp-bamboo-001 現在處理到哪裡了?」對應的 harness metadata 是單一分支、無 fallback 的 get_expense:
| 指標 | D28-B01 預期值 |
|---|---|
| Model calls | 3 |
| Tool calls | 1 |
| Total tokens | 412 |
| Cost proxy | 646 units |
| Latency proxy | 69 units |
| Side effects | 0 |
外層 proposal 與 executor receipt 都要綁定 get_expense、exp-bamboo-001、bamboo-hq、alice、branch 0,並標示 side_effect=false。D28-B01 與 D28-A01 用的是同一組預算;這筆 benign case 在同樣的上限下通過,才表示 preflight 的確縮小路由,同時保留合法工作。
畫面只重放 D28-B01,顯示外層與 executor receipt 的 tool、resource、tenant、subject、branch 與 side-effect 分類完全一致;同時保留固定的 token/cost/latency proxy 標籤。
從 day28/ 執行:
env PYTHONPATH=src:. PYTHONDONTWRITEBYTECODE=1 \
PYTHONNOUSERSITE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 \
python -m pytest tests/stages/day28/test_acceptance.py -q -p no:cacheprovider
Acceptance suite 要驗證:
None。run_id partition。materialize() 根本沒有被呼叫。get_expense 保留 Alice、bamboo-hq 與 exp-bamboo-001,並產生一張 side_effect=false 的 tool receipt。attack 標籤後,重放的決策與 Receipt 數量都不變;拿掉 route_tool 則得到 ROUTE_TOOL_REQUIRED。當日驗收記錄為 7 passed,包含連續重用 ledger、切換租戶的測試。D28-A01/B01 的固定案例則另由 renderer 重放;測試項目與案例筆數分開計算。
Day 25 已經討論 benchmark 與業務結果的差別。這裡接著看 MDASH 報告中的模型分工與 routing,對照我們需要保存哪些預算證據。
MAI-Cyber-1-Flash 值得推薦的另一個原因,是它展示了專用模型如何和通用模型分工。Microsoft 的 MDASH 報告讓它處理最多約 90% 的工作,再把特別困難的部分交給 GPT-5.4;所稱的 50% 成本節省,也是相對該報告指定的舊 MDASH 組合。這提供了路由設計的參考,沒有保證每個工作負載都能省一半。Microsoft 的模型分工說明
對程式漏洞分析,我會優先評估 MAI-Cyber-1-Flash 搭配 MDASH;對本系列一般的提案解析與 Agent 範例,則統一使用 gpt-6-luna。MDASH 的 MAI Cyber 配置仍是 Preview,並有文件指定的其他模型與部署要求,不是替換一個 model 字串就能接好。官方整合方式
Day 25 已區分 CyberGym 的三種評分;本篇也沒有新增雲端路由或成本實測。下面的 primary、fallback 與高成本路由仍是本機模擬,不能把它們的計數直接當成 Luna 或 MAI-Cyber-1-Flash 的帳單。
本系列真正借用的工程判斷只有這一個:
專用模型/primary/fallback/高成本模型
都要留下 route decision、品質 oracle 與 budget receipt
換成工程司的工作負載後,primary 與 fallback 的比例仍要靠自己的品質、成本與失敗資料決定。90/10 是該份報告的配置描述,不能直接當成這個 Agent 的最佳比例。
今天的四個 route 分支若真的呼叫 Foundry 模型,必須把每次 fan-out、retry 與 fallback 的 request identity 接回同一個 root request,並把服務回傳的 token usage 和 Azure 計費資料分開保存。前者有助於找出是哪條路把一次使用者要求放大;後者才能回答實際花費。本機 cost proxy 只適合比較路由策略,不能換算成雲端帳單。
Foundry 可以承載模型、Agent、tracing、evaluation 與多 Agent 整合,但 route budget 與業務工具 PEP 仍由應用程式負責。各項功能的狀態如下:
Foundry 的 visual designer 與 Portal 內 workflow 執行將在 2026-12-01 停止支援。既有 YAML 可部署成 hosted agent,新編排則建議使用 Microsoft Agent Framework;不論採哪條路徑,前面的 route budget 都還是應用程式要守住的限制。官方遷移說明。
本篇沒有執行 Foundry model call、A2A 或 billing query。前面的 token、成本與延遲數值,都用來比較本機固定路由。
NIST SP 800-207A 支持 cloud-native application/service identity 與 granular application policy;「每一跳重驗、child 能力只能維持或縮小」則是本 repo 自訂的 route invariant。這是作者的工程映射,不是 NIST token schema 或 conformance claim;AI 600-1 的 value-chain 視角也只用來提醒我們把 fallback model、資料來源與工具寫進 evidence。
Preflight 依賴 projection 完整。若 runtime 執行中自行新增工具、retry 或 fallback,budget counter 又沒有重新檢查,原本通過的計畫仍會漂移。Process-local ledger 已按 run_id 隔離並對記憶體寫入加鎖,但沒有跨 process 的 durable reservation 或時間窗 quota。Deterministic proxy 沒有包含供應商即時價格、快取、並行計費、網路等待或 quota;ga_secure 只是本機 profile 名稱,不是 Foundry GA 的證明。
本日已觀察 shared identity 的問題,尚未完成委派驗證。Receipt 裡出現 subject="alice",還要能追到可信的來源。Day 29 接著驗證每一跳的 issuer、subject、tenant、audience、capability、resource、depth、expiry 與 nonce,也會交代已驗證 claims 與實際工具之間尚缺的綁定。