技術札記
陳舊的審批:提請那一刻是對的,等待數小時後點擊放行還算不算數
寫給企業架構師、業務主管與內部審計:現有 AI Agent 的人工審批(Human-in-the-loop)常因負責人離席或開會帶來數小時延遲。如何在提請審批、主管簽批到底層寫入的全流程中,防範人類異步等待與底層數據變更的時差(TOCTOU)風險。
GembaOS 是企業 AI Agent 的治理執行平台:以政策判定權限、以綁定參數的單次放行令授權每一個動作、以可追責的簽署鏈記錄人的審批,部署於企業本地機房或私有 VPC。
這篇文章探討企業在引入 AI Agent 時最常踩中的工程陷阱:現有 Agent 產品依賴人工點擊「允許(Allow)」,但在負責人不在電腦前、開會或等待審批的數小時延遲中,現實世界早已發生改變——引發嚴重的檢查時與使用時時差(Time-of-Check to Time-of-Use, TOCTOU)事故。
事故現場:現有 Agent 產品的「盲目放行」陷阱
星期三上午 10:00,客服 Agent 在處理一筆客訴時,於協作群組或終端向客服主管發起了一條工具調用申請:「申請對訂單 #8821 執行 HK$3,500 退款,請授權 [Allow / Deny]」。
此時 Agent 的判斷依據完全正確:10:00 當刻的 CRM 系統顯示訂單未退款、客戶投訴成立、符合退款政策。
然而,現實企業中沒有任何主管能 24 小時守在螢幕前:
- 客服主管當時正在參加長達兩小時的跨部門業務例會;
- 在主管離席等待審批的這兩小時內,客戶於 10:40 親自前往線下門市,門市收銀員已在 POS 機為其辦妥了現金退款;
- 或者,另一位維運同事於 11:00 處理客訴爭議時,手動將該訂單金額修改為 HK$5,000 或已標記結案。
中午 12:00,主管開完會回到座位,看到聊天群組裡滯留了兩小時的通知:「Agent 申請退款 HK$3,500」。主管回憶了案件背景,認為邏輯合理,隨手點擊了 [Allow]。
在現有的 Agent 產品與框架中,災難在這一刻發生: 大部分 Agent 框架僅將「人工點擊 Allow」視為解除線程阻塞(Unpause)的信號。按鈕一按,Agent 便直接發送兩小時前組裝好的 API 請求——對已經在門市退款的訂單再次扣款 HK$3,500,造成企業資金的重複損失。
這類故障不是因為大模型產生了幻覺,也不是因為提示詞注入攻擊。主管的商業判斷在兩小時前是正確的,Agent 也沒有越權,只是在「發起審批」與「人工確認」之間存在無法消弭的人類時間差,而現有系統卻盲目相信了兩小時前的陳舊快照。
| 狀態失效的方式 | 現有 Agent 產品常見的現場場景 | 根本起因 |
|---|---|---|
| 時間流逝與離席 | Agent 拋出審批通知,主管開會或下班至翌日才點擊 Allow,期間線下或人工已處置 | 人類異步延遲 |
| 數據被並發修改 | 主管點擊 Allow 前,其他同事手動修改了訂單金額、收件地址或收款帳號 | 人工介入衝突 |
| 多個 Agent 衝突 | 另一個自動化任務在主管審批期間對同一筆訂單發起了變更並搶先寫入 | 並發競爭 |
| 網絡重複重試 | 網關超時引發自動重試機制,將已成功的扣款或寫入請求再次發送 | 基礎設施重放 |
支付接口自帶的保護,擋得住甚麼、擋不住甚麼
上面的事故常被這樣反駁:「支付網關本來就有冪等鍵和退款上限,不會重複退。」對,也不對。網關擋得住的是它自己看得見的兩件事;事故裏出問題的,是它看不見的第三件。
| 保護 | 誰提供 | 擋得住 | 擋不住 |
|---|---|---|---|
| 重試去重(冪等鍵) | 支付網關等有此功能的接口 | 同一個請求因超時被重發 | 兩小時前組裝好、換了鍵再發的第二次請求 |
| 額度上限(可退金額) | 支付網關 | 退款超過原扣款 | 1,000 的訂單分兩次各退 450;門市已用現金退過的訂單 |
| 審批依據是否仍成立 | 只有拿着審批單的調用方能查 | 訂單已在 CRM 結案、金額被同事改過、客戶已在門市退款 | — |
網關只知道「這筆卡付款還有 HK$3,500 可退」;它不知道客戶 10:40 已經在門市拿了現金。第三層只能由拿着審批單的一方,在寫入前回到 CRM 和訂單記錄裏再看一次。GembaOS 三層都做:目標接口認冪等鍵的就用目標的(鍵由 GembaOS 按審批單號與工單 ID 生成),不認的就自己保證只發一次;額度先向目標讀一次可退金額再比對,目標自己也執行上限的,記錄上標明是目標執行的;審批依據則在寫入前逐項重讀比對。
人工審批通過,不等於可以盲目執行
許多企業或現有 Agent 框架將「聊天工具裡按下 Allow」或「審批流裡的一個 Approved 狀態」,直接等同於「可以直接調用 API 寫入」。這種將二者混為一談的設計,正是重大生產事故的溫床。
審批回答的是商業決策是否合規;而「現在、對著這個即時狀態、只允許執行一次」,必須由嚴密的執行期治理架構來保障。
| 核心問題 | 由「人工審批」回答 | 由「GembaOS 執行期門禁」回答 |
|---|---|---|
| 這個業務決定在策略上是否正確? | 是(由具名負責人簽署背書) | — |
| 審批人當時看到的事實現在是否依然成立? | 否(主管開會期間現實已變) | 治理內核的執行前現狀重讀與斷言校驗 |
| 該業務記錄當前是否處於可寫狀態? | 否 | 記錄租約鎖與適配器並發防護 |
| 金額是否仍在目標系統的實時上限內? | 否 | 寫入前向數據源重讀最新限額 |
| 該操作是否能確保精確執行一次? | 否 | 單次放行令與內核生成的唯一冪等鍵 |
GembaOS 怎樣縮窄審批時差的風險
GembaOS 的架構起點,正是承認**「人類的審批必然伴隨異步延遲(數分鐘、數小時甚至隔夜)」**。因此,GembaOS 絕不把審批按鈕當作無條件放行的綠燈:
[Agent 發起工具調用]
│ (附帶底層數據版本與前提斷言)
▼
┌──────────────┐ ┌──────────────────────────┐
│ AI 整理事實 │ ───> │ 人類異步延遲審批(開會/離席)│
└──────────────┘ └──────────────────────────┘
│
[主管簽署通過]
│
▼
┌──────────────────────────┐
│ 大模型完全退出調用鏈路 │
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ GembaOS 治理內核執行前門禁 │
│ 1. 重讀底層資料庫實時現狀 │
│ 2. 比對前提斷言(是否已漂移)│
│ 3. 驗證單次放行令時效 TTL │
│ 4. 獲取記錄排他租約鎖 │
│ 5. 內核注入唯一冪等鍵 │
└──────────────────────────┘
│
[實時狀態與前提一致]
▼
┌──────────────────────────┐
│ 調用底層 API 完成安全寫入 │
└──────────────────────────┘
| 潛在風險點 | 治理內核在寫入前的防護機制 |
|---|---|
| 審批前提在等待時漂移 | 治理內核直接向底層資料庫重讀即時數據(訂單狀態、餘額、關聯欄位),與審批單上鎖定的前提斷言進行比對;若主管審批時現實已變(如已線下退款),立即中止執行、作廢放行令,並在工單標註漂移差異。 |
| 放行令過期 | 審批簽署時生成的單次放行令具備剛性時間窗口(如 30 分鐘),逾期自動失效,防止數天前的歷史授權被意外觸發。 |
| 並發衝突與覆蓋 | 目標接口支持版本號或條件寫入時,寫入帶上審批時讀到的版本或期望值,過期由目標拒絕;不支持時,GembaOS 把經它的寫入串行化並在鎖內重讀,窗口毫秒數記在鏈上。 |
| 預算或權限超限 | 在調用前向授權中心重新獲取實時限額,以實時數值比對獲批金額,杜絕基於快照副本的超額放行。 |
| 網絡超時引發重複 | 單次放行令僅可核銷一次;冪等鍵由治理內核根據審批單與工單唯一生成,Agent 無法篡改。目標認這個鍵的,重試命中同一筆執行;不認的,超時後不重試,標為未核實交人。 |
目標接口給多少保護,GembaOS 就做甚麼
每個接入的寫操作都在接口契約裏宣告兩件事:重試是否安全(冪等、按鍵冪等、不可重試),以及目標能否拒絕一次基於過期讀取的寫入(版本號、條件寫入、甚麼都沒有)。GembaOS 按宣告選擇做法,並把選了哪一種寫進該次執行的記錄。
| 目標接口的能力 | 常見的接口 | GembaOS 的做法 |
|---|---|---|
| 版本號(ETag、If-Match、修改時間) | Salesforce、kintone、帶 revision 欄的自家 API | 寫入帶上審批時讀到的版本,過期由目標拒絕,審批單退回 |
| 條件寫入(「狀態仍是 X 才更新」) | 自家資料庫的條件 UPDATE、儲存程序 | 寫入帶上審批時的期望值,由目標核對 |
| 甚麼都沒有 | 自研 ERP/CRM 的普通 REST 端點、直接 UPDATE 的舊系統、付款檔案(SFTP 批次)、HubSpot 一類沒有條件更新的 SaaS、MES/WMS 解除隔離、目錄系統的重設密碼、發郵件 | GembaOS 把經它的同一記錄的寫入串行化,在鎖內重讀、比對、寫入,再回讀核對效果;重讀到寫入之間的毫秒數記在鏈上 |
第三行才是企業裏的多數。出貨的十二個接入配置裏,九個宣告的是「甚麼都沒有」,因為被接的系統就是這樣。付款檔案更是連冪等鍵的概念都沒有:同一份檔案丟兩次就付兩次,所以它被宣告為「不可重試」,超時後 GembaOS 不會再丟一次。
哪些是硬保證,哪一處是縮窄
對着一個沒有任何保護的接口,仍然成立的有三件:
- 只發一次。 一張放行令只能核銷一次,執行走「預留 → 執行 → 核銷」三步;宣告為不可重試的操作,超時後不會盲目重發,該次執行標為「未核實」,進缺口登記冊交給該流程的負責人查證。
- 事實變了就不寫。 寫入前 GembaOS 自己重讀記錄,和審批人簽署時看到的逐項比對,不一致就作廢放行令、退回審批單。
- 經 GembaOS 的寫入互不踩踏。 同一記錄上兩個獲批動作,一個持有租約時另一個等待;等到放行令過期仍拿不到,就退回。
縮窄而不是消滅的只有一處:GembaOS 之外的寫入者——門市的收銀員、在 ERP 界面裏手工改記錄的同事——在「重讀」到「寫入」之間那幾十毫秒插進來。這個窗口 GembaOS 不宣稱關閉,而是記錄在案:每次執行的 ExecutionChecked 事件寫明用了哪一種保護(目標的版本號、條件寫入,還是 GembaOS 自己的租約)、窗口有多少毫秒,以及寫後回讀的結果。回讀對不上或讀不到,不會靜靜過去,而是成為一條「未核實」記錄,有人要去看。
審批人在簽署時能看到甚麼
這套機制並非隱藏在後台的黑盒,而是直接呈現在負責人的審批介面上:

審批單上明確標註了防時差控制項:
- 有效期限:該次商業決定允許被執行的時間窗口(如 30 分鐘);
- 執行次數:嚴格限制為 1 次,杜絕重複扣款;
- 執行前校驗清單:列出在調用底層 API 前將與真實資料庫進行二次對照的關鍵欄位。
總結
審批等待時間不是單純的用戶體驗問題,而是關係到企業資產安全的工程正確性問題。
只要有人類參與審批,時間就必然會流逝。在流逝的時間裡,資料可能被修改、同事可能並發操作、限額可能變更、網絡可能觸發重試。
停留於「某某主管已經在聊天工具裡點了 Allow」的傳統工作流無法解決上述任何風險。GembaOS 的答案是:將審批視為對特定時間點的商業背書,並由治理內核在寫入前的最後一刻,嚴格驗證該背書的前提在當前現實中依然成立;驗證不了的,留下記錄交給人。
本文回答的問題
等待審批期間或審批通過後,業務資料被改動怎麼辦?
執行會立即中止。在真正呼叫底層業務 API 之前,GembaOS 治理內核會向真實資料庫重新讀取即時現狀,與 Agent 提請審批時鎖定並經審批人確認的前提斷言進行逐項比對。若關鍵欄位已被修改(例如門市已退款、訂單金額已變更),系統立即中斷執行、作廢單次放行令,並將工單連同變更差異退回給負責人重新決策。
提請審批後等待了幾個小時,主管點擊批准後還能繼續執行嗎?
若現實已變或放行令超期,系統絕不盲目執行。主管點擊批准時,系統簽發的單次放行令(Clearance Token)設有剛性有效期限(如退款為 30 分鐘);更關鍵的是,執行前治理內核會先重讀資料庫現狀,若主管離席期間現實已漂移,即便剛點擊批准亦會立即阻斷退回。
同一個審批會不會被重複執行兩次?
GembaOS 絕不會發第二次:單次放行令只能核銷一次,冪等鍵由治理內核按審批單號與工單 ID 生成,Agent 改不了。目標接口認這個鍵的(支付網關一類),重試命中同一筆執行;不認的(付款檔案、自研端點),超時後 GembaOS 不會盲目重試,而是把這次執行標為「未核實」,交負責人查證後再決定。
兩個操作同時修改同一筆記錄會怎樣?
先看目標接口能做到甚麼。接口支持版本號或條件寫入(Salesforce、自家資料庫的條件更新)時,寫入帶上審批時讀到的版本,過期就被目標拒絕。接口甚麼都不支持時,GembaOS 自己把經它的同一記錄的寫入串行化,在鎖內重讀再寫,並把重讀到寫入之間的窗口毫秒數記在鏈上;這個窗口對 GembaOS 之外的寫入者是縮窄,不是關閉。
資料來源
截至 2026 年 9 月,GembaOS 未取得任何第三方認證;本站的控制對應表為自評,未經審計。GembaOS 處於 Design Partner 階段。 了解 Design Partner 計劃。
最後更新: · GembaOS v0.2