レッスン3: 外部メモリ: ファイルと検索
学習目標:
- セッション間で存続しなければならないメモリが、ウィンドウの外、ファイルに書き込まれなければならない理由を説明する
- CLAUDE.mdのような人間が書いたメモリファイルと、Autoメモリのようなモデルが書いたメモリファイルを区別する
- 「オンデマンド検索」と「すべてを前もってロードする」のトレードオフを述べる
- メモリファイルを読み書きするツールに安全なパス境界チェックを追加する
前提: レッスン2を終え、コンパクションとツール結果クリアリングの違いを理解していること | 前: レッスン2 << | 次: レッスン4 >>
セッションが終了すると、ウィンドウ内のすべてが消える
レッスン2は未解決の問題で終わりました: スケジュールアシスタントにユーザーが先週「私は辛い食べ物を食べない」と伝え、今週ユーザーが空のウィンドウで新しい会話を開くと — エージェントはその文が言われたことを全く知りません。
切り詰め、コンパクション、ツール結果クリアリングではこれを修正できません。これら3つはすべて「この1つの会話内でスペースが不足した」を扱います。ここでの問題は異なります: この会話には、最初のターンから先週のコンテンツが全く含まれていませんでした。公式Cookbookは率直に線を引きます — クリアリングとコンパクションは両方とも現在のコンテキストで動作します; 新しいセッションが開始してウィンドウが空のときは、どちらも役に立ちません。メモリがその問題を解決します1。ウィンドウは、コンテナとして、この1つのセッションが続く限りしか存続しません。セッションを閉じると、他の場所に移動されなかったウィンドウ内のものは本当に消えます。
このセッションを超えて情報を生かし続ける唯一の方法は、セッションが終了する前に、ウィンドウの外のどこか — 外部メモリに書き込むことです: この会話のライフサイクルに縛られていないストレージ、通常は単にディスク上のファイルです。次のセッションが開始すると、そのファイルを読み戻し、その内容を新しいコンテキストウィンドウにロードします。
CLAUDE.md: 人間が書き、毎回完全にロードされる
外部メモリの最も直接的なパターンは、人間がメモリファイルを維持し、プロジェクトに保存し、毎セッションの開始時に完全に読み込むことです。Claude CodeのCLAUDE.mdは代表的なケースです: 公式ドキュメントは、CLAUDE.mdファイルが毎セッションの開始時にコンテキストウィンドウにロードされ、会話自体と並んでトークンを消費し、推奨されるサイズターゲットは各ファイルを200行未満に保つことです — ファイルが長いほど、より多くのコンテキストを消費し、指示への遵守度が低下します。2 200行はソフトな推奨であることに注意してください; 実際のハードリミットは4 MiBです: それより大きいCLAUDE.mdは完全にスキップされます。2
このファイルの1つの詳細は注目に値します: 公式ドキュメントは、CLAUDE.md内のブロックレベルのHTMLコメントは、コンテンツがエージェントのコンテキストに注入される前に削除されることを説明しています。2 言い換えれば、<!-- -->の内側に書くものは、人間がファイルを開いたときには表示されますが、エージェントが読むバージョンにはそのコメントが含まれません — これにより、人間は「エージェントのトークン予算を消費せずに自分にメモを残す」方法を得ます。
CLAUDE.mdには、レッスン2のコンパクションに直接結びつく別の特性があります: ドキュメントは、プロジェクトルートのCLAUDE.mdがコンパクションを生き延びることを指摘しています — /compactの後、Claudeはそれをディスクから再読み取りし、セッションに再注入します。2 言い換えれば、このようなファイルはコンパクションによって「偶然保存される」のではありません; それは別途再読み取りされ再注入されます — そのコンパクションがその内容を要約に保持したかどうかにまったく依存しません。
Autoメモリ: モデルが書き、オンデマンドで検索される
CLAUDE.mdは人間が書き、毎回完全にロードされます。補完的なパターンがあります: 会話が進むにつれて、モデルに覚えておく価値のあるものを書き留めさせ、それを独自のメモリファイルに保存します — Claude CodeはこのメカニズムをAutoメモリと呼びます。CLAUDE.mdとの分業は補完的であり、比較表は違いを明確に示します: CLAUDE.mdはあなたが書き、AutoメモリはClaudeが書きます。2
モデル自身が書くメモリは通常、2つの層に分かれています: インデックスファイル(たとえば、MEMORY.md)と、トピックごとに分類された特定のメモリファイルの山です。インデックスファイルも無制限にロードされるわけではありません — ドキュメントが与えるルールは: 毎会話の開始時に、MEMORY.mdの最初の200行、または最初の25KB、どちらか先に達した方のみがロードされます; その閾値を超えるコンテンツはセッション開始時にロードされません。2
これがオンデマンド検索です: セッションの開始時に、エージェントはインデックスエントリの要約(「このトピックの詳細なノートはあるファイルにある」のようなもの)のみを見て、各特定のメモリファイルの完全な内容は見ません。ドキュメントは直接的です: トピックファイルは起動時にロードされません; Claudeは情報が必要なときに標準のファイルツールでそれらをオンデマンドで読み取ります2。現在のタスクが実際に特定のトピックを必要とするときにのみ、その特定のメモリファイルのコンテンツがこのラウンドのコンテキストウィンドウに引き込まれます。
並べて見ると、CLAUDE.mdとAutoメモリはメモリの2つの異なる次元を処理します:
- CLAUDE.md — 人間がキュレートし、サイズ管理された、毎回適用されるルールと規約; 「プロジェクトがどのように機能することになっているか」といった安定した情報に適合し、前もって完全にロードされます。
- Autoメモリ — 数が多く、特定のタスクにのみ重要な特定の詳細; オンデマンド検索に適合し、このタスクが必要としないメモリにウィンドウ予算が浪費されません。
両方とも外部メモリです。唯一の違いは「誰がそれを書くか」と「いつそれがロードされるか」です — これはレッスン2のメンタルモデルと呼応しています: メモリの目的は、セッション間で存続するように情報をウィンドウの外に移動することであり、その情報が前もって完全にロードされるか、オンデマンドで検索されるかは、それがどれだけ安定しているか、どのくらいの頻度で使用されるかによって決まります。
メモリファイルの読み書きに安全な境界を追加する
CLAUDE.mdのような人間が書いたファイルでも、Autoメモリのようなモデルが書いたファイルでも、エージェントがメモリファイルを読み書きするためのツールを持つと、具体的なエンジニアリング上の質問に直面します: そのツールは、プロジェクトディレクトリの外のファイルを読み書きするように説得できますか?
文字列プレフィックスマッチだけを行うパスチェックは、「メモリディレクトリをエスケープする」リクエストをブロックするように見えますが、古典的な穴があります。メモリルートが/project/memoryの場合、単純なstartsWith("/project/memory")チェックは、/project/memory-evilのようなパスも通します。なぜなら、それはその文字列で始まるからです — それがメモリルートの外にある完全に異なるディレクトリであるにもかかわらず。安全な方法は、パスがルートと完全に等しいか、「ルート + パス区切り文字」で始まることを要求することです:
組み合わせabs === MEMORY_ROOT || abs.startsWith(MEMORY_ROOT + path.sep)が、「ルート自体と等しいパス」または「ルート + セパレータで始まるパス」のみが通過することを実際に保証するものです — /project/memory-evilは、どちらの条件も満たさないため、/project/memory内のパスと間違えられることはありません。このパターンは、レッスン6で永続的なメモリレイヤーの読み書きツールを構築するときに直接再利用され、レッスン5では、この境界チェックが無力である場合に、メモリファイルがどのような攻撃対象になるかを明確にします。
まとめ
- セッションが終了すると、外に移動されなかったウィンドウ内のものは永久に失われます; セッション間で情報を保持するには、セッションが終了する前にウィンドウの外の外部メモリに書き込まなければなりません
- CLAUDE.mdは人間が書き、毎セッション完全にコンテキストにロードされ、公式サイズターゲットは200行です(ハードリミット4 MiB、より大きいファイルは完全にスキップされます); ブロックレベルのHTMLコメントは注入前に削除され、プロジェクトルートのCLAUDE.mdは
/compactの後に再読み取りされ再注入されます2
- Autoメモリはモデルが書き、インデックスファイルと特定のトピックファイルに分割されます; インデックスは最初の200行または25KBのみをロードし、トピックファイルは起動時にロードされず、必要なときにオンデマンドで読み取られます2。そのため、使用されないメモリにウィンドウ予算が浪費されません
- 2つは補完的です: CLAUDE.mdは毎回役立つ安定したルールに適合します; Autoメモリは特定のタスクにのみ必要な大量の詳細に適合します
- メモリファイルの読み書きツールは、安全なパス境界チェックを行う必要があります; 組み合わせ条件
abs === ROOT || abs.startsWith(ROOT + path.sep)は両方の半分が必要です。単独のstartsWithチェックには同じプレフィックスバイパスホールがあるためです
>> レッスン4: 構造化された状態: エージェントがタスクの進捗をどう記憶するか