Agentic Reliability Engineering

第四章
從混沌到自我修復——代理式韌性引擎

本章為最終書稿的第三章。請注意,GitHub 儲存庫將於稍後正式啟用。
若您願意實際參與本草稿的審閱與意見回饋,請與編輯聯繫:jleonard@oreilly.com

自我修復的系統,不是「會復原」的系統,而是「會學習」的系統。

這句話就是本章的整個論點,本章其餘的篇幅,都是這句話所隱含的工程工作。長久以來,可靠性一直被框架為「預防」:設計備援、強化架構、消除單點故障、縮短平均復原時間。這些做法一直為工程團隊帶來良好的服務,至今依然有用,但它們都建立在一個愈來愈站不住腳的假設之上——它們假設一個系統將會遇到的失效模式,可以事先被窮舉出來。現代系統太過複雜、太過互聯、太過動態,這個假設已經無法成立。依賴關係會在執行期改變,負載模式會毫無預警地變化,資料品質會起伏波動,模型會漂移。在這樣的條件下,純粹建立在「預先設想」之上的韌性,會變得脆弱。

本章所主張的轉變並不小。它把韌性從一項「結構性特質」,轉變成一種「活的能力」。傳統的韌性系統,試著在不改變自己的情況下吸收失敗;代理式的韌性系統,則會吸收失敗,「並且」因此而改變。復原時間依然重要,只是它單獨已經不再足夠——一個每個星期二都以同樣方式失敗、卻總能迅速復原的系統,並沒有變得更有韌性,它只是變得更擅長修理自己。本章接下來所使用的「韌性」一詞,指的是這樣一種特質:同一類失效,每發生一次,就變得更不容易發生、影響更小,或吸收得更快。這是一項可量測的特質,本章大部分篇幅都會用來談如何量測它。

這個轉變把「失敗」當成資料,而不是例外。在大多數組織裡,失敗是一種要被降到最低、被隱藏,或被悄悄處理掉的東西。事故是事後才被檢討的,混沌實驗是偶爾才進行的,往往與日常營運脫節。有價值的訊號被產生出來,卻又流失,因為產生它們的系統並沒有消費它們。代理式可靠性採取不同的立場:失敗是一個系統所能擁有的最豐富資訊來源之一,前提是它被擷取下來、被結構化,並被回饋進決策之中。延遲飆升、部分中斷、退化的回應、意外的重試、模型的失常行為——這些都藏著關於「系統在哪裡脆弱」的線索。目標不是製造更多的失敗,而是從已經發生的失敗中,萃取出更多的學習。

這個重新框架的第二個後果,是負責運行系統的人,開始對自己的可靠性計畫抱有一種不同的期待曲線。傳統的韌性產生的是「階梯函數」:投資一筆備援,正常運行時間一夜之間改善;部署一個斷路器,一整類的連鎖失效變得不可能發生。代理式韌性產生的則是「複利曲線」:改善不會在單一一季內到來,而是橫跨許多季,隨著系統對每一類失效的回應,一次迭代一次迭代地精煉。真正重要的數字也隨之改變——平均復原時間依然被追蹤,但它不再是頭條新聞;頭條新聞是「重複性失效率」的趨勢、「復原行動有效性」的趨勢,以及代理「校準程度」的趨勢。抱著「階梯函數」的期待踏入代理式可靠性的團隊,往往在第一季就感到失望;認清「曲線本身才是重點」的團隊,則不會。

讓這一切成為可能的,是前一章的工作。決策級遙測,是本章運作所在的基質——沒有結構化、經過語意標註的訊號,接下來的一切都無法運作;有了它們,一個系統就能觀察自己、對自己所看到的東西進行推理、採取有邊界的行動,並根據結果更新自己未來的行為。這正是代理式韌性的運作迴圈,第 3.1 節會為它命名。

本章也要在一個詞彙上格外謹慎。「自我修復」(self-healing)這個詞,在業界已經被過度使用太久,從「負載平衡器重啟了一個 pod」到「系統重寫了自己的復原政策」,什麼都可以叫它。在本章開場句之後,這個詞只會再出現一次,就是我們要討論它究竟具體指涉哪些機制的時候。其他地方,我們會直接說出我們真正的意思:「韌性」(resilience)是系統的一項特質;「復原」(recovery)是恢復穩定狀態的行動;「學習」(learning)是對未來行為的更新。這三者在這個迴圈裡各有自己的拍子,也各有自己的失效模式。把它們都塞進「自我修復」這個籠統的詞裡,恰恰會掩蓋本章存在的目的:讓這些工程工作變得可見。

3.1 核心韌性迴圈:偵測、推理、行動、學習

每一個自我修復的系統,無論規模或技術為何,都運行著同樣的四拍迴圈:系統注意到某件事正在改變;它理解這個變化代表什麼;它以有邊界的方式做出回應;然後,它根據結果,更新自己未來的行為。當這四拍中的任何一拍缺失或薄弱時,韌性就會崩壞成「過度反應」或「癱瘓」;當這四拍都存在且設計良好時,韌性就會隨時間複利累積。本書其餘篇幅,會把這個迴圈稱為 DRAL(Detect, Reason, Act, Learn,偵測、推理、行動、學習)。第二部分的每一章,開頭都會指向這個迴圈中的某一拍。這是本書最常被重複使用的抽象概念,本節接下來會謹慎地把它安裝到位。

第一拍是「偵測」(Detect)。在代理式系統裡,偵測和警報不是同一回事。警報是一次要求人類注意的門檻突破;偵測則是更早、更細緻的辨識——察覺到某件事正在以系統應該關注的方式改變。最有價值的偵測訊號,很少是二元的,它們是弱訊號、漂移,以及橫跨遙測資料的細微關聯:error budget 的燃燒速度是否正在加速超出預期?尾端延遲是否正在漂出歷史包絡線?重試、逾時、或備援機制是否正在組合式地增加?資料品質是否正以某種將影響下游行為的方式惡化?這些問題,只有在可觀測性層是決策級的情況下才回答得出來——原始指標本身無法支撐它們。偵測需要結構化的情境、一致的標籤,以及對服務邊界與依賴關係的理解。這正是為什麼第二章必須排在前面。偵測也是縮小爆炸半徑最早的機會,一旦錯過,迴圈中接下來的每一拍都會變得更難。

第二拍是「推理」(Reason)。這正是代理式系統與傳統自動化根本上的分歧之處。傳統自動化透過規則,把訊號直接對應到行動:若延遲超過門檻,重啟 pod;若錯誤率超過門檻,呼叫待命人員。推理則是根據意圖、風險與信心,來評估各個選項:系統會問,最可能正在浮現的失效模式是什麼?我對這個判斷有多少信心?在目前的邊界內,哪些行動是被允許的?出手行動相對於等待,預期的影響分別是什麼?推理結合了多個輸入——遙測資料提供證據,政策定義限制,歷史結果形塑信心,意圖釐清優先順序。它的輸出不是單一一個行動,而是一組帶著信心分數、依照目前被允許運作的自主邊界評估過的候選選項,並依信心高低排序。推理延遲在這裡很重要,因為一個在失效已經擴散之後才抵達的決策,和「沒有決策」沒有兩樣。這正是為什麼運算效率與乾淨的資料模型,是韌性設計的一部分,而不是事後的最佳化工作。

第三拍是「行動」(Act)。行動,是韌性變得可見的地方,也是犯錯代價最高的地方。代理式行動的紀律在於:不行動,本身也是一種選擇。一個永遠都會行動的代理,不是代理,而是反射動作。代理要在三項限制之下,決定是否行動、做什麼、在哪裡停下:行動的範圍必須有邊界,代理知道自己被允許影響的最大周界;行動要在可能的情況下是可逆的,一個無法被撤銷的行動,很少應該是自主的;行動要在執行過程中持續被評估,代理會盯著結果,若行動沒有奏效,就中止或升級。可能的行動清單本身,重要性不如規範它們的規則:調整速率限制、轉移流量、降級非關鍵功能、重啟元件、隔離依賴——這些都是合理的選項,但沒有一個在每種情況下都適用。第 3.5 節會談代理如何在這些選項之間做選擇;這裡重要的是,這個選擇是在明確的當責邊界之下做出的。

第四拍是「學習」(Learn)。學習,正是把代理式韌性與自動化的反應性區分開來的地方。一個會偵測、會推理、會行動,卻不會更新未來行為的系統,會永遠對同樣的輸入產生同樣的結果。學習問的是一個看似簡單的問題:系統是不是因為採取了行動而變得更好?這個答案會同時回饋到好幾個地方:影響未來決策的信心模型會變得更精準;關於脆弱性的假設會被精煉;復原策略會依有效性重新排序;政策門檻與自主邊界,會根據觀測到的校準情形被調整。沒有這一拍,系統或許能快速復原,但每次都會以同樣的方式復原,而隨著時間過去,它的行動與系統實際狀態之間的落差會愈拉愈大。有了這一拍,復原會沿著一條曲線改善——同一類失效,每一次迴圈運行後,都會變得更不容易發生、影響更小,或吸收得更快。

這個迴圈不是一條管線,而是一個持續運行的控制迴圈,四拍彼此增強。薄弱的偵測會削弱推理;糟糕的推理會產出不安全的行動;沒有邊界的行動會阻礙學習,因為結果無法乾淨地歸因到行動本身;壞掉的學習,保證了下一次迭代會重蹈覆轍。反過來說,任何一拍的改善,都會提升下一拍的品質。一支採用代理式可靠性的團隊,採用的不是四項各自獨立的能力,而是一套當四拍被一起設計時會產生複利效果的回饋系統。

範例 DRAL 在一次延遲飆升上的單次迭代。偵測:checkout-api 的 p99 延遲突破 400 毫秒門檻,附上拓撲情境。推理:上游的 payment-gateway 處於已知的降級狀態;爆炸半徑侷限於結帳旅程;對「節流非關鍵路徑」這項策略的信心是 0.87。行動:把非關鍵請求路徑節流至 60%,為期十分鐘,並監控穩定狀態是否恢復。學習:p99 在四分鐘內回到基準線;結果被儲存;對這類失效使用這項策略的信心被向上調整。整次迭代耗時不到三十秒,下次同樣的模式出現時,代理會以更高的信心採用同一套策略。

本章其餘部分,都是讓這個迴圈值得信賴的工程工作。第 3.2 節主張,正是「學習」這一拍,才讓這個系統真正稱得上是代理式的,並說明為什麼一個沒有學習能力的自動化系統,不是同一回事。第 3.3 節與第 3.4 節談系統如何透過有界故障注入,探索自身的脆弱性。第 3.5 節談代理如何在第三拍中,從可用的行動裡做出選擇。第 3.6 節介紹讓這個迴圈可被量測的五項 SLO,這正是把這個迴圈從一套架構,轉變為一項營運實務的關鍵。第 3.7 節談圍繞在「行動」這一拍周圍的當責工作,第 3.8 節談這個迴圈接線錯誤時會浮現的失效模式。到第 3.9 節,我們會建構出本書其餘部分據以運作的韌性引擎。

3.2 失敗作為一種學習訊號

DRAL 的「學習」這一拍,正是代理式系統與自動化系統的分野。沒有它,系統是反應式的;有了它,系統會自我調適。本節主張,「學習」上的工程投資,正是大多數嘗試代理式可靠性的團隊投資最不足的地方,並說明當它被做好時,會改變什麼。

在傳統的可靠性模型裡,失敗被當成一種例外:出了問題,恢復服務,寫一份事故後檢討報告,然後往前走。即使有實踐混沌工程,失敗往往也被侷限在排定好的演練裡,與日常營運脫節。有價值的訊號被產生出來,卻很少被整合進持續的決策之中——這些資訊被擷取進一份文件裡,一個人類讀過一次,系統卻從未看見。這種模式的代價,在任何單一事故裡都看不出來,只有在跨越好幾季之後才會顯現——同一類失效,總是產出來自同一份操作手冊的同一種回應,同樣的復原時間,卻沒有人能指出到底哪裡改善了。

代理式韌性把失敗當成第一等輸入。算作「失敗」的範圍,比傳統事故要寬得多。許多最有價值的學習訊號,其實在一項服務正式當機之前很久就已經到來:error budget 燃燒速度的持續加速;重試率的上升,即使仍低於警報門檻;只影響一部分使用者的尾端延遲惡化;被備援機制掩蓋的部分功能故障;由資料漂移造成的模型輸出退化;服務之間意外的交互作用。在傳統設定裡,這些都不會呼叫人類,因為它們沒有突破任何一個二元門檻。但對一個會學習的系統而言,這些恰恰就是指出「信心被錯放在哪裡」的訊號。目標不是把一切都標記為事故,而是擷取「系統的行為與預期不同」這件事的結構化證據。

要讓失敗真正發揮學習訊號的作用,它必須以系統能夠據以推理的形式抵達。原始的日誌與指標並不夠,每一次失敗都需要情境:預期的穩定狀態是什麼?涉及哪些服務或元件?最近有什麼變更上線?類似的情況過去表現如何?採取了什麼行動、結果如何?當這份情境齊備時,失敗就變得可以被比較。系統可以問:這個模式以前出現過嗎?過去的回應有幫助嗎?對這些回應的信心,應該上升還是下降?沒有這份情境,每一次失敗都是它自己的特例,學習的複利效果為零,系統會永遠停留在同樣的脆弱程度。第二章打下的可觀測性基礎,正是讓這一切成為可能的關鍵——沒有一致的結構、標籤與擁有權中繼資料,失敗只是軼事;有了它們,失敗就變成了訓練資料。

附註 在代理式系統裡,不學習的代價會複利累積。每一次沒有更新系統自我模型的復原,都是一次未來的誤校準;每一次沒有被精煉的相似性比對,都會誤導迴圈的下一次迭代。跳過學習的帳單不會立刻送達,它會在六個月後,以一個代理在一個「本該從中學到教訓」的相似情境裡,自信地犯錯的形式送達。

本節值得點名的另一項特質是:並非所有的學習都是有益的。如果失敗訊號被誤讀或過度概化,系統可能會學到錯誤的教訓——它可能對罕見的邊界情況反應過度,可能為了避免小錯誤而抑制正常流量,可能為了保住可用性而不必要地降級效能,也可能不斷強化過度保守的行為,直到自主性崩解成永久性的升級。這正是為什麼學習必須永遠與結果量測、明確邊界配對在一起。「學習」這一拍,不是「代理更新了自己的模型」,而是「代理更新了自己的模型,而這次更新被拿去對照後續的結果做評估,然後這次更新要嘛站得住腳,要嘛被回復」。這正是科學實驗一直以來所要求的同一套紀律,唯一的差別,是代理正在生產環境裡、對著團隊已經宣告好的有邊界周界,持續不斷地進行這些實驗。

本節沒有涵蓋的,是事後檢討(postmortem)。事後檢討依然有價值,第 5.8 節會在事故應變的脈絡下正式接續它。這裡的重點更具體:DRAL 的「學習」這一拍,不是一場季度檢討會議,而是運行中系統的一項持續性特質,正是它,區分了「會反應的自動化」與「代理式系統」。建構了其他三拍、卻跳過學習的團隊,會產出一套擴展執行力、卻不擴展判斷力的自動化。把學習做對的團隊,往往會在一季之內發現,自己的事故輪廓開始以舊指標無法完整描述的方式改變形狀。第 3.6 節會介紹能夠描述它的指標。

3.3+3.4 有界探索:假設與故障注入

單靠從生產環境的失敗中學習是不夠的。生產環境的失敗按自己的時程到來,往往聚集在系統既有的弱點周圍,而不是它的盲點周圍。要學得比失敗能教的更快,系統必須自己提出問題——這正是有界探索的用途。系統辨識出自己還不理解的東西,形成一個關於系統應該如何表現的假設,然後安全地測試這個假設。執行這項測試的機制,就是有界故障注入(bounded fault injection,簡稱 BFI)。我們會正式為它命名,並在本書其餘篇幅中使用這個詞。

在 BFI 之前,要先有假設的產生。假設,是測試的先決條件。在傳統的混沌工程裡,假設是非正式且以人為中心的——我們預期系統在壓力下會有某種表現,我們弄壞某個東西,我們觀察發生了什麼,解讀存在於工程師的腦中。在代理式系統裡,假設必須是明確的、機器可讀的、可測試的。一個有用的假設要宣告三件事:系統預期的穩定狀態、被施加的條件或壓力,以及在既定邊界內的預測結果。與其說「這個服務應該能容忍依賴 X 的延遲」,一個由系統產生的假設會斷言:當某個下游依賴經歷指定程度、指定時長的降級時,某個特定的 SLO 將維持在其目標區間之內。假設的精準度,決定了後續學習的價值——模糊的假設,只會產出模糊的教訓。

假設不是憑空出現的,它們源自於信心的缺口:近期的部署或設定變更、新引入的依賴或流量模式、反覆出現的低信心決策、缺乏歷史先例的失敗訊號、預期行為與觀測行為之間的分歧、復原信心持續偏低的區域——這些都是浮現候選假設、供代理考慮的觸發點。代理接著會在這些候選假設之間排序優先順序,權衡潛在影響、爆炸半徑,以及信心缺口的大小。這裡的輸出不是「把一切都測一遍」,而是「這是一個答案將會最大程度改變我們下次行為方式的假設」——那正是值得投入安全預算的假設。

假設也需要一個明確的穩定狀態定義。系統只有在知道「正常」長什麼樣子時,才能對「偏離」進行推理,而這要求穩定狀態必須是機器可讀的,而不是由一個儀表板隱含暗示的。一份針對結帳服務、可行的穩定狀態宣告可能長這樣:

service: checkout-api
steady_state:
  availability:
    slo: 99.9
    window: 30d
  latency:
    p95_ms: 400
    evaluation_window: 5m
  error_rate:
    max_percent: 0.5
    evaluation_window: 5m
  dependencies:
    payment-gateway:
      max_latency_ms: 600
      allowed_error_percent: 1.0
  recovery_constraints:
    max_instances_added: 4
    max_cost_increase_percent: 10
    require_human_approval:
      - traffic_failover
      - regional_isolation

這份宣告做到了三件儀表板做不到的事。它消除了模糊性:系統不再靠推斷得知「健康」是什麼意思,而是依照一份明確的契約進行評估。它讓每一個接觸這項服務的代理,都擁有相同的參考框架:偵測、假設產生、BFI、復原行動,全都引用同一份定義。它把護欄內建其中:即使系統在自我修復,也是在宣告好的經濟與營運邊界內進行的。沒有以這種方式定義的穩定狀態,假設的產生就會退化成瞎猜;有了它,探索就變得有的放矢。

有界故障注入,是把一個假設轉化為測試的機制。「有界」這半個詞,正是它與傳統混沌工程實踐區分開來的關鍵。作為一門純粹的學科,混沌工程的焦點在於「故障」本身:殺掉一個節點、增加延遲、丟棄封包,問的問題是「系統能不能撐過去」。而在 BFI 裡,焦點在於「系統的理解」:在這個特定的壓力下、在這個特定的周界內、持續這個特定的時長,系統會不會正確地偵測、推理、行動?故障是輸入,系統的行為才是被研究的對象。而「邊界」,正是讓這個實驗可重複、安全、值得持續運行(而不是每季才做一次)的關鍵。

BFI 的安全架構,建立在一小組跨工具保持不變的架構原語之上:在實驗開始前必須滿足的前置條件(目前的 error budget 消耗量、流量水準、近期的變更活動、對復原機制的信心);持續監控影響、一旦超過門檻就中止的執行期護欄;驗證復原並擷取學習的後置條件;信心跌破門檻時的升級路徑。這些不是由人類執行的程序性檢查,而是自動觸發、由機器評估的關卡。一個針對「付款閘道延遲」假設的 BFI 實驗範例,可能長這樣:

experiment:
  name: payment_latency_probe
  fault:
    type: latency
    target: payment-gateway
    inject_ms: 250
    duration_seconds: 120
  preconditions:
    max_error_budget_burn_percent: 20
    min_healthy_instances: 3
    traffic_percentile: p75
  runtime_guards:
    abort_if:
      error_rate_percent: 1.0
      latency_p95_ms: 500
  recovery:
    auto_rollback: true
    notify_on_abort: true
  learning:
    record_metrics:
      - latency
      - error_rate
      - recovery_time

系統不會只是注入故障然後等待,它在每個階段都會評估安全性。如果某道護欄被觸發,實驗就會停止,復原隨即開始;如果代理準備啟動實驗時,前置條件並未滿足,實驗就會被延後。學習的輸出會被自動擷取,以與其他每一個 DRAL 結果相同的方式結構化,並回饋進驅動未來決策的信心模型之中。「驗證即程式碼」(validation-as-code)是與此密切相關的一門學科,它把這些模式從一次性的腳本,提升為一套系統;第 7.4 節會正式接續這個主題。

代理式系統裡假設的一項重要特質是:它們的目的不是證明正確性,而是發掘脆弱性。一個被推翻的假設不是負面的結果,而是一次高價值的學習事件,因為它揭露了系統的信心被錯放在哪裡。系統會對稱地看待假設的結果——被證實,會提高對相關策略的信心;被推翻,會揭露一個缺口。兩者都會餵進「學習」。這種心態,讓系統不會迴避困難的測試,也正是讓 BFI 能夠持續運行、而不是偶爾才舉行的儀式的原因。

第二項值得點名的特質,是假設會自己排優先順序。代理不會把它產生的每一個候選假設都跑一遍,而是依照三項考量來權衡它們:這個假設將會填補的信心缺口有多大、這次測試的爆炸半徑成本有多高,以及如果假設被推翻,這次學習的價值有多高。一個高價值的假設,是那種會改變代理下次遇到類似情況時的行為的假設;一個低價值的假設,則是那種代理在跑測試之前就能預測出結果的假設。這門紀律,就是把安全預算投資在前者身上,並淘汰後者。跨領域的假設通常是價值最高的,因為它們跨越了人類很少能同時看見的邊界:一次部署,結合一次細微的流量轉移,觸發了一個關於某個「原本一直穩定」的依賴的問題。單一訊號永遠無法讓這個假設浮現出來,但幾個訊號合在一起就能,而代理的長處,正在於能夠跨領域關聯弱訊號,而不受限於人類模式辨識能力所受限的認知負荷。

警告 沒有邊界的 BFI,不是「有界」故障注入,而是混沌。這門紀律,存在於前置條件、執行期護欄、中止條件與後置條件之中。沒有這些,系統測試的就不是一個假設,而是在破壞東西,然後祈禱復原層能接住後果。

對較大型的組織而言,同樣的 BFI 模式往往存在於一個政策引擎裡,而不是服務自己的設定裡。假設宣告由代理產生,安全限制則來自一個中央治理層。代理提出實驗,政策引擎依照目前的條件與當責邊界對其進行評估,只有在政策回傳「允許」時,實驗才會運行。這種模式,在「擁有服務的團隊」與「擁有安全範圍的團隊」之間,加上了一道明確的區隔,能更好地跨團隊擴展,支援更嚴格的稽核要求,並讓安全範圍能獨立於任何單一服務而演進。代價是多一套系統要維護。大多數成熟的組織,最終會讓這兩種模式並存運作:貼近工作負載的本地宣告,加上處理跨切面限制的政策強制執行。

本節之所以對全書的更大論點很重要,是因為它改變了「學習」與「風險」之間的關係。在傳統的韌性模型裡,學習是要付出可用性代價的——你跑一次實驗,系統退化,你學到了東西,然後把這個代價當成一項季度投資接受下來。在代理式韌性模型裡,實驗是小而頻繁的、一旦觸發護欄就會自動中止,並回饋進一個回應會沿著曲線持續改善的系統。學習近乎免費。學習速度的限制因子,不再是團隊「願不願意弄壞生產環境」,而是團隊在宣告穩定狀態、前置條件、中止條件時的紀律。這是一種不同性質的工作,也正是成熟的代理式團隊花費自己可靠性預算的地方。

3.5 適應性復原策略

當「推理」這一拍完成時,代理已經擁有一個關於「哪裡出了問題」的假設,以及一組依優先順序排列的行動選項。「行動」這一拍,就是代理做出選擇的地方。本節談的是代理「如何」做選擇。問題不是「哪個行動最快」,甚至也不是「哪個行動最可能奏效」,而是「在代理能用一句話描述其最壞情況的行動之中,哪一個造成的破壞最小」。正是這個框架,把適應性復原與反射式復原區分開來。

傳統的復原,假設同樣的失敗值得同樣的回應。但實務上,同一個症狀可能代表截然不同的底層狀況。延遲上升,可能意味著一次暫時性的依賴減速、一次流量轉移造成的容量失衡、一次糟糕的部署、快取層的資料傾斜,或一次區域性的網路問題。對這一切都套用同一套復原行動,有時候沒有效率,有時候則有害。適應性復原一開始就承認這種不確定性,並使用訊號、歷史,以及學到的信心,來選擇最可能以最小破壞恢復穩定狀態的行動。

這個決策結合三種輸入。「情境」是現在正在發生什麼:遙測資料、近期變更、流量形狀、依賴健康度。「信心」是系統對「某個特定行動會奏效」有多確定,這個信心是從過去的事故與 BFI 結果校準出來的。「影響」是每個選項預測的爆炸半徑、對使用者的影響,以及成本。系統選的不是最快的行動,而是「情境—信心—影響」這個組合最有利的行動;如果沒有任何可用行動能通過這道門檻,它也願意選擇「等待」。

四種被命名的策略,涵蓋了代理式復原在實務上大部分的工作。本書會反覆使用這幾個詞,值得在此謹慎命名。

「回滾」(rollback)撤銷最近一次的變更。這是因果故事最乾淨的復原策略——如果系統在變更之前是健康的,變更之後不健康,那麼變更就是顯而易見的嫌疑犯。回滾果斷且可解釋,它的最壞情況也很容易理解:系統回到一個直到不久之前都還在正常運作的狀態。限制在於,回滾只能處理由近期變更引起的失敗;一次由流量轉移、資料漂移,或上游依賴造成的惡化,不會因為回滾了本地服務而解決。

「隔離」(isolate)把一個失效的元件,從依賴它的路徑中移除——斷路、切斷依賴、區域引流。當失敗被侷限在特定元件、而系統其餘部分能在沒有它的情況下優雅降級時,隔離就是正確的策略。它的最壞情況,是被隔離元件的下游消費者,正好經歷了代理原本試圖防止的那種惡化。這裡的紀律,是在拉下開關之前,就要知道哪些消費者會受到影響,以及拓撲結構是否允許這麼做。

「降級」(degrade)減少系統所提供的功能,以保住剩下功能的正常運作——關閉功能旗標、放棄次要請求、回傳精確度較低的回應。當失敗是容量或飽和度問題、而選擇是在「所有人都得到部分服務」與「沒有人得到完整服務」之間時,降級就是正確的策略。它的最壞情況,是降級持續的時間比預期還長,而底層原因被降級本身給掩蓋了。這裡的紀律,是把降級的時間限縮在有邊界的範圍內,並在無法解除降級時升級處理。

「擴容並延後」(scale-and-defer)增加容量以吸收眼前的問題,同時把根因分析延後交給人類——新增執行個體、提高速率限制、擴大池的大小。當失敗單靠容量就能被復原、且額外容量的成本可以接受時,這就是正確的策略。它的最壞情況,是底層原因沒有被處理、後續會再次發生——這正是為什麼「擴容並延後」應該永遠觸發一次後續調查,而不是自行把迴圈閉合起來。

代理在這些選項之間的選擇,很少是二選一的。適應性復原是漸進式的:代理從能通過信心門檻、破壞性最小的行動開始,觀察結果,只有在較輕的行動失敗時才升級。先做流量塑形或速率限制;如果塑形撐不住,再對單一元件做針對性的擴容;如果症狀顯示是陳舊資料,就做快取失效或資料刷新;如果某個元件顯然是原因,就做依賴隔離;如果牽涉到近期變更,就做部分回滾;完全回滾或容錯移轉,只作為最後手段。每一步,都會依照第 3.3 節宣告的穩定狀態契約與復原限制進行評估。如果系統移回穩定狀態之內,升級就停止;如果沒有,就考慮下一個行動。這種做法把變更的擾動降到最低,減少對使用者的影響,也產出乾淨的學習資料,因為「哪個行動奏效」是可以被辨識出來的。

小提示 選擇器只有一行:選擇你能用一句話描述其最壞情況的那個策略。如果最壞情況需要一整段話才能講清楚,這個策略還沒有被理解到足以自主執行的程度。

適應性復原還有一項值得點名的特質,就是它攜帶著明確的、向人類移交的條件。代理不會窮盡每一種策略才升級,它會在「進一步自主行動的信心」跌破團隊宣告的門檻的那一刻就升級,而且它會帶著結構化的情境升級:試過了什麼、發生了什麼、代理相信剩餘的風險是什麼。接到呼叫的人類,不是從零開始,而是接續這個迴圈,附帶著代理的推理軌跡,面對一個狀態已經被部分理解的系統。這正是第五章要正式接續的主題:一個代理式系統要求人類介入的那一刻,這次請求的品質,就是「有效的移交」與「比人工處理還糟的結果」之間的差別。適應性復原,正是讓這次請求變得精準的紀律。

3.6 韌性與決策品質的代理式 SLO

傳統的 SLO 量測的是服務有沒有維持在線,它們不量測代理的決策好不好。一個代理式系統可能達成它曾經簽署過的每一項可用性與延遲目標,卻依然陷入麻煩,因為描述服務行為的 SLO,並不描述代理行為。本節介紹五項填補這道落差的代理式 SLO。每一項都有正式的定義、一個目標區間,以及一種典型的失效模式。合在一起,它們讓代理行為變得可觀察、可調校、可治理,就像服務行為在過去十年裡一直被對待的方式一樣。本書會反覆回到這五項指標,我們會在此謹慎地為它們命名一次。

這五項是:自主解決率(Autonomous Resolution Rate,ARR)、決策品質 SLO(Decision Quality SLO,DQ-SLO)、推理延遲 SLO(Reasoning Latency SLO,RL-SLO)、行動有效性 SLO(Action Effectiveness SLO,AE-SLO),以及校準誤差(Calibration Error,CE)。本書會把這一組稱為「五大旗艦」(the five flagships)。還有其他 SLO 存在,也各有其位置——溯源率(Grounding Rate)、決策預算(Decision Budget)、建議採納率(Suggestion Acceptance Rate)等等,會在後續章節出現。五大旗艦是承重的那幾根樑柱,是每一個代理式系統從第一天就需要的 SLO,其他一切都是從它們衍生出來的。SLO 的通膨,在這項工作裡是真實的風險;試圖同時對著十五項 SLO 運作的團隊,最終會等於沒有對著任何一項運作。這門紀律,是先把這五項儀表化做好。

「自主解決率」(ARR)量測系統在不升級給人類、同時維持在自己的當責邊界之內的前提下,完全解決事故的頻率。公式很直接:

ARR = (自主解決的事故數 / 偵測到的事故總數) × 100

舉一個實例:在一個三十天的視窗內,系統偵測到 220 起事故,其中 154 起被自主復原路徑完全解決,沒有違反政策或計畫外升級;剩下的 66 起需要人類介入。這個視窗的 ARR 是 70%。ARR 是一項成熟度指標,而不是一個該被盲目最大化的目標。一支 ARR 跑在 30% 的團隊,做的大多是「人類主導、代理輔助」的復原;一支 ARR 跑在 85% 的團隊,已經建立起真正的代理式營運。一支一季內從 30% 爬升到 85% 的團隊,幾乎可以肯定是在另外四項 SLO 上偷工減料。ARR 典型的失效模式,是可以靠放寬升級標準來灌水,這正是為什麼單獨讀它毫無意義——只有和下一項旗艦指標 DQ-SLO 一起讀,它才有意義。ARR 也應該永遠依事故嚴重度、服務層級與復原類型做分段,因為一個單一的彙總 ARR,恰恰會掩蓋自主性運作良好的情況,也掩蓋運作不良的情況。

「決策品質 SLO」(DQ-SLO)量測系統選對了行動與否,而不只是它有沒有行動。一個決策若在定義好的時間窗內恢復穩定狀態、沒有違反復原限制、也沒有觸發後續事故,就算成功。公式:

DQ-SLO = (成功的自主決策數 / 自主決策總數) × 100

在一週內,系統做出 92 次自主決策,74 次乾淨地恢復穩定狀態,18 次需要回滾、升級或次要緩解措施。這一週的 DQ-SLO 大約是 80%。DQ-SLO 是自主性的主要安全訊號,DQ-SLO 下滑,通常代表信心模型過時、假設範圍設得不好,或底層系統發生了代理尚未學到的變化;DQ-SLO 隨時間上升,則是擴大自主邊界的正當理由。它典型的失效模式,正好與 ARR 相反:DQ-SLO 可以靠把任何代理不確定的決策都升級處理來人為維持在高水準,這會表現為 DQ-SLO 高達 95%、ARR 卻只有 25% 的組合。這兩項指標必須一起上升,這個趨勢才代表真正的成熟。一個有用的解讀方式:DQ-SLO 回答「代理出手時,對不對」;ARR 回答「代理有多常願意出手」。一支團隊需要這兩個答案都很高,自主性才是真實的。

「推理延遲 SLO」(RL-SLO)量測系統從偵測到做出一個已提交、符合政策的決策,需要花多長時間。公式:

RL-SLO = 決策提交時間 − 偵測時間

RL-SLO 通常以 p95 與 p99 追蹤,並依事故嚴重度分段。對第一級事故而言,一個可行的目標是 p95 低於 30 秒、p99 低於 90 秒;對第二級事故,p95 低於 90 秒、p99 低於五分鐘。具體數字因領域而異,重要的是團隊已經宣告了目標,並依此運作。RL-SLO 典型的失效模式,是過長的推理延遲幾乎總能追溯到三個原因之一:低效的調查路徑、過多的工具呼叫,或不夠清楚、需要代理與政策引擎來回溝通才能行動的政策。這三者都是可以修正的,而 RL-SLO 正是能揭露哪一個是主導因素的指標。一支 ARR 與 DQ-SLO 都健康、RL-SLO 卻在惡化的團隊,正在被自己生產流量的速度慢慢甩開,而這個代價,會在遠早於它出現在可用性數字之前,先以使用者可見影響的緩慢上升的形式到來。快取投資、預先計算的拓撲,以及政策編譯上的投資,回報也會體現在 RL-SLO 上;沒有量測它的團隊,無從得知這些投資是否奏效。

「行動有效性 SLO」(AE-SLO)量測代理的行動,是否真正達成了預期結果、且沒有附帶損害。一個行動若在目標時間窗內恢復穩定狀態、維持在成本與爆炸半徑限制之內、且沒有製造次要事故,就算有效。公式:

AE-SLO = (有效的復原次數 / 復原總次數) × 100

在 48 次復原事件裡,39 次乾淨地恢復穩定狀態,6 次造成次要惡化,3 次觸發新事故。AE-SLO 大約是 81%。AE-SLO 區分了「快速的復原」與「好的復原」。一支為了 RL-SLO 而犧牲 AE-SLO 的團隊,建構出的是一個快、卻經常出錯的代理,這比一個慢、卻大多正確的代理還要糟。AE-SLO 典型的失效模式,是沒有明確儀表化的情況下,次要效應很容易被漏掉——一個解決了眼前延遲問題、卻讓每月雲端帳單翻三倍的擴容行動,同樣要算進 AE-SLO 裡;沒有次要效應的儀表化,團隊就不會知道。做好儀表化的 AE-SLO,正是讓代理式復原的成本,以和可用性同樣清晰的方式,呈現在業務面前的關鍵。它也是最常揭露「哪些復原策略應該被偏好、哪些應該被淘汰」的 SLO;依策略類型追蹤 AE-SLO,能提供不需要爭論就能做出這個淘汰決定的證據。

「校準誤差」(CE)量測代理所宣稱的信心,與它實際結果之間的落差。如果代理以宣稱信心 0.9 觸發了 100 次行動,大約應該有 90 次成功。如果只有 70 次成功,代理就是過度自信,CE 偏高;如果有 95 次成功,代理就是信心不足,CE 同樣偏高,只是方向相反。公式是一個標準的校準量測:

CE = mean(|宣稱信心 − 觀測成功率|)(依信心區間分組)

CE 是防止系統悄悄漂移的 SLO。ARR、DQ-SLO、RL-SLO 與 AE-SLO 都可能各自看起來健康,同時 CE 卻在上升,因為每一項都可能透過扭曲代理自我認知的方式來達成。一個學會把信心低於 0.85 的決策全都升級處理的代理,在 DQ-SLO 與 AE-SLO 上會看起來很棒,但隨著世界改變,它的 CE 會上升,因為那個門檻是依照一個較舊的分布設定的。一支持續盯著 CE 的團隊,能在漂移出現在使用者可見指標的好幾個月前,就抓到這個漂移。CE 典型的失效模式,是它是最常被遺忘的 SLO——建立起前四項的團隊,往往覺得自己已經抵達終點,而 CE 卻悄悄惡化,直到下一次分布外事件揭露出:代理其實一直在依照過時的假設運作。第 11.5 節談的決策預算,直接建立在 CE 之上。

附註 五大旗艦,正式命名一次:自主解決率、決策品質、推理延遲、行動有效性、校準誤差。每一個代理式系統從第一天起,就需要這五項全部。其他 SLO 會在後續章節出現,但沒有一個能取代這五項。

五大旗艦並非彼此獨立,它們的價值來自於彼此的關聯性。高 ARR 配上低 DQ-SLO,代表不安全的自主性:代理經常出手,也經常出錯。高 DQ-SLO 配上低 ARR,代表過度限制的護欄:代理出手時是對的,但很少出手。低 RL-SLO 配上低 AE-SLO,代表倉促的決策:代理很快,但很粗糙。在其他數字都健康的情況下 CE 緩慢上升,代表漂移:世界已經改變,而代理還沒察覺。把這五項一起讀,正是用證據取代直覺的方式,也是讓一支團隊能負責任地——而不是憑感覺地——擴大自主性的紀律。

圖 4-2 五大旗艦代理式 SLO,對應到它們各自量測的 DRAL 拍點。

五大旗艦有幾項營運上的特質,值得明確點出。第一,這五項都應該以和傳統服務 SLO 相同的頻率被追蹤——每月彙總對它們來說都太粗略了,每週是最起碼有用的視窗,而對較動態的那幾項(ARR、RL-SLO)而言,每日追蹤才是合適的。第二,這五項都應該出現在與服務可用性、延遲相同的儀表板上,而不是一個獨立的「AI 指標」儀表板——代理式可靠性整個論點的核心,就是「代理行為是系統行為的一部分」;把這些指標隔離開來,正好違背了這個整合。第三,error budget 依然適用。如果 DQ-SLO 在一個 30 天視窗內的目標是 92%,而系統實際跑在 87%,自主性就應該自動收緊:信心門檻上升,升級變得更頻繁,代理的行為朝著自己操作範圍中較保守的一端移動,直到預算恢復為止。這正是傳統 SLO 的 error budget 一直在強制執行的同一套紀律,只是套用在代理身上。

警告 SLO 通膨是真實的風險。五大旗艦是刻意選定的最小承重集合,要抵抗為每一個新代理或工作流程新增指標的誘惑。次要指標會在它們各自合適的章節出現:溯源率在 4.9 節,決策預算在 11.5 節,建議採納率在 4.9 節。把旗艦指標固定在五項,正是讓它們能在跨團隊間保持清晰易懂、可操作的關鍵。

五大旗艦所帶來的,正是傳統 SLO 曾經為服務帶來的同一件事:一個共享的、技術性的「好」的定義,被持續觀察,並被刻意地據以運作。沒有它們,代理式可靠性只是一種姿態;有了它們,它才是一門工程學科。本書其餘部分會不斷使用這五項指標。第五章在事故應變期間盯著它們,第七章在混沌之下盯著它們,第十一章在 CE 之上建構決策預算,第十四章用它們來跨代理協調。在這裡把它們做對,接下來每一章都會站在更穩固的地基上。

3.7 自我修復系統中的當責邊界

第 1.5 節命名的自主邊界,定義的是系統「可以」做什麼;本節要介紹的「當責邊界」(accountability boundary),定義的是人類「擁有」什麼。這兩者不同,把它們混為一談,是早期代理式導入最常見的架構錯誤。本節要把這個區別講清楚,因為本書其餘部分都仰賴這個區別。

當責不會被委派,它只會被指派。

這句話是本章的哲學支點,值得在此停下來細想。在傳統的維運裡,當責大多是隱含的:人類調查、決定、行動、承擔後果;即使自動化涉入其中,責任最終還是能追溯回批准腳本或按下按鈕的那個人。代理式系統打破了這個假設——決策是持續發生的,往往以機器的速度,基於對局部資訊的機率性推理做出。如果在這樣的世界裡,當責依然是隱含的,它會在出事的那一刻立刻崩潰:人類要嘛因為「是系統做的」而抽身,要嘛在第一次失敗之後就徹底關閉自主性。這兩種結果,都不是本書想主張的。

這個轉變,是要讓當責變得明確、可觀察,並以程式碼、而不是以文化來強制執行。對系統做出的每一個自主決策,都必須以機器可讀的形式回答四個問題:這個結果由誰擁有?授權了什麼樣的權限?在什麼條件下允許自主?控制權必須在什麼時候交還給人類?如果這些答案存在於操作手冊或團隊章程裡,代理無法尊重它們——它們必須存在於系統本身,在決策當下被評估。

因此,當責邊界是一項執行期的特質。它包圍著自主邊界,也正是它讓自主邊界變得安全。一個當責邊界,為某個系統、服務,或某一類失效,宣告自主行動的最大範圍,並且是在行動「之前」就被強制執行,而不是事後才被稽核。這道邊界至少要宣告四項特質:「行動範圍」——代理被允許改變什麼(擴容、重啟、流量塑形、回滾);「風險上限」——代理可能招致的最大可接受爆炸半徑、error budget 衝擊,或使用者降級程度;「信心門檻」——自主執行所需要的最低決策信心;「升級觸發條件」——會暫停自主性、要求人類核准的明確條件。這四項,把自我修復從一項憧憬性的能力,轉化成一種受治理的系統行為。

本節要求讀者內化的關鍵區別,是「執行權的委派」與「當責的委派」之間的不同。代理式系統委派的是前者,不會委派後者。擁有這項服務的團隊,繼續擁有這個結果——他們定義可接受的穩定狀態,設定風險門檻,核准行動範圍,維護 error budget,審查並演進政策。代理不做任何這些事,代理完全在團隊宣告好的限制之內運作,而當某件事出錯時,失敗要追溯回這道邊界,而不是代理本身:要嘛是這道邊界設得太寬鬆,要嘛是信心模型校準錯誤,要嘛是風險上限定義不當——這三者,全都是屬於團隊的設計責任。代理是機制,團隊才是當責方。

這與傳統自動化的立場根本上不同。自動化腳本會悄無聲息地失敗,或災難性地失敗,因為它們的意圖是靜態的——一旦腳本被核准,核准它的人類,實質上就把當責委派給了一個可能早已不再符合它所運作的世界的、被凍結的產物。代理式系統則以可觀察的方式失敗,因為它們的意圖被持續地評估。當責邊界,讓那些腳本會隱藏起來的設計決策浮現出來——每一次這道邊界被觸發,團隊都會被迫直面自己的假設,這令人不太舒服,卻恰恰是讓系統變得更安全的那項特質。

圖 4-3 當責邊界包圍著自主邊界。移動的是執行權,不變的是當責。
附註 當責不會被委派,它只會被指派。擁有一項服務的團隊,繼續擁有它的結果。當一個代理進入迴圈時,改變的是「誰執行」,而不是「誰負責」。這道邊界,正是把這個區別從修辭變成可操作事實的產物。

這個框架的一個實務後果,是當責邊界成為團隊被期待要持續演進的工程產物,而不是每年審查一次的靜態文件。它們是有版本控管、經過測試、被稽核、並依照結果而改變的。一道從未被調整過的邊界,幾乎可以肯定要嘛太緊(自主性被限制在系統實際已經掙得的成熟度之下),要嘛太鬆(自主邊界比團隊能負責任地監督的範圍還寬)。第 3.6 節的五大旗艦 SLO,正是給予團隊「該不該調整」的證據——DQ-SLO 與 AE-SLO 隨時間上升、CE 保持穩定,就是擴大邊界的理由;數字下滑,則代表邊界需要收緊。這道邊界是一個控制介面,操作它,是團隊工作的一部分。

這也正是為什麼當責邊界不能被中央統一設定後就放著不管。每一項服務都有它自己合適的邊界,因為每一項服務都有自己的風險輪廓、自己的使用者群、自己的依賴關係、自己的成熟度。跨切面的政策依然有其位置——有些限制(法規、財務、倫理)必須在整個組織裡一致地適用。但團隊專屬的邊界,屬於那支團隊,維護它,正是如今「擁有一項服務」所意味著的一部分工作。第十三章會詳細展開這對營運模型的意涵。這裡的重點是:即使執行權已經轉移,當責依然停留在它一直以來所在的位置。

3.8 自我修復系統的失效模式

代理式韌性有它自己典型的失效模式,與第 2.6 節可觀測性的失效、第五章事故應變的失效有所不同。本節要點名那些特別從自我修復的動態中——從回饋迴圈、從持續性行動、從出錯的學習中——浮現出來的失效模式。每一種都是可以被預防的,只要它被點名並被儀表化;沒有一種是偶然的。

第一種是「振盪與抖動」(oscillation and thrashing)。系統在矯正行動之間快速交替,永遠無法穩定下來——激進的擴容緊接著激進的縮容,反覆的斷路器開啟又關閉,區域之間交替的流量轉移。每一個個別的行動,在代理的推理裡都局部地站得住腳,但合在一起,它們放大的是不穩定,而不是抑制它。這個模式,會在穩定狀態定義不良、復原行動過於激進、控制迴圈裡沒有阻尼或遲滯機制,或推理延遲短於系統的沉降時間時浮現。實質上,代理反應的速度比現實能回應的速度還快。緩解方式,是在行動之間設置最短保持時間、在復原政策裡加入阻尼因子,並要求在跨越多個評估視窗、信心確實有所改善之後,才能重複同一個行動。一個自我修復的系統,就和一個人一樣,需要「等待與觀察」的紀律。

第二種是「過度矯正與隱藏的損害」(over-correction and hidden damage)。系統成功地恢復了表面上的健康,卻同時製造出更深層、長期的問題——頻繁的重啟掩蓋了一次記憶體洩漏,激進的節流隱藏了正在增長的下游壓力,可用性以資料一致性為代價被保住。從外面看,系統看起來很健康;在內部,它正在累積債務。這個模式,會在成功標準只關注短期訊號、沒有針對二階效應的儀表化,以及 error budget 被當成「復原燃料」而不是「學習訊號」時浮現。緩解方式,是把復原頻率當成第一等訊號來追蹤,對「重複針對同一元件使用同一矯正行動」進行懲罰,並引入「復原債務」計數器,當同一個傷口每天都在被治療時,讓自主性自動降級。這裡的比喻很重要:如果一個系統每天都在治療同一道傷口,那不是在痊癒,而是在悄悄失血。

第三種是「靜默的學習腐化」(silent learning corruption)。系統從本身就具有誤導性的結果中更新自己的信心模型,而這種腐化會向前傳播。一個看似成功的復原行動,其實只是因為一次無關的上游變更掩蓋了症狀;一個由在非典型流量下進行的 BFI 測試所驗證的假設;一個因為產出了相同表面指標而被強化的相似性比對,儘管底層條件其實不同。代理學到了錯誤的教訓,然後自信地把它套用到後續決策上。這個模式,會在結果歸因不完整、學習更新是基於單一事件的證據而非相互佐證的模式,以及系統沒有機制回頭檢視並修正先前學習的內容時浮現。緩解方式,是要求在套用一次學習更新之前,需要多個相互佐證的結果,讓相似性資料庫保持可稽核、可修訂,並把 CE 儀表化成能標記「學到的信心正在偏離觀測現實」的金絲雀指標。這種失效模式,也正是為什麼每一次復原,都必須記錄「為什麼」它復原了,而不只是記錄「它復原了」這個事實。

第四種是「相似性資料庫中毒」(similarity-store poisoning)。這是「靜默的學習腐化」的一個特定案例,嚴重到值得單獨命名。代理的「像這種情況」案例庫,被誤分類、誤標記,或在異常條件下被記錄下來的條目所腐化。這些被腐化的條目,接著會影響每一個檢索到它們的後續決策,而這種不良影響很難被偵測到,因為它表現為 CE 穩定、低程度的上升,而不是單一一次明顯的糟糕決策。緩解方式與第三種失效模式重疊:相互佐證的學習、可修訂的歷史、儀表化的校準。額外的紀律,是把相似性資料庫當成一項有版本控管、有存取控制、有明確保留政策的生產資料資產來對待——一個沒有人擁有的相似性資料庫,會悄悄地腐化。

第五種是「信心崩潰與決策癱瘓」(confidence collapse and decision paralysis)。這是「過度矯正」的另一個極端。代理變得愈來愈猶豫不決,把一切都升級給人類——自主性在技術上依然存在,卻很少被真正運用。這個模式,會在信心門檻設得過於嚴格、錯誤的行動被懲罰、卻沒有獎勵正確的克制,以及學習模型主要是基於「失敗懲罰」而不是「校準過的結果」來訓練時浮現。代理學到的教訓是:什麼都不做最安全。緩解方式,是在「懲罰錯誤的行動」與「獎勵正確的克制」之間取得平衡,引入分級的自主性而不是「行動或升級」的二元選擇,並追蹤 CE,而不只是行動的成功率。如果這種失效模式沒有被處理,ARR 會崩潰,但更深層的症狀是:代理已經不再是這個迴圈的參與者,而變成了一個昂貴的遙測資料轉發器。

警告 一個復原了卻沒有記錄「為什麼」復原的代理式系統,正在退化回同樣的失效。沒有解釋的復原,是假裝成智慧的自動化。沒有歸因,「學習」這一拍毫無意義。

這五種失效模式,共享一項值得點名的結構性特質。它們每一個都是「設計」上的失敗,不是「工具」上的失敗;每一個都源自於團隊如何把回饋迴圈接起來,而不是他們選了哪個平台。每一個都能透過五大旗艦 SLO 被偵測到,尤其是 CE——它是這一整類失效所產生的緩慢漂移最可靠的金絲雀指標。而每一個,也都可以被本章一直主張的那幾項紀律預防:明確的穩定狀態、有邊界的行動、儀表化的學習、包圍著自主邊界的當責邊界。點名這些失效模式的目的,不是要勸退代理式導入,而是要讓這些失效模式變得清晰易辨,讓團隊能在自己的系統裡認出它們,並針對它們進行工程設計,而不是在壓力之下才發現它們。

圖 4-4 自我修復系統的五種失效模式。每一種都是設計上的失敗,由五大旗艦 SLO 浮現出來。

3.9 從自我修復到系統性韌性

本章所使用的「韌性」一詞,指的是這樣一種特質:同一類失效,每發生一次,就變得更不容易發生、影響更小,或吸收得更快。這是一項可量測的特質,而不是一種憧憬,過去九節的工作,就是要讓它變得可量測。第二章的決策級遙測,提供了基質;DRAL 提供了運作迴圈;有界故障注入,提供了學得比失敗能教的更快的機制;五大旗艦 SLO,提供了證據;當責邊界,提供了治理;被點名的失效模式,提供了診斷用的詞彙。這些每一項都是各自獨立的工作,只有當它們被接在一起、成為一套系統時,才會變成韌性。

接下來這一章,以及以它開頭的那個部分,談的正是這種「接線」。第二部分,會把本章建構出的韌性引擎,套用到工程師每天運行的工作流程上:第五章談事故應變,第六章談交付,第七章談混沌,第八章談事前情報。這幾章的每一章,開頭都會指向 DRAL 的某一拍,說「在這個工作流程下,這一拍長這樣」。但在這些工作流程能透過代理式的視角被重新呈現之前,它們所運行的架構,必須先被命名。第四章就是要做這件事,它是第一部分裡最長的一章,因為架構這門學科,決定了本章的一切究竟能不能被真正部署出來。代理式可靠性的四個平面(訊號、推理、執行、治理)、決定「哪個等級的自主性適合哪個等級的組織就緒度」的成熟度模型,以及把這一切連接起來的人機協同控制迴路,都會在那裡出現。

在翻頁之前,值得說明本章「沒有」主張什麼。它沒有主張自我修復能消除失敗,沒有主張代理會取代維運人員,也沒有主張這個迴圈一旦安裝完成就會自己運轉。它的主張更狹窄,也更有用:一個圍繞著 DRAL 設計、用五大旗艦 SLO 儀表化、被明確當責限縮住、並持續從自身結果中學習的系統,吸收失敗的方式,會與一個不具備這些特質的系統截然不同。復原會變得更小、更頻繁,也更常是正確的;同一類失效,會變得更不容易重演;人類會更少介入例行案例,更常介入新奇的案例。這一切之所以發生,不是因為系統變聰明了,而是因為系統被設計成能夠學習,而學習被變得低成本、安全,且持續不斷。這,就是代理式韌性。本書其餘部分,要談的是如何運用它。