OpenClaw エージェントのメモリデバッグに 5 日間費やして学んだこと

@code_rams
英語5 か月前 · 2026年2月22日
212K
859
78
40
2.7K

TL;DR

OpenClaw AI エージェントのメモリを最適化するための包括的なガイドです。コンテキスト圧縮の修正、ハイブリッド検索の実装、エージェントが長期的な知識を保持するための書き込みルールの確立について解説します。

私のエージェントの名前は Chiti です。Telegram 上で動作し、2 つの SaaS 製品のカスタマーサポートを担当し、ツイートの下書き、請求書の管理、そしてタイムゾーンを越えた共同創業者との調整を行います。これは私が持っている最もジュニア社員に近い存在です。

そして数週間、それは物事を忘れ続けました。

さりげなくではなく、はっきりと。私は 1 時間かけて毎日の cron ジョブを設定し、モデルを切り替えると、次のセッションで Chiti はまるで一度も話したことがないかのように振る舞いました。2 日前の決定を参照すると、空白の表情を返されました。タスクを続けるように頼むと、最初からやり直しました。

そこで私は機能開発をやめ、時間があるときに 5 日間かけて、ひたすらメモリ修正に取り組みました。これが私が見つけたすべて、壊したすべて、そして実際にうまくいったすべてです。

1 日目: 長い会話の後、エージェントはすべてを忘れる

最初の問題は説明するのは簡単でしたが、診断するのは苦痛でした。

長い会話の後、Chiti は以前のコンテキストを失い始めました。徐々にではなく、突然消え去るのです。20 メッセージ前に伝えたことは消え、セッションの最初に決めたことはなかったことになりました。

原因は compaction(圧縮)でした。会話がコンテキストウィンドウを埋めると、OpenClaw は古いメッセージを要約に圧縮して新しいメッセージのためのスペースを確保します。要約は大筋を捉えますが、詳細は失われます。名前、数字、正確な決定は消えてしまいます。

これは設計上の仕様です。コンテキストウィンドウは有限です。しかしデフォルトの動作はすべてを平等に扱うため、メッセージ #3 で慎重に作成した指示も、メッセージ #7 の何気ない雑談と同じ扱いを受けてしまいます。

私がやったこと:

compaction の前にメモリフラッシュを有効にしました。これにより、圧縮が実行される前にエージェントが重要なコンテキストをディスクに書き込むよう指示します。

json
1{
2 "compaction": {
3 "memoryFlush": {
4 "enabled": true,
5 "softThresholdTokens": 4000
6 }
7 }
8}

セッションがコンテキスト制限に近づくと、OpenClaw はサイレントターンをトリガーし、compaction がそれらを消去する前に、エージェントに永続的な事実を memory/YYYY-MM-DD.md に保存するよう促します。エージェントは重要なものを書き込み、compaction が実行され、コンテキスト要約が失っても重要なものはディスクに残ります。

学んだこと:

compaction は敵ではありません。compaction 中の情報損失が敵です。修正方法は、記憶に値するものを圧縮が触れる前にファイルに書き込むことです。コンテキストウィンドウにしかなければ一時的なものですが、ディスクにあれば残ります。

2 日目: 検索がガラクタを返す

日々のログが蓄積し MEMORY.md が大きくなるにつれ、エージェントが実際に物事を見つけられるようにする必要がありました。組み込みのメモリ検索は無関係な結果を返したり、明らかな一致を見逃したりしていました。

問題は検索バックエンドでした。OpenClaw のデフォルトの SQLite ベースの検索は、ベクトル埋め込み(意味的類似性)を使用して関連チャンクを見つけます。広いクエリには有効ですが、完全一致には弱いです。特定のクライアント名を検索しても、似たような言葉を使っているまったく別のトピックの結果が返ってきました。

私がやったこと:

メモリ検索バックエンドを QMD に切り替えました。QMD は BM25(キーワード一致)とベクトル埋め込み、リランカーを組み合わせています。そのため「Charles payment failure」を検索すると、その正確な単語を含む結果と意味的に関連する結果の両方を見つけ、関連性でリランクします。

また、QMD のパスに learnings フォルダを含めました:

json
1{
2 "memory": {
3 "qmd": {
4 "paths": "paths": [
5 {
6 "path": "/Users/ramya/clawd",
7 "name": "memory-root",
8 "pattern": "MEMORY.md"
9 },
10 {
11 "path": "/Users/ramya/clawd",
12 "name": "memory-alt",
13 "pattern": "memory_alt.md"
14 },
15 {
16 "path": "/Users/ramya/clawd/memory",
17 "name": "memory-dir",
18 "pattern": "**/*.md"
19 },
20 {
21 "path": "/Users/ramya/clawd/learnings",
22 "name": "learnings",
23 "pattern": "**/*.md"
24 }
25 ]
26 }
27 }
28}

学んだこと:

純粋な意味検索は理論上は良さそうですが、固有名詞、特定の数字、正確なフレーズでは失敗します。ハイブリッド検索(キーワード + ベクトル + リランキング)は、実際のエージェントメモリにおいて大幅に優れています。エージェントがファイル内にあるとわかっているものを見つけられない場合、ボトルネックはファイル自体ではなく、検索バックエンドにある可能性が高いです。

3 日目: エージェントは見つけるが、使わない

これが最もフラストレーションのたまる日でした。検索が機能していることを確認し、手動でクエリを実行して正しい結果を得られました。しかし実際の会話中、Chiti はメモリに明らかに存在する関連コンテキストを取得しませんでした。

問題は、取得が自動ではないことでした。エージェントは検索するかどうかを自分で決める必要があります。そして会話が適切なきっかけをトリガーしなければ、エージェントは情報を探しに行きません。

私がやったこと:

ブートシーケンスに明示的な取得指示を追加しました。エージェントが必要なときに検索することを期待するのではなく、いつ検索すべきかを指示しました:

markdown

開始する前に:

  • 関連するコンテキストのために日々のログを検索する
  • このタイプのタスクに関するルールを LEARNINGS.md で確認する
  • クライアントが言及された場合、その履歴を検索する

また、取得テストを構築しました。日々のログに特定のマーカーを仕掛けます。「MARKER: 2026-02-20 — コードがプッシュされたと主張する前に必ず git status を確認することを忘れないでください」など。その後、待機し、新しいセッションを開始して、「昨日のマーカーは何ですか?」と尋ねます。エージェントが見つければ取得は機能しています。見つけなければ何かが壊れています。

学んだこと:

「情報が存在する」ことと「エージェントがその情報を使用する」ことには違いがあります。両方が必要です。検索インフラは前者を処理します。ブート指示と取得習慣は後者を処理します。両方を別々にテストしてください。

4 日目: compaction に耐性を持たせる

これまでにメモリフラッシュ、ハイブリッド検索、取得指示を導入しました。しかし特定のシナリオでコンテキストを失い続けました: compaction が複数回実行される非常に長いセッションです。

問題は、メモリフラッシュが compaction サイクルごとに一度しかトリガーされないことでした。セッションが 2 回または 3 回の compaction を経験するほど長い場合、最初の compaction だけがフラッシュ処理を受け、それ以降はリスクにさらされました。

私がやったこと:

compaction と連携するコンテキストプルーニングを設定しました:

json
1{
2 "contextPruning": {
3 "mode": "cache-ttl",
4 "ttl": "6h",
5 "keepLastAssistants": 3
6 }
7}

これにより、6 時間経過した古いコンテキストは積極的に刈り取られ、最後の 3 つのアシスタント応答は保持されます。メモリフラッシュと組み合わせることで、エージェントは重要なものを早期にディスクに書き込み、オーバーフローを引き起こす前に古いコンテキストがクリーンアップされます。

また、MARKER テストプロトコルを追加しました: 重要な設定変更のたびに、日々のログにマーカーを仕掛け、compaction の境界を越えて取得をテストします。マーカーが生き残れば変更は有効、そうでなければ何かが壊れています。

学んだこと:

長いセッションこそ、メモリシステムが本当に試される場です。短い会話では compaction に達することはほとんどありません。コンテキストを失い、理由がわからないのは、2 時間の集中作業セッションです。メモリシステムは素早いチャットだけでなく、負荷がかかった状態でテストしてください。

5 日目: システムプロンプトが 28% も肥大化していた

この日、すべてがつながりました。/context detail を実行し、数字を見つめました。

私のエージェントは、メッセージを読む前に 11,887 トークンのシステムプロンプトをロードしていました。51 のスキル、そのうち 20 は一度も使ったことがありません。MEMORY.md は 200 行の会社 wiki で、毎セッションロードされていました。さらに、BOOT.md(OpenClaw が認識すらしない)と AGENTS.md の 200 行も深いところに埋もれた別のブートシーケンスの 2 つが競合していました。

最悪なのは、モデルを切り替えるたびに Chiti がすべてを忘れることです。引き継ぎプロトコルも、現在のコンテキストの書き戻しもありません。ただ消えるだけです。

根本原因:

OpenClaw は新しいセッションごとに以下のファイルを自動読み込みします: AGENTS.md、SOUL.md、TOOLS.md、IDENTITY.md、HEARTBEAT.md、MEMORY.md。

それ以外のファイル(LEARNINGS.md、日々のログ、ドキュメント、参照ファイル)は、エージェントがツールを使って自分で読み込む必要があります。それらのファイルを読み込む指示が自動読み込みファイル(特に AGENTS.md)のいずれかに含まれていなければ、エージェントはそれらを決して見ることがありません。

私の BOOT.md には完全なブートシーケンスがありました。しかし OpenClaw は BOOT.md を自動読み込みしません。そのため、指示はそこに置かれたまま、読まれることなく、何の役にも立ちませんでした。

私がやったこと:

完全な監査とクリーンアップを実施しました:

  1. ブートシーケンスを AGENTS.md の先頭に移動(スタートアップ指示のための信頼できる唯一の場所)
  2. BOOT.md を削除(OpenClaw が認識しない)
  3. BOOTSTRAP.md を削除(一度限りのオンボーディングファイル。すでに完了済みで、毎セッション 361 トークンを消費していた)
  4. MEMORY.md を 200 行から 90 行にスリム化し、参照ドキュメントを docs/ フォルダに移動
  5. 毎セッション 3,000 トークンを消費していた未使用のマーケティングスキル 20 個を削除
  6. 書き込み規律を追加: すべてのタスクはその結果をログに記録し、すべてのミスはルールになる
  7. 引き継ぎプロトコルを追加: モデル切り替えまたはセッション終了の前に、エージェントが現在のコンテキストを日々のログに書き込む

ブートシーケンスは次のようになりました:

markdown

何よりも前に:

  1. USER.md を読む
  2. learnings/LEARNINGS.md を読む
  3. memory/YYYY-MM-DD.md(今日と昨日)を読む
  4. MEMORY.md(メインセッションのみ。グループでは決して読まない)
  5. PROTOCOL_COST_EFFICIENCY.md を読む
  6. 表示: LOADED: USER | LEARNINGS | DAILY | MEMORY | PROTOCOL

書き込み規律:

markdown

すべてのタスクの後:

  1. 決定 + 結果をログに記録 → memory/YYYY-MM-DD.md
  2. ミスがあれば learnings/LEARNINGS.md に追記
  3. 重要なコンテキストがあれば MEMORY.md を更新(ハートビートレビュー中のみ。タスク中は決して直接更新しない)

引き継ぎプロトコル:

markdown

セッション終了またはモデル切り替えの前:

HANDOVER セクションを memory/YYYY-MM-DD.md に書き込む:

  • 議論された内容
  • 決定された内容
  • 保留中のタスク(正確な詳細を含む)
  • 残りの次のステップ

結果:

  • System prompt: 11,887 → 8,529 トークン
  • Skills: 51 → 32
  • Session tokens: 18,280 → 14,627
  • 28% 軽量化。同じエージェント。同じモデル。ノイズが減っただけ。

学んだこと:

本当の修正はファイルを追加することではありませんでした。何もしていないファイルを削除することでした。システムプロンプト内のすべてのトークンは、エージェントがすべてのメッセージで引きずるオーバーヘッドです。未使用のスキル、肥大化したメモリファイル、システムが読まないファイル — それらはすべて静かに積み上がります。

1 日目に知っておきたかったルール

5 日間にわたって壊しては直した結果、OpenClaw メモリを設定する人に伝えたいルールを以下にまとめます。

1. 自動読み込みされるのはこのファイルだけ: AGENTS.md、SOUL.md、TOOLS.md、IDENTITY.md、USER.md、HEARTBEAT.md、MEMORY.md。

それ以外はすべて、AGENTS.md 内で明示的な読み込み指示が必要です。ブートシーケンスに含まれていなければ、エージェントはそのファイルを見ません。BOOT.md は OpenClaw では実際には機能しません。私は数週間それを使っていましたが、何の効果もありませんでした。

2. ブートシーケンスは AGENTS.md の先頭に置く。

途中でも、末尾でもありません。最上部です。自動読み込みファイルはシステムプロンプトに注入されるため、ブート指示はエージェントが最初に処理するものである必要があります。

3. 書き込み規律は読み込み規律よりも重要。

ほとんどの人はエージェントが読むためのファイルを設定しますが、書き戻しを強制しません。エージェントが決定、結果、ミスをディスクに記録しなければ、それらはコンテキストウィンドウにしか存在しません。そしてコンテキストウィンドウは compaction されます。書き戻しこそが、一時的なコンテキストを永続的なメモリに変える方法です。

4. タスク中に MEMORY.md に直接書き込んではいけない。

日々のログは生の追記専用です。MEMORY.md は厳選された長期メモリです。エージェントに何でも MEMORY.md にダンプさせると、数週間で 200 行の混乱したファイルになります。定期的なレビュー(ハートビートまたは cron)の際に、最近の日々のログから洞察を抽出して MEMORY.md を厳選してください。これは、エージェントがまさにそのようなことをして MEMORY.md を役立たずにするまで気づかなかった別の OpenClaw ユーザーから学びました。

5. LEARNINGS.md は最も過小評価されているファイル。

エージェントが犯すすべてのミスは、1 行のルールになるべきです。「git status を確認せずにコードがプッシュされたと主張しない」「グループチャットで MEMORY.md 全体を読まない」「スケジュール前に必ずユーザーのタイムゾーンを確認する」。これらのルールは積み重なります。数週間後には、エージェント自身の失敗から構築された独自の運用マニュアルができます。

6. ストレージだけでなく、取得もテストする。

情報の保存と取得は別の問題です。ファイルがインデックスされ検索可能でも、エージェントがそれらを探すことを知らなければアクセスされないことがありました。マーカーを仕掛け、セッションをまたいでテストし、モデル切り替えをまたいでテストしてください。昨日保存したものをエージェントが見つけられなければ、ストレージは意味がありません。

7. 引き継ぎプロトコルがモデル切り替えの解決策。

OpenClaw エージェントはモデルを切り替えるとすべてのコンテキストを失います。新しいモデルは新しいコンテキストウィンドウで開始し、自動読み込みファイルだけを見ます。切り替え前に現在の状態を日々のログにダンプする引き継ぎプロトコルがなければ、新しいモデルは何が起こっていたのか全くわかりません。これは数週間にわたって私の最大の悩みの種でした。

8. /context detail を定期的に実行する。

このコマンドは、何がトークンを消費しているかを正確に表示します。インストールしたことを忘れたスキル、気づかないうちに大きくなったファイル、一度も使ったことのないツール。私は 20 個の未使用スキルが毎セッション 3,000 トークンを燃やしているのを見つけました。それは、一度も触ったことのない機能のために、すべてのメッセージで 3,000 トークンのオーバーヘッドがあるということです。

9. ハイブリッド検索は純粋な意味検索より優れている。

BM25(キーワード)+ ベクトル(意味)+ リランキングは、ベクトル単独よりもはるかに優れた結果をもたらします。クライアント名、特定の数字、正確なフレーズ — 意味検索はこれらを見逃します。キーワード検索がそれらを捉えます。両方を使いましょう。

10. compaction は敵ではない。書き込まれていないコンテキストが敵だ。

私は compaction と戦うのに何日も費やしましたが、修正はよりシンプルだと気づきました: 重要なものはすべて compaction が実行される前にファイルに書き込まれるようにすることです。メモリフラッシュがこれを自動的に処理します。ディスクにあれば compaction を乗り越えます。会話の中にしかなければリスクがあります。

現在のセットアップ

参考までに、現在のワークスペースは次のようになっています:

workspace/

├── AGENTS.md(ブートシーケンス + 書き込み規律 + 引き継ぎプロトコル)

├── SOUL.md(個性と行動)

├── IDENTITY.md(名前、役割)

├── USER.md(所有者情報)

├── TOOLS.md(ツール使用ガイドライン)

├── HEARTBEAT.md(自律チェックイン動作)

├── MEMORY.md(厳選された長期メモリ、約 90 行)

├── PROTOCOL_COST_EFFICIENCY.md

├── learnings/

│ └── LEARNINGS.md(ミスからのルール)

├── memory/(日々のログ: YYYY-MM-DD.md)

├── docs/(参照ドキュメントを MEMORY.md から移動)

│ ├── tweetsmash-arch.md

│ ├── knowledge-transfer.md

│ ├── infrastructure.md

│ └── group-chat-rules.md

└── skills/(32 スキル、51 から削減)

System prompt: 8,529 トークン。Session tokens: 14,627 / 200,000 コンテキストウィンドウ(7.3%)。エージェントは起動し、必要なものを読み、学んだことを書き、モデル切り替え前にコンテキストを引き継ぎます。

ここにたどり着くまでに 5 日かかりました。そのほとんどは、ファイルが多いほどメモリが良くなるという前提を捨てることでした。そうではないのです。規律がすべてです。私の実験はまだ続いています。

私は共同創業者と一緒にソーシャルメディアブックマークツールの TweetSmash と LinkedMash を開発しています。OpenClaw エージェントを本番環境で運用する際に学んだことを X で共有しています: @code_rams

YouMindで再制作

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
クリエイターのために

あなたの Markdown をきれいな 𝕏 記事に

自分の長文を投稿するとき、画像・表・コードブロックを 𝕏 向けに整形するのは手間がかかります。YouMind は Markdown 全体を、そのまま投稿できるきれいな 𝕏 記事に変換します。

Markdown → 𝕏 を試す

解読すべきパターンをもっと

最近のバイラル記事

バイラル記事をもっと見る