Agent Mentor Learn
エージェントメモリと状態 · 第 1 回 / 全 6 回

レッスン1: コンテキストウィンドウはエージェントが持つすべてのメモリ

学習目標:

  • 「エージェントが以前の会話を覚えている」というのが錯覚である理由を説明する
  • 単一リクエストのコンテキストウィンドウに何がカウントされ、何がカウントされないかを挙げる
  • コンテキストロットとは何か、そしてウィンドウが大きいからといって自動的に作業しやすくなるわけではない理由を説明する
  • APIコールがステートレスかどうかを判断し、それがメモリにとって何を意味するかを説明する

前提: このシリーズの最初の4つのコース(エージェントツール呼び出し: エージェントに実際に行動させるのツール呼び出しのラウンドトリッププロトコルを含む)| 次: レッスン2 >>

記憶しているように見える会話

カスタマーサポートエージェントとやり取りしているとします:

text
User: My name is Sarah, order number ORD-2026-8842.Agent: Got it, Sarah. Let me check on ORD-2026-8842 ... it's out for delivery, expected tomorrow.User: What about the one I bought last time?Agent: Do you mean ORD-2026-8842? That one arrives tomorrow — if you're asking about a different order, could you give me the order number?

2ターン目で、エージェントは明らかにあなたの名前がSarahであることを「覚えて」おり、ORD-2026-8842について尋ねたことを覚えています。まるでそれらの詳細をどこかにファイルし、次回取り出せるかのように見えます。

真実ははるかにシンプルです。リクエストのたびに、あなたのコードは会話の開始から現在までのすべてのメッセージを再パッケージ化し、そのままモデルに送信します。1 モデルは「あなたの名前がSarahであることを覚えている」わけではなく、「ユーザーが言った: 私の名前はSarah」を毎回ゼロから再読しているのです。前のターンのすべての文、すべてのツール呼び出しは、あなた自身のコードによってこのターンのリクエストに配置されなければなりません。モデル自体は何も保存しません。

APIコールはステートレス

さらに率直に言えば: あるコールと次のコールの間で、サーバー側で静かに保持されるものは何もありません。このリクエストが処理されると、それらのトークンはモデルの視界から消えます。次のリクエストが何も運ばなければ、モデルは白紙の状態を見ます — あなたの名前すら知りません。

メモリのように感じられる理由は、ホストアプリケーションがあなたのために雑用をこなすからです: 履歴メッセージの完全な配列を再送信します。これはモデルの能力ではありません; それは成長し続けるあなたのコード内の配列です。この性質には名前があります — ステートレスです。公式ドキュメントの言葉を借りれば: "The Messages API is stateless, which means that you always send the full conversational history to the API."1 (Messages APIはステートレスであり、つまり常に完全な会話履歴をAPIに送信する必要があるということです) サーバーはリクエスト間でセッションごとのプライベートな状態を保持しません; すべての「メモリ」はクライアント自身が運搬し再送信しなければなりません。

実際にコンテキストウィンドウに含まれるもの

モデルが見ることができるすべてのものは、コンテキストウィンドウと呼ばれるコンテナ内に存在します。ドキュメントは、リクエスト内のすべてがカウントされることを明示しています: "Everything in the request counts toward the context window: the system prompt, every message in messages (including tool results, images, and documents), and your tool definitions. The output Claude generates for the turn, including its extended thinking, counts too."2 (リクエスト内のすべてがコンテキストウィンドウにカウントされます: システムプロンプト、messagesの中のすべてのメッセージ(ツール結果、画像、ドキュメントを含む)、そしてツール定義です。Claudeがそのターンで生成する出力も、拡張思考を含めてカウントされます)

このリクエストでは、systemtools、そしてmessages配列内の3つのメッセージすべてがコンテキストウィンドウにカウントされます。モデルの返信が生成されると、それらの出力トークンも同じターンのウィンドウ使用量にカウントされます。レスポンスには**usageフィールド**が含まれ、そのターンが実際に消費した入力トークンと出力トークンの数を示します: "Every response reports what the request consumed in its usage field."2 (すべてのレスポンスは、リクエストが消費したものをusageフィールドで報告します)

このリクエストにパッケージされていないもの — たとえば、まだどのツールもクエリしていないデータベース内の注文レコード — はコンテキストウィンドウの一部ではありません。モデルはそれを見ることができず、それが存在することを知ることもできません。だからこそ「メモリ」は何らかのメカニズムによってこのリクエストに積極的に運ばれなければならないのです; 自動的に到達可能ではありません。

ウィンドウは腐る — 大きいほど良いわけではない

ウィンドウが保持できる量には上限があります — 容量はモデルによって異なり、公式の数値は最大100万トークンです: "The context window (up to 1M tokens, depending on the model) holds the conversation history plus the new output Claude generates."2 (コンテキストウィンドウは(モデルによって最大1Mトークン)会話履歴とClaudeが生成する新しい出力を保持します) しかし、それは可能な限りいっぱいに詰め込むべきだという意味ではありません。ドキュメントは特定の現象、コンテキストロットを名付けています: "As token count grows, accuracy and recall degrade, a phenomenon known as context rot. This makes curating what's in context just as important as how much space is available."2 (トークン数が増えると、精度と想起が低下します。これはコンテキストロットとして知られる現象です。これにより、コンテキストに何を入れるかということは、どれだけのスペースが利用可能かということと同じくらい重要になります) 言い換えれば、ウィンドウが保持するコンテンツが多くなり、散らかるほど、モデルはそこから正しい答えを引き出すのが下手になります — これは、コンテキストに何を入れるかが、どれだけスペースが残っているかと同じくらい重要であることを意味します。2

これはメモリ設計に直接影響します: 履歴全体をコンテキストにダンプすることは無料ではなく(ウィンドウ容量を満たします)、コストがないわけでもありません(古い情報の山に埋もれた、今まさに関連する1つの文を見つけるのにモデルはより苦労します)。レッスン2では、その蓄積を実際にどう処理するか — 履歴を切り詰める方法、要約する方法、無制限に成長させない方法をカバーします。

「メモリ」という言葉は実際には比喩

冒頭のサポート会話に戻ります。「エージェントがあなたの名前がSarahであることを覚えている」と言うとき、正確な言い方は: あなたのコードが完全な履歴 —「私の名前はSarah」を含む履歴— をそのままモデルに再送信し、モデルがこのリクエスト内でそれを再読したということです。ストレージなし、リコールなし、再送信のみです。

これは言葉尻をとらえているわけではありません。「メモリは再送信によって生み出される錯覚である」と理解することは、本当に「覚えている」エージェントをどう設計するかに直接影響します:

  • 履歴配列が無制限に成長すると、ウィンドウは遅かれ早かれいっぱいになり、その上コンテキストロットにも遭遇します — これがレッスン2で解決する問題です。
  • ある情報が複数のセッションにわたって持続する必要がある場合(この1つの会話内だけでなく)、messages配列に詰め込むことに頼ることはできません; それはどこか外部に書き込まれなければなりません — レッスン3でカバーする外部メモリです。
  • エージェントがタスクの途中で「どこまで進んでいるか」を知る必要がある場合、その進捗も同様にコンテキストウィンドウ内で可視な構造化された状態でなければならず、モデルが推測するものではありません — これがレッスン4のテーマです。

この3つのスレッドはすべて、同じ事実の異なる帰結です: モデルはウィンドウ内にあるものだけを知っています; ウィンドウ内にないものは、モデルに関する限り存在しません。

まとめ

  • 「エージェントが以前の会話を覚えている」は錯覚です: 真実は、ホストアプリケーションが毎回完全な履歴をリクエストに再パッケージし、モデル自体は何も保存しないということです1
  • APIコールはステートレスです; サーバーはリクエスト間でセッションごとのプライベートな状態を保持しないため、すべての「メモリ」はクライアントによって積極的に運ばれなければなりません1
  • コンテキストウィンドウは、このリクエストが実際に運ぶすべてを保持します: システムプロンプト、messages内のすべてのメッセージ(ツール結果、画像、ドキュメントを含む)、ツール定義、そしてターンのモデル自身の出力2
  • ウィンドウにはサイズの上限があり、大きいからといって自動的に使いやすいわけではありません — コンテキストロットは、積み上げるトークンが多いほど、精度と想起が低下することを意味します2
  • 「モデルはウィンドウ内にあるものだけを知っている」と理解することは、次の3つのレッスンが解決する問題に直結します: 履歴をどう管理するか、メモリをどう外部化するか、状態をどう構造化するか

レッスン2: 会話履歴の管理: 追加、切り詰め、要約 >>

Footnotes

  1. Using the Messages API — https://platform.claude.com/docs/en/build-with-claude/working-with-messages 2 3 4

  2. Context windows — https://platform.claude.com/docs/en/build-with-claude/context-windows 2 3 4 5 6 7

練習

01

以下は、モデルに送信された実際のリクエスト(簡略化)と、「同様に存在する」がこのリクエストには表示されない3つのものです:

レベル1: コンテキストウィンドウにカウントされるものをマークする

存在する3つの追加のもの:

  1. データベース内のORD-2026-8842注文レコードの実際の内容(まだどのツールもクエリしていない)
  2. 同じユーザーが先週別のエージェントとのセッションで言及した配送先住所
  3. このリクエストが処理された後にモデルが生成する返信テキスト

それぞれについて決定してください: これら4種類のコンテンツ(system / tools / messages内のユーザーメッセージ / 上記の3つの追加アイテム)のうち、どれがこのリクエストのコンテキストウィンドウにカウントされ、どれがカウントされないか、それぞれ1文の理由を添えて答えてください。

完了基準 · ローカルでチェック
02

同僚が設計しているエージェントをこのように説明しています: 「私たちの会話はすでに20ターンも深く、データベースは完全な履歴を保存しているので、このターンのリクエストがユーザーの最新の文だけを送信しても、モデルは会話を続けることができます。なぜなら、サーバーは同じセッションであることを知っているからです。」

レベル2: この説明の中の間違った仮定を見つける

この説明の中の間違った仮定を指摘し、実際にこの方法で構築された場合にどのようなシナリオで壊れるかを説明してください。

完了基準 · ローカルでチェック