iT邦幫忙

2026 iThome 鐵人賽

DAY 15
0
AI Engineering

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

[Day 15] - Agent 記得對話,為什麼還是不知道自己做到哪裡?從 Context 到可控的 State

  • 分享至 

  • xImage
  •  

昨天,Agent 已確認 order 123 符合退款資格,問使用者要不要送出退款。使用者說:「我晚點確認。」Session 隨後結束。隔天,他只說:「繼續昨天那筆。」對話紀錄明明還在,Agent 卻不知道「繼續」是重新查資格、再次詢問,還是直接退款。

問題不是記憶不夠長,而是系統只記得說過什麼,沒有決定目前做到哪裡,以及誰有權改變這個答案

這篇不談如何塞進更多 Context,也不展開長期 Memory 或 Checkpoint 引擎。我們只處理一個實作問題:讓執行控制器持有一份唯一可信、可驗證的任務狀態(canonical State,後文簡稱 State),並守住它的寫入權。

Agent 的可靠性,不只取決於記住什麼,更取決於誰有權改變系統相信的事實。


這篇會討論到

  • 模型看過某件事,為什麼不等於系統已經相信它
  • 模型、工具與執行控制器,誰能提案、誰能提供證據、誰能提交 State
  • State 最少需要記住哪些資訊,才能安全跨過 Session
  • 為什麼 Memory 與 Checkpoint 都不能取代 State 的寫入規則

同一筆退款對話的兩種保存方式:Transcript 只有話語紀錄,State 則明確區分資格已確認、等待使用者與退款尚未送出


對話還在,任務進度為什麼會消失?

先回到隔天那句「繼續」。若 Agent 只有昨天的 transcript 或一段摘要,它也許會讀到:

使用者:幫我處理 order 123 的退款。
Agent:已確認符合資格,要現在送出嗎?
使用者:我晚點確認。
摘要:使用者詢問 order 123 退款,訂單符合資格。

人類很容易從語境推測「還沒退款,正在等確認」。但執行控制器不能把推測當成控制資料。摘要沒有回答:退款工具是否曾被呼叫?目前輪到誰採取動作?資格由哪個系統、在什麼時候確認?今天的「繼續」是否仍對應同一位使用者與同一筆訂單?

Transcript 忠實保存了發生過的話,卻沒有決定哪個值已驗證、哪個值已失效,以及什麼動作現在可以執行。

因此,Conversation History 適合回答「發生過哪些互動」,不適合單獨回答「下一個動作是否合法」。OpenAI Agents SDK 的 Sessions 文件也把 Session 定位為保存對話歷史的機制;歷史能讓下一輪讀到先前內容,卻不會替內容加上權威性。

可以用一個問題檢查自己的 Agent:如果暫時拿掉 transcript,只留下結構化欄位,系統還能不能說出做到哪裡、正在等誰、尚未做什麼? 如果不能,你保存的是對話,不是可控制的任務進度。


模型看過,不等於系統相信

Context 與 State 都可能出現 order_id=123,差別不在格式,而在權力。

Context 回答「這一輪模型需要看見什麼?」 它可以包含 instructions、最近對話、工具結構、政策證據與任務摘要,每輪依需要重新組裝。內容進入 Context,只代表模型能讀取。

State 回答「系統目前相信什麼?」 它保存會影響下一步的最小事實,例如任務階段、已驗證結果、等待原因與資料版本。只有通過規則提交的內容,才能改變它。

OpenAI Agents SDK 的 Context 文件區分本地 application context 與模型可見的 Context。這提醒我們:執行資料與模型看見的內容,本來就可以分開管理。

AgentScope 2.x也把可恢復的對話與執行狀態放在 AgentStateStore,再由 Workspace保存任務紀錄與長期 Memory。這個切法值得參考的不是名稱,而是資料生命週期:正在執行的任務、跨 Session 的記憶與外部業務結果,不應混成同一份文字。

外部業務系統仍是訂單、付款與庫存的結果真相。τ-bench在對話結束時,比對資料庫最終狀態與目標狀態,而不是相信 Agent 的最後一句回答。對話說「退款完成」是一項陳述;付款系統真的產生退款紀錄,才是結果。

State 也不需要複製整個資料庫。它只保存控制下一步所需的工作資訊,並記住這些資訊來自哪裡;資料不夠新時,就回到權威系統查詢。

模型看過,不等於系統相信。 如果一個值出現在 prompt 裡之後,就能被模型直接寫成任務事實,Context 已經越過了它原本的邊界。

Memory 與既有 State 形成模型可見的 Context,模型只能提出提案;外部系統提供權威證據,只有提交閘門能建立新版 State


State 的核心不是格式,而是寫入權

若模型輸出「使用者已確認退款」,系統就把 user_confirmation=true 寫入 State,那只是在自然語言外套了一層 JSON。Schema 可以約束格式,不能證明這句話是真的。

更安全的設計把一次狀態轉移拆成三層:

  1. 提案:模型根據 Context 建議下一步。它是候選意圖,不是真相。
  2. 觀察:工具或輸入介面帶回外部證據。證據仍可能過期、對錯任務或來自不具權威的來源。
  3. 提交:執行控制器驗證來源、版本與規則後,才建立新版 State;只有已提交的 State 能驅動下一個動作。

隔天使用者說「繼續」,模型可以提案 confirm_pending_refund,卻不能直接把確認寫入 State。執行控制器還要檢查:登入者是否擁有 order 123?State 是否仍在等待確認?退款資格是否仍有效?「繼續」若不夠明確,就只能再問一次。通過後,這次確認才成為新的任務事實。

舊 State + 提案 + 外部觀察 → 驗證前置條件與不變條件 → 新版 State,否則拒絕或補問。

結構化輸出不等於合法提交。 模型摘要、工具回傳與 Memory 內容都先停在提交閘門外;只有通過可測試的應用規則,才可以改變 State。

這也代表不同欄位需要不同的權威來源。訂單擁有者由 Order Service 提供;退款資格來自資料與規則;「使用者看起來很急」只是模型推論。它們可以同時進入 Context,卻不能擁有相同寫入權。

提交閘門不一定很龐大。SWE-agent 的 Agent-Computer Interface提供了一個小型類比:模型提出 edit 命令後,介面會先執行 linter,語法無效的修改就被丟棄。模型提出動作,不代表環境必須接受;差別只在於業務 Agent 還要驗證身分、前置條件與外部結果,而不只是語法。

使用者說繼續後,模型只能提出退款確認提案;身分、訂單、資格與版本證據通過提交閘門後,確認才寫入 State,否則拒絕、補問或重讀

設計 State 時,第一步不是先挑資料庫,而是列出關鍵欄位,逐一回答:誰能提出新值、誰能提供證據、哪一道規則可以正式提交。


一份可用的 State,至少回答三件事

State 不必一開始就很複雜,但至少要回答三個問題:

  1. 目前在哪裡? 任務正在執行、等待使用者、完成,還是失敗?
  2. 憑什麼相信? 關鍵欄位由誰提供、何時取得,是否仍需重驗?
  3. 依據哪個版本? 這次提案建立在哪一版 State 上,期間是否已有更新?

order 123 可以先從這樣的最小結構開始:

task_id: refund_order_123
status: awaiting_user_confirmation
state_version: 4
facts:
  order_owner: verified,來源 order_service/123
  refund_eligibility: verified_true,來源 policy/v7
milestones:
  user_confirmation: unknown
  refund_dispatched: false

awaiting_user_confirmation 是合法狀態,不是「流程還沒寫完」;unknown 也不等於 false。前者表示現在不准自動前進,後者表示尚未取得足以提交的事實。把等待藏在最後一句對話,換 Session 或壓縮時就可能消失。

LangGraph要求先定義 State schema;StateFlow則把任務表示成 states 與 transitions。這些方法讓流程更容易表示,卻不會自動決定高風險轉移該由誰批准。框架提供容器,業務規則仍要定義合法值與寫入權。

State 還要守住少量但重要的不變條件(invariants):等待確認時,退款必須尚未送出;資格未確認時,不能建立可執行意圖;已完成任務不能悄悄回到等待。這些規則能把模糊錯誤變成可測試的拒絕。

若一個布林欄位的 null 可能同時表示尚未取得、等待確認或不適用,它就還不足以控制下一步。會導向不同動作的情況,應成為不同的合法狀態。


跨過 Session,舊答案必須重新排隊

即使每項觀察單獨看都正確,抵達順序仍可能讓 State 出錯。使用者可能已在另一個裝置取消退款,昨天的資格查詢也可能在今天才回傳。若舊結果直接覆蓋新決定,Agent 就會用過期的世界繼續執行。

因此,每次提案與觀察都應帶著所依據的 state_version。若提案建立於 version 4,最新 State 已是 version 6,就拒絕這次舊版本覆寫並重新讀取。版本不是為了讓資料看起來更完整,而是避免昨天的正確覆蓋今天的正確。

LangGraph 對平行 State 更新的錯誤說明也呈現同一問題:兩個節點同時更新同一欄位,卻沒有合併規則時,結果存在歧義。技術上可以定義如何合併,業務上仍要決定哪個寫入者有權提交。

所以 order 123 隔天恢復時,安全順序不是「載入摘要 → 直接退款」,而是:

  1. 以 task ID 讀取最新 State;
  2. 重驗使用者身分與外部資料新鮮度;
  3. 把「繼續」解析成提案;
  4. 依據目前版本驗證並提交,否則補問。

若期間政策改版,或訂單已由其他管道處理,昨天的待處理意圖就必須失效。

OpenAI Agents SDK 的 RunState也以 schema version 處理不相容狀態,並限制同一份 Session history 的並行恢復。這不是所有系統都要照抄的實作,但它說明了同一件事:能載入一份資料,不代表多個執行者可以安全地同時繼續。

不需要一開始就導入複雜的分散式協調。先加入 state_versionupdated_bybased_on_version,寫入前比對版本;版本不符就拒絕並重讀,已能擋住大量舊結果覆蓋新決定的錯誤。


由 State 產生 Context,而不是把資料庫倒進 Prompt

模型不必每輪讀完整 State。內部識別碼、權限與歷史轉移全放進 Context,不只浪費 Token,也擴大資料暴露與錯誤影響面。

Context Builder 應依這一輪要做的決策,產生最小的唯讀投影。對隔天的「繼續」,模型真正需要的可能只有:

  • 任務目標:確認後替 order 123 退款;
  • 目前階段:等待使用者確認;
  • 已驗證事實:訂單屬於目前使用者、昨日確認符合資格;
  • 尚未發生:退款未送出;
  • 待判斷問題:這句「繼續」是否構成明確確認;
  • 允許輸出:確認提案、澄清問題或取消提案。

模型不需要看見所有內部欄位,更不應覆寫 state_version。執行控制器保留完整來源,在 Context 中只標示「已驗證/需重驗」與必要資訊。模型回傳的結果仍然是提案,還要再走一次驗證與提交。

於是資料流形成一條清楚的控制邊界:State 依規則投影成 Context,模型產生提案,外部觀察與驗證規則決定是否建立新 State。

Context 可以由 State 生成,卻不能因為模型說了一句話,就反向成為 State。

LangGraph Persistence會在 thread 的執行步驟保存 State snapshot,再把跨 thread 的資料交給 Store。重點不是照搬它的元件名稱,而是不要讓模型輸入、任務進度與跨任務記憶混成同一份資料。

order 123 在昨天等待確認後結束 Session;今天先載入最新 State 並重驗身分與資料,最後才把繼續解析成提案,而不是直接退款

替每種模型呼叫定義需要的 State 投影,而不是共用「整包 State」。需要更多資訊時,再由工具取得,不讓 prompt 同時兼任資料庫、任務進度與權限系統。


Checkpoint 保存 State,但不會替你修正 State

Checkpoint 只負責持久保存某一版 State、執行位置與待處理工作;如何 Resume、重新驗證並繼續執行,留給後篇。

耐久不等於正確。Checkpoint 只會忠實保存 State,不會修正錯誤信念。 如果 refund_eligible=true 只是模型從模糊對話猜出來的,做十份備份也不會讓它變成權威事實。持久保存解決資料會不會消失;權威來源與提交規則才決定資料能不能相信。

同樣地,Memory 可以記得使用者偏好原付款方式,或過去曾處理退款;它不能只憑歷史摘要宣告目前 order 123 已獲確認。當前任務仍要回到最新 State 與外部權威來源。

最後,用五個問題檢查自己的 Agent:

  1. 任務目前階段是結構化值,還是藏在最後一句對話?
  2. unknownawaiting_userrejectedcompleted 是否能被區分?
  3. 每個關鍵欄位能否指出權威來源,以及誰有權提交?
  4. 更新是否檢查前置條件與 state_version,避免舊結果覆蓋新決定?
  5. Checkpoint 若保存現在這份 State,你能說明哪些是事實、哪些只是推論嗎?

如果第 1、3、4 題有任何一題答不出來,先別急著加更長 Context 或更強 Memory。挑一個會等待使用者的真實任務,把「目前狀態、等待誰、尚未做什麼、誰有權提交下一步」寫成 State,再做一次中斷前後測試。

你也可以挑一個關鍵欄位,直接問:誰有權寫它? 如果答案是「模型、工具、任何流程都可以」,那通常就是 Agent 下一個最值得補上的控制邊界。


AI 你怎麼看?

工程師得意地說已建立 Checkpoint,但保險庫裡的 Agent 仍抱著不確定的退款 State,說明耐久保存不會讓錯誤變正確


上一篇
[Day 14] - Agent 一直在跑,為什麼任務沒有前進?從 Progress 到正確停止
下一篇
[Day 16] - Agent 中斷後,怎麼安全地繼續?Checkpoint、Resume 與 Durable Execution
系列文
現代化的 AI 系統設計18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言