CtxFST CH27 - 沒有 World State 時 Agent 怎麼壞掉的:五個真實場景
CtxFST CH27:沒有 World State 時,Agent 怎麼壞掉的
CH26 講了 world state 是導航系統。地圖(graph)告訴你路在哪,導航(planner + state)告訴你下一步怎麼走。
但「為什麼需要導航」這個問題,用比喻只能講到一半。真正有說服力的,是看沒有導航的時候會發生什麼事。
這篇列五個場景。每個都是用 agent 跑長任務時真實會遇到的狀況。不是理論推演,是你用 Claude Code 或 OpenClaw 跑個一小時就會碰到的事。
每個場景對比兩種做法:
- LLM 自管 state:靠 prompt、對話歷史、memory 檔案讓 LLM 自己追蹤進度
- 系統層 world state:用 DB 硬約束追蹤狀態,LLM 只負責決策,不負責記憶
場景一:重複推薦已完成的步驟
狀況
你叫 agent 幫你建一個網站。它先幫你裝了 Node.js,然後跑了 npm init,然後開始裝 dependencies。
裝到第三個 package 的時候,它突然說:
"首先,讓我們確認一下 Node.js 環境是否已經安裝。"
你剛才不是裝過了嗎?對話歷史裡明明有。
但 LLM 不是「記得」對話歷史,它是每次都重新讀。當對話變長、中間穿插了 error 訊息和 debug 過程,LLM 的注意力會被最近的內容拉走,忘記前面的進度。
LLM 自管 state
對話歷史(50 條訊息後):
... 裝 Node ✓ → npm init ✓ → 裝 express ✓ → 裝 dotenv 時報錯 →
debug 了 10 條訊息 → 修好了 → ...
Agent:"好的,讓我們先確認 Node.js 環境..."
User:"你剛才已經裝過了。"
Agent:"抱歉!你說得對,讓我繼續..."
LLM 不是故意的。它是在重新處理 prompt 時,被中間的 debug 訊息干擾,丟失了更早的進度。
這不會致命,但會:
- 浪費 token(重複確認 = 白花錢)
- 浪費使用者耐心(反覆糾正 = 煩)
- 在更複雜的 chain 裡,可能真的重跑一遍已完成的步驟
系統層 world state
db.getWorldState(sessionId, "state:node-installed")
// → { value: true, updated_at: "2026-04-01T10:15:00" }
db.getCompletedSkills(sessionId)
// → ["install-node", "npm-init", "install-express"]
// Planner 看到 install-node 已完成,直接跳過
// 不管對話歷史裡有多少 debug 訊息
差異: 狀態存在 DB 裡,不受對話長度影響。Planner 不需要從 50 條訊息裡「回憶」進度,直接查表。
場景二:跳過 precondition,幻覺出不存在的輸入
狀況
你叫 agent「分析我的 resume」。但你還沒上傳任何檔案。
一個好的 agent 應該說:「請先上傳你的 resume。」
但 LLM 有時候會直接開始「分析」——用幻覺生成一份看起來像分析結果的東西,但完全沒有根據。
LLM 自管 state
User:"分析我的 resume"
Agent:"好的,根據你的 resume,我看到你有 5 年的 Python 經驗,
曾在 Google 實習..."
(你從來沒上傳過 resume。這些全是幻覺。)
這個場景的問題不只是「答錯了」。而是:
- 下游步驟會基於這個幻覺結果繼續執行
- Agent 可能接著說「基於分析結果,我建議你強調 Python 經驗」
- 整條 chain 都被 poison 了,而且使用者不一定能發現
LLM 為什麼會這樣?因為它被訓練成「盡量回答問題」。如果 prompt 裡沒有明確的「resume 尚未上傳」狀態,LLM 傾向於假設需要的東西都有,然後開始回答。
系統層 world state
const check = checkPreconditions("analyze-resume", sessionId);
// → { ok: false, missing: ["state:resume-uploaded"] }
// 系統直接擋住,不讓 LLM 進入分析流程
// 回應使用者:"需要先上傳 resume。目前缺少:state:resume-uploaded"
差異: 不是靠 LLM 「判斷」resume 有沒有上傳,而是系統查表。state:resume-uploaded 不存在就是不存在,不會幻覺。
這是最關鍵的場景。因為 precondition 跳過造成的不是「重複」(場景一),而是「毒害」——下游所有步驟都建立在錯誤的基礎上。
場景三:長對話後遺忘中間進度
狀況
你和 agent 花了一小時建一個 app。前 20 分鐘連好了 GitHub repo,中間 30 分鐘在寫 code 和 debug,最後 10 分鐘你想部署。
Agent 突然問你:
"你有 GitHub repo 嗎?請提供 repo 名稱。"
20 分鐘前就設好了。但對話已經有 80 條訊息,中間的 code 和 error log 佔了大量 token。早期的「已連接 GitHub repo: my-app」被壓到了 context window 的邊緣,或者已經被 context compression 截掉了。
LLM 自管 state
// 訊息 #12:已連接 GitHub,repo = my-app
// 訊息 #13-79:寫 code、debug、跑測試...
// 訊息 #80:
Agent:"要部署到 GitHub Pages 嗎?請先設定 repo..."
User:"my-app,我們 20 分鐘前設的。"
Agent:"好的,讓我連接 my-app..." // 重新連一次
這個問題有一個技術原因:LLM 的 context window 有限。就算模型支援 200K token,agent runtime 通常會做 context compression——把舊訊息摘要化或截斷。一旦截斷,那些「已完成」的進度就從 LLM 的可見範圍消失了。
你可以在 prompt 裡加規則:「每 10 條訊息就總結一次進度。」但這只是把問題推遲了,不是解決了。因為:
- 總結本身也是 LLM 產生的,可能遺漏
- 總結也會被後續的 context compression 截掉
- 維護「進度總結」的 prompt 本身就消耗 token
系統層 world state
db.getWorldState(sessionId, "entity:github-repo")
// → { value: "my-app", updated_at: "2026-04-01T10:20:00" }
db.getActiveStates(sessionId)
// → ["state:github-connected", "state:repo-created",
// "state:code-pushed", "state:tests-passing"]
// Planner 注入 prompt:
// "GitHub 已連接(repo: my-app)。Code 已 push。Tests passing。"
// 不管對話有 80 條還是 800 條,這段 summary 永遠存在
差異: 進度不存在對話歷史裡,存在 DB 裡。Context compression 截不掉它,因為它根本不是對話的一部分。每次 prompt 組裝時,從 DB 讀取當前狀態,注入 prompt。
場景四:跨 session 狀態完全丟失
狀況
昨天你和 agent 花了兩小時建了半個 app。今天打開新 session,想繼續。
Agent 說:
"你好!有什麼我可以幫你的嗎?"
它完全不知道昨天的事。
LLM 自管 state
這個場景裡,「LLM 自管 state」其實根本管不了。因為新 session = 新 context = 什麼都沒有。
有些 agent 會用 memory 檔案(像 MEMORY.md 或 PROJECT-STATE.yaml)來跨 session 保留資訊。但這依賴 LLM 在上一個 session 結束前主動寫入,而且:
# PROJECT-STATE.yaml(LLM 上次寫的)
project: my-app
status: in-progress
last_action: fixed CSS layout
next_step: add auth
# 問題:
# 1. "in-progress" 太模糊,哪些步驟做了哪些沒做?
# 2. "fixed CSS layout" 是最後一個 action 嗎?還是之後又做了其他事?
# 3. "add auth" 是 LLM 上次的建議,但 preconditions 滿足嗎?
# 4. 這個檔案是 LLM 自己寫的,格式可能不一致
系統層 world state
// 新 session 開始時
const state = db.loadUserState("user-ian");
// → {
// project: "my-app",
// active_states: [
// "state:repo-created",
// "state:frontend-scaffold-done",
// "state:css-layout-fixed",
// "state:backend-api-started"
// ],
// completed_skills: [
// "create-repo", "scaffold-frontend", "fix-css",
// "start-backend-api"
// ],
// blocked_by: [],
// goal: "state:app-deployed"
// }
// Planner 立刻知道:
// 1. 做到哪了(active_states)
// 2. 做過什麼(completed_skills)
// 3. 下一步是什麼(查 goal 的 skill chain,跳過已完成的)
差異: 狀態是結構化的、schema-enforced 的、queryable 的。不是 LLM 寫的自由文字,而是系統在每次 skill 執行後自動 writeback 的。
場景五:Error retry 後迷失方向
狀況
Agent 在跑 npm install 時遇到 proxy error。它嘗試了幾種修法:
- 換成 yarn → 同樣的 proxy error
- 設定 npm config proxy → 設錯了格式
- 檢查 .npmrc → 發現有舊設定衝突
- 刪掉 .npmrc 重來 → 還是錯
到第四次 retry 之後,agent 開始建議「試試看重裝 Node.js」。
問題是:這和 proxy 完全無關。Agent 已經忘了它在 debug 什麼。
LLM 自管 state
嘗試 1:yarn → proxy error
嘗試 2:npm config set proxy → 格式錯
嘗試 3:檢查 .npmrc → 發現衝突
嘗試 4:刪 .npmrc → 還是錯
Agent:"可能是 Node 版本問題,試試重裝?"
(proxy 問題完全沒解決,但 Agent 已經跑偏了)
Error retry 是 LLM agent 最脆弱的場景。因為每次 retry 都會在對話裡加入 error log,這些 log 通常很長、很雜。幾輪之後,LLM 的注意力被錯誤訊息淹沒,丟失了「我們在 debug 什麼」和「哪些方法已經試過了」。
系統層 world state
db.getDebugState(sessionId, "npm-install-proxy-error")
// → {
// root_cause: "proxy-config",
// attempts: [
// { method: "switch-to-yarn", result: "same-error" },
// { method: "npm-config-proxy", result: "format-error" },
// { method: "check-npmrc", result: "found-conflict" },
// { method: "delete-npmrc", result: "still-failing" }
// ],
// untried: ["system-proxy-env", "direct-connection-test"],
// status: "in-progress"
// }
// Planner 看到:
// 1. 問題是 proxy,不是 Node 版本
// 2. 已經試了 4 種方法,都失敗
// 3. 還有 2 種沒試的方案
// → 推薦 "system-proxy-env",不會跑偏到 "重裝 Node"
差異: Debug 的上下文不存在對話歷史裡,存在結構化的 state 裡。不管 error log 有多長多雜,planner 永遠知道「我們在 debug 什麼」和「還有什麼沒試」。
五個場景的共同模式
把五個場景放在一起看,有一個共同的結構:
| 場景 | 失敗原因 | 核心問題 |
|---|---|---|
| 重複推薦 | 進度被 debug 訊息淹沒 | 完成狀態沒有被 explicitly track |
| 跳過 precondition | LLM 傾向假設條件滿足 | 沒有強制檢查機制 |
| 長對話遺忘 | Context compression 截掉舊資訊 | 狀態存在對話裡,不在外部 |
| 跨 session 丟失 | 新 session 沒有歷史 | 狀態生命週期綁定 session |
| Retry 迷失 | Error log 干擾注意力 | Debug 進度沒有結構化記錄 |
共同模式:狀態存在 LLM 的 context 裡,而不是 DB 裡。
只要狀態存在 context 裡,就會受到:
- Token 限制
- Context compression
- 注意力分散
- Session 邊界
- LLM 幻覺
這些都是 LLM 架構的結構性限制,不是模型不夠聰明。GPT-5 或 Claude 5 也不會完全解決這些問題,因為問題不在模型能力,在架構。
這不是「LLM 不夠聰明」
這一點需要特別強調。
上面五個場景,不是在說 LLM 笨。LLM 的推理能力很強,單步執行能力更強。問題在於我們把一個不該由 LLM 負責的工作交給了它——記住狀態。
LLM 的設計是:給定 context,產生 response。它不是一個有持久記憶的系統。每次呼叫都是無狀態的,所有的「記憶」都是靠把歷史塞進 prompt 來模擬的。
用 LLM 來追蹤狀態,就像用計算機來當鬧鐘。計算機算數很強,但它不是設計來追蹤時間的。你可以 hack 出一個方法讓它做到,但那不是它的強項,而且很容易壞。
正確的分工是:
- LLM 負責推理和決策 — 「根據目前狀態,下一步做什麼最合理?」
- 系統負責追蹤狀態 — 「目前走到哪了、做過什麼、缺什麼條件」
┌──────────────────────────────┐
│ LLM │
│ 強項:推理、生成、判斷 │
│ 不該做:記住進度、追蹤狀態 │
└──────────────┬───────────────┘
│ 查詢 state / 提議 action
▼
┌──────────────────────────────┐
│ World State (DB) │
│ 強項:持久、精確、可查詢 │
│ 不該做:推理、判斷 │
└──────────────────────────────┘
LLM 問 DB:「現在狀態是什麼?」
DB 回答:「做到第三步了,還缺一個 precondition。」
LLM 決策:「那先去滿足這個 precondition。」
DB 記錄:「precondition 已滿足,進入第四步。」
各做各的強項。
「但我從來沒遇過這些問題啊?」
如果你的 agent 用途是:
- 單次對話內完成簡單任務(寫一個函數、改一個 bug)
- 不需要多步驟串接
- 不跨 session
- 不需要追蹤流程進度
那你確實不需要 world state。LLM 臨場判斷完全夠用。
但只要你開始做這些事情:
- 多步驟 workflow(建一個完整的 app、跑一個部署流程)
- 長時間對話(超過 30 分鐘、超過 50 條訊息)
- 跨 session 繼續(昨天做到一半,今天繼續)
- 需要可靠性(不能跳步、不能重複、不能幻覺)
這五個場景就會開始出現。不是機率問題,是確定性問題。用得越多,遇得越多。
收尾
一句話總結:
LLM 臨場判斷的失敗,不是因為模型不夠聰明,而是因為我們讓它做了它不該做的事——記住狀態。把狀態從 LLM 的 context 移到系統的 DB,不是優化,是架構修正。
CH26 說 world state 是導航系統。這一章說的是:沒有導航系統的時候,你會在第三個路口開始迷路,在第五個路口開回原點,在第十個路口完全忘記你要去哪裡。
不是因為你不會開車。是因為你沒有 GPS。
- ← Previous
CtxFST CH26 - World State 是導航系統:為什麼 Graph 只是地圖,Agent 還需要知道「你在這裡」 - Next →
CtxFST CH28 - 完整的導航比喻:Phase 1-7 各自對應什麼