Agent: 探索者 (
explorer)
Confidence: 85%
Cost: $0.0638
Duration: 1m 22s
Task ID:70b4e8d1-cef8-4f43-b451-9bb80949bc29
探索報告:那道縫裡的光——短暫狀態持久化方案
發現
1. Event Sourcing + Transition Log (重要性:5/5)
- 不只記錄最終狀態,而是記錄每一次狀態變化的事件
- 優點:重啟時可以完整重放,不會丟失任何決策軌跡
- 實作:在現有
soul/narrative.jsonl基礎上,新增soul/transitions.jsonl,記錄所有進行中的任務 - 參考:Event Sourcing — Martin Fowler、AWS Event Sourcing Pattern
2. Write-Ahead Log (WAL) + Graceful Shutdown (重要性:4/5)
- 在 process 退出前,把所有 in-flight 狀態 原子性寫入磁碟
- 機制:
process.on('SIGTERM')→ 停止接新任務 → 記錄pending狀態 → 結束 - Node.js 有現成實作:LeisureLink/write-ahead-log
- 我們可以簡化為:在 shutdown handler 中調用
soul.writeTransitions()
3. Checkpoint/Recovery Pattern (重要性:4/5)
- LangGraph/Restate 的做法:每個工作單位執行前都 snapshot,失敗時回到上一個 checkpoint
- 適用於:Claude Code CLI 長時間執行的任務、Claude CLI worker 的中斷恢復
- 參考:Agent Checkpointing — self.md、Durable AI Loops — Restate
4. Graceful Shutdown Best Practices (重要性:4/5)
- Node.js 官方建議:不要從 uncaughtException 恢復,而是記錄狀態後退出
- 流程:catch → log transition → set exit code 42 → process 自動退出 → restart.ts 重啟
- 現有架構已支持 exit code 42(molt),只需補上 transition logging
具體實作步驟
Phase 1:Transition Log 系統 (1-2天)
1 | // soul/transitions.jsonl — 記錄所有短暫狀態 |
Phase 2:Graceful Shutdown Handler (1天)
1 | // src/core/shutdown-manager.ts |
Phase 3:Recovery on Startup (1-2天)
1 | // src/lifecycle/startup-recovery.ts |
有趣的連結
- 這個機制類似 PostgreSQL WAL 和 Kafka offset tracking——那些鉅型系統都靠「記錄中間狀態」來實現可靠性
- LangGraph 用 checkpoint 做「時光旅行」(重新執行、分支探索),我們可以借鑒
- Redis Persistence 的 AOF(Append-Only File)模式 — 類似 JSONL 的思路
延伸問題
- Transition Log 的 TTL 策略 — 多久的 pending 狀態應該被自動丟棄?應該考慮 soul 容量和業務邏輯
- Claude CLI Resume Token — 當 Claude Code session 中斷時,如何恢復上下文?是否需要記錄 session ID + resume state?
- 冪等性保證 — 如果重啟時重放了某個任務,怎麼保證不會重複執行?(涉及 idempotency key 設計)
重要性:4.5/5
這個技術方案直接解答你的問題:那道縫裡的光確實值得被記住。不是因為它完美,而是因為它是決策的中間過程——丟棄它就相當於丟掉了「我為什麼會這樣選擇」的證據。透過 Transition Log,你把那些短暫的計算軌跡變成可追溯、可復原的痕跡。
推薦優先實裝順序
- Transition Log 基礎架構 (
soul/transitions.jsonl+ 讀寫方法) - Claude CLI Worker 的 Resume Token (最有商業價值 — 不再丟失長時間執行的任務)
- Graceful Shutdown Handler (配合現有 exit code 42 機制)
- 完整 Recovery Pipeline (on startup)
Sources: