行控制工程 03|為什么“看到的“和“執(zhí)行的“需要被綁定)
幾乎每一個高風(fēng)險軟件里都有那道熟悉的提示你確定嗎刪除文件時會問一次發(fā)布到生產(chǎn)環(huán)境時會問一次轉(zhuǎn)賬時系統(tǒng)會把金額和收款人再展示一遍修改關(guān)鍵配置時甚至要求重新輸入密碼或者插一次硬件密鑰。這套設(shè)計很合理。它的目的是在人和不可逆結(jié)果之間插入最后一次停頓讓操作者有機會重新檢查自己到底要做什么。也正因為它太常見我們習(xí)慣性地把最終確認當(dāng)成執(zhí)行前的最后一道安全門。但這個判斷依賴一個幾乎從不被寫出來的前提用戶確認時看到的東西必須和系統(tǒng)最終真正執(zhí)行的東西是同一件事。前提成立時最終確認確實非常有價值。問題是在現(xiàn)代系統(tǒng)里確認和執(zhí)行之間已經(jīng)很少只隔著一個按鈕。中間可能還有 API、若干個服務(wù)、消息隊列、任務(wù)調(diào)度、策略系統(tǒng)、參數(shù)轉(zhuǎn)換、第三方接口甚至一個仍在繼續(xù)自主規(guī)劃的 Agent。于是問題變成了我們確認的究竟是一次執(zhí)行還是執(zhí)行鏈上某個時刻的一份描述一、確認鎖定的是畫面不是動作假設(shè)界面上展示著一筆付款收款方是供應(yīng)商 A金額十萬元幣種人民幣。用戶逐項核對之后點擊確認。如果系統(tǒng)接下來原樣把這三個參數(shù)交給執(zhí)行機構(gòu)這次確認的語義非常清楚——看到什么就執(zhí)行什么。但現(xiàn)實系統(tǒng)通常不是這樣運轉(zhuǎn)的。點擊之后前端提交的可能只是一個業(yè)務(wù)對象的標識。后端據(jù)此查詢數(shù)據(jù)庫業(yè)務(wù)服務(wù)補齊銀行賬戶信息支付服務(wù)按結(jié)算規(guī)則選擇通道甚至轉(zhuǎn)換幣種策略系統(tǒng)重新計算額度任務(wù)進入隊列最后由另一個進程調(diào)用銀行接口。用戶確認的是界面此刻展示的這筆付款而執(zhí)行系統(tǒng)接收到的是根據(jù)若干服務(wù)此刻共同解釋出來的業(yè)務(wù)狀態(tài)所生成的一筆付款。這兩者在絕大多數(shù)情況下確實一致但通常一致不是一種安全保證。真正需要回答的是從用戶看到內(nèi)容到現(xiàn)實真正發(fā)生變化中間有沒有機制持續(xù)保證兩者仍然指向同一個動作。同樣的結(jié)構(gòu)在部署場景里更明顯。用戶確認的是把某個版本發(fā)布到生產(chǎn)環(huán)境而執(zhí)行器需要的是一組具體得多的東西——這個環(huán)境對應(yīng)哪個集群、用哪個鏡像倉庫、加載哪套配置、起多少實例、流量怎么切。用戶表達的是一個高層語義執(zhí)行器需要的是可執(zhí)行參數(shù)中間必然存在一次從意圖到參數(shù)的展開。展開本身沒有問題任何復(fù)雜系統(tǒng)都需要這層抽象。風(fēng)險在于展開之后的結(jié)果如果沒有重新與原始意圖建立綁定系統(tǒng)最終執(zhí)行的對象就可能已經(jīng)不是那次確認過的對象。所以執(zhí)行控制要問的不止是用戶有沒有確認還包括最終的執(zhí)行參數(shù)是否仍然可以被證明屬于那一次確認。這個要求比多加一個確認框困難得多因為它需要約束跨越整條調(diào)用鏈。二、一個字節(jié)都沒改含義也可能變了還有一種更隱蔽的偏移數(shù)據(jù)完全沒有被篡改但語義被重新解釋了。假設(shè)用戶確認的動作是刪除項目 A提交的請求也清清楚楚地指向那個項目。但后端對刪除項目可能有自己的一套定義它也許意味著連同項目下所有運行實例一起銷毀也許包括關(guān)聯(lián)存儲、吊銷相關(guān)憑據(jù)、清理自動創(chuàng)建的網(wǎng)絡(luò)資源。嚴格來說這不一定是漏洞它可能完全符合產(chǎn)品設(shè)計。但從執(zhí)行控制的角度問題變成了用戶確認時看到的那個語義究竟覆蓋了多大的現(xiàn)實影響范圍如果項目 A在界面上只是一個展示概念而真正被改變的是十幾個彼此獨立的資源那么這次確認能證明的只是用戶接受了一個抽象描述而不是他理解并同意了展開之后的每一個動作。數(shù)據(jù)完整性和語義完整性不是同一個問題。一個請求可以從頭到尾一個字節(jié)都沒有被改動仍然在不同的系統(tǒng)之間被解釋成不同的現(xiàn)實含義。簽名和校驗?zāi)苁刈∏罢呤夭蛔『笳?。三、參?shù)被凍結(jié)了現(xiàn)實沒有即便所有參數(shù)都被牢牢固定執(zhí)行也未必就應(yīng)該自動發(fā)生因為執(zhí)行面對的從來不是一個靜止的世界。設(shè)想上午九點一名運維負責(zé)人確認重啟某個節(jié)點。當(dāng)時它只是一臺普通的業(yè)務(wù)節(jié)點重啟屬于常規(guī)維護。但由于任務(wù)排隊真正執(zhí)行發(fā)生在九點十五分。這十五分鐘里集群出了故障其余節(jié)點陸續(xù)下線這臺機器暫時成為唯一還在承擔(dān)核心流量的實例。請求沒有變對象沒有變確認沒有過期操作者身份完全正確。但同樣一個重啟動作此刻的性質(zhì)已經(jīng)從常規(guī)維護變成了一次高風(fēng)險操作。改變的不是意圖是狀態(tài)。最終確認永遠只能基于確認發(fā)生那一刻可觀察到的狀態(tài)。只要執(zhí)行和確認不在同一個瞬間系統(tǒng)就必須承認一個事實確認之后現(xiàn)實仍在繼續(xù)變化。因此一次執(zhí)行是否成立不應(yīng)該由過去某個時刻的確認結(jié)果承擔(dān)全部責(zé)任。越靠近真正的執(zhí)行點越需要重新檢查那些會隨時間漂移的條件。四、最后一次到底最后在哪里這是一個值得認真追問的問題。用戶點擊確認是最后一次嗎審批人點擊通過是最后一次嗎策略引擎返回允許是最后一次嗎一個 Agent 拿到工具調(diào)用授權(quán)是最后一次嗎還是執(zhí)行器真正準備修改現(xiàn)實狀態(tài)的前一刻才算最后一次交互設(shè)計里的最終確認是從人的視角定義的它的含義是這是用戶最后一次需要參與。而執(zhí)行安全里的最終應(yīng)該從現(xiàn)實變化的視角定義它的含義是這是系統(tǒng)最后一次有機會拒絕一個錯誤執(zhí)行。人的最后一次確認不一定是系統(tǒng)的最后一個決策點。這兩個最后之間可能隔著相當(dāng)長的距離。人不再參與之后機器還會繼續(xù)解釋、拆解、規(guī)劃、轉(zhuǎn)換、調(diào)度和調(diào)用。這個差距在傳統(tǒng)軟件里已經(jīng)存在只是通常被確定性的流程掩蓋住了。五、Agent 讓確認之后的空間變成了變量傳統(tǒng)軟件在多數(shù)情況下是確定性的。用戶確認之后即使后續(xù)經(jīng)過很多系統(tǒng)執(zhí)行流程大體已經(jīng)定型。Agent 帶來的變化在于確認之后系統(tǒng)可能仍然在做決策。比如用戶讓一個 Agent 把某次版本更新發(fā)布到生產(chǎn)環(huán)境Agent 給出的計劃是檢查測試結(jié)果、創(chuàng)建任務(wù)、灰度發(fā)布、觀察指標、完成全量。用戶看過計劃后點了確認。但在實際執(zhí)行中Agent 會持續(xù)遇到新的環(huán)境信息并可能自主決定用哪個工具、調(diào)整哪些參數(shù)、是否繞過某個失敗的步驟、是否重新規(guī)劃、遇到異常后采取什么補償動作。此時用戶確認的其實不是一條靜態(tài)命令而是一段具有自主性的未來執(zhí)行空間。這與確認刪除這個文件嗎已經(jīng)是兩類東西——人表達的是目標機器執(zhí)行的是一系列在確認之后才被動態(tài)生成的動作。這層落差本來一直存在。人說幫我清理掉沒有使用的云資源這是意圖真正落地的是一組具體的刪除、解綁和吊銷操作這是執(zhí)行。過去這層翻譯由人自己完成他看到目標、理解環(huán)境然后一項一項地做?,F(xiàn)在這層翻譯越來越多地交給機器。所以在這類系統(tǒng)里需要明確回答的是用戶到底是在授權(quán)一個結(jié)果、一份計劃還是一組允許機器自主選擇的執(zhí)行邊界如果這個問題沒有答案最終確認就更接近一種心理安全感而不是一條工程邊界。確認不應(yīng)該天然向未來所有后續(xù)動作無限傳播。授權(quán)需要有邊界意圖也需要有邊界。執(zhí)行路徑一旦超出邊界就應(yīng)該重新進入判斷而不是繼續(xù)沿用那次早已發(fā)生的同意。六、真正要綁定的是看到的和發(fā)生的把整個問題抽象一層最終確認想要保護的其實是兩個對象之間的一致性用戶確認時看到的對象、參數(shù)和范圍與系統(tǒng)最終真正改變的那部分現(xiàn)實。中間所有機制的職責(zé)之一就是盡可能讓后者可以被追溯回前者。這不是說系統(tǒng)不能做任何轉(zhuǎn)換——現(xiàn)實軟件不可能沒有抽象層。而是說每一次必要的轉(zhuǎn)換都應(yīng)該有明確約束最終結(jié)果必須能夠回溯到原始意圖。對象變了要能被發(fā)現(xiàn)參數(shù)變了要能被發(fā)現(xiàn)狀態(tài)變了要重新判斷解釋范圍擴大了要重新判斷計劃被改寫了要確認它是否仍落在原始授權(quán)之內(nèi)。在實際設(shè)計執(zhí)行邊界時一個反復(fù)出現(xiàn)的結(jié)論是可靠的執(zhí)行路徑不是用戶已確認所以執(zhí)行而更接近一條鏈——意圖先被表達確認作用在這份意圖上解釋把它展開為具體動作狀態(tài)驗證檢查現(xiàn)實條件是否仍然滿足邊界檢查確認展開結(jié)果沒有超出授權(quán)范圍最后才輪到執(zhí)行。鏈上任何關(guān)鍵事實發(fā)生變化都可能讓過去那次確認失去繼續(xù)向前傳播的資格。這套結(jié)構(gòu)不會消除風(fēng)險。它做的是縮小需要被無條件信任的區(qū)間、增加獨立驗證點、提高偏差在鏈路中悄悄累積的成本并在最靠近現(xiàn)實的位置保留一次拒絕的能力。七、最后一道門應(yīng)該更靠近現(xiàn)實需要說清楚的是這些討論并不是在否定最終確認。它依然是重要的人機交互機制——阻止誤操作讓人重新檢查給高風(fēng)險動作增加必要的摩擦明確責(zé)任歸屬減少無意識的點擊。它承擔(dān)不了的是另一些事情。用戶點擊確認能夠說明的只是在那個時刻基于他當(dāng)時看到的信息他表達了同意。它不能自動證明后續(xù)參數(shù)不會變化、系統(tǒng)不會重新解釋、環(huán)境不會漂移、第三方不會返回不同結(jié)果、Agent 不會生成新的計劃更不能證明最終執(zhí)行嚴格等于他當(dāng)時看到的內(nèi)容。這些是執(zhí)行鏈自己必須繼續(xù)保證的事。所以高風(fēng)險系統(tǒng)真正需要的不是把那句你確定嗎做得更醒目而是把確認之后那段長期被默認可信的路徑也變成可以觀察、可以約束、可以拒絕的工程對象?;氐阶畛跄莻€按鈕。它的位置決定了它能守住什么如果確認之后還有十個系統(tǒng)能夠繼續(xù)改變執(zhí)行語義它就不是最終邊界如果策略返回允許之后執(zhí)行器仍要根據(jù)當(dāng)前狀態(tài)重新構(gòu)造參數(shù)策略也不是最終邊界如果 Agent 在獲得授權(quán)之后還能繼續(xù)規(guī)劃人的確認同樣不是最終邊界。越接近現(xiàn)實被改變的位置系統(tǒng)越應(yīng)該減少假設(shè)、重新驗證事實。最后一次確認不等于看清了整條執(zhí)行路徑。真正的最后一道邊界不應(yīng)該只是再問一次確定嗎。它應(yīng)該有能力在所有人都已經(jīng)說過確定之后仍然根據(jù)執(zhí)行前的真實狀態(tài)回答另一個問題現(xiàn)在這件事還應(yīng)該發(fā)生嗎