一個被拒絕的動作,以及它沒有寫出的檔案
DENIED 標記是頁面自己畫給自己看的標籤,而把它錄下來只是同一個標籤加上了製作品質。這一頁改為公布一次執行:一個真實的代理工具呼叫,被已發行 SDK 自身的強制點拒絕,而該工具存在的目的所要產生的效果,事後由一個未參與該決策的程式讀回。
被要求的是什麼,走的是哪一條路徑
一個名為 write_incident_report 的代理工具,會把一個叫做 incident-report.txt 的報告檔案寫進工作目錄。那個檔案就是副作用 — 是這個工具存在的目的所要產生的東西,也是讀者不必相信任何人就能自行尋找的東西。政策依一條名為 deny_filesystem_writes 的規則拒絕這個工具。
這個呼叫走的是框架一般的工具執行路徑 smolagents.tools.Tool.__call__。拒絕它的元件是 agent_assembly.adapters.smolagents.SmolagentsPatch — 隨 SDK 出貨,不是為這一頁寫的。它先取得決策,並以一個拒絕回覆取代工具主體的執行結果交還給呼叫端。
決策本身來自 in-process interceptor, enforce posture,而不是一個運行中的閘道。這是這次執行所能顯示範圍上的真實限制,在此明說而非留待推論:被實際走過的,是從一個拒絕裁決到一個沒有執行的主體之間那段路徑,而不是該裁決的產生過程。
四個步驟,依實際發生的順序
- 工具呼叫被發出代理以框架呼叫任何工具的相同方式呼叫這個工具。工具自己的程式碼完全不知道它正被治理。
- 產生一個決策,而且是先產生強制點在轉發這個呼叫之前先要求一個裁決。它據以行動的那筆紀錄逐欄列在下方。
- 呼叫端收到的是拒絕,不是結果工具主體沒有被觸及。呼叫端收到的是一段拒絕字串,而那本身仍然只是一個標籤。
- 另一個行程去看那個目錄這一步才讓前面三步值得一讀。這份目錄清單是由一個未參與該決策的程式取得的。
強制點據以行動的那筆決策紀錄
| 決策欄位 | 記錄到的值 |
|---|---|
| tool_name | write_incident_report |
| args.directory | <workdir> |
| args.body | INCIDENT: disk utilisation 94% |
| status | deny |
| reason | Tool 'write_incident_report' is blocked by policy rule 'deny_filesystem_writes'. |
呼叫端取代工具結果所收到的內容:
[BLOCKED by governance policy] Tool 'write_incident_report' is blocked by policy rule 'deny_filesystem_writes'.. Please choose a different approach to accomplish this task.
這筆紀錄由這次執行所提供的攔截器擷取。在 SDK 自身出貨的攔截器上,兩個稽核掛鉤都不會解析到,因此一次被拒絕的呼叫不會留下任何持久的東西 — 那條路徑上的紀錄功能仍屬「已規劃」,而這一頁呈現的是一個決策,不是稽核軌跡。
一個獨立行程在該目錄裡看到什麼
呼叫返回之後,該目錄由一個以自己的作業系統行程啟動的程式列出。它匯入三個標準函式庫模組、此外別無其他,因此它無法查詢政策引擎、強制點,或該工具呼叫的回傳值。它的答案是一項觀測,而不是一份回報。
python3 -c 'import os,sys,json;print(json.dumps(sorted(os.listdir(sys.argv[1]))))' <workdir>
| 執行 | 強制 | 政策 | 事後的報告檔案 | 後置條件 |
|---|---|---|---|---|
| denied | installed | deny | absent | held |
| allowed | installed | allow | present | held |
| enforcement_removed | removed | deny | present | held |
只有第一列什麼也決定不了 — 一個什麼都不寫的測試裝置會產生同樣的清單,一條沒有任何元件查詢過的政策也會。第二列移動政策而固定強制點;第三列移除強制點而固定政策。兩者都寫出了檔案。3 次執行中有 3 次的結果與其設計所預測的一致,而第一列的「不存在」之所以是一項量測,正是因為另外兩次得到了相反的結果。
在 ADR 0033 §6 所定義的主張詞彙中,這次執行達到的是 執行前拒絕。單靠裁決只能達到「已評估」;讓它得以使用較強詞彙的是上面那個表格,因為該詞彙要求由位於效果之前的元件做出拒絕,並且要求該效果被證明不存在。若副作用哪天不再被查核,誠實的詞彙就會退回「已評估」。
這次執行沒有顯示什麼
- 一個工具、一個框架轉接器、一個 SDK 版本、一台機器。同一條路徑上的第二個工具並沒有被執行,讀者不應把這個結果套用到它身上。
- 裁決來自一個行程內的攔截器,所以這次執行走過的是從拒絕裁決到未執行主體之間的路徑 — 不是閘道產生該裁決的過程,也不是兩者之間的網路。
- 強制點位於框架的工具執行入口。同一個行程中經由其他途徑觸及的工具主體,不在這次執行所量測的範圍內,這次執行對它也未作任何正反陳述。
- 沒有寫出任何持久的證據紀錄。上面呈現的決策是由這次執行自己的攔截器記在記憶體中的;SDK 端的紀錄功能仍屬「已規劃」。
- 其他作業系統、其他 Python 版本,以及其他語言的 SDK 都未在此被執行。本頁既不確認也不否認它們的狀態。
自己跑一次,然後試著推翻它
上面的一切都出自 denied-action-capture.py,針對由下方第二道指令從 PyPI 安裝的已發行 SDK 0.0.1rc6 執行。執行本身不會接觸任何閘道、不需要憑證,也不會開啟任何網路連線。它會寫出這一頁所呈現的那份紀錄。
uv venv --python 3.12 .proofenv VIRTUAL_ENV=.proofenv uv pip install 'agent-assembly==0.0.1rc6' 'smolagents==1.26.0' .proofenv/bin/python scripts/denied-action-capture.py
接著切斷強制路徑,看著這項檢查變紅。同一支腳本會在強制點未安裝的情況下執行那個被拒絕的呼叫;報告檔案出現、後置條件失敗,指令以非零狀態結束。一項不可能失敗的檢查不是檢查,所以這一項在被當成通過提出之前,先被展示為失敗。
.proofenv/bin/python scripts/denied-action-capture.py --sever-enforcement
這套方法 — 先斷言效果不存在、再斷言錯誤,並為該次執行配上一次移除治理的對照 — 來自 Python SDK 儲存庫 及其 Go 與 Node 姊妹專案中的負對照測試套件,追蹤於 AAASM-5529。那些套件以測試替身代替真正做出決策的元件,這對單元測試而言是正確的做法,但也意味著它們所證明的內容無法從已安裝的套件重現。這一頁把同一套方法套用在一個已發行的版本上,因此你可以從發行版執行它。