操作員審批迴路 是做什麼的?
與外部交易對手對話的代理絕不應自行判斷發送,且應在內部隨時通知操作員。此技能定義了合約:每份外發草稿都作為義務歸檔,由操作員決定確切文字,並有一個傳遞分類帳證明實際發送的內容。
何時使用
- 代理在共享頻道、電子郵件或聊天中起草回覆給客戶、供應商、投資者或合作夥伴,且必須經人類批准後才能發送。
- 你需要一份審計軌跡,將每條已發送訊息連結到確切的草稿文字、批准的操作員以及決策時間。
- 你曾見過過時的批准釋放出重寫的草稿,或兩個工作者送了同一條已批准的訊息兩次。
- 草稿不斷向交易對手索取分類帳中已有的資訊。
運作方式
物件
| 物件 | 意義 |
|---|---|
| 義務 (Obligation) | 我們欠交易對手的一件事。狀態依序為 "drafted"(已起草)、"approved"(已批准)或 "rejected"(已拒絕)、"sent"(已發送)。承載 direction(方向)、counterparty(交易對手)、channel(頻道)和 updated_at(更新時間)。 |
| 草稿 (Draft) | 側車行,保存確切草稿文字、該文字的 SHA-256、來源座標(平台、頻道、討論串、使用者)以及優先級(P0 到 P3)。每個義務一個,重新歸檔時取代。 |
| 決策 (Decision) | 操作員的批准或拒絕,記錄操作員 ID、nonce 以及決策時的草稿紀元。 |
| 批准快照 (Approval snapshot) | 由已獲授權的決策寫入者記錄的不可變文字、雜湊、紀元和目的地。缺少快照則無法授權派送。 |
| 索賠 (Claim) | 帶有隨機 token 和狀態的持久保留;每個義務最多一個活躍索賠。 |
| 送達記錄 (Delivery) | 分類帳行,證明一次發送或通知針對一對 (義務, 決策)。 |
參考綱要位於 references/approval-ledger.sql。
歸檔草稿
- 清理輸入。移除控制字元,將單行欄位中的空白折疊,並強制長度上限(草稿、摘要、上下文、交易對手)。空值或超長欄位會被拒絕,而非靜默截斷。
- 執行基線閘門(如下)。可能會拒絕歸檔。
- 使用 SHA-256 對草稿文字進行雜湊。雜湊前綴放入摘要中,以便審批面板顯示正在批准哪個文字。
- Upsert。如果對於相同的(交易對手, 頻道)已存在一個已起草的義務,則取代該側車草稿並推進義務的
updated_at。該推進即為紀元輪替:任何鍵結到舊紀元的決策將無法再釋放新文字。否則,插入一個狀態為 "drafted" 的新義務。 - 僅將歸檔收據路由到已配置、經過驗證的內部操作目的地。如果來源就是該內部目的地,則在該處確認。永不靜默意味著內部報告,而非自動的外部回覆。保持草稿雜湊、批准狀態、操作員身份和工作流程元數據遠離交易對手可見的頻道。未知或未分類的來源保持安靜;直接訊息不會被自動視為內部。
如果經過驗證的內部目的地不可用,則將歸檔結果保留在內部工具結果或操作員介面中。絕不降級到外部或未知來源。暴露給外部的工具結果不是內部介面。
歸檔草稿不授權外部回應。任何政策允許的澄清提問或中立回覆都是獨立的外部發送決策,須遵守現有提及、頻道、僅草稿、凍結和永不約束於交易對手-頻道紀律。它不得透露內部批准元數據。
基線閘門
在任何草稿歸檔前,查詢交易對手的當前基線(時間分類帳、合約儲存或 CRM):
- 記錄中有已簽署或已交付的合約:拒絕歸檔,並附上證據和建議。在簽約後向交易對手詢問規格正是此閘門旨在阻止的確切失敗。
- 操作員覆寫:
force_despite_signed_contract允許歸檔通過,並在草稿上下文中標記[BASELINE_OVERRIDE_SIGNED_CONTRACT]。 - 閘門服務無法連接:歸檔繼續進行,上下文中標記
[BASELINE_CHECK_UNAVAILABLE]。面板會看到防護已關閉。失敗絕不會使閘門靜默禁用。 - 當事實可用時,將最新的幾個附加到上下文中,作為
[BASELINE FACTS: ...]摘要,以便草稿帶有當前真相。
決策
審批面板列出狀態為 drafted 且方向為 we_owe_them 的義務。批准或拒絕會寫入一條承載草稿紀元(draft_updated_ts)的決策行,並在同一事務中翻轉義務狀態。紀元與當前 updated_at 不匹配的決策已過時,不得釋放任何內容。
對於已獲授權的批准決策,同一事務會插入一個不可變的 obligation_approval_snapshots 行:決策和義務 ID、當前草稿紀元、確切文字和 SHA-256、平台/頻道/討論串以及類型 draft_sent。決策寫入者必須在寫入前建立授權;參考內容從未對操作員進行身份驗證或製造決策。自動批准政策不會在參考內容中啟用或擴充。沒有快照的舊決策需要明確調解或新的批准;永遠不要從當前可變草稿回溯授權。
傳遞
SQLite 參考位於 references/approval_claims.py。它授予派送權限,但絕不呼叫傳輸。使用從 SQL fixture 初始化的現有本地參考資料庫;該模組不套用綱要或生產遷移。只有受信任的決策寫入者才能填入批准記錄。所有寫入者必須啟用外鍵和遞迴觸發器,並遵守綱要防護;管理資料庫篡改超出此模型範圍。
- 探索已批准的草稿。探索不是權限。
claim()開啟自己的BEGIN IMMEDIATE事務,驗證當前批准的紀元、確切文字、計算的 SHA-256 以及針對快照的完整目的地,然後插入唯一的索賠,再返回其 token。衝突會阻止工作者在傳輸前進行。已完成的收據不能再次索賠。 begin_dispatch()重新驗證綁定,並使用 token 原子性地將claimed更改為dispatching。只有其獲勝的呼叫者才能在提交後收到確切的draft_text和目的地。絕不重新產生文字、重新讀取可變側車以進行傳輸,或重複使用負載進行另一次嘗試。嵌套的呼叫者事務會被拒絕;權限不能依賴於後續呼叫者的提交。沒有資料庫事務在傳輸過程中保持開啟。- 確認的成功結果會進入
complete(),它原子性地記錄送達座標、將索賠標記為已送達,並將義務狀態翻轉為sent。完全相同的完成是不操作的;衝突的座標會失敗。收據 UNIQUE 鍵用於去重記錄,而非先前的外部效果。 - 異常、超時、開始派送後工作程序死亡,或收據持久化失敗會導致封鎖的嘗試。
mark_unknown()記錄不確定性。未知索賠永不失效、永不重新開啟或自動重試,也不允許對該義務的另一個決策繞過它。受信任的呼叫者可以使用帶有已確認的成功座標和證據的reconcile();該模組不驗證該證據。缺少收據不是未送達的證明。
保證是每個批准的決策有一次自動派送嘗試,而不是精確一次的外部送達。開始派送後但在傳輸前崩潰可能導致零次發送和一個被持有的索賠。為新的嘗試釋放未知結果需要圍欄原始執行器並驗證提供者語義;此參考刻意不提供此類重試操作。
| 索賠狀態 | 允許的後續狀態 |
|---|---|
| claimed | dispatching 或 dispatch 前取消 |
| dispatching | delivered 或 unknown |
| unknown | 僅透過受信任的調解送達 |
| delivered, cancelled | 終端;決策金鑰不可重複使用 |
當索賠處於活躍狀態時,資料庫防護會凍結義務、草稿和決策的寫入,包括取代。快照和索賠無法被刪除。在重新歸檔前使用其 token 取消已索賠的操作;過時的 token 隨後將一無所獲。在派送開始後,保留新的編輯或撤銷以進行調解。這樣會序列化變更,而不是假裝召回正在進行的操作。
被拒絕的決策和沒有草稿側車/快照的舊行永遠不會進入此外部草稿-發送路徑。在內部操作員介面上報告它們以進行手動處理。內部收據頁尾保持內部:approved by <operator> · receipt <decision_id> · draft sha256 <prefix>。絕不改動已批准的外部文字以附加工作流程元數據。
重點本地驗證使用臨時資料庫、獨立連線和模擬嘗試計數器,而不是提供者或真實訊息:
python3 -m unittest discover -s tests/skills -p 'test_approval_delivery_claims.py'。
測試需要 Python 3.11+ 且支援 SQLite 序列化;參考僅使用標準庫。現有的桌面模式合約檢查仍然是一個單獨的兼容性檢查。
限時自動批准(可選)
草稿可能承載 auto_send_after(紀元秒數)。掃描程序會批准在截止時間後沒有決策的草稿,記錄操作員 auto-ttl,然後正常路徑進行送達。操作員操作總是獲勝:決策在掃描程序看到之前就翻轉了狀態,而重新歸檔會輪替紀元並移動或清除截止時間。掃描程序在寫入事務內重新檢查狀態和紀元,因此競賽條件會解析為不操作。沒有截止時間的草稿永遠保持硬性閘控。
信號關聯
草稿可以命名它所回應的傳入義務(signal_obligation_id)。這是延遲測量(傳入信號到已起草回應)的唯一真實連結,並讓 SLA 掃描程序將該傳入項目視為已應答。如果引用的行不存在,則拒絕歸檔。
範例
歸檔草稿
file_request(
draft="Thanks, we can hold the slot until Friday. Which start date works?",
counterparty="acme-supplier",
context="reply to delivery window question",
origin_platform="slack", origin_channel="#acme-shared",
origin_thread="1712345678.000100", priority="P1",
signal_obligation_id=412)
-> {obligation_id: 431, draft_sha256: "9f2c...", refiled: false}
已配置的經過驗證的內部操作目的地會看到:
Draft filed for approval (P1, sha 9f2c8a1b). Waiting on operator.
交易對手可見的來源頻道不會收到歸檔通知。如果沒有可用的經過驗證的內部目的地,收據會保留在內部工具結果或操作員介面中,沒有外部降級方案。
在引導後重新歸檔
操作員要求更短的草稿。為相同的(交易對手, 頻道)再次歸檔會返回 refiled: true,側車文字和雜湊發生變更,updated_at 會推進。點擊舊面板行上的批准會帶有舊紀元,因此被忽略。
閘門拒絕
DeskApprovalError: baseline gate refused this draft: the ledger shows a
signed contract for 'acme-supplier'. Evidence: master agreement executed
2026-08-14. Recommendation: do not ask. Re-file with
force_despite_signed_contract=true if this is genuinely a new thread.
內部頻道中的送達頁尾
Confirmed for Friday, start date 2026-09-08.
approved by operator-a · receipt 118 · draft sha256 9f2c8a1b2d3e4f50
要測試的不變性
- 歸檔收據僅發送到已配置的經過驗證的內部操作;只有當來源是該經過驗證的內部目的地時,來源才會收到一個。
- 未知來源保持安靜。不可用的內部目的地使用內部工具結果或操作員介面,沒有外部降級方案。
- 相同的(交易對手, 頻道)被歸檔兩次會產生一個義務、兩個紀元。
- 帶有過時紀元的決策永遠不會導致送達行。
- 兩個並發索賠者產生一個派送權限;失敗者從不嘗試傳輸。
- 未知結果和收據持久化失敗永遠不會啟用自動重試。
- 成功完成記錄收據和發送狀態在同一個事務中。
- 被更改的紀元、文字、雜湊或目的地無法獲取或開始索賠。
- 活躍索賠阻止重新歸檔;只有派送前取消才能解除該保留。
- 閘門不可用時會標記標記;閘門簽署時若無強制則拒絕。
- Auto-ttl 永遠不會針對操作員隨後重新歸檔的文字觸發。