🦔

LLM エージェントのコンテキスト戦略:Just-in-Time に必要な情報だけを注入する

に公開

はじめに

LLM を使ったエージェントを設計するとき、わりと考えずに詰め込んでしまいがちなのが「コンテキスト」です。ドキュメント、データ、コード、会話履歴……「全部渡せば精度が上がる」と思われがちですが、現状のLLMの仕組みでは誤りです。

本記事の主題は 「どういったコンテキスト戦略をとるべきか」 です。以下の内容を解説します。

  • コンテキストウィンドウが大きくなっても変わらない LLM の本質的な性質
  • Just-in-Time Context — 必要な情報を、必要なときに、必要なだけ注入するのが原則
  • コンテキスト戦略の具体例(Agent Skills / データパイプライン / Tool Calling によるデータ操作)

コンテキストウィンドウが大きくなっても変わらないこと

「コンテキストウィンドウが 100 万トークンになれば、何でも詰め込めばいい」——この考えはかなり危険です。LLM の注意機構の性質と、トークン課金モデルのコスト構造を理解していないと、コンテキストを詰め込むほど精度が下がるという逆説的な結果を招きます。

LLM の注意機構は均一に働かない

LLM は与えられたコンテキスト全体を均等には処理しないという特性を持っています。

大量の無関係な情報を中間に詰め込むと「Lost in the Middle」と呼ばれる現象が起き、重要な情報が見落とされます。コンテキストウィンドウが広がっても、この注意の偏りは解消されていません。

コスト・レイテンシは線形に増える

コンテキスト量 推論コスト レイテンシ 応答品質
必要最小限 高(関連情報が密)
大量一括投入 中〜低(ノイズ混入)

トークン課金モデルでは、使わない情報に払うコストはゼロにできるのに、全量投入では常に最大コストがかかります。

「必要なデータを、必要なときに、必要なだけ」が原則

コンテキストウィンドウの拡大は「詰め込む量の上限が増えた」ことを意味しますが、戦略の正解は変わりません。

Just-in-Time Context — タスクが要求する瞬間に、そのタスクに最小限必要な情報だけをコンテキストに注入する。

この原則は以下のあらゆる場面で統一的に適用できます。

場面 悪い戦略(大量投入) 良い戦略(JIT)
専門知識の提供 全ドキュメントをプロンプトに貼る スキル概要のみ宣言し、必要時に SKILL.md をロード
データ分析 CSV 全行をプロンプトに展開 メタ情報のみ取得し、操作は Tool Calling に委譲
コード参照 リポジトリ全体を渡す 関連ファイルのみシンボル検索で特定して注入
会話履歴 全履歴を保持し続ける 要約+直近 N ターンのみ保持

コンテキスト戦略の具体例

「Just-in-Time Context」の原則を踏まえたとき、実際のエージェント設計ではどのようなアプローチがあるのか。以下に 3 つの具体例を紹介します。

Agent Skills:知識の Just-in-Time ロード

Agent Skills は「スキルの存在をシステムプロンプトで宣言し、詳細は必要になったときだけロードする」という 遅延評価(Lazy Loading) の発想です。

構造:スキルは 3 要素で構成される

Agent Skills は以下の 3 つの要素で構成されています。

要素 内容 格納場所
スキル名 スキルを一意に識別する名前(例: testing システムプロンプト
説明 いつこのスキルを使うべきかの発火条件(1〜2 文) システムプロンプト
スキルの内容 詳細な手順・判断基準・コード例を書いたテキストファイル 外部ファイル(SKILL.md)

このうちスキル名と説明だけがシステムプロンプトに埋め込まれます。スキルの内容は外部ファイルに分離されており、システムプロンプトには含まれません。

仕組み:スキルの内容は Tool Calling で動的にチャット履歴へ注入される

システムプロンプトにスキル一覧を持つエージェントは、ユーザーのリクエストを受け取ると次のように動作します。

スキルの内容はリクエストが来て初めてチャット履歴に注入されます。関係のないタスクではスキルの存在は認識されるものの、内容はロードされないため、コンテキストが無駄に膨らむことはありません。


データパイプライン:指示データをソースから LLM へ戦略的に流す

LLM に渡すコンテキストの品質は、渡す直前ではなく、データが生まれた時点から整備できるという発想がデータパイプラインです。ETL(Extract / Transform / Load)のような仕組みを使い、さまざまなソースから収集した情報を LLM が使いやすい形に変換してから注入します。

パイプラインの各ステージ

  1. 収集(Extract): ドキュメント・コード・レビュー記録などを各ソースから集める
  2. 変換(Transform): フィルタリング → 構造化 → 圧縮の順で LLM 向けに整形する
  3. 注入(Load): 整備済みのコンテキストをシステムプロンプトや外部ファイルに配置する

加工の前後比較

Before(生データ):

"弊社では長年の運用経験から、データベースへのアクセスはリポジトリパターンを採用することが望ましいとされており、過去に直接クエリを書いたことによるバグが多発したという背景があります。"

After(加工済み):

  • DB アクセスは必ずリポジトリパターン経由にする
  • 直接クエリ記述は禁止(過去に多数のバグ原因)

トークン数を削減しながら、エージェントへの伝達精度は上がります。


ワークスペース上のデータを LLM が Tool Calling で直接操作する

大量のデータをそのままコンテキストに渡すと、Lost in the Middle による精度劣化・コスト増大・レイテンシ悪化が起きます。これを防ぐのが「データ本体はワークスペースに置いたまま、LLM は Tool Calling で操作だけを指示する」という設計です。

LLM はデータを保持するのではなく、何を・どの順番で操作するかを推論するオーケストレーターとして機能します。実際の計算はツール側(Python・DuckDB など)が担い、LLM のコンテキストには操作結果のサマリーだけが返ってきます。

全体アーキテクチャ

Step 1: 生データの基本情報を取得する

分析を始める前に LLM はまず describe_data ツールを呼び出し、列名・データ量・基本統計量を把握します。生データを全量読み込むのではなく、メタ情報だけを取得してコンテキストを節約します。

このメタ情報を見て LLM は次に呼ぶべき操作(外れ値検出か?欠損値除去か?)を判断します。

Step 2: Tool Calling で実行できる操作一覧

ツール名 操作 主なパラメータ
describe_data 列名・行数・基本統計量・欠損数を返す file_path
filter_data 条件に合う行だけ抽出する file_path, condition
drop_columns 不要な列を削除する file_path, columns[]
calculate 新しい計算列を追加する file_path, expression
join_data 2 つのデータを結合する left, right, on, how
detect_outliers 外れ値を検出・除去する file_path, column, method
aggregate グループ集計する file_path, group_by, agg

Step 3: 典型的な操作フロー例

なぜ生データをコンテキストに読み込まないのか

大量データをコンテキストに展開するほど精度は上がるどころか、Lost in the Middle によって重要な情報が埋もれます。データはワークスペースに残したまま、LLM にはメタ情報と操作結果のサマリーだけを渡すのが正しい設計です。

まとめ

本記事で説明したコンテキスト戦略は、3 つのアプローチで構成されています。

Agent Skills:知識の動的ロード

要素 役割 格納場所
スキル概要 存在の宣言・発火条件 System プロンプト
スキル詳細 専門知識・手順・判断基準 SKILL.md
動的ロード Just-in-Time なコンテキスト注入 Tool Calling (read_file)

データパイプライン:指示データをソースから LLM へ戦略的に流す

要素 役割 タイミング
収集(Extract) 各ソースからデータを集める リクエスト発生前
変換(Transform) フィルタ・構造化・圧縮で LLM 向けに整形 リクエスト発生前
注入(Load) 整備済みコンテキストをプロンプトに配置 リクエスト発生前

Tool Calling によるデータ直接操作:大量データの精度劣化を防ぐ

要素 役割
describe_data 列名・統計量などメタ情報のみ取得
各種操作ツール フィルタ・結合・外れ値検出・集計を委譲
結果サマリー 操作結果のみコンテキストに返す

コンテキストウィンドウは常に「今この瞬間のタスクに最も関係の高い情報」で満たす——これが効率の良いコンテキスト戦略の本質です。

Discussion