Session 管理

OpenClaw 將每個 Agent 的一個直接對話 Session 視為主要 Session。直接對話會收斂到 agent:<agentId>:<mainKey>(預設為 main),而群組/頻道對話則各自擁有獨立的 Key。session.mainKey 會被採用。

透過 session.dmScope 控制私訊的分組方式:

  • main(預設):所有私訊共享主要 Session,維持對話連貫性。
  • per-peer:依發送者 ID 跨頻道隔離。
  • per-channel-peer:依頻道 + 發送者隔離(多使用者收件匣建議使用)。
  • per-account-channel-peer:依帳號 + 頻道 + 發送者隔離(多帳號收件匣建議使用)。 使用 session.identityLinks 可將帶有供應商前綴的 Peer ID 對應到統一身分,讓同一個人在不同頻道之間共享私訊 Session(適用於 per-peerper-channel-peerper-account-channel-peer)。

安全私訊模式(多使用者環境建議啟用)

安全警告: 如果你的 Agent 可能接收來自多人的私訊,強烈建議啟用安全私訊模式。否則所有使用者共享同一對話上下文,可能導致私人資訊在使用者之間外洩。

預設設定下的問題範例:

  • Alice(<SENDER_A>)傳訊給你的 Agent 談論私人話題(例如看診預約)
  • Bob(<SENDER_B>)傳訊給你的 Agent 問「我們剛才在聊什麼?」
  • 因為兩則私訊共享同一 Session,模型可能用 Alice 的對話上下文來回答 Bob

解決方式: 設定 dmScope 來隔離每位使用者的 Session:

// ~/.openclaw/openclaw.json
{
  session: {
    // 安全私訊模式:依頻道 + 發送者隔離私訊上下文。
    dmScope: "per-channel-peer",
  },
}

何時應該啟用:

  • 你核准了不止一位發送者的配對
  • 你的私訊允許清單有多筆項目
  • 你設定了 dmPolicy: "open"
  • 有多支電話號碼或帳號可以傳訊給你的 Agent

注意事項:

  • 預設為 dmScope: "main" 以維持連貫性(所有私訊共享主要 Session)。單一使用者環境下這樣就夠了。
  • 本機 CLI 初始化設定在未設定時會預設寫入 session.dmScope: "per-channel-peer"(已有明確設定值的會保留)。
  • 同一頻道上的多帳號收件匣,建議使用 per-account-channel-peer
  • 如果同一個人透過不同頻道聯絡你,使用 session.identityLinks 可將其私訊 Session 收斂到統一身分。
  • 你可以用 openclaw security audit 驗證私訊設定(參閱 security)。

Gateway 是唯一真實來源

所有 Session 狀態歸 Gateway 所有(即「主控」OpenClaw)。UI 用戶端(macOS App、WebChat 等)必須向 Gateway 查詢 Session 清單和 Token 計數,而非讀取本地檔案。

  • 遠端模式下,你關心的 Session Store 存在於遠端 Gateway 主機上,而不是你的 Mac。
  • UI 中顯示的 Token 計數來自 Gateway Store 的欄位(inputTokensoutputTokenstotalTokenscontextTokens)。用戶端不會自行解析 JSONL 對話記錄來「修正」總數。

狀態存放位置

  • Gateway 主機上:
    • Store 檔案:~/.openclaw/agents/<agentId>/sessions/sessions.json(每個 Agent 各一)。
  • 對話記錄:~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 主題 Session 使用 .../<SessionId>-topic-<threadId>.jsonl)。
  • Store 是一個 sessionKey -> { sessionId, updatedAt, ... } 的 Map。刪除項目是安全的,下次訊息到達時會自動重建。
  • 群組項目可能包含 displayNamechannelsubjectroomspace,用於在 UI 中標示 Session。
  • Session 項目包含 origin 後設資料(標籤 + 路由提示),讓 UI 能解釋 Session 的來源。
  • OpenClaw 不會讀取舊版 Pi/Tau Session 資料夾。

維護

OpenClaw 會對 Session Store 進行維護,讓 sessions.json 和對話記錄等檔案在長期使用後維持合理的大小。

預設值

  • session.maintenance.mode: warn
  • session.maintenance.pruneAfter: 30d
  • session.maintenance.maxEntries: 500
  • session.maintenance.rotateBytes: 10mb
  • session.maintenance.resetArchiveRetention: 預設與 pruneAfter 相同(30d
  • session.maintenance.maxDiskBytes: 未設定(停用)
  • session.maintenance.highWaterBytes: 啟用預算時預設為 maxDiskBytes80%

運作方式

維護在 Session Store 寫入時執行,也可用 openclaw sessions cleanup 手動觸發。

  • mode: "warn":報告哪些項目會被清除,但不會實際修改項目或對話記錄。
  • mode: "enforce":依以下順序執行清理:
    1. 清除超過 pruneAfter 的過期項目
    2. 將項目數限制在 maxEntries(從最舊的開始)
    3. 對已移除且不再被參照的項目,封存其對話記錄檔案
    4. 依保留策略清除舊的 *.deleted.<timestamp>*.reset.<timestamp> 封存檔
    5. sessions.json 超過 rotateBytes 時進行輪替
    6. 若設定了 maxDiskBytes,向 highWaterBytes 目標強制執行磁碟預算(從最舊的產出物開始,然後是最舊的 Session)

大型 Store 的效能注意事項

高流量環境中常見大型 Session Store。維護工作屬於寫入路徑的工作,因此非常大的 Store 可能增加寫入延遲。

最影響成本的因素:

  • 非常高的 session.maintenance.maxEntries
  • 過長的 pruneAfter 期間讓過期項目持續存在
  • ~/.openclaw/agents/<agentId>/sessions/ 中存在大量對話記錄/封存產出物
  • 啟用磁碟預算(maxDiskBytes)但未設定合理的修剪/上限值

建議做法:

  • 正式環境使用 mode: "enforce",讓增長自動受限
  • 同時設定時間和數量限制(pruneAfter + maxEntries),不要只設一個
  • 大型部署設定 maxDiskBytes + highWaterBytes 作為硬上限
  • highWaterBytes 應明顯低於 maxDiskBytes(預設為 80%)
  • 修改設定後執行 openclaw sessions cleanup --dry-run --json 來驗證預期影響
  • 若有頻繁使用中的 Session,手動清理時加上 --active-key

自訂範例

使用保守的強制策略:

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "45d",
      maxEntries: 800,
      rotateBytes: "20mb",
      resetArchiveRetention: "14d",
    },
  },
}

為 Sessions 目錄啟用硬性磁碟預算:

{
  session: {
    maintenance: {
      mode: "enforce",
      maxDiskBytes: "1gb",
      highWaterBytes: "800mb",
    },
  },
}

針對較大規模安裝進行調校(範例):

{
  session: {
    maintenance: {
      mode: "enforce",
      pruneAfter: "14d",
      maxEntries: 2000,
      rotateBytes: "25mb",
      maxDiskBytes: "2gb",
      highWaterBytes: "1.6gb",
    },
  },
}

從 CLI 預覽或強制執行維護:

openclaw sessions cleanup --dry-run
openclaw sessions cleanup --enforce

Session 修剪

OpenClaw 預設會在每次 LLM 呼叫前,從記憶體中的上下文裁減舊的工具結果。 這不會改寫 JSONL 歷史紀錄。參閱 /concepts/session-pruning

壓縮前的記憶刷寫

當 Session 接近自動壓縮時,OpenClaw 可以執行一輪靜默的記憶刷寫,提醒模型將重要筆記寫入磁碟。這只有在 Workspace 可寫時才會執行。參閱 MemoryCompaction

傳輸層 → Session Key 的對應

  • 直接對話遵循 session.dmScope(預設 main)。
    • mainagent:<agentId>:<mainKey>(跨裝置/頻道的連貫性)。
      • 多支電話號碼和多個頻道可對應到同一個 Agent Main Key;它們充當同一對話的傳輸層。
    • per-peeragent:<agentId>:direct:<peerId>
    • per-channel-peeragent:<agentId>:<channel>:direct:<peerId>
    • per-account-channel-peeragent:<agentId>:<channel>:<accountId>:direct:<peerId>(accountId 預設為 default)。
    • 如果 session.identityLinks 比對到帶有供應商前綴的 Peer ID(例如 telegram:123),統一 Key 會取代 <peerId>,讓同一個人跨頻道共享 Session。
  • 群組對話隔離狀態:agent:<agentId>:<channel>:group:<id>(頻道用 agent:<agentId>:<channel>:channel:<id>)。
    • Telegram 論壇主題會在群組 ID 後加上 :topic:<threadId> 進行隔離。
    • 舊版 group:<id> Key 仍可識別,用於遷移。
  • 收到的上下文可能仍使用 group:<id>;頻道從 Provider 推斷並正規化為標準的 agent:<agentId>:<channel>:group:<id> 格式。
  • 其他來源:
    • Cron 工作:cron:<job.id>
    • Webhook:hook:<uuid>(除非 Hook 有明確設定)
    • Node 執行:node-<nodeId>

生命週期

  • 重設策略:Session 持續使用直到過期,到期判定在下一則收到的訊息時進行。
  • 每日重設:預設為 Gateway 主機當地時間凌晨 4:00。Session 的最後更新早於最近一次每日重設時間時即視為過期。
  • 閒置重設(選用):idleMinutes 提供一個滑動閒置時間窗。同時設定每日和閒置重設時,先到期的那個會強制產生新 Session。
  • 舊版閒置模式:如果你設定了 session.idleMinutes 但沒有任何 session.reset/resetByType 設定,OpenClaw 會維持純閒置模式以向後相容。
  • 依類型覆蓋(選用):resetByType 可針對 directgroupthread Session 分別覆蓋策略(thread = Slack/Discord 討論串、Telegram 主題、連接器提供的 Matrix 討論串)。
  • 依頻道覆蓋(選用):resetByChannel 覆蓋特定頻道的重設策略(適用於該頻道的所有 Session 類型,優先於 reset/resetByType)。
  • 重設觸發:確切的 /new/reset(加上 resetTriggers 中的額外觸發字串)會啟動新的 Session ID 並將訊息的其餘部分傳遞下去。/new <model> 接受模型別名、provider/model 或供應商名稱(模糊比對)來設定新 Session 的模型。若單獨送出 /new/reset,OpenClaw 會執行一個簡短的「hello」問候回合來確認重設。
  • 手動重設:從 Store 中刪除特定 Key 或移除 JSONL 對話記錄;下次訊息會自動重建。
  • 隔離的 Cron 工作每次執行都會產生全新的 sessionId(不會重用閒置 Session)。

傳送策略(選用)

針對特定 Session 類型阻擋投遞,無需逐一列出個別 ID。

{
  session: {
    sendPolicy: {
      rules: [
        { action: "deny", match: { channel: "discord", chatType: "group" } },
        { action: "deny", match: { keyPrefix: "cron:" } },
        // 比對原始 Session Key(含 `agent:<id>:` 前綴)。
        { action: "deny", match: { rawKeyPrefix: "agent:main:discord:" } },
      ],
      default: "allow",
    },
  },
}

執行期間覆蓋(僅限擁有者):

  • /send on → 允許此 Session
  • /send off → 拒絕此 Session
  • /send inherit → 清除覆蓋設定,使用設定規則 以獨立訊息送出以確保生效。

設定(選用重新命名範例)

// ~/.openclaw/openclaw.json
{
  session: {
    scope: "per-sender", // 群組 Key 保持獨立
    dmScope: "main", // 私訊連貫性(共用收件匣請設定 per-channel-peer/per-account-channel-peer)
    identityLinks: {
      alice: ["telegram:123456789", "discord:987654321012345678"],
    },
    reset: {
      // 預設:mode=daily, atHour=4(Gateway 主機當地時間)。
      // 若同時設定 idleMinutes,先到期的那個生效。
      mode: "daily",
      atHour: 4,
      idleMinutes: 120,
    },
    resetByType: {
      thread: { mode: "daily", atHour: 4 },
      direct: { mode: "idle", idleMinutes: 240 },
      group: { mode: "idle", idleMinutes: 120 },
    },
    resetByChannel: {
      discord: { mode: "idle", idleMinutes: 10080 },
    },
    resetTriggers: ["/new", "/reset"],
    store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
    mainKey: "main",
  },
}

查看資訊

  • openclaw status — 顯示 Store 路徑和近期 Session。
  • openclaw sessions --json — 匯出所有項目(可用 --active <minutes> 篩選)。
  • openclaw gateway call sessions.list --params '{}' — 從運行中的 Gateway 取得 Session(遠端 Gateway 請使用 --url/--token)。
  • 在對話中以獨立訊息送出 /status 可查看 Agent 是否可達、Session 上下文使用量、當前的 thinking/fast/verbose 切換狀態,以及 WhatsApp web 憑證上次刷新時間(有助於察覺需要重新連結)。
  • 送出 /context list/context detail 可查看系統提示詞和注入的 Workspace 檔案內容(以及最大的上下文貢獻者)。
  • 送出 /stop(或獨立中止短語如 stopstop actionstop runstop openclaw)可中止當前執行、清除該 Session 排隊中的後續處理,並停止由此 spawn 出的子 Agent 執行(回覆中會包含已停止的數量)。
  • 以獨立訊息送出 /compact(可選說明)可摘要較舊的上下文並釋放視窗空間。參閱 /concepts/compaction
  • JSONL 對話記錄可直接開啟以檢視完整回合。

使用技巧

  • 主要 Key 專供一對一對話使用;讓群組保有各自的 Key。
  • 自動化清理時,刪除個別 Key 而非整個 Store,以保留其他地方的上下文。

Session 來源後設資料

每個 Session 項目會記錄它的來源(盡力而為)到 origin

  • label:人類可讀標籤(從對話標籤 + 群組主題/頻道推導)
  • provider:正規化的頻道 ID(含擴充套件)
  • from/to:來自收到信封的原始路由 ID
  • accountId:供應商帳號 ID(多帳號時)
  • threadId:頻道支援時的討論串/主題 ID 來源欄位會為私訊、頻道和群組填入。如果連接器只更新投遞路由(例如保持私訊主要 Session 的新鮮度),仍應提供收到的上下文以維護 Session 的說明性後設資料。擴充套件可透過在收到的上下文中傳送 ConversationLabelGroupSubjectGroupChannelGroupSpaceSenderName,並呼叫 recordSessionMetaFromInbound(或將同樣的上下文傳給 updateLastRoute)來實現。