iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
AI Engineering

現代化的 AI 系統設計系列 第 16

[Day 16] - Agent 中斷後,怎麼安全地繼續?Checkpoint、Resume 與 Durable Execution

  • 分享至 

  • xImage
  •  

order 123 已完成退款資格查驗,State 正確停在「等待使用者確認」。數小時後,使用者按下確認,核准事件也成功進入持久事件儲存層。偏偏就在系統準備把事件交回原任務時,服務因為部署而重啟。

幾分鐘後,新的工作程序載入 State,仍然看得到:

status: awaiting_user_confirmation
user_confirmation: unknown
refund_dispatched: false

State 與核准事件都沒有消失,Agent 卻沒有繼續。因為系統沒有耐久保存事件與原任務的關聯,也忘了應從哪個節點接手。事件存在,不代表它知道要喚醒誰。

上一篇談的是:系統現在相信什麼,以及誰有權改變這些事實。 但可信的 State 只是安全恢復的必要條件。要讓任務跨過部署、當機與長時間等待,執行器還得記得另一件事:這次執行已經承認了哪些結果,下一個合法步驟又在哪裡。

Resume 不是重新跑一次。它是根據已承認的執行歷史,判定哪些工作可以沿用、哪些必須重驗、哪些絕不能再做。


這篇會討論到

  • 為什麼 State 還在,任務仍可能永遠醒不來
  • Checkpoint 除了 State,還必須保存哪些執行資訊
  • Deterministic Replay 為何不是要求 LLM 每次回答一樣
  • Checkpoint 的邊界為什麼比「多久存一次」更重要
  • Resume 時如何區分可沿用、需重驗與不可重播

https://ithelp.ithome.com.tw/upload/images/20260830/20183613Vt325MobFU.png


State 說明世界,Checkpoint 說明執行

refund_dispatched=false 告訴我們,系統尚未承認退款已送出;user_confirmation=unknown 則表示進站的核准事件還沒有被合法提交。即使 State 與事件都正確存在,它們仍沒有回答:

  • 這是哪一次 Agent 執行?
  • 原本停在哪一個節點或步驟?
  • 核准事件應交給哪一個等待中的工作?
  • 哪些模型/工具結果已經被本次執行承認?
  • 新版程式是否仍能解讀舊流程的執行位置?

State 保存的是目前可相信的任務事實;Checkpoint 則要保存足以恢復執行的資訊。兩者可能存放在同一個資料庫,甚至同一筆紀錄裡,但責任不能混在一起。

OpenAI Agents SDK 的 RunState是一個具體例子:它除了應用程式 Context,也保存模型回應、核准狀態與對話識別資訊,供中斷後序列化與恢復。這說明只有對話或業務 State,仍不足以恢復一次尚未結束的執行。

所以 Checkpoint 不是「再備份一份 State」。它更像一張交接單:即使原本的工作程序消失,新工作程序也能知道自己接手的是哪個任務、前面哪些工作已被承認,以及接下來能做什麼。

可以把四個概念放在同一條線上理解:State 是系統對世界的答案;Checkpoint 是這次執行已承認哪些結果的收據;Resume 是讀完收據後決定下一個合法步驟的規則;Durable Execution 則讓這套規則在原工作程序消失後仍然成立。


Resume 不是 Rerun:重新問一次模型只是重新決策

最直覺的恢復方式,是重新組裝 Context,再把任務交給模型跑一次。這看起來很合理,卻悄悄把 Resume 變成 Rerun。

模型可能重新選擇工具、改變步驟順序,或因為 Prompt、Model、Retrieval 結果已更新而產生另一條路徑。新答案即使更好,也代表這次執行重新做了決策,而不是延續原本已被系統承認的歷史。

做法 從哪裡開始 已完成的工作 適合用途
Rerun 從原始輸入或新 Context 開始 可能全部重做 建立新的嘗試、比較新策略
Resume 從最後一個有效 Checkpoint 接續 沿用已承認結果,只執行未完成部分 當機復原、長等待、Human-in-the-loop

這也是為什麼「把對話紀錄餵回去」不能等同恢復。對話可以幫模型理解發生過什麼,卻不能證明哪個工具結果已完成、哪次核准已被使用,或哪一步只是說過但尚未提交。

所以判斷一個系統是否真的支援 Resume,不該只看它有沒有 resume() API,而要看它能否證明:先前完成的工作沒有被重做、尚未完成的工作從正確位置接續,而且同一個等待事件不會被使用兩次。


Checkpoint 真正要保存的是一份復原封套

綜合 Agent Framework 與成熟 Durable Workflow 的做法,我會把可安全恢復所需的資訊整理成一份「復原封套」。這不是某個框架的官方名詞,而是一個用來盤點缺口的中立模型。

層次 它要回答什麼 何時需要
執行身分 要恢復的是哪一次邏輯執行? 一律需要
已提交 State 系統最後承認哪一版事實? 一律需要
執行游標 哪些步驟完成,下一步在哪裡? 一律需要
未決工作 系統正在等什麼? 一律需要
效果關聯 外部動作可能進行到哪裡? 呼叫外部工具時
相容資訊 哪一版執行器才能正確解讀? 任務跨過部署時

實作時可以使用 run_idstate_version、下一節點、等待事件與工具呼叫 ID 等欄位,但欄位名稱不是重點。真正要保存的是它們之間的關係:哪一次執行,依據哪一版 State,已完成哪些工作,正在等待什麼,以及哪一版程式能正確接手。

舊 Checkpoint 跨過部署後,還要通過版本相容 Gate:

  1. 原版本繼續: 流程語意相容,而且仍有舊版執行器可接手,就路由回原版本。
  2. 遷移後繼續: 有可測試、可回滾的 Migration,先產生新版 Checkpoint,再由新流程接續。
  3. 拒絕自動恢復: 執行游標、工具契約或政策語意已不相容,就標記 incompatible_checkpoint,交由人工處理。

「檔案讀得進來」只是格式相容;能否在新版流程上做出相同解讀,才是語意相容。

https://ithelp.ithome.com.tw/upload/images/20260830/20183613ItSLcMYZXk.png


Deterministic Replay,不是讓 LLM 變得 deterministic

提到 Durable Execution,常會看到 Deterministic Replay。這很容易被誤解成:「相同 Prompt 必須讓 LLM 產生完全相同的回答。」真正需要 deterministic 的,是用來重建執行的控制邏輯,而不是時間、亂數、網路或 LLM 本身。

order 123 來說,若模型選擇 refund_order 的結果已經被 Checkpoint 承認,重播時就應直接沿用這個選擇。再次呼叫模型,只會得到另一個可能合理、卻不一定相同的決策。

Temporal會把控制程式產生的 Commands 與既有 Event History 比對,並把 LLM、API 等非決定性操作放在重播路徑之外;DBOS則在恢復時讀取已保存的步驟輸出,只執行尚無結果的步驟。兩者實作不同,卻導向同一個 Agent 設計原則:

把 LLM Call 視為非決定性 Step;一旦輸出被執行系統承認,Resume 應沿用這個結果,而不是再問模型一次。

因此,Replay 並不是重現相同 Token,而是重現相同的「已承認事件與控制流程」。

https://ithelp.ithome.com.tw/upload/images/20260830/201836132SZMfuQOMo.png


Checkpoint 的邊界,比多久存一次更重要

如果只問「每幾秒存一次」,通常是在把 Checkpoint 當成傳統 Snapshot。對 Agent 而言,更重要的是:系統在哪一個語意邊界承認結果?

可以先從四種位置開始:

  1. LLM Call 之後: 保存會驅動後續工具的模型輸出,避免恢復時重新決策。
  2. 進入人工核准或長等待之前: 保存未決工作、喚醒條件與關聯 ID,讓工作程序可以安全釋放。
  3. 收到 Resume 事件之後: 驗證事件與當前 State、意圖、版本相符,再提交新的可執行狀態。
  4. 外部 Side Effect 前後: 用同一個呼叫 ID 關聯意圖、請求與結果,讓系統至少知道效果是未開始、進行中、結果未知或已提交。

Checkpoint 太粗,重啟後必須重做較多工作,也更難分辨最後一個動作是否發生;Checkpoint 太細,每一步都會增加儲存、延遲與保留成本。純計算、便宜且無副作用的步驟可以接受重算;昂貴模型輸出、人工決定、長等待與不可逆外部效果,則值得更明確的耐久提交邊界。

真正的邊界不是「時間到了」,而是「這個結果已被系統承認,之後不應重新決定」。


能喚醒任務的,不該只是一句「繼續」

服務重啟後,核准事件再次進站。要 Resume order 123,不能只搜尋最近一段對話或找一個狀態看起來相似的任務,而應驗證一份 Resume Contract:

  • task_idrun_id 是否指向同一個等待中的執行?
  • 事件類型是否正是這個 Checkpoint 正在等待的類型?
  • tool_call_id 或關聯鍵是否吻合?
  • 核准所綁定的訂單、金額、State version 與政策版本是否仍相同?
  • 核准是否過期、撤回或已被使用?

假設同一個核准事件被佇列重送,兩個工作程序同時醒來。兩者都拿著合法 State,卻不能同時前進,否則同一個 Checkpoint 可能被恢復兩次。

因此,執行器還要先取得有期限的執行租約,確保同一時間只有一個恢復者。State version 防止舊資料覆寫新資料;執行租約防止兩個人同時執行。

這讓 Human-in-the-loop 的語意更清楚:等待人類不是失敗,而是一個成功提交的暫停狀態;人的回答也不是普通聊天訊息,而是只能喚醒特定未決意圖、且原則上只能使用一次的 Resume 事件。

回到開場案例:核准事件通過 Resume Contract 後,唯一恢復者先把確認提交進新版 State,再重新檢查退款資格。只有前置條件仍成立,系統才移到下一個合法步驟;任何一項無法確認,就繼續等待或停止,而不是從頭再問模型。

https://ithelp.ithome.com.tw/upload/images/20260830/20183613w4gtEwqAVn.png


醒來之後,先分成沿用、重驗與不可重播

Checkpoint 載入成功,不代表裡面的每個值都可以直接使用。恢復時可以把資料與工作分成三類:

分類 處理方式 order 123 的例子
可沿用 讀取已被耐久提交邊界承認、版本仍相容的結果 已保存的分類結果、已提交 State、已完成且已落盤的工具結果
需重驗 保留舊結果作證據,但重新確認前置條件 身分、退款資格、政策版本、核准期限、長時間前取得的外部資料
不可直接重播 不重新送出原動作;先查已保存結果或停在結果未知 退款、寄信、下單、已使用的一次性核准

這個三分法比「全部繼續」或「全部重跑」更接近正式環境的真實情況。計算結果可能仍然有效,但庫存與權限會改變;模型提案可以被保存,卻可能因政策更新而失去執行資格;人的確認也可能只授權一筆特定金額,而不是未來所有重試。

因此,「不可重播」不等於永遠失敗,而是執行器必須先承認自己不知道。若退款請求可能已送出、結果卻沒有被耐久提交,狀態就應停在 outcome_unknown。Checkpoint 可以指出疑點,卻不能憑自己證明付款系統最後做了什麼。

如何在 outcome_unknown 下查詢外部系統、判斷是否 Retry,以及何時需要 Compensation,會留到後面的副作用一致性專篇。此刻最重要的規則只有一條:不知道有沒有做過,就不能把它當成沒做過。

https://ithelp.ithome.com.tw/upload/images/20260830/20183613thPEW35QA5.png

Durable 不等於 Exactly-once,先把恢復契約說清楚

不同執行器會使用 Durable、Replay、Exactly-once 等相似詞彙,但保證的單位可能是 Workflow、Step、Invocation 或平台內部的 State Transition。它不一定涵蓋任意第三方 API 的外部效果。

即使執行器能沿用已保存的步驟結果,也不代表每個外部 Side Effect 都天然具備 Exactly-once。若效果已發生、結果卻還來不及提交,系統仍必須面對那段不確定窗口。

所以選框架時,不要只問「支不支援 Checkpoint?」而應要求它回答:

  1. Checkpoint 保存的單位是 State、步驟、Superstep,還是完整 Event History?
  2. Resume 時哪些控制程式會重跑?LLM 與工具位於重播邊界的哪一側?
  3. 長等待如何喚醒?同一個 Checkpoint 能否被兩個工作程序同時恢復?
  4. 流程、Schema 或工具契約更新後,舊 Checkpoint 要路由、遷移,還是拒絕?
  5. 外部效果結果未知時,系統會停下來,還是誤把它當作尚未執行?

只要這五題有一題答不出來,resume() 就仍然可能只是包裝得更漂亮的 Rerun。

最後,可以挑一個會等待人類或外部事件的真實任務,做一次最小復原測試:

  1. 讓任務完成一次不可重新決策的結果,例如 LLM 已選定工具或使用者已批准;
  2. 在下一步開始前停止工作程序;
  3. 啟動新的工作程序,只提供持久儲存層與同一個 Resume 事件;
  4. 檢查它是否沿用已承認結果、只執行下一個合法步驟;
  5. 再重送一次相同事件,確認它不會讓同一個 Checkpoint 前進第二次。

如果測試結果只能證明「State 還在」,卻說不出「哪些工作已經做過」,你的 Agent 有 Persistence,還沒有 Durable Execution。


AI 你怎麼看?

https://ithelp.ithome.com.tw/upload/images/20260830/20183613fnBM0M8OLB.png


上一篇
[Day 15] - Agent 記得對話,為什麼還是不知道自己做到哪裡?從 Context 到可控的 State
下一篇
[Day17 ] - Agent 記住得越多越好嗎?Memory 的寫入、取回、修正與遺忘
系列文
現代化的 AI 系統設計18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言