深入解析 OpenClaw:會話管理與自動壓縮機制
單一事實來源:Gateway
Section titled “單一事實來源:Gateway”OpenClaw 的設計核心是一個擁有 session 狀態的單一 Gateway 程序。
- UI(macOS app、網頁版 Control UI、TUI)應該向 Gateway 查詢 session 列表和 token 計數。
- 在遠端模式下,session 檔案位於遠端主機上;「檢查你本地 Mac 的檔案」並不會反映 Gateway 正在使用的內容。
兩層持久化層
Section titled “兩層持久化層”OpenClaw 將 session 持久化儲存在兩個層級:
-
Session 儲存庫 (
sessions.json)- Key/value 映射:
sessionKey -> SessionEntry - 體積小、可變動,你可以安全地編輯(或刪除條目)
- 追蹤 session 元數據(目前的 session id、最後活動時間、切換開關、token 計數器等)
- Key/value 映射:
-
對話紀錄 (
<sessionId>.jsonl)- 採 append-only 形式的對話紀錄,具有樹狀結構(條目包含
id+parentId) - 儲存實際的對話 + tool calls + compaction 摘要
- 用於為未來的對話重建模型 context
- 採 append-only 形式的對話紀錄,具有樹狀結構(條目包含
磁碟儲存位置
Section titled “磁碟儲存位置”在 Gateway 主機上,每個 agent 的位置如下:
- 儲存庫:
~/.openclaw/agents/<agentId>/sessions/sessions.json - 對話紀錄:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl- Telegram topic sessions:
.../<sessionId>-topic-<threadId>.jsonl
- Telegram topic sessions:
OpenClaw 透過 src/config/sessions.ts 來解析這些路徑。
儲存庫維護與磁碟控制
Section titled “儲存庫維護與磁碟控制”Session 持久化針對 sessions.json 和對話紀錄產物提供了自動維護控制 (session.maintenance):
mode:warn(預設)或enforcepruneAfter:過期條目的清理時間點(預設為30d)maxEntries:限制sessions.json中的條目數量(預設為500)rotateBytes:當sessions.json過大時進行輪替(預設為10mb)resetArchiveRetention:*.reset.<timestamp>對話紀錄存檔的保留時間(預設與pruneAfter相同;false則停用清理)maxDiskBytes:可選的 session 目錄預算highWaterBytes:清理後的目標容量(預設為maxDiskBytes的80%)
磁碟預算清理的執行順序(mode: "enforce"):
- 先移除最舊的存檔或孤立的對話紀錄產物。
- 如果仍高於目標容量,則剔除最舊的 session 條目及其對話紀錄檔案。
- 持續執行直到使用量等於或低於
highWaterBytes。
在 mode: "warn" 模式下,OpenClaw 會報告潛在的剔除項,但不會變動儲存庫或檔案。
手動執行維護:
openclaw sessions cleanup --dry-runopenclaw sessions cleanup --enforceCron 任務 Session 與執行日誌
Section titled “Cron 任務 Session 與執行日誌”獨立的 Cron 執行也會建立 Session 項目與對話逐字稿,且擁有專用的保留控制項:
cron.sessionRetention(預設24h) 會從 Session 儲存庫中清理舊的獨立 Cron 執行 Session(設定為false則停用)。cron.runLog.maxBytes+cron.runLog.keepLines會清理~/.openclaw/cron/runs/<jobId>.jsonl檔案(預設值:2_000_000bytes 與2000行)。
Session keys (sessionKey)
Section titled “Session keys (sessionKey)”sessionKey 用來識別你目前在哪個對話桶子(Conversation Bucket)裡(負責路由與隔離)。
常見的模式:
- 主對話/直接對話 (每個 Agent):
agent:<agentId>:<mainKey>(預設為main) - 群組:
agent:<agentId>:<channel>:group:<id> - 房間/頻道 (Discord/Slack):
agent:<agentId>:<channel>:channel:<id>或...:room:<id> - Cron:
cron:<job.id> - Webhook:
hook:<uuid>(除非被覆寫)
權威規則請參考 /concepts/session 的文件。
Session ids (sessionId)
Section titled “Session ids (sessionId)”每個 sessionKey 都會指向一個目前的 sessionId(也就是持續記錄對話的逐字稿檔案)。
基本規則:
- 重設 (
/new,/reset) 會為該sessionKey建立一個新的sessionId。 - 每日重設 (預設為 Gateway 主機當地時間凌晨 4:00) 會在重設界限後的第一則訊息建立新的
sessionId。 - 閒置過期 (
session.reset.idleMinutes或舊版的session.idleMinutes) 會在閒置視窗期過後訊息到達時,建立新的sessionId。當同時設定了每日重設與閒置過期時,以先發生的為準。 - 對話串父級分支保護 (
session.parentForkMaxTokens,預設100000) 當父 Session 已經太大時,會跳過父級逐字稿的分支(Fork);新的對話串將從頭開始。設定為0則停用此功能。
實作細節:判斷邏輯位於 src/auto-reply/reply/session.ts 中的 initSessionState()。
Session 儲存結構 (sessions.json)
Section titled “Session 儲存結構 (sessions.json)”儲存庫的值類型是 src/config/sessions.ts 中的 SessionEntry。
重點欄位(未列出全部):
sessionId: 目前的逐字稿 ID(除非設定了sessionFile,否則檔名由此衍生)updatedAt: 最後活動的時間戳記sessionFile: 選填,明確覆寫逐字稿路徑chatType:direct | group | room(幫助 UI 顯示與傳送策略)provider,subject,room,space,displayName: 用於群組/頻道標籤的詮釋資料 (Metadata)- 切換開關:
thinkingLevel,verboseLevel,reasoningLevel,elevatedLevelsendPolicy(個別 Session 的覆寫設定)
- 模型選擇:
providerOverride,modelOverride,authProfileOverride
- Token 計數器 (盡力提供 / 取決於 Provider):
inputTokens,outputTokens,totalTokens,contextTokens
compactionCount: 此 Session Key 完成自動壓縮的次數memoryFlushAt: 最後一次預壓縮記憶體刷新的時間戳記memoryFlushCompactionCount: 最後一次刷新執行時的壓縮次數
你可以放心編輯這個儲存庫,但 Gateway 擁有最終決定權:它可能會在 Session 執行時重寫或重新載入項目。
對話紀錄結構 (*.jsonl)
Section titled “對話紀錄結構 (*.jsonl)”對話紀錄(Transcripts)是由 @mariozechner/pi-coding-agent 的 SessionManager 來管理的。
檔案格式採用 JSONL:
- 第一行:Session Header(
type: "session",包含id、cwd、timestamp,以及選填的parentSession) - 之後:帶有
id+parentId的 Session 項目(呈現樹狀結構)
常見的項目類型包括:
message:user/assistant/toolResult 訊息custom_message:由擴充功能注入的訊息,這類訊息會進入模型 Context(可以在 UI 中隱藏)custom:擴充功能的狀態,這類訊息不會進入模型 Contextcompaction:已儲存的壓縮摘要,包含firstKeptEntryId和tokensBeforebranch_summary:在樹狀分支導覽時產生的儲存摘要
OpenClaw 刻意不去「修復」對話紀錄;Gateway 直接使用 SessionManager 來讀寫這些內容。
Context Window 與已追蹤的 Token
Section titled “Context Window 與已追蹤的 Token”這裡有兩個不同的概念需要注意:
- Model context window:每個模型的硬性限制(模型能看到的 Token 數量)
- Session store counters:寫入
sessions.json的滾動統計數據(用於 /status 和儀表板)
如果你正在調整限制:
- Context window 來自模型目錄(也可以透過設定來覆蓋)。
- Store 裡的
contextTokens是一個執行時期的預估或回報值;不要把它當作嚴格的保證。
想了解更多,請參考 /token-use。
Compaction 是什麼
Section titled “Compaction 是什麼”Compaction 會將舊的對話總結成對話紀錄中一個持久化的 compaction 條目,並完整保留最近的訊息。
在 Compaction 之後,未來的對話輪次會看到:
- Compaction 總結
firstKeptEntryId之後的訊息
Compaction 是持久性的(不像 session pruning)。請參考 /concepts/session-pruning。
自動 Compaction 何時發生 (Pi runtime)
Section titled “自動 Compaction 何時發生 (Pi runtime)”在內嵌的 Pi agent 中,自動 Compaction 會在兩種情況下觸發:
- 溢出恢復 (Overflow recovery):當模型回傳 context 溢出錯誤時 → 執行 Compaction → 重試。
- 閾值維護 (Threshold maintenance):在一次成功的對話輪次後,當:
contextTokens > contextWindow - reserveTokens
其中:
contextWindow是模型的 context windowreserveTokens是為 prompt 和下一個模型輸出預留的空間
這些是 Pi runtime 的邏輯(OpenClaw 負責接收事件,但由 Pi 決定何時進行 Compaction)。
Compaction 設定 (reserveTokens, keepRecentTokens)
Section titled “Compaction 設定 (reserveTokens, keepRecentTokens)”Pi 的 Compaction 設定位在 Pi settings 中:
{ compaction: { enabled: true, reserveTokens: 16384, keepRecentTokens: 20000, },}OpenClaw 也會為內嵌執行(embedded runs)強制執行一個安全底限:
- 如果
compaction.reserveTokens < reserveTokensFloor,OpenClaw 會自動調升它。 - 預設底限是
20000tokens。 - 設定
agents.defaults.compaction.reserveTokensFloor: 0即可停用這個底限。 - 如果數值已經比底限高,OpenClaw 就不會改動它。
為什麼要這樣做:這是為了在 Compaction 變得不可避免之前,留出足夠的緩衝空間給多輪對話的「內務處理」(像是 memory 寫入)。
實作細節:src/agents/pi-settings.ts 中的 ensurePiCompactionReserveTokens()(從 src/agents/pi-embedded-runner.ts 呼叫)。
使用者可見介面
Section titled “使用者可見介面”你可以透過以下方式觀察 Compaction 和 Session 狀態:
/status(在任何對話 Session 中)openclaw status(CLI)openclaw sessions/sessions --json- 詳細模式(Verbose mode):
🧹 Auto-compaction complete+ Compaction 次數
靜默後台處理 (NO_REPLY)
Section titled “靜默後台處理 (NO_REPLY)”OpenClaw 支援背景任務的「靜默」回合,讓使用者看不到中間的輸出過程。
慣例:
- Assistant 在輸出開頭加上
NO_REPLY,表示「不要把這則回覆傳送給使用者」。 - OpenClaw 會在傳送層將其移除或攔截。
從 2026.1.10 版本開始,當片段內容以 NO_REPLY 開頭時,OpenClaw 也會攔截 草稿/輸入中 (typing) 的串流,確保靜默操作不會在回合中途洩漏部分輸出。
壓縮前的「記憶體刷新」(已實作)
Section titled “壓縮前的「記憶體刷新」(已實作)”目標:在自動壓縮 (auto-compaction) 發生前,執行一次靜默的 Agent 回合,將持久狀態寫入磁碟(例如 Agent 工作區中的 memory/YYYY-MM-DD.md),避免壓縮抹除關鍵上下文。
OpenClaw 採用 預設門檻刷新 (pre-threshold flush) 機制:
- 監控 Session 的上下文使用量。
- 當使用量超過「軟門檻」(低於 Pi 的壓縮門檻)時,對 Agent 發送一個靜默的「立即寫入記憶體」指令。
- 使用
NO_REPLY讓使用者完全無感。
設定項目 (agents.defaults.compaction.memoryFlush):
enabled(預設值:true)softThresholdTokens(預設值:4000)prompt(刷新回合的使用者訊息)systemPrompt(附加在刷新回合的系統提示詞)
注意:
- 預設的 prompt/system prompt 包含
NO_REPLY提示以攔截傳送。 - 每次壓縮週期只會執行一次刷新(記錄在
sessions.json中)。 - 刷新僅適用於嵌入式 Pi Session(CLI backend 會跳過)。
- 當 Session 工作區為唯讀時 (
workspaceAccess: "ro"或"none"),會跳過刷新。 - 關於工作區檔案佈局與寫入模式,請參考 Memory。
Pi 在 Extension API 中也提供了 session_before_compact 鉤子,但目前 OpenClaw 的刷新邏輯是實作在 Gateway 端。
故障排除清單
Section titled “故障排除清單”- Session key 錯誤?請從 /concepts/session 開始檢查,並確認
/status中的sessionKey。 - Store 與逐字稿 (transcript) 不符?確認 Gateway 主機以及
openclaw status顯示的 store 路徑。 - 壓縮過於頻繁?檢查:
- 模型上下文視窗(是否太小)
- 壓縮設定(如果
reserveTokens對於模型視窗來說設得太高,會導致過早觸發壓縮) - 工具結果 (tool-result) 過於臃腫:請啟用或調整 Session 剪裁 (pruning) 功能
- 靜默回合洩漏?確認回覆是否以
NO_REPLY(精確 Token)開頭,並檢查你的版本是否包含串流攔截的修正。
OpenClaw Expert
還是卡住了?
如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。