Agent Mentor Learn
コンテキストエンジニアリング: 有限のアテンションを効くところに使う · 第 3 回 / 全 6 回

レッスン3: ジャストインタイム取得: エージェント自身にコンテキストを取りに行かせる

学習目標:

  • アテンションバジェットを使ってプリロードの隠れたコストを値付けし、ある素材が初期コンテキストに入るべきかどうかを判断する
  • ジャストインタイム取得の仕組みを説明する: 軽量な識別子、シグナルとしてのメタデータ、探索を通じて段階的に発見される関連コンテキスト
  • 具体的なエージェントを 1 つ取り上げてハイブリッド戦略を描く——何をプリロードし、何を識別子として残して実行時に取りに行かせるか

前提: レッスン1 とレッスン2 を読み終え、本シリーズのコース7「Agent Harness Fundamentals: Loops and Control」のハーネスのループが手の届くところにあること | 前: レッスン2 << | 次: レッスン4 >>

まずは、全部詰め込みたい衝動をこらえる

コードベースの質問応答エージェントを作っているとしましょう。リポジトリにはソースファイルが 200 個あり、ユーザーは「この関数はどこで定義されている?」「この設定を変えたら何が壊れる?」といったことを尋ねます。真っ先に思いつく手は、200 ファイルを全部読んで初期コンテキストに貼り付けることです——コンテキストウィンドウはもう十分大きいので、収まります。

収まります。だからといって、そこに入るべきだということにはなりません。理由はレッスン1 で扱いました。モデルは「アテンションバジェット」から引き出して大量のコンテキストを解析するのであり、"Every new token introduced depletes this budget by some amount."1 計算は進むほど悪くなります: "as the number of tokens in the context window increases, the model's ability to accurately recall information from that context decreases."1 救いは、これが崖ではなく坂だということです——"some models exhibit more gentle degradation than others, this characteristic emerges across all models" であり、これらの要因は合わさって "create a performance gradient rather than a hard cliff."1 そこでレッスン1 の結論に戻ります。ここで繰り返す価値があります: "context, therefore, must be treated as a finite resource with diminishing marginal returns."1

200 ファイルの話に戻ります。ユーザーは 1 つの具体的な質問をし、本当に関係するのはおそらく 2〜3 ファイルです。残り 197 ファイルが運ぶ十数万トークンは、無害な背景ではありません。同じ予算の中から、本当に大事な内容と注意を奪い合います。さらに悪いことに、エージェントはループの中で動き、"An agent running in a loop generates more and more data that could be relevant for the next turn of inference."1 初期コンテキストがすでに 8 割方埋まっていれば、ループは数ターンで壁にぶつかります。

そこで問いはこうなります。どんなときに素材をモデルの目の前に直接置き、どんなときに素材のありかだけを伝えて取りに行かせるのか。それがこのレッスンの全体です。

2 つの戦略を並べる

まず、それぞれを素直に述べます。

プリロード: 推論が始まる前に、必要になりうるものをすべて初期コンテキストに入れる。モデルはターン1 でそのすべてを見て、以後は何も取得しなくてよい。

ジャストインタイム取得: 初期コンテキストには元の素材を置かず、軽量な識別子だけを置く。やり方は "maintain lightweight identifiers (file paths, stored queries, web links, etc.)"1(ファイルパス、保存済みクエリ、ウェブリンクなどの軽量な識別子を保持する)で、実行時に必要に応じてエージェントがツール経由で中身をロードします。

プリロードジャストインタイム取得
初期コンテキスト大きい小さい
素材が届くタイミングターン1 でモデルの目の前にまず 1〜数ターンのツール呼び出しを要する
トークンの行き先大半が「役に立つかもしれない」に「いま確実に必要」に
典型的な失敗モード注意が薄まり、重要な内容が埋もれる取得が迷走して空回りし、ターンと予算を焼く

自分が実際にどう働いているか考えてみてください。あなたはコードベースを暗記していません。持ち歩いているのは「認証まわりのロジックは auth ディレクトリにある」「設定の解析はたぶん config.js」といった、内容を指す索引で、詳細が要るときにファイルを開きます。ジャストインタイム取得は、その働き方をエージェントに渡すものです。

ただし、表の最後のセルをもう一度見てください。取得はタダではありません。ジャストインタイムの取得は毎回、ツール呼び出しの完全な往復です——モデルが呼び出しを出し、ハーネスが実行し、結果が返り、モデルがもう一度推論します。本シリーズのコース7「Agent Harness Fundamentals: Loops and Control」では、ハーネスに最大ターン数と予算上限という 2 つのバルブを取り付けました。取得が使うのは、まさにその 2 つのバルブが統べているものです。ですから「常にジャストインタイム」も答えではありません。これは計算すべき勘定であり、このレッスンの後半のトレードオフのフレームワークがその計算をします。

ジャストインタイム取得の実際の仕組み

これを動かすものは 3 つです。識別子の集合(何が存在し、だいたいどこにあるかをモデルに知らせる)、いくつかの取得ツール、そして複数ターンの探索を許すループ。組み合わさることで、これは "allows agents to incrementally discover relevant context through exploration."1(探索を通じて、関連するコンテキストをエージェントが段階的に発見できるようにする)

過小評価されがちなのがここです。識別子のメタデータ自体がシグナルであるということ。ファイル名もディレクトリ構造も、その内容が何のためのもので、どれくらい関連しそうかを宣伝しています。1 tests/refund.test.js は開かなくても中身が分かりますし、2 年間触られていない legacy/ ディレクトリは、おそらく最初に見に行く場所ではありません。Anthropic のマルチエージェント研究システムの記事は、その根底の動きを鋭く言い当てています: "The essence of search is compression: distilling insights from a vast corpus."2(検索の本質は圧縮である——広大なコーパスから洞察を蒸留すること)。ジャストインタイム取得のどのステップも——ディレクトリの一覧、キーワードの検索、ファイルの選択——その圧縮を実行し、「関連するかもしれない広い範囲」を「実際に読まなければならない小さな一片」へ絞り込みます。

コードに落とします。先ほどのコードベース質問応答エージェントに 3 つのツールを与えます。実装はどれも短いものです:

そのうえでツール定義をレッスン2 の基準——"self-contained,"(自己完結)、"extremely clear with respect to their intended use,"(意図された用途について極めて明確)、そして "minimal overlap in functionality"(機能の重なりが最小限)——で書きます:1

システムプロンプトが運ぶのは安定した部分だけです。役割、引用の要件、取得の振る舞いの規約。ファイルの内容は 1 行も入っていないことに注目してください:

これを小さな EC のリポジトリにつないで「注文の返金額はどこで計算されている?」と尋ねると、典型的な実行はこうなります。(リポジトリの内容は説明用ですが、各呼び出しの形と各戻り値の書式は上の実装で固定されています。ターン3 のファイル本体は長すぎて印字できないので、1 行の括弧書きで代用しています。)

ターン1  list_files({ path: "." })      →  README.md         src/         tests/ターン2  grep({ pattern: "Refund", path: "src" })      →  src/payments/refund.js:12:export function calculateRefundAmount(order, policy) {         src/orders/service.js:6:import { calculateRefundAmount } from "../payments/refund.js";         src/orders/service.js:88:  const amount = calculateRefundAmount(order, policy);ターン3  read_file({ path: "src/payments/refund.js" })      →  (このファイルは 60 行。全文を返した)ターン4  モデルはツールの呼び出しをやめ、直接答える:         「返金額は src/payments/refund.js:12 の calculateRefundAmount で         計算されています。注文モジュールは src/orders/service.js:88 から         それを呼んでいます。」

ターン2 と 3 のあいだで何が起きたかを見てください。grep は 3 件の一致行を返しましたが、モデルは両方のファイルを読みませんでした。行の内容から、定義があるのは refund.js で、service.js は単なるインポート元かつ呼び出し側だと判断し、ちょうど 1 ファイルだけを開いたのです。これは、メタデータがモデルの代わりに一次フィルタをかけたということです: "the metadata of these references provides a mechanism to efficiently refine behavior."1(これらの参照のメタデータは、振る舞いを効率的に洗練させる仕組みを提供する)。この軌跡全体でコンテキストに入ったのは、ディレクトリ一覧 1 回、grep の出力 3 行、そして 60 行のファイル 1 つ——200 ファイルではありません。

もう 2 つ、見返す価値のある細部があります。1 つ目は、2 つの実装に組み込まれた切り詰めです。grep は最大 50 行、read_file は最大 400 行しか返しません。レッスン2 では、ツールは "returning information that is token efficient"1(トークン効率のよい情報を返す)べきだと指摘しました——取得ツールはコンテキストの供給業者であり、供給業者が出荷量に上限をかけて初めてループは生き延びます。2 つ目は失敗のケースです。grep が空振りを続けると、モデルはキーワードを変えて何度も検索するかもしれません。それこそ、ハーネスのコースの空回り検知と予算バルブが捕まえるために存在する場面です——探索はよいことですが、無制限の探索はそうではありません。

ハイブリッドこそが通常形

結論は「ジャストインタイム取得の勝ち」だと思ったかもしれません。違います。実システムがどちらかの極に座ることはめったになく、よくある形はハイブリッドです——"retrieving some data up front for speed, and pursuing further autonomous exploration at its discretion."1(速度のために一部のデータを前もって取得し、そのうえでエージェントの裁量でさらに自律的な探索を進める)

あなたが毎日使っている Claude Code は、その生きた実例です: "CLAUDE.md files are naively dropped into context up front, while primitives like glob and grep" が実行時のジャストインタイムの探索を支えます。1 公式ドキュメントは端的にこう述べています——"CLAUDE.md is a special file that Claude reads at the start of every conversation."3 毎回必ずロードされるので、ドキュメントは広く当てはまる素材だけを入れ、1 行ずつこう問うことを勧めています: "Would removing this cause Claude to make mistakes?"3(これを消したら Claude はミスをするか?)。答えが No なら、その行は去るべきです。ドキュメントは結果を率直に書いています: "Bloated CLAUDE.md files cause Claude to ignore your actual instructions!"3

スキルは第 3 の道を行きます——"Claude loads them on demand without bloating every conversation."3 この 3 つを並べると、ハイブリッド戦略の階層図が見えてきます:

  • CLAUDE.md: 安定していて、毎ターン拘束力がある → 最初にプリロード;
  • スキル: 特定タスクでしか使わない、パッケージ化された専門能力 → オンデマンドでロード;
  • コードベース本体: 巨大で、毎回必要なのはごく一部 → glob と grep でジャストインタイムに探索。

自分のエージェントを設計するとき、あなたが描いているのは同じ図のあるバージョンです。どの素材が「CLAUDE.md の枠」に座り、どの素材が「コードベースの枠」に座るのか。

すぐ使えるトレードオフのフレームワーク

候補となる素材ごとに、2 つの問いを投げます:

  1. それは安定しているか? 特定の質問とは無関係に、内容は時間が経っても居座り続けるか。
  2. 毎ターン——あるいはほぼ毎ターン——使われるか?

両方 Yes → プリロード。典型例: コーディング規約、中核となるビジネス上の制約、エージェントの行動規則、トップレベルのディレクトリ構造。この種の素材はたいてい小さくもあります——「毎ターン必要」と称するものが巨大だと分かったら、まず本当に毎ターン必要なのかを疑ってください。

どちらかが No → 識別子を残してジャストインタイムに取得。典型例: あるモジュールの全ソース(そのモジュールについての質問でしか要らない)、過去のチケット(特定の障害を追うときだけ参照する)、長い設計ドキュメント(方針をすり合わせるときだけ開く)。

そのうえで、取得のコストを天秤に載せて結果をもう一度確かめます。取得は 1 回ごとに往復が増え、レイテンシが増え、予算を使います。ですから、小さくて頻繁に使う素材を意地でも取得に回さないでください。600 語分のプリロード領域と引き換えに、毎セッション list_files のターンを 1 つ増やすのは損な取引です。逆方向に、まず出番のなさそうな 2,000 行のスクリプトをプリロードするのは、純粋なアテンションバジェットの燃焼です。1

Claude Code のドキュメントは、賭け金を一行で表しています: "The context window is the most important resource to manage."3 プリロードとジャストインタイム取得は対立する教義ではありません。その資源を管理するための両手です。

このレッスンが RAG を意図的に飛ばす理由

「取得」と言うと、多くの人はベクトルストア、埋め込み、RAG パイプラインへ一足飛びに向かいます。このレッスンはそのどれにも意図的に触れません——コースの README が境界を引いており、ここでの「ジャストインタイム取得」はもっと素朴なものを指します。ファイルシステムと検索のツールを持ったエージェントが、必要に応じて中身を引いてくることです。

これは教育上の手抜きではありません。ファイルパスには階層と命名の意味論が無料でついてきますし、grep の結果は正確で説明可能です。この 2 つだけで、探索を通じて関連するコンテキストを段階的に発見するループを丸ごと支えるには十分です。1 エージェント構築についての Anthropic のガイダンスも、これに見合う程度の感覚を示しています: "you should consider adding complexity only when it demonstrably improves outcomes."4 動詞に注目してください——consider(検討する)です。これは禁止ではなく、天秤にかける姿勢です。コードベースの質問応答エージェントなら、最も単純な道(ファイルシステム+ grep)を歩いて、どこで足りなくなるかを測れるようになるまで進む方が、初日からベクトル検索を立ち上げるよりこの姿勢に合っています。

1 本、糸を垂らしたままにしておきます。ジャストインタイム取得をどれだけ規律正しくやっても、長いタスクをこなすエージェントはターンを重ねるごとにツール結果を溜め込み続け1、コンテキストウィンドウはいずれにせよ天井に近づいていきます。その時点では「少なく取る」のがうまいだけでは足りません。捨てるのと書き留めるのもうまくなる必要があります。それがレッスン4 です。

まとめ

  • 「収まる」はプリロードの理由になりません。新しいトークンはすべてアテンションバジェットを削り、トークン数が増えるほどコンテキストからの正確な想起は落ちます——硬い崖ではなく性能の傾斜です。コンテキストは限界収穫が逓減する有限の資源として管理しなければなりません1
  • ジャストインタイム取得の仕組み: コンテキストには軽量な識別子(ファイルパス、保存済みクエリ、ウェブリンク)だけを保ち、実行時にツールが必要に応じて中身をロードします。識別子のメタデータ——ファイル名、ディレクトリ構造——はそれ自体が関連性を知らせるシグナルであり、それによってエージェントは探索を通じて関連するコンテキストを段階的に発見できます1
  • 取得はタダではありません。1 回ごとにツール呼び出しの往復であり、レイテンシに加えて、ハーネスのコースの制御バルブが統べるターンと予算を使います
  • ハイブリッドこそが通常形です。速度のために一部のデータを前もって取得し、そのうえでモデルの裁量でさらに自律的な探索を進めさせます1。Claude Code は出来合いの参照実装です——CLAUDE.md は最初に丸ごと投入され、スキルはオンデマンドでロードされ、コードベースは glob と grep でその場で探索されます1 3
  • フレームワークは 2 つの問いです。安定しているか、毎ターン必要か。両方 Yes ならプリロード、そうでなければ識別子を残す。小さくて頻繁な素材を無理に取得へ回さず、大きくて稀な素材を無理にプリロードへ回さないこと
  • このレッスンで言う「ジャストインタイム取得」は、ファイルシステムと検索のツールによるオンデマンドの引き出しを指し、ベクトルストアは関与しません。より重い取得機構を持ち込む前に、この程度の感覚を思い出してください——複雑さを足すのは、それが結果を明確に改善すると示せるときだけ検討する4

>> レッスン4: コンパクションとノート: 長時間タスクのコンテキスト管理

Footnotes

  1. Effective context engineering for AI agents — Anthropic Engineering — https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

  2. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system

  3. Best practices for Claude Code — Claude Code Docs — https://code.claude.com/docs/en/best-practices 2 3 4 5 6

  4. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2

練習

01

社内のオンコール運用の質問応答エージェントのコンテキストを設計していて、候補の素材が 5 つ挙がっています:

レベル1: コンテキストの棚卸しを仕分ける
  1. オンコールハンドブック(約 500 語——本番データベースのパスワードは決して渡さない、といったすべての回答が守るべきレッドライン);
  2. 過去のインシデントチケット 800 件全部(1 件あたり数百語から数千語);
  3. サービスカタログのトップレベル構造(40 個のサービス名と、それぞれ 1 行の責務。約 600 語);
  4. あるサービスの完全なデプロイスクリプト(約 2,000 行。デプロイの質問にしか関係しない);
  5. よくあるアラートのキーワードからチケット検索クエリへの対応表(約 30 行)。

このレッスンのフレームワークを使って、各項目を「プリロード」か「識別子を残してジャストインタイムに取得」に仕分け、それぞれ 1 行で理由を述べてください。

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

本シリーズのコース7「Agent Harness Fundamentals: Loops and Control」で書いたハーネスのループを引っ張り出し、このレッスンの 3 つのツール(list_files、grep、read_file)を登録し、自分の実際のリポジトリに向けて、質問応答エージェントに仕立ててください。要件:

レベル2: 自分のハーネスに取得ツールを足す
  • システムプロンプトがプリロードするのは安定した情報だけ——役割、引用の要件、取得の規約——で、ファイルの内容は入れない;
  • 3 つのツールの説明がいずれも自己完結していて重なりがなく、戻り値が切り詰められている;
  • 実際の質問を 1 つ走らせ、ツール呼び出しの軌跡を記録し、粗いところから細かいところへの段階的な発見が現れているかを確認する。
完了基準 · ローカルでチェック