はじめに

  • AIエージェントの限界は、モデルのサイズだけでなく、「コンテキスト管理」という技術にも依存しています。これはCPUに対するメモリの設定のようなもので、エージェントの思考の深さと効率を決定します。
  • コンテキストウィンドウはゴミ箱ではありません:情報過多は「毒を盛り」、AIの判断を妨げ、混乱させます。正確性は、膨大な量よりもはるかに重要です。
  • 熟練者は「書く、選ぶ、圧縮する、隔離する」の4つのテクニックを用いてAIのコンテキストを管理し、限られた「メモリ」を要所に活用しながらコスト削減と効率向上を実現します。
  • 未来の競争はシステムの効率性の競争です。複数のエージェントアーキテクチャを用いてタスクを「隔離」し、それぞれのエージェントが自分の小さな窓の中で極限まで達成することが、複雑なタスクシステムの構築における鍵となります。

コア要約

エージェントがタスクを実行するには、コンテキストが不可欠です。いわゆる「コンテキストエンジニアリング」とは、エージェントがタスクを実行する各ステップでそのコンテキストウィンドウに適切な情報を正確に注入する技術と科学のことを指します。この記事では、現在の主流エージェントが採用しているコンテキストエンジニアリングの戦略を、いくつかの一般的なパターンにまとめます。

Context Engineering

コンテキストエンジニアリング(Context Engineering)

Andrej Karpathyが述べるように、大規模言語モデル(LLM)は「新しいオペレーティングシステム」のようなものです。LLMがCPUであり、その「コンテキストウィンドウ」がRAMとして機能し、モデルの作業記憶を担っています。RAMの容量が限られているように、LLMのコンテキストウィンドウも様々なコンテキストのソースを処理する際に容量のボトルネックに直面します。オペレーティングシステムの核心的な仕事の一つは、CPUのRAMを効率的に利用する方法を管理することです。「コンテキストエンジニアリング」も同様の役割を果たします。Karpathyはこれを非常に的確にまとめています:

コンテキストエンジニアリングは「……次のステップ(計算)に向けて、コンテキストウィンドウを正確に埋める巧妙な技術と科学です。」

Context Engineering

LLMアプリケーションを構築する際には、どのタイプのコンテキストを管理する必要があるのでしょうか?コンテキストエンジニアリングという包括的な概念は、以下の異なるコンテキストタイプを含んでいます:

  • • 指示(Instructions) – プロンプト、メモ、少量のサンプル、ツールの説明など
  • • 知識(Knowledge) – 事実、メモなど
  • • ツール(Tools) – ツール呼び出しのフィードバック情報

エージェント向けコンテキストエンジニアリング

今年、LLMが推論とツール呼び出しの能力を向上させる中で、エージェントへの関心が高まっています。エージェントはLLMとツールを交互に呼び出してタスクを実行し、特に長期間にわたる複雑なタスク処理に優れています。

Context Engineering for Agents

しかし、長期タスクや累積したツール呼び出しのフィードバックは、エージェントが大量のトークンを消費することを意味します。これは様々な問題を引き起こす可能性があります:コンテクストウィンドウの容量制限を超え、コストと遅延が増加し、エージェントの性能を低下させることもあります。Drew Breunigは、過度なコンテキストが以下の方法で性能問題を引き起こす可能性があることを明確に指摘しました:

  • • コンテキスト投毒(Context Poisoning):幻覚(誤った情報)がコンテキストに入る場合。
  • • コンテキスト干渉(Context Distraction):コンテキスト情報が多すぎて、モデルの元々のトレーニング知識が埋もれてしまう場合。
  • • コンテキスト混乱(Context Confusion):無関係なコンテキスト情報がモデルの応答に影響を与える場合。
  • • コンテキスト衝突(Context Clash):コンテキストの異なる部分が互いに矛盾する場合。

これらの問題を考え、Cognition AI社はコンテキストエンジニアリングの重要性を強調しています:

“コンテキストエンジニアリング”……実際はAIエージェントを構築するエンジニアの最重要な任務です。

Anthropic社も明確に指摘しています:

エージェントは通常、数百ラウンドの対話を必要とします。そのため、慎重なコンテキスト管理戦略を採用することが求められます。

では、今日の開発者はこの課題にどう対処しているのでしょうか?私は既存の方法を4つの大カテゴリ——書き込み(Write)、選択(Select)、圧縮(Compress)、隔離(Isolate)——にまとめ、それぞれ例を挙げて説明します。

Memory Type

コンテキストへの書き込み(Write Context)

コンテキストへの書き込みは、情報をコンテキストウィンドウの外に保存し、エージェントのタスク実行時に使用できるようにすることを指します。

スクラッチパッド(Scratchpads)

人間が問題を解決する際にはノートを取り、将来関連するタスクを処理する際に使うことを記憶します。エージェントもこの能力を徐々に獲得しています!「スクラッチパッド」を通じてメモを取ることは、エージェントがタスクを実行中に情報を持続的に保持する方法の一つです。その核心的な考え方は、情報をコンテキストウィンドウの外に保存しつつ、エージェントがいつでも取り出せるようにすることです。Anthropicの多エージェント研究システムはその明確な例を提供しています:

「チーフリサーチャー」は最初に問題を解決する方法を考え、その計画を「メモリ」に保存し、コンテキストを持続化します。なぜなら、コンテキストウィンドウが20万トークンを超えると切り捨てられる恐れがあるからです。計画の保持が極めて重要です。

スクラッチパッドの実装方法はいくつかあります。単純なツール呼び出しでファイルに書き込むこともあれば、ランタイムの状態オブジェクトのフィールドとしてセッション全体にわたって保持されることもあります。いずれの場合でも、スクラッチパッドはエージェントが有用な情報を保存し、タスクをより良く完了させることを可能にします。

記憶(Memories)

スクラッチパッドはエージェントが単一のセッションでタスクを解決するのを助けますが、時にはエージェントが複数のセッションを跨いで物事を記憶する必要があります。Reflexionモデルは各ラウンドのエージェントの行動後に反省し、それらの自己生成記憶を再利用するという考えを導入しました。Generative Agentsモデルは、過去のエージェントのフィードバックを定期的に合成して記憶を生成します。

これらの概念はChatGPT、Cursor、Windsurfなどの人気製品に適用されています。これらはすべて、ユーザーとエージェントの相互作用に基づいて自動生成される長期記憶を持つメカニズムを備えています。

Memories

コンテキストのフィルタリング(Select Context)

コンテキストのフィルタリングは、必要な情報をコンテキストウィンドウに調整し、エージェントがタスクを実行できるようにすることを指します。

スクラッチパッド(Scratchpad)

スクラッチパッドからコンテキストをフィルタリングするメカニズムは、その実装方法によって異なります。ツールであれば、エージェントはただツール呼び出しを使って読み取るだけです。もしそれがエージェントのランタイム状態の一部であれば、開発者は各ステップで状態の特定部分を選択的にエージェントに提示することができます。これは、以降のラウンドでLLMに対してスクラッチパッドコンテキストを提示する際に非常に細かなコントロールを提供します。

記憶(Memories)

もしエージェントが記憶を保存する能力を持っている場合、現在のタスクに関連する記憶を選択する能力も必要です。これにはいくつかの利点があります。エージェントは期待される行動パターンを学ぶために少サンプルの例(シナリオメモリ)を選択したり、自己の行動を導くために指示(プログラムメモリ)を選んだりすることができます。また、タスクに関連する背景を提供するために事実(セマンティックメモリ)を選ぶことも可能です。

Memory Type

一つの大きな課題は、選ばれた記憶が関連性を持つことを確認することです。一部の人気のあるエージェントは、固定されたファイルの一部しか使用せず、これらのファイルは常にコンテキストにロードされることがあります。例えば、多くのコードエージェントは指示(「プログラムメモリ」)を保存するためのファイルを使用したり、場合によっては例(「シナリオメモリ」)を保存したりします。Claude CodeはCLAUDE.mdを使用し、CursorやWindsurfはルールファイルを使用しています。

しかし、エージェントが大量の(例えば「セマンティックメモリ」タイプの)事実や関係を保存している場合、フィルタリングはより困難になります。ChatGPTは良い例であり、大量のユーザー専用の記憶から情報を保存およびフィルタリングします。

ベクトル埋め込みや知識グラフは、フィルタリングを支援するためによく使用される記憶インデックステクニックです。それでも、記憶のフィルタリングは依然として挑戦に満ちています。AI Engineer World Expoで、Simon Willisonは記憶フィルタリングの失敗例を共有しました:ChatGPTが彼の位置情報を記憶から取得し、彼が要求した画像に意図せず注入したのです。このような意外な、または望まれない記憶検索は、一部のユーザーにコンテキストウィンドウが「彼ら自身のものではなくなった」と感じさせることがあります!

ツール(Tools)

エージェントはツールを使用する必要がありますが、提供されるツールが多すぎると、彼らは圧倒されるかもしれません。これは通常、ツールの説明が重複するため、モデルがどのツールを選ぶべきか迷わせてしまうことから来ます。一つの方法は、ツールの説明にRAG(Retrieval-Augmented Generation)を適用し、セマンティックな類似性に基づいてタスクに最も関連するツールを取得することです。最近のいくつかの論文では、この方法が工具選択の正確性を3倍向上させることが示されています。

知識(Knowledge)

RAG自体は広大なトピックであり、コンテキストエンジニアリングの核心的な課題になり得ます。コードエージェントは大規模な生産アプリケーションにおけるRAGの最良の例の一つです。WindsurfのVarunは、その中のいくつかの課題をうまく要約しています:

コードのインデックス化 ≠ コンテキスト検索……私たちがやっているのは、AST(抽象構文木)でコードを解析し、セマンティックな境界に沿ってブロック化することです……しかし、コードベースの規模が増えるにつれて、ベクトル埋め込み検索は検索の啓発的手法としての信頼性を失います……私たちは、grep/ファイル検索、知識グラフに基づく検索、および……関連性の順位付けを行う再ランキングステップなど、さまざまな技術の組み合わせに依存しなければなりません。

コンテキストを圧縮する(Compress Context)

コンテキストを圧縮するとは、タスクの実行に必要なトークンのみを保持することです。

コンテキスト要約(Context Summarization)

エージェントの対話は数百ラウンドにわたる可能性があり、大量のトークンを消費するツールを使用します。要約はこれらの課題に対処するための一般的な方法です。Claude Codeを使用したことがあれば、その実際の適用を目にしたことがあるでしょう。コンテキストウィンドウの使用率が95%以上に達すると、Claude Codeは「自動圧縮」を実行し、ユーザーとエージェントとの完全な対話の軌跡を要約します。このエージェントの軌跡の圧縮はいくつかの戦略を採用でき、再帰的な要約や階層的な要約が含まれます。

Context Summarization

エージェントの設計において適時に要約ステップを追加することも有益です。たとえば、特にトークンを大量に消費するツール(検索ツールのような)の呼び出し後処理に利用できます。特定のイベントや意思決定をキャッチする必要がある場合、要約は挑戦的なものになる可能性があります。このためにCognitionは微調整モデルを使用し、このステップに多大な作業投入が必要であることを浮き彫りにしています。

コンテキストトリミング(Context Trimming)

要約は通常LLMを用いて最も関連性の高いコンテキスト片を抽出しますが、トリミングはフィルタリングのようなもので、あるいはDrew Breunigが言うように「剪定」することです。これは、ハードコーディングされたヒューリスティックルールを用い、メッセージリストから古いメッセージを削除するなどの方法で行うことができます。Drewはまた、質問応答タスク向けに訓練されたコンテキストトリッパーであるProvenceについて言及しています。

コンテキストを隔離する(Isolating Context)

コンテキストを隔離するとは、コンテキストを分割し、エージェントがタスクを実行できるようにすることを指します。

複数エージェント(Multi-agent)

コンテキストを隔離する最も一般的な方法の一つは、それを複数のサブエージェントに分散させることです。OpenAIのSwarmライブラリの動機の一つは「注意点の分離」であり、サブタスクを処理するためのエージェントチームを構成することです。各エージェントは特定のツールセット、指示、独立したコンテキストウィンドウを持っています。

Multi-agent

Anthropicの多エージェント研究システムは、独立したコンテキストを持つ複数のエージェントが単一のエージェントよりも優れたパフォーマンスを示すことを強力に証明しています。これは各サブエージェントのコンテキストウィンドウがより狭いサブタスクに集中できるためです。彼らのブログに記載されているように:

サブエージェントはそれぞれのコンテキストウィンドウを持ち並行して動作し、問題のさまざまな側面を探求します。

もちろん、複数エージェントにも課題があり、トークン消費など(例えば、Anthropicはそのトークン使用量が会話の15倍であると報告)や、サブエージェントの作業を計画するための注意深いプロンプトエンジニアリングが求められ、サブエージェント間の調整の問題もあります。

環境を通じたコンテキスト隔離(Context Isolation with Environments)

HuggingFaceの深層学習研究者プロジェクトは、別の興味深いコンテキスト隔離の例を示しています。ほとんどのエージェントはツール呼び出しAPIを使用し、これらのAPIはJSONオブジェクト(ツールパラメータ)を返し、次にツール(例えば検索API)にフィードバック(検索結果など)を取得するために渡されます。一方、HuggingFaceは CodeAgentを使用し、必要なツール呼び出しのコードを直接出力します。このコードはその後、サンドボックス環境で実行されます。ツール呼び出しからの特定のコンテキスト(例えば戻り値)は、その後LLMに返されます。

Context Isolation with Environments

これにより、LLMと環境内でコンテキストが隔離されます。Hugging Faceは、これは大量のトークンを消費するオブジェクトを隔離する優れた方法であると指摘しています:

Code Agentsは状態をより適切に取り扱うことができます……画像や音声、その他のデータを保存する必要がありますか?問題ありません、変数として代入すれば、後で使用できます。

状態(State)

注目すべきは、エージェントのランタイム状態オブジェクトもコンテキストを隔離する良い方法であるという点です。これはサンドボックスのような作用を果たします。状態オブジェクトは、コンテキストに書き込むことができるフィールドを含むスキーマ(例えばPydanticモデルなど)を設計可能です。スキーマ中のあるフィールド(例えばmessages)は、エージェントの各インタラクションラウンドでLLMに対して公開されますが、そのスキーマは他のフィールド内で情報を隔離し、より選択的に使用できるようにします。

結論

エージェントのコンテキストエンジニアリングのパターンはまだ進化を続けていますが、私たちは一般的な方法を次の4つのカテゴリにまとめることができます——書き込み、選択、圧縮、および隔離

  • • コンテキストへの書き込みとは、情報をコンテキストウィンドウの外に保存し、エージェントのタスク実行時に使用できるようにすることです。
  • • コンテキストのフィルタリングとは、必要な情報をコンテキストウィンドウに調整し、エージェントがタスクを実行するのを手助けすることです。
  • • コンテキストの圧縮とは、タスクの実行に必要なトークンのみを保持することです。
  • • コンテキストの隔離とは、コンテキストを分割し、エージェントがタスクを実行できるようにすることです。

これらのパターンを理解し、適用することは、今日の効率的なエージェントを構築するための核心的な取り組みです。