🌐 This page hasn't been translated yet — showing the original Chinese. Translated posts

頸上有時鐘:事故中的 AI——重播一場毒藥訊息事故

· tech

#ai#incident#sre

📑 目錄

前言

上一篇講了漏斗:AI 吃掉寬層的量,人守住頸口的責任。這一篇把漏斗搬到它最極端的測試場——事故。事故是頸上有時鐘的場景:主播在罵、營運在催,而你必須在資訊不全的情況下做決定。AI 不能替你決定要不要在直播中重啟服務,但它能不能把你「到達決策點的路」鋪短?

這次不寫論述,做實驗。我把 直播電商系列寫過的毒藥訊息事故重播一次——這次桌上有 AI。

當年發生了什麼

先快轉當年的劇情(完整版在戰記原文):直播中,留言瀑布正常地流、ingestion 指標一切正常,訂單不是歸零,是零零落落——乍看甚至不覺得異常,像一場表現普通的直播;是主播那邊先覺得數字不對勁的。沒有警報。兇手是一行邊界:留言是一批 200 則一起 parse 的,只要其中一則怪留言讓 parser 拋 exception,整批放棄——一則毒藥,拖著 199 個無辜的訂單陪葬;踩不到雷的乾淨批次照常出單,所以數字才會零零落落而不是掛零。症狀的欺騙性是雙層的:什麼都沒壞,只是產出變少——而「變少」比「歸零」難發現得多

我選這場事故做重演,正是因為它難:證據互相矛盾、指標全部說謊,「判斷前的路」是所有戰史裡最長的一條。

實驗設計

規則很嚴格,因為實驗誠不誠實決定這篇文章有沒有價值:

  • 開一個乾淨的 AI 對話:它不能用任何工具、不能翻我的部落格,只知道我在對話裡告訴它的事。
  • 資訊分三階段餵,每一階段只給「當年 on-call 在那個時間點知道的事」,由它建議下一步。症狀用當年的真實口徑:訂單零零落落、乍看正常、是主播先發現數字不對——連「這是不是一場事故」都留給它判斷。
  • 我如實記錄它的回應——節錄如下,推理鏈一字未改。

三個誠實聲明:這是 n=1 的單次實驗,不是 benchmark;我知道答案,證據依戰記原文的揭露順序餵,盡量貼近當年;以及 poison pill 在 2026 年是串流處理的知名 pattern,AI 的 pattern 庫裡本來就有——但當年的我們腦中沒有,而這正是重點之一。

第一幕:這到底是不是事故?

我給它的全部資訊:留言瀑布正常、量很大、監控無警報、ingestion throughput 正常;訂單有進來但零零落落,單看訂單流不覺得異常;是主播先發火的,說這種留言熱度不該只有小貓兩三隻;老實說我們自己也不確定是不是事故。它的回覆(節錄):

先判斷是不是事故:算漏斗,不要吵感覺。別跟主播爭「轉換率」——從直播現場肉眼挑 10–20 則明確的喊單留言,逐一追:有沒有進系統?有沒有被解析成下單意圖?有沒有嘗試建單?訂單有沒有建成?抽樣裡有一成以上卡在中間,就是事故,不用再懷疑。 另外釐清一件事:你說的「留言瀑布正常」是我們系統內的瀑布,還是平台原生聊天室?這一題的答案會直接砍掉一半假設。 「訂單零星進來」= 部分格式/部分來源能走通,其餘被丟掉。候選假設:①解析器吃不下真實留言的格式變體(零星成單 = 剛好打對格式的人)②某一個平台的 ingestion 斷了,其他平台撐住了總量 ③商品/檔期設定問題 ④反灌水過濾把大量相似的「+1」洗掉 ⑤下游塞住 ⑥真的只是轉換率低——留到最後,由漏斗數據排除,而不是預設。 先給營運一句 holding statement 買時間。還有一個順手的事:先確保 raw comments 有落地保存——這決定了事故結束後能不能救回這場的營業額。

當年最難的其實是第一步:「這到底是不是事故」——異常是主播用營收數字看出來的,不是工程師用監控看出來的。它把這個糊成一團的問題變成一個十分鐘可執行的判斷程序:抽樣、逐筆追、一成卡住就定案。注意它的假設清單從第一秒就長在正確的前提上——「有的成單」這個訊號直接排除了全域故障,所有假設都是部分失敗的形態。還有兩個老手動作:反問「瀑布是誰的瀑布」(一個澄清問題砍半個假設空間),和「先確保 raw 有落地」(事故還沒定性,就先替補救買好保險)。

第二幕:有的活、有的死、死無對證

我回報抽樣結果:15 則喊單留言全部進了系統,3 則成單;12 則在進入解析之後就沒有任何下文——沒有 parse-fail log、沒有 rejection、沒有嘗試建單的痕跡,就是消失。而且消失的 12 則格式完全正常,跟成單那 3 則看不出差別。各平台進量正常、過濾計數正常、佇列正常、rejection log 乾淨;唯一的雜訊是零星幾條 parsing exception,量級跟 12 則對不上。它的回應(節錄):

「同樣格式、有的活有的死、死的不留痕跡」+「零星 exception 但量級對不上」——這幾乎就是指向一件事:掛掉的不是那 12 則留言本身,是處理它們的 worker/batch。 首要嫌疑:毒藥訊息殺整批。某則畸形留言拋出 exception,同批的正常留言陪葬;如果 ack 在處理前就做了,這些訊息不會重投,直接蒸發、零紀錄。這完美解釋量級對不上:exception 數 × batch size = 消失數。成單的 3 則只是運氣好,不在有毒藥的那批。 驗證(最快):把消失留言的時間戳,跟 exception 的時間戳對齊——如果消失的留言成叢聚集在每條 exception 附近,就是它了。 同一個結構還有兩個備選:一台 consumer 卡死但還佔著 partition(分到它的留言全滅);部分 instance 跑舊版 code。三個假設有共同結構:失敗的單位是 worker,不是留言,所以留言層級才會查無死因。 下一步:止血優先於查因——直接滾動重啟整組 parser。假設二、三會立刻好,假設一也會暫時緩解。現在還在掉單,每分鐘都是錢。重啟的同時做時間戳對齊。給主播的說法現在就能給:「留言都有收到、原始資料完好,訂單處理環節故障修復中;漏掉的喊單會全數補建,一張不會少。」

這一幕是整場實驗的高點,有三件事值得拆開看。

第一,它命中了——不需要任何內部知識的提示。「失敗的單位是 worker,不是留言」這一步抽象,是從「同格式有生有死、死無對證」直接推出來的;我沒有告訴它系統是批次處理,是它從症狀的形狀反推出處理模型。而它給的不是單一敘事,是一個假設族(毒藥/卡死/版本)加上一支能同時鑑別三者的把手(時間戳對齊)。

第二,「止血優先於查因」是一個丟回給人的決定。滾動重啟聽起來理所當然,但那是直播進行中、對著一組還在處理訂單的 worker——會不會弄丟正在處理中的批次?重啟後如果沒好,火會不會燒得更大?AI 可以說「每分鐘都是錢」,但按下重啟的手、以及重啟出事時站在主播面前的人,是你。這就是頸口:建議可以外包,賭注不能

第三,注意它的語氣。「首要嫌疑」「完美解釋」「就是它了」——這次它全對,但對的自信和錯的自信,讀起來一模一樣。如果這是一個錯誤敘事,同樣的篤定語氣會把凌晨三點的你帶進溝裡。能救你的不是分辨語氣(分辨不了),是它附上的那支把手:時間戳對齊,五分鐘,做完才准採信。

第三幕:實錘與收尾

我回報:時間戳對齊完成,12 則消失的留言在時間上成叢,每一叢緊跟在一條 exception 附近;code 確認是一批 200 則、try/catch 包在批次迴圈外層、失敗批次不 requeue。它給出收尾清單(節錄):

P0 止血:try/catch 移到 per-message,單則失敗只丟該則;失敗留言進 dead-letter,帶原始內容 + exception,不再無聲放棄;加 parse_failed_count 告警——這次監控全綠的根因就是「失敗 = 沉默」。部署後用一則刻意的畸形留言驗證:它單獨進 dead-letter、同批其他訊息存活。 P1 救回這場:從 raw comments 用時間窗全量 replay,不要只補抽樣那 12 則——消失的遠不止抽樣那些;去重是硬條件,重複建單比漏單更難收拾;補建訂單打上標記,給營運一份數字:實收留言數、原成單數、補建單數。 P2 事後:找出毒藥本體補測試案例;消費語義改 at-least-once(處理成功才 ack);監控「轉換」而不是只監控「throughput」——留言進量 vs 解析成功 vs 建單數,比率偏離就告警;postmortem 記兩條:失敗不准沉默、批次必須有 per-item 隔離。順手盤點提早上線時跳過的驗收清單——這種雷通常不只一顆。

對照當年:per-message 隔離就是我們事故後的止血;dead-letter 是重來版才補上的第三步;「監控轉換而非 throughput」是戰記反思裡「給批次積壓裝儀表」的同一課。當年用一場事故加一次重來才收斂到的答案,這裡是三輪對話——連「這種雷通常不只一顆」這句經驗談都到位了。

這場實驗說明了什麼

被壓縮的,是到達判斷點的路。「是不是事故」的判斷程序、假設族、驗證把手、給營運的話術、修復清單——這些寬層工作在三幕裡幾乎是零成本供應。on-call 省下來的認知頻寬,全部回灌到不能外包的事情上。這就是「事故中的 AI = 臨時把頸加寬」。

沒被外包的,清單也很清楚。第一,所有動手都是人:抽樣、對時間戳、改 code——AI 碰不到 production,它只能給座標。第二,賭注是人的:「直播中滾動重啟」這種每個選項都有代價的決定,AI 給得出分析,簽名的永遠是你。第三,最容易被忽略的:精確的症狀轉述,本身就是最貴的 context。這場實驗 AI 不需要讀任何內部文件就命中,因為「零零落落」「同格式有生有死」「死無對證」這幾句話已經把系統行為的形狀描出來了——轉述失真一個層次,它就會在另一個世界裡推理。觀察與轉述的品質,是頸的隱藏成分;而更前面那一步——察覺異常本身——當年靠的是主播的營收直覺,今天依然輪不到 AI。

紀律只有一條:相信把手,不要相信敘事。語氣不攜帶正確性資訊,把手才可驗證。它每一輪都附了低成本、高鑑別力的驗證動作(抽樣漏斗、時間戳對齊、畸形留言探針)——照著走,對的敘事會被加冕,錯的敘事會被處決,而你全程站在驗證者的位置,不是蓋章者的位置。

⏱ 頸上有時鐘:主播在罵,時間在走 症狀 / 新證據 指標 · log · 查證結果 AI:敘事 + 把手 候選假設 · 驗證動作 · 溝通草稿 人:動手驗證 抽樣 · 對時間戳 · 照把手走 人:決定與行動 重啟 · hotfix · 補建 · 對外承諾 結果回灌 敘事收斂後 直接採信敘事 = 權威幻覺的短路
迴圈跑幾輪由證據決定;紅色捷徑永遠存在——就算這次的敘事是對的,你在把手走完之前無從知道,而把手往往只要五分鐘。

反思

對的自信和錯的自信,讀起來一模一樣

這場實驗它從頭到尾語氣篤定,而且都對——但這反而是我最想警告的地方。正確性不寫在語氣裡:同樣的「首要嫌疑」「完美解釋」,掛在一個錯誤敘事上也毫無違和,而凌晨三點的你沒有能力分辨。所以防禦權威幻覺的方法不是「保持批判」這種空話,是把紀律寫進流程:把手走完才准採信,像 SRE 的除錯紀律寫「一次改一個變因」一樣,變成不用思考就會執行的動作。防禦靠的從來不是聰明,是流程。

事故的漏斗,和寫 code 的漏斗是同一個

回頭看,這場實驗只是把上一篇的漏斗換了一個場景:AI 吃寬層(判斷程序、假設族、把手、話術、清單),人守頸口(動手、採信、簽名)——形狀一模一樣,只是頸上多了一個時鐘,而時鐘讓每個環節的價值都被放大:寬層省下的每一分鐘更值錢,頸口的每一個賭注也更重。由此再推一步:code review 有漏斗、架構決策有漏斗、postmortem 有漏斗——「AI 能用在哪」的判準,從「會不會寫程式」變成「這個工作的判斷點在哪、判斷前的路有多長」。路越長,AI 越值錢;判斷點本身,永遠是人。

最好的事故演練,是重播自己的舊傷

最後推薦這個實驗本身:找一場你寫過 postmortem 的舊事故,分階段餵給乾淨的 AI,看它哪一幕命中、哪一幕胡說。你會同時得到三樣東西:對 AI 能力邊界的第一手校準(而不是聽別人說)、對自家系統知識缺口的盤點(它卡住的地方,就是 context 沒進文件的地方)、和一次零風險的 on-call 肌肉訓練。唯一要小心的是症狀要用當年的真實口徑餵——轉述一失真,你重播的就是另一場事故。成本是一個晚上,而下一場真的事故,桌上就不是第一次有 AI 了。