このページの目次
記憶と状態
API はステートレスであり、エージェントの「記憶」はすべてエンジニアリングによって構築されています。3層の記憶——セッション内での履歴再送信、compaction/context editing によるウィンドウ管理、memory tool/stores によるセッション横断的な永続化——は、それぞれ異なるメカニズムを用いて異なる問題を解決します。
概要
API はステートレスです(コンテキストエンジニアリング参照)。モデルが「知っている」情報はすべてそのリクエストに含まれており、前回のやり取りを自動的に記憶することはありません。したがって、エージェントの「記憶」はすべてエンジニアリングによって構築されたものであり、呼び出し元がライフサイクルに応じて階層化して維持する一連のメカニズムです。「どのくらい長く存続するか」という観点で3層に分けることは、この問題を明確に理解するための最も適切なフレームワークです。
flowchart TD
A["① セッション内<br/>履歴の完全再送信 (プロトコル標準機能)"] --> B["② ウィンドウ上限接近時<br/>compaction による要約 / context editing による切り捨て"]
B --> C["③ セッション横断的な永続化<br/>memory tool / memory stores (セッション外へ保存)"]
第1層は「このラウンドで覚えている」、第2層は「対話が長すぎてあふれないようにする」、第3層は「終了して再開しても覚えている」ことを意味します。3層は異なるメカニズムを用いて異なる問題を解決しており、長期稼働するエージェントではこれら3層を併用することがよくあります。それぞれの境界を理解していなければ、ツールを誤用してしまいます(例えば、セッションを跨いで設定を記憶したいのに、履歴の再送信のみを頼りにしてしまうなど)。
第1層: セッション内 —— 履歴再送信のコスト
モデルが前回のやり取りを覚えているのは、履歴全体が再度送信されるからです。この層は「無料」(プロトコル標準機能)ですが、2つのコストがかかります。
- コストは線形に増加: 毎回履歴全体を再送信するため、入力トークンはラウンド数に応じて累積します。
- いずれあふれる: 履歴が無限に長くなれば、いつかウィンドウ上限に到達します(
stop_reason: "model_context_window_exceeded")。
prompt caching を使用すると、再送信される安定したプレフィックス部分が約 0.1x のコストで処理されます(コンテキストエンジニアリング参照)。これによりコストが軽減されますが、あふれ問題の解決には第2層が必要です。
第2層: ウィンドウ接近時の管理 —— 圧縮 vs 切り捨て
相反する2つのAPIプリミティブがあります。
| compaction (圧縮) | context editing (切り捨て) | |
|---|---|---|
| 動作 | 過去の履歴を要約して compaction block に変換 | 古い tool_result / thinking を削除 |
| 情報 | 要約して保持 | 直接削除 |
| Beta | compact-2026-01-12 | context-management-2025-06-27 |
| 重要な注意点 | response.content (compaction block を含む) を塊で返す必要がある。テキストのみを返すと状態が失われる | ブロックのみを削除し、会話構造は変更しない |
覚え方: compaction は「要約」、context editing は「削除」です。これらはどちらも単一セッション内でウィンドウを空けるものであり、セッション横断的な問題は解決しません。プロセスを終了すれば、それらは消えてしまいます。
第3層: セッション横断的な永続記憶
「終了して再開しても覚えている」ためには、重要な情報をセッション外に書き込む必要があります。2つのパスがあり、自己管理型かマネージド型かによって選択します。
パス A: memory tool (バックエンド自己管理)
ツール {"type": "memory_20250818", "name": "memory"} を宣言すると、モデルは /memories ディレクトリの読み書き能力、つまり6つのコマンドを獲得します。
| コマンド | 意味 |
|---|---|
view | ファイルの読み取り / ディレクトリのリスト表示 |
create | ファイルの新規作成 / 上書き |
str_replace | ファイル内の文字列を1箇所置換 |
insert | 指定行の後に挿入 |
delete | ファイルの削除 |
rename | ファイル名の変更 / 移動 |
ストレージバックエンドは実装側で自行実装します(ローカルFS、オブジェクトストレージ、DBなど)。モデルはコマンドを発行するだけで、実装側が実行して tool_result を返します。Python/TypeScript SDK にはスケルトンコード(BetaAbstractMemoryTool / betaMemoryTool + ハンドラ)が用意されており、view/create/str_replace/... のストレージロジックを埋めるだけです。モデルは「ユーザーの設定」「プロジェクトの規約」「失敗した経験」などをファイルに書き込み、次のセッションではまず view で読み戻します。
このアプローチは、このセッションでも実際に使用されています。永続記憶は
~/.claude/projects/.../memory/下に配置され、1事実1ファイル +MEMORY.mdインデックスという構成です。MEMORY.mdは常駐し、各事実は必要に応じて読み込まれます。これが memory tool パターンを具体化したものです(今ラウンドで「本サイトのビルド方法」という事実を更新しました)。
パス B: Managed Agents memory stores (マネージド)
ワークスペースレベルの永続記憶庫。オブジェクトモデルは3層構造です。
メカニズムの要点:
- ファイルシステムとしてマウント: store は FUSE を介してコンテナ
/mnt/memory/<store>/にマウントされ、エージェントは通常のファイルツールで読み書きできます。システムプロンプトには、マウントが存在することを伝える説明文が自動的に注入されます。 - アクセス制御:
access: "read_only" | "read_write"。ファイルシステム層で強制されます。 - 楽観的並行制御:
memories.updateはprecondition: {type:"content_sha256", ...}をサポートしており、一致しない場合は 409 を返します。これにより、読み取り-変更-書き込みの処理で互いに上書きされません。 - 監査とロールバック: 変更のたびに
memory_versionが生成され、リスト表示・取得・redact(内容を書き換えてアクターとタイムスタンプのみを残し、機密情報漏洩/PII 削除に使用)が可能です。 - 1セッションあたり最大8つの store をマウントできます(「共有読み取り専用リファレンス + ユーザーごと読み書き」のように階層化できます)。
記憶 vs コンテキスト: どちらを使うべきか?
初心者は「長いコンテキスト」を「記憶」と混同しがちです。区別しましょう。
- コンテキスト (context): 1つのリクエストに詰め込まれるもので、1ラウンド終了とともに消滅します(再送信しない限り)。「ウィンドウ」です。
- 記憶 (memory): リクエストやセッションを跨いで存続するもので、セッション外に書き込まれます。「ハードディスク」です。
「このラウンドで見たい」場合はコンテキストを使用(RAG でチャンクをウィンドウに配置)、これ「以降も覚えておきたい」場合は記憶を使用(ファイルに書き込み)します。compaction/context editing はコンテキスト層の管理であり、記憶そのものではありません。
デザインとセキュリティ
優れた記憶設計(およびこのセッションでの記憶の書き方):
- 1つの経験につき1ファイル。先頭に1行の要約を置き、関連性の判断を容易にします。
- 自明でない情報のみ保存。コードや履歴に既に存在する情報は保存しない(ノイズになるため)。「なぜそうなのか」「どこでつまずいたか」を保存します。
- 更新時は上書き、不要時は削除。重複を積み重ねず、既存のエントリを更新します。古くなったものは削除します。
セキュリティのレッドライン:
- パスは必ず検証する: モデルが渡す
pathは信頼できない出力です。正規パス(realpath/Path.resolve())に解析し、記憶のルートディレクトリ内にあることを確認します。..、シンボリックリンク、絶対パスによる逸脱、URL エンコードされたトラバーサル(%2e%2e%2f)を拒否します。生の path で直接open()しないでください。 - シークレットを絶対に保存しない: API キー、パスワード、トークンは記憶に保存しません。PII には注意(GDPR/CCPA 対応)。memory stores の
redactは事後の対処手段です。 - マルチテナント分離: ユーザーごとに独立した記憶ディレクトリ + 認証。参考実装には内蔵のアクセス制御はありません。
ベストプラクティス
- 「存続期間」に応じて3層を使い分ける。 セッション内では履歴の再送信、ウィンドウ接近時には compaction/context editing、セッション横断では memory tool/stores を使用——履歴の再送信で「設定を記憶しよう」としてはいけません。
- 1つの経験につき1ファイル、先頭に1行の要約。 関連性の判断を容易にし、1つの
MEMORY.mdインデックスを常駐させ、各エントリは必要に応じて読み込む(このセッションでもそのようにしています)。 - 自明でない情報のみ保存。 コードや履歴に既に存在する情報は保存しない(ノイズになるため)。「なぜそうなのか」「どこでつまずいたか」「決定した規約」を保存します。
- 更新時は上書き、不要時は削除。 重複を積み重ねず、既存のエントリを更新します。古くなったものは削除します。
- パスは正規化してから境界を検証する。 モデルが渡す
pathは信頼できないため、resolve()して記憶のルート内にあることを確認し、../シンボリックリンク/URL エンコードされたトラバーサルを拒否します(セキュリティと保護参照)。 - シークレットは記憶に保存せず、マルチテナントはユーザーごとに分離する。 参考実装には内蔵のアクセス制御はありません。PII には注意し、
redactは事後の対処手段です。
トレードオフと失敗パターン
- 記憶に何でも詰め込む: ノイズになり、検索精度が低下する → 高価値で自明でない項目のみを保存する。
- 記憶をトランザクションデータベースのように扱う: 記憶はモデルが読むためのノートである → 同時書き込みには
content_sha256による楽観的ロック(memory stores)または自己管理型のバージョン管理を使用する。 - セッション横断でのデータ混線: マルチテナントが分離されていない → ユーザーごとにディレクトリを分け、認証を行う。
- パスの検証漏れ: ディレクトリトラバーサルにより任意のファイルが読み書きされる → 正規化 + 境界チェックを行う。
参考
- Anthropic 公式ドキュメント: Memory Tool、Managed Agents Memory Stores (platform.claude.com)
Keywords: stateless, 会话历史, prompt caching, compaction, context editing, model_context_window_exceeded, persistent memory, memory tool, memory_20250818, /memories, view/create/str_replace/insert/delete/rename, BetaAbstractMemoryTool, memory stores, memstore, FUSE mount, access, content_sha256, precondition, redact, memory version, path traversal, secret, PII, 多租户隔离