セッション管理 & コンパクション(詳細解説)
このドキュメントでは、OpenClaw がセッションをエンドツーエンドで管理する方法を説明します。- セッションルーティング(受信メッセージがどのように
sessionKeyにマップされるか) - セッションストア(
sessions.json)と、その追跡内容 - トランスクリプトの永続化(
*.jsonl)と、その構造 - トランスクリプトの衛生管理(実行前のプロバイダー固有の修正)
- コンテキスト制限(コンテキストウィンドウと追跡トークンの違い)
- コンパクション(手動 + 自動コンパクション)と、コンパクション前作業をフックする場所
- サイレントなハウスキーピング(例: ユーザーに表示される出力を生成すべきでないメモリ書き込み)
真実の情報源: Gateway
OpenClaw は、セッション状態を所有する単一の Gateway プロセス を中心に設計されています。- UI(macOS アプリ、Web Control UI、TUI)は、セッション一覧やトークン数を Gateway に問い合わせるべきです。
- リモートモードでは、セッションファイルはリモートホスト上にあります。「ローカルの Mac のファイルを確認する」だけでは、Gateway が使用している内容は反映されません。
2 つの永続化レイヤー
OpenClaw は、セッションを 2 つのレイヤーで永続化します。-
セッションストア(
sessions.json)- キー/値マップ:
sessionKey -> SessionEntry - 小さく、可変で、編集(またはエントリ削除)しても安全
- セッションメタデータ(現在のセッション id、最終アクティビティ、トグル、トークンカウンターなど)を追跡
- キー/値マップ:
-
トランスクリプト(
<sessionId>.jsonl)- ツリー構造を持つ追記専用トランスクリプト(各エントリは
id+parentIdを持つ) - 実際の会話 + ツール呼び出し + コンパクション要約を保存
- 将来のターンに向けてモデルコンテキストを再構築するために使用
- ツリー構造を持つ追記専用トランスクリプト(各エントリは
ディスク上の保存場所
Gateway ホスト上で、エージェントごとに保存されます。- ストア:
~/.openclaw/agents/<agentId>/sessions/sessions.json - トランスクリプト:
~/.openclaw/agents/<agentId>/sessions/<sessionId>.jsonl- Telegram のトピックセッション:
.../<sessionId>-topic-<threadId>.jsonl
- Telegram のトピックセッション:
src/config/sessions.ts を介してこれらを解決します。
セッションキー(sessionKey)
sessionKey は、「どの会話バケットに属しているか」(ルーティング + 分離)を識別します。
一般的なパターン:
- メイン/ダイレクトチャット(エージェントごと):
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>(上書きされない限り)
セッション id(sessionId)
各 sessionKey は、現在の sessionId(会話を継続するトランスクリプトファイル)を指します。
親指のルール:
- リセット(
/new、/reset)は、そのsessionKeyに対して新しいsessionIdを作成します。 - 日次リセット(デフォルトは Gateway ホストのローカル時間で午前 4:00)は、リセット境界後の次のメッセージで新しい
sessionIdを作成します。 - アイドル期限切れ(
session.reset.idleMinutesまたはレガシーのsession.idleMinutes)は、アイドルウィンドウ後にメッセージが到着した際に新しいsessionIdを作成します。日次とアイドルの両方が設定されている場合、先に期限切れした方が優先されます。 毎日+アイドルが設定されている場合、いずれかの方が最初の勝利に失効します。
src/auto-reply/reply/session.ts 内の initSessionState() で行われます。
セッションストアのスキーマ(sessions.json)
ストアの値型は、src/config/sessions.ts 内の SessionEntry です。
主なフィールド(網羅的ではありません):
sessionId: 現在のトランスクリプト id(sessionFileが設定されていない限り、ファイル名はこれから導出)updatedAt: 最終アクティビティのタイムスタンプsessionFile: 任意の明示的なトランスクリプトパス上書きchatType:direct | group | room(UI や送信ポリシーに有用)provider,subject,room,space,displayName: グループ/チャンネルのラベリング用メタデータ- Toggles:
thinkingLevel,verboseLevel,reasoningLevel,elevatedLevelsendPolicy(セッション単位の上書き)
- モデル選択:
providerOverride,modelOverride,authProfileOverride
- トークンカウンター(ベストエフォート / プロバイダー依存):
inputTokens,outputTokens,totalTokens,contextTokens
compactionCount: このセッションキーで自動コンパクションが完了した回数memoryFlushAt: 直近のコンパクション前メモリフラッシュのタイムスタンプmemoryFlushCompactionCount: 最後のフラッシュ実行時のコンパクション回数
トランスクリプト構造(*.jsonl)
トランスクリプトは、@mariozechner/pi-coding-agent の SessionManager によって管理されます。
ファイル形式は JSONL です。
- 1 行目: セッションヘッダー(
type: "session"、id、cwd、timestamp、任意でparentSessionを含む) - 以降:
id+parentId(ツリー)を持つセッションエントリ
message: ユーザー/アシスタント/toolResult メッセージcustom_message: モデルコンテキストに「入る」拡張注入メッセージ(UI から非表示にできる)custom: モデルコンテキストに「入らない」拡張状態compaction:firstKeptEntryIdとtokensBeforeを持つ永続化されたコンパクション要約branch_summary: ツリーブランチ移動時の永続化サマリー
SessionManager を使用して読み書きします。
コンテキストウィンドウ vs 追跡トークン
2つの異なる概念が重要です:- モデルのコンテキストウィンドウ: モデルごとのハード上限(モデルに見えるトークン数)
- セッションストアのカウンター:
sessions.jsonに書き込まれるローリング統計(/status やダッシュボードで使用)
- コンテキストウィンドウはモデルカタログに由来し、設定で上書きできます。
- ストア内の
contextTokensは、実行時の推定/レポート値です。厳密な保証として扱わないでください。
Compaction: What is it
コンパクションは、古い会話をトランスクリプト内の永続化されたcompaction エントリに要約し、最近のメッセージを保持します。
圧縮後、今後のターン表示:
- コンパクション要約
firstKeptEntryId以降のメッセージ
自動コンパクションが発生するタイミング(Pi ランタイム)
組み込み Pi エージェントでは、自動コンパクションは次の 2 つの場合に発生します。- オーバーフロー回復: モデルがコンテキストオーバーフローエラーを返す → コンパクション → 再試行。
- しきい値メンテナンス: 成功したターンの後、次を満たした場合:
contextTokens > contextWindow - reserveTokens
ここで:
contextWindowはモデルのコンテキストウィンドウreserveTokensは、プロンプト + 次のモデル出力のために予約されるヘッドルーム
コンパクション設定(reserveTokens, keepRecentTokens)
Pi のコンパクション設定は、Pi 設定にあります。
compaction.reserveTokens < reserveTokensFloorの場合、OpenClaw が引き上げます。- デフォルトの下限は
20000トークンです。 - 下限を無効化するには
agents.defaults.compaction.reserveTokensFloor: 0を設定します。 - すでに高い場合、OpenClaw は変更しません。
src/agents/pi-settings.ts 内の ensurePiCompactionReserveTokens()
(src/agents/pi-embedded-runner.ts から呼び出されます)。
ユーザーに見えるサーフェス
コンパクションやセッション状態は、次から観測できます。/status(任意のチャットセッション内)openclaw status(CLI)openclaw sessions/sessions --json- 詳細モード:
🧹 Auto-compaction complete+ コンパクション回数
サイレントなハウスキーピング(NO_REPLY)
OpenClaw は、ユーザーに中間出力を見せるべきでないバックグラウンドタスク向けに「サイレント」ターンをサポートします。
コンベンション:
- アシスタントは、出力の先頭に
NO_REPLYを付けて「ユーザーに返信を配信しない」ことを示します。 - OpenClawストリップ/配信レイヤーでこれを抑制します。
2026.1.10 以降では、部分チャンクが NO_REPLY で始まる場合、下書き/タイピングのストリーミング も抑制されます。これにより、サイレント操作がターン途中で部分出力を漏らしません。
コンパクション前の「メモリフラッシュ」(実装済み)
目的: 自動コンパクションが発生する前に、サイレントなエージェントターンを実行して、耐久的な状態をディスクに書き込み(例: エージェントワークスペース内のmemory/YYYY-MM-DD.md)、コンパクションで重要なコンテキストが消えないようにします。
OpenClaw は しきい値前フラッシュ アプローチを使用します。
- セッションのコンテキスト使用量を監視。
- 「ソフトしきい値」(Pi のコンパクションしきい値より低い)を超えたら、エージェントにサイレントな「今すぐメモリを書き込め」指示を実行。
NO_REPLYを使用して、ユーザーには何も表示しません。
agents.defaults.compaction.memoryFlush):
enabled(デフォルト:true)softThresholdTokens(デフォルト:4000)prompt(フラッシュターン用のユーザーメッセージ)systemPrompt(フラッシュターンに追加されるシステムプロンプト)
- デフォルトのプロンプト/システムプロンプトには、配信を抑制するための
NO_REPLYヒントが含まれています。 - フラッシュは、コンパクションサイクルごとに 1 回実行されます(
sessions.jsonで追跡)。 - フラッシュは、組み込み Pi セッションでのみ実行されます(CLI バックエンドではスキップ)。
- セッションワークスペースが読み取り専用の場合(
workspaceAccess: "ro"または"none")、フラッシュはスキップされます。 - ワークスペースのファイルレイアウトと書き込みパターンについては Memory を参照してください。
session_before_compact フックも公開していますが、OpenClaw のフラッシュロジックは現時点では Gateway 側にあります。
トラブルシューティング チェックリスト
- セッションキーが間違っていますか? セッションキーが間違っていますか? セッションキーが誤っている? /concepts/session から始め、
/status内のsessionKeyを確認してください。 - ストアとトランスクリプトが一致しませんか? ストアとトランスクリプトが一致しませんか? ストアとトランスクリプトの不整合? Gateway ホストと、
openclaw statusから取得したストアパスを確認してください。 - 圧縮スパム? 確認:
- モデルのコンテキストウィンドウ(小さすぎないか)
- コンパクション設定(モデルウィンドウに対して
reserveTokensが高すぎると、早期にコンパクションが発生する可能性があります) - tool-result の肥大化: セッションプルーニングを有効化/調整してください
- 漏れるサイレントターン? サイレントターンが漏れる? 返信が
NO_REPLY(正確なトークン)で始まっていること、かつストリーミング抑制修正を含むビルドであることを確認してください。