跳到內容

深入解析 OpenClaw:會話管理與自動壓縮機制

OpenClaw 的設計核心是一個擁有 session 狀態的單一 Gateway 程序。

  • UI(macOS app、網頁版 Control UI、TUI)應該向 Gateway 查詢 session 列表和 token 計數。
  • 在遠端模式下,session 檔案位於遠端主機上;「檢查你本地 Mac 的檔案」並不會反映 Gateway 正在使用的內容。

OpenClaw 將 session 持久化儲存在兩個層級:

  1. Session 儲存庫 (sessions.json)

    • Key/value 映射:sessionKey -> SessionEntry
    • 體積小、可變動,你可以安全地編輯(或刪除條目)
    • 追蹤 session 元數據(目前的 session id、最後活動時間、切換開關、token 計數器等)
  2. 對話紀錄 (<sessionId>.jsonl)

    • 採 append-only 形式的對話紀錄,具有樹狀結構(條目包含 id + parentId)
    • 儲存實際的對話 + tool calls + compaction 摘要
    • 用於為未來的對話重建模型 context

在 Gateway 主機上,每個 agent 的位置如下:

  • 儲存庫:~/.openclaw/agents/<agentId>/sessions/sessions.json
  • 對話紀錄:~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl
    • Telegram topic sessions:.../<sessionId>-topic-<threadId>.jsonl

OpenClaw 透過 src/config/sessions.ts 來解析這些路徑。

Session 持久化針對 sessions.json 和對話紀錄產物提供了自動維護控制 (session.maintenance):

  • mode:warn(預設)或 enforce
  • pruneAfter:過期條目的清理時間點(預設為 30d)
  • maxEntries:限制 sessions.json 中的條目數量(預設為 500)
  • rotateBytes:當 sessions.json 過大時進行輪替(預設為 10mb)
  • resetArchiveRetention:*.reset.<timestamp> 對話紀錄存檔的保留時間(預設與 pruneAfter 相同;false 則停用清理)
  • maxDiskBytes:可選的 session 目錄預算
  • highWaterBytes:清理後的目標容量(預設為 maxDiskBytes 的 80%)

磁碟預算清理的執行順序(mode: "enforce"):

  1. 先移除最舊的存檔或孤立的對話紀錄產物。
  2. 如果仍高於目標容量,則剔除最舊的 session 條目及其對話紀錄檔案。
  3. 持續執行直到使用量等於或低於 highWaterBytes。

在 mode: "warn" 模式下,OpenClaw 會報告潛在的剔除項,但不會變動儲存庫或檔案。

手動執行維護:

Terminal window
openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

獨立的 Cron 執行也會建立 Session 項目與對話逐字稿,且擁有專用的保留控制項:

  • cron.sessionRetention (預設 24h) 會從 Session 儲存庫中清理舊的獨立 Cron 執行 Session(設定為 false 則停用)。
  • cron.runLog.maxBytes + cron.runLog.keepLines 會清理 ~/.openclaw/cron/runs/<jobId>.jsonl 檔案(預設值:2_000_000 bytes 與 2000 行)。

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 的文件。


每個 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()。


儲存庫的值類型是 src/config/sessions.ts 中的 SessionEntry。

重點欄位(未列出全部):

  • sessionId: 目前的逐字稿 ID(除非設定了 sessionFile,否則檔名由此衍生)
  • updatedAt: 最後活動的時間戳記
  • sessionFile: 選填,明確覆寫逐字稿路徑
  • chatType: direct | group | room (幫助 UI 顯示與傳送策略)
  • provider, subject, room, space, displayName: 用於群組/頻道標籤的詮釋資料 (Metadata)
  • 切換開關:
    • thinkingLevel, verboseLevel, reasoningLevel, elevatedLevel
    • sendPolicy (個別 Session 的覆寫設定)
  • 模型選擇:
    • providerOverride, modelOverride, authProfileOverride
  • Token 計數器 (盡力提供 / 取決於 Provider):
    • inputTokens, outputTokens, totalTokens, contextTokens
  • compactionCount: 此 Session Key 完成自動壓縮的次數
  • memoryFlushAt: 最後一次預壓縮記憶體刷新的時間戳記
  • memoryFlushCompactionCount: 最後一次刷新執行時的壓縮次數

你可以放心編輯這個儲存庫,但 Gateway 擁有最終決定權:它可能會在 Session 執行時重寫或重新載入項目。

對話紀錄(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:擴充功能的狀態,這類訊息不會進入模型 Context
  • compaction:已儲存的壓縮摘要,包含 firstKeptEntryId 和 tokensBefore
  • branch_summary:在樹狀分支導覽時產生的儲存摘要

OpenClaw 刻意不去「修復」對話紀錄;Gateway 直接使用 SessionManager 來讀寫這些內容。


這裡有兩個不同的概念需要注意:

  1. Model context window:每個模型的硬性限制(模型能看到的 Token 數量)
  2. Session store counters:寫入 sessions.json 的滾動統計數據(用於 /status 和儀表板)

如果你正在調整限制:

  • Context window 來自模型目錄(也可以透過設定來覆蓋)。
  • Store 裡的 contextTokens 是一個執行時期的預估或回報值;不要把它當作嚴格的保證。

想了解更多,請參考 /token-use。

Compaction 會將舊的對話總結成對話紀錄中一個持久化的 compaction 條目,並完整保留最近的訊息。

在 Compaction 之後,未來的對話輪次會看到:

  • Compaction 總結
  • firstKeptEntryId 之後的訊息

Compaction 是持久性的(不像 session pruning)。請參考 /concepts/session-pruning。


自動 Compaction 何時發生 (Pi runtime)

Section titled “自動 Compaction 何時發生 (Pi runtime)”

在內嵌的 Pi agent 中,自動 Compaction 會在兩種情況下觸發:

  1. 溢出恢復 (Overflow recovery):當模型回傳 context 溢出錯誤時 → 執行 Compaction → 重試。
  2. 閾值維護 (Threshold maintenance):在一次成功的對話輪次後,當:

contextTokens > contextWindow - reserveTokens

其中:

  • contextWindow 是模型的 context window
  • reserveTokens 是為 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 會自動調升它。
  • 預設底限是 20000 tokens。
  • 設定 agents.defaults.compaction.reserveTokensFloor: 0 即可停用這個底限。
  • 如果數值已經比底限高,OpenClaw 就不會改動它。

為什麼要這樣做:這是為了在 Compaction 變得不可避免之前,留出足夠的緩衝空間給多輪對話的「內務處理」(像是 memory 寫入)。

實作細節:src/agents/pi-settings.ts 中的 ensurePiCompactionReserveTokens()(從 src/agents/pi-embedded-runner.ts 呼叫)。

你可以透過以下方式觀察 Compaction 和 Session 狀態:

  • /status(在任何對話 Session 中)
  • openclaw status (CLI)
  • openclaw sessions / sessions --json
  • 詳細模式(Verbose mode):🧹 Auto-compaction complete + Compaction 次數

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) 機制:

  1. 監控 Session 的上下文使用量。
  2. 當使用量超過「軟門檻」(低於 Pi 的壓縮門檻)時,對 Agent 發送一個靜默的「立即寫入記憶體」指令。
  3. 使用 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 端。


  • Session key 錯誤?請從 /concepts/session 開始檢查,並確認 /status 中的 sessionKey。
  • Store 與逐字稿 (transcript) 不符?確認 Gateway 主機以及 openclaw status 顯示的 store 路徑。
  • 壓縮過於頻繁?檢查:
    • 模型上下文視窗(是否太小)
    • 壓縮設定(如果 reserveTokens 對於模型視窗來說設得太高,會導致過早觸發壓縮)
    • 工具結果 (tool-result) 過於臃腫:請啟用或調整 Session 剪裁 (pruning) 功能
  • 靜默回合洩漏?確認回覆是否以 NO_REPLY(精確 Token)開頭,並檢查你的版本是否包含串流攔截的修正。
OpenClaw

OpenClaw Expert

還是卡住了?

如果這篇文件沒有解決你的情境,直接問 OpenClaw Expert,拿到可執行步驟。