Effective context engineering for AI agents

概要

この記事は、AI エージェント(Claude などの大規模言語モデルを使った自律的なシステム)を構築する際に、「どんな情報を AI に渡すか」 をどう設計すべきかについて解説しています。

1. コンテキストエンジニアリングとは?

まず「コンテキスト」とは

コンテキスト(Context) とは、AI モデルに送る「すべての入力情報」のことです。

具体的には以下が含まれます:

  • システムプロンプト(AIへの指示)
  • ユーザーとの会話履歴
  • ツールの定義
  • 外部から取得したデータ
  • 例示(few-shot examples)

プロンプトエンジニアリングとの違い

概念 焦点 特徴
プロンプトエンジニアリング 「何を書くか」 1回の指示をどう書くか
コンテキストエンジニアリング 「何を渡すか全体」 ループする中で常に最適な情報を選び続ける

比喩で説明すると、プロンプトエンジニアリングは「メールの文面を練る」作業、コンテキストエンジニアリングは「会議に必要な資料一式を厳選して準備する」作業に近いです。

コンテキストエンジニアリングとは、絶えず変化する可能性のある情報の中から、限られたコンテキストウィンドウに何を入れるかをキュレーションする芸術であり科学です。

2. なぜコンテキストエンジニアリングが重要なのか

「コンテキスト腐敗(Context Rot)」という現象

LLMには注意力の予算(Attention Budget) のようなものがあります。

人間の脳も、一度に処理できる情報量には限界があります。LLMも同様で、コンテキストに入れるトークン(単語や文字の単位)が増えれば増えるほど、情報を正確に取り出す能力が低下する ことが研究でわかっています。

技術的な背景:

LLMは「Transformer」というアーキテクチャを使っています。このアーキテクチャでは、すべてのトークンに「注意」を向けます。トークン数が n の場合、n² の関係性を処理する必要があるため、トークンが増えると処理が「薄く広く」なってしまいます。

わかりやすい例え:

100人の会議で全員の発言を同時に聞こうとすると、一人ひとりへの集中力が薄れますよね。LLMも同じです。

3. 効果的なコンテキストの作り方

基本原則

目標:望む結果を最大化する、最小限の高信号トークンを見つける

つまり「無駄を省いて、必要な情報だけを厳選して渡す」ということです。

3.1 システムプロンプトの設計

「適切な高度(Right Altitude)」を見つける

システムプロンプトには2つの失敗パターンがあります:

失敗パターン 問題点
過度に具体的 脆く、メンテナンスが大変 「もしユーザーが"こんにちは"と言ったら"こんにちは!お元気ですか?"と返して...」のように全パターンを網羅しようとする
過度に曖昧 具体的な指針がなく期待通りに動かない 「ユーザーに親切に対応してください」だけで具体例がない

適切な高度 とは、その中間—具体的だが柔軟性があり、AIが応用できるヒューリスティック(経験則)を提供するレベルです。

構造化のコツ:

XMLタグやMarkdownのヘッダーを使ってセクションを分けると効果的です。

<background_information>
  あなたはカスタマーサポートのAIです。
</background_information>

<instructions>
  - 常に丁寧な言葉遣いを使う
  - 問題解決を優先する
</instructions>

3.2 ツールの設計

エージェントは「ツール」を使って外部と連携します(ファイル読み込み、Web検索、API呼び出しなど)。

良いツール設計の原則:

  • 自己完結型:1つのツールは1つの明確な目的
  • 機能の重複を避ける:どのツールを使うべきか迷わない設計
  • エラーに強い:失敗時の処理が明確
  • 説明が明確:入力パラメータの意味がはっきりしている

悪い例: 「データ取得ツール」「情報検索ツール」「データ参照ツール」が全部あると、どれを使えばいいかAIが迷います。人間が迷うような設計なら、AIも迷います。

3.3 例示(Few-shot Examples)

例を示すことは非常に効果的ですが、エッジケースを全部詰め込むのはNG です。

代わりに、多様で代表的な例を厳選 して渡しましょう。AIにとって、例は「百聞は一見にしかず」です。

4. コンテキストの動的な取得(Just-in-Time アプローチ)

従来のアプローチ vs 新しいアプローチ

アプローチ 方法 特徴
従来(Pre-retrieval) 事前に関連データを全部取得してコンテキストに入れる シンプルだが、無関係な情報も入りがち
新しい(Just-in-Time) 軽量な参照(ファイルパス、URLなど)だけ保持し、必要な時に取得 効率的だが、設計が複雑

Claude Code の例

Claude Code(Anthropicのコーディングエージェント)は、大きなデータベースを分析する際、全データをコンテキストに入れません。代わりに:

  1. 必要なクエリを書く
  2. headtailコマンドで一部だけ確認
  3. 必要に応じて追加データを取得

これは人間と同じです。私たちも百科事典を暗記するのではなく、必要な時に検索しますよね。

トレードオフ

  • 実行時の探索は、事前に計算されたデータを取得するよりも遅くなる
  • LLM が適切なツールの選択や情報にナビゲートさせるためのガイダンスがないと、誤用したり、行き止まりに陥ったり、重要な情報を識別できなかったりする

ハイブリッドアプローチ

最も効果的なのは、両方を組み合わせる ことです:

  • CLAUDE.mdのような重要情報は最初から渡す
  • 詳細なファイルは grepglob で必要な時に取得
  • 法務や財務などの動的コンテンツが少ないコンテキストに適している(ヒューリスティックを備えている)

5. 長時間タスクへの対応

エージェントが数時間にわたる作業(大規模なコード移行、包括的なリサーチなど)を行う場合、コンテキストウィンドウの限界を超えます。

5.1 コンパクション(Compaction)

会話の要約と圧縮 を行う手法です。

  1. コンテキストが上限に近づいたら
  2. これまでの内容を要約
  3. 新しいコンテキストウィンドウで要約から再開

何を残すか:

  • アーキテクチャの決定事項
  • 未解決のバグ
  • 重要な実装詳細

何を捨てるか:

  • 古いツール呼び出しの生の結果
  • 冗長なメッセージ

注意点: 過度な圧縮は、後で重要になる微妙な情報を失う可能性があります。

5.2 構造化されたメモ取り(Structured Note-taking)

エージェントが自分でメモを書いて保存 する手法です。

Claude Codeの例:

## TODO

- [ ] ユーザー認証機能の実装
- [x] データベース接続の設定
- [ ] エラーハンドリングの追加

Claudeがポケモンをプレイする実験 では、この手法が威力を発揮しました:

  • 「過去1,234ステップ、ルート1でポケモンを訓練中」
  • 「ピカチュウが8レベル上がった、目標は10」
  • 探索済みエリアのマップを自作
  • どの攻撃がどの敵に効くかの戦略メモ

コンテキストがリセットされても、自分のメモを読んで作業を継続できます。

Memory Toolにより、ファイルベースのシステムを通じてコン​​テキストウィンドウ外の情報の保存と参照を容易にします。

5.3 サブエージェントアーキテクチャ

1つのエージェントですべてを処理するのではなく、専門化したサブエージェント に作業を分担させます。

メインエージェント(司令塔)
    ├── サブエージェントA:コード検索担当
    ├── サブエージェントB:ドキュメント分析担当
    └── サブエージェントC:テスト実行担当

各サブエージェントは自分の作業で数万トークンを使っても、メインエージェントには1,000〜2,000トークンの要約だけを返します。これにより、詳細な作業とハイレベルな調整を分離できます。

使い分けの目安

手法 適したケース
コンパクション 継続的な対話が必要なタスク
メモ取り 明確なマイルストーンがある開発作業
サブエージェント 並列探索が有効な複雑なリサーチ