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 時如何區分可沿用、需重驗與不可重播

refund_dispatched=false 告訴我們,系統尚未承認退款已送出;user_confirmation=unknown 則表示進站的核准事件還沒有被合法提交。即使 State 與事件都正確存在,它們仍沒有回答:
State 保存的是目前可相信的任務事實;Checkpoint 則要保存足以恢復執行的資訊。兩者可能存放在同一個資料庫,甚至同一筆紀錄裡,但責任不能混在一起。
OpenAI Agents SDK 的 RunState是一個具體例子:它除了應用程式 Context,也保存模型回應、核准狀態與對話識別資訊,供中斷後序列化與恢復。這說明只有對話或業務 State,仍不足以恢復一次尚未結束的執行。
所以 Checkpoint 不是「再備份一份 State」。它更像一張交接單:即使原本的工作程序消失,新工作程序也能知道自己接手的是哪個任務、前面哪些工作已被承認,以及接下來能做什麼。
可以把四個概念放在同一條線上理解:State 是系統對世界的答案;Checkpoint 是這次執行已承認哪些結果的收據;Resume 是讀完收據後決定下一個合法步驟的規則;Durable Execution 則讓這套規則在原工作程序消失後仍然成立。
最直覺的恢復方式,是重新組裝 Context,再把任務交給模型跑一次。這看起來很合理,卻悄悄把 Resume 變成 Rerun。
模型可能重新選擇工具、改變步驟順序,或因為 Prompt、Model、Retrieval 結果已更新而產生另一條路徑。新答案即使更好,也代表這次執行重新做了決策,而不是延續原本已被系統承認的歷史。
| 做法 | 從哪裡開始 | 已完成的工作 | 適合用途 |
|---|---|---|---|
| Rerun | 從原始輸入或新 Context 開始 | 可能全部重做 | 建立新的嘗試、比較新策略 |
| Resume | 從最後一個有效 Checkpoint 接續 | 沿用已承認結果,只執行未完成部分 | 當機復原、長等待、Human-in-the-loop |
這也是為什麼「把對話紀錄餵回去」不能等同恢復。對話可以幫模型理解發生過什麼,卻不能證明哪個工具結果已完成、哪次核准已被使用,或哪一步只是說過但尚未提交。
所以判斷一個系統是否真的支援 Resume,不該只看它有沒有 resume() API,而要看它能否證明:先前完成的工作沒有被重做、尚未完成的工作從正確位置接續,而且同一個等待事件不會被使用兩次。
綜合 Agent Framework 與成熟 Durable Workflow 的做法,我會把可安全恢復所需的資訊整理成一份「復原封套」。這不是某個框架的官方名詞,而是一個用來盤點缺口的中立模型。
| 層次 | 它要回答什麼 | 何時需要 |
|---|---|---|
| 執行身分 | 要恢復的是哪一次邏輯執行? | 一律需要 |
| 已提交 State | 系統最後承認哪一版事實? | 一律需要 |
| 執行游標 | 哪些步驟完成,下一步在哪裡? | 一律需要 |
| 未決工作 | 系統正在等什麼? | 一律需要 |
| 效果關聯 | 外部動作可能進行到哪裡? | 呼叫外部工具時 |
| 相容資訊 | 哪一版執行器才能正確解讀? | 任務跨過部署時 |
實作時可以使用 run_id、state_version、下一節點、等待事件與工具呼叫 ID 等欄位,但欄位名稱不是重點。真正要保存的是它們之間的關係:哪一次執行,依據哪一版 State,已完成哪些工作,正在等待什麼,以及哪一版程式能正確接手。
舊 Checkpoint 跨過部署後,還要通過版本相容 Gate:
incompatible_checkpoint,交由人工處理。「檔案讀得進來」只是格式相容;能否在新版流程上做出相同解讀,才是語意相容。

提到 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,而是重現相同的「已承認事件與控制流程」。

如果只問「每幾秒存一次」,通常是在把 Checkpoint 當成傳統 Snapshot。對 Agent 而言,更重要的是:系統在哪一個語意邊界承認結果?
可以先從四種位置開始:
Checkpoint 太粗,重啟後必須重做較多工作,也更難分辨最後一個動作是否發生;Checkpoint 太細,每一步都會增加儲存、延遲與保留成本。純計算、便宜且無副作用的步驟可以接受重算;昂貴模型輸出、人工決定、長等待與不可逆外部效果,則值得更明確的耐久提交邊界。
真正的邊界不是「時間到了」,而是「這個結果已被系統承認,之後不應重新決定」。
服務重啟後,核准事件再次進站。要 Resume order 123,不能只搜尋最近一段對話或找一個狀態看起來相似的任務,而應驗證一份 Resume Contract:
task_id 與 run_id 是否指向同一個等待中的執行?tool_call_id 或關聯鍵是否吻合?假設同一個核准事件被佇列重送,兩個工作程序同時醒來。兩者都拿著合法 State,卻不能同時前進,否則同一個 Checkpoint 可能被恢復兩次。
因此,執行器還要先取得有期限的執行租約,確保同一時間只有一個恢復者。State version 防止舊資料覆寫新資料;執行租約防止兩個人同時執行。
這讓 Human-in-the-loop 的語意更清楚:等待人類不是失敗,而是一個成功提交的暫停狀態;人的回答也不是普通聊天訊息,而是只能喚醒特定未決意圖、且原則上只能使用一次的 Resume 事件。
回到開場案例:核准事件通過 Resume Contract 後,唯一恢復者先把確認提交進新版 State,再重新檢查退款資格。只有前置條件仍成立,系統才移到下一個合法步驟;任何一項無法確認,就繼續等待或停止,而不是從頭再問模型。

Checkpoint 載入成功,不代表裡面的每個值都可以直接使用。恢復時可以把資料與工作分成三類:
| 分類 | 處理方式 | order 123 的例子 |
|---|---|---|
| 可沿用 | 讀取已被耐久提交邊界承認、版本仍相容的結果 | 已保存的分類結果、已提交 State、已完成且已落盤的工具結果 |
| 需重驗 | 保留舊結果作證據,但重新確認前置條件 | 身分、退款資格、政策版本、核准期限、長時間前取得的外部資料 |
| 不可直接重播 | 不重新送出原動作;先查已保存結果或停在結果未知 | 退款、寄信、下單、已使用的一次性核准 |
這個三分法比「全部繼續」或「全部重跑」更接近正式環境的真實情況。計算結果可能仍然有效,但庫存與權限會改變;模型提案可以被保存,卻可能因政策更新而失去執行資格;人的確認也可能只授權一筆特定金額,而不是未來所有重試。
因此,「不可重播」不等於永遠失敗,而是執行器必須先承認自己不知道。若退款請求可能已送出、結果卻沒有被耐久提交,狀態就應停在 outcome_unknown。Checkpoint 可以指出疑點,卻不能憑自己證明付款系統最後做了什麼。
如何在 outcome_unknown 下查詢外部系統、判斷是否 Retry,以及何時需要 Compensation,會留到後面的副作用一致性專篇。此刻最重要的規則只有一條:不知道有沒有做過,就不能把它當成沒做過。

不同執行器會使用 Durable、Replay、Exactly-once 等相似詞彙,但保證的單位可能是 Workflow、Step、Invocation 或平台內部的 State Transition。它不一定涵蓋任意第三方 API 的外部效果。
即使執行器能沿用已保存的步驟結果,也不代表每個外部 Side Effect 都天然具備 Exactly-once。若效果已發生、結果卻還來不及提交,系統仍必須面對那段不確定窗口。
所以選框架時,不要只問「支不支援 Checkpoint?」而應要求它回答:
只要這五題有一題答不出來,resume() 就仍然可能只是包裝得更漂亮的 Rerun。
最後,可以挑一個會等待人類或外部事件的真實任務,做一次最小復原測試:
如果測試結果只能證明「State 還在」,卻說不出「哪些工作已經做過」,你的 Agent 有 Persistence,還沒有 Durable Execution。
