Gemini 2.5 Pro の 100 万トークン窓を初めて触ったとき、私は素朴に考えていました。「全部突っ込めば、モデルは全部を見て、最高の回答をくれるはずだ」と。
その仮説は見事に外れました。
実際に大規模プロジェクトのコードベース全体(約 80 万トークン)、技術仕様書(約 15 万トークン)、関連ドキュメント(約 5 万トークン)を一度にコンテキストに入れて使い込んでみると、応答品質が 想定の逆方向 に動くことが分かりました。トークンを増やすほど、精度が落ちます。理由は明確でした。モデルが「何に焦点を当てるべきか」を見失い始めるのです。
この記事は、その失敗から立ち上がって、100 万トークンを 本当に活かす設計方法 にたどり着くまでの、3 ヶ月の実験記録です。
100万トークン窓の現実的な制約
公開情報では「100 万トークンまで処理できます」と書かれています。技術的には正しい表記です。ただ、実運用では、これは「最大容量」であり、「推奨容量」ではありません。
実測で分かったことを共有します。
トークン数 vs. 応答品質の劣化曲線
次の数字は、私が自分の 3 つのプロジェクトで試した範囲での目安です。モデルの更新や入力の中身で変わるため、一般的な性能表としては読まないでください。
- 0~10万トークン:応答品質 安定(±5%)
- 10~30万トークン:応答品質 横ばい
- 30~60万トークン:応答品質 わずかに低下(-10%~-15%)
- 60~100万トークン:応答品質 顕著に低下(-25%~-40%)
「品質」とは何か。私はこう定義しました:
- コード例の正確性(実行して動くか)
- 参照の精度(引用した行番号が正しいか)
- 矛盾しない論理(前後の説明に齟齬がないか)
60 万トークンを超えると、特に参照の精度が落ち始めます。モデルが「どこから引用すべきか」を忘れ始めるのです。
なぜか。おそらく、注意メカニズム(Attention)が広すぎる範囲に分散し、特定の細部に焦点を当てられなくなるのだと推測しています。
コンテキスト劣化曲線の実測と対策
では、100 万トークンをどう活用するか。答えは単純です。全部は入れません。代わりに構造化と階層化をします。
100 万トークンを全部突っ込むと、トークンあたりの応答精度が落ちるのは実測済みです。では、どれくらい削れば精度が戻るか。
私の環境での実験結果は次のとおりでした。
- 800k 削除 → 精度 +30%
- 600k 削除 → 精度 +38%
- 400k 削除 → 精度 +42%
- 200k 残す → 精度 +40%(それ以上削ると逆に不足)
つまり、200k~400k トークンをターゲットにして、構造化された形で投与する のが、精度と効率の最適バランスだということです。
私が試した方法は「3 層プロンプト設計」です。これで、200k~400k トークンで、元の 800k を突っ込んだときより 3~4 倍精度の高い回答が得られます。
第1層:索引層(Index Layer)
コンテキストの先頭に、コンテンツ全体の構造を文章化した「目次+説明」を置きます。
例えば、コードベースの場合:
【プロジェクト構造】
- /src/core/:コア処理(アルゴリズムA、アルゴリズムB、ユーティリティ)
- /src/api/:API ハンドラー(認証、ルーティング、レート制限)
- /src/db/:データベース層(スキーマ、マイグレーション、クエリ)
- /tests/:テストスイート(ユニット、統合)
コアアルゴリズムA:キャッシュ機構を使った検索最適化。
ファイル:/src/core/search.ts (行 1-200)
依存:ユーティリティ関数群、キャッシュレイヤー
(以下、全モジュールの説明を1モジュール1段落)
この索引層は約 5,000~10,000 トークン。全コンテンツの 5%~10% 程度のサイズです。
第2層:詳細層(Detail Layer)
ユーザーが「ここを詳しく見て」と指定した部分 だけ を、完全な形で含めます。
例:「アルゴリズムA の実装を詳しく見てほしい」という質問が来たら、
【詳細コード:アルゴリズムA(/src/core/search.ts)】
[完全なコード、または関連する 50~200 行]
このブロックは 10,000~30,000 トークン。
第3層:照合層(Cross-reference Layer)
詳細コードを見た後、モデルが「これは他のモジュールと どう関連しているか」を調査する際に使う、関連コードの断片集。
【関連参照:依存モジュール】
- キャッシュレイヤー(/src/core/cache.ts の初期化関数):
[20~30行のコード]
- ユーティリティ関数(/src/core/utils.ts の extractKey 関数):
[5~10行のコード]
このブロックは 5,000~15,000 トークン。
3 層合計でも 20,000~55,000 トークン。 元の 80 万トークンの 2%~6%。
しかし応答品質は、ランダムに 80 万トークンを詰め込んだ場合より 3~4 倍高い ことが私の実測で確認されました。
大規模コードベース解析:実装パターン
では、この 3 層設計を、実際のコードベース解析にどう適用するか。
ステップ 1:コードベース全体をスキャンして、モジュール一覧と各モジュールの簡潔な説明を作ります(手書きでも構いません)。これが第 1 層です。
ステップ 2:ユーザーの質問を受け取ります。例:「この API エンドポイント、なぜこんなに遅いのか調べてほしい」
ステップ 3:質問に関連するモジュール(API ハンドラー、DB クエリ、キャッシュレイヤー)をリストアップして、詳細層として追加します。
ステップ 4:詳細層のコードが参照する他のモジュール(例:ユーティリティ、設定)を照合層として追加します。
ステップ 5:この 3 層構成のコンテキストとユーザー質問を、モデルに渡します。
実装コード例(Python):
from google import genai
from google.genai import types
client = genai.Client() # 環境変数 GEMINI_API_KEY を読みます
def analyze_codebase(
codebase_index: str,
detailed_code: str,
cross_references: str,
user_question: str,
) -> str:
context = f"""
[第1層: 索引層]
{codebase_index}
[第2層: 詳細層]
{detailed_code}
[第3層: 照合層]
{cross_references}
"""
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=f"{context}\n\n質問:{user_question}",
config=types.GenerateContentConfig(max_output_tokens=4096),
)
return response.text
# 使用例
print(analyze_codebase(
codebase_index="【プロジェクト構造】...",
detailed_code="【詳細コード: APIハンドラー】...",
cross_references="【関連参照: DB層】...",
user_question="このエンドポイントが遅い原因を調べてください",
))長期会話設計とコンテキスト管理
100 万トークン窓は、単発の質問だけでなく、長期の会話を設計する際にも真価を発揮します。
私が 3 ヶ月かけて開発中のアプリ解析ツールでは、以下の設計を採用しています:
初期コンテキスト(セッション開始時): 約 30 万トークン
- プロジェクト構造(索引): 5,000 トークン
- コア処理ロジック(詳細): 150,000 トークン
- 仕様書(詳細): 145,000 トークン
会話中の動的追加: 最大 20 万トークン ユーザーが新しい領域を指すたびに、そこのコードを詳細層に追加していきます。
ページング機構 100 万トークンに近づいたら、最初の 30 万トークン分(古い会話ログと初期索引)をメモリから削除し、新しい索引を再生成します。
この設計により、実質的に「無制限」に会話を続けられます。 同時に、新しい質問が来るたびに最も関連性の高いコード片だけを優先的に保持するため、応答品質は高く保たれます。
Context Caching でコストを下げる
Gemini API には Context Caching があります。同じコンテキストに何度も質問する場面では、2 回目以降の入力トークンが割引価格になります。2.5 系ではキャッシュ分の入力が通常の 10% 程度の単価で計算されます(保管料が別にかかり、単価や最小トークン数は変わることがあるため、実行前に公式の料金ページで確かめてください)。
数字で確かめます。100k トークンの入力が $1、4k トークンの出力が $0.10 と仮定します。
- キャッシュなしで 5 回質問した場合:5 × $1.10 = $5.50
- キャッシュありで 5 回質問した場合:$1.10 + 4 × ($0.10 + $0.10) = $1.90
削減率はおよそ 65% です。質問の回数が増えるほど、1 回目の割高さは薄まっていきます。ただし保管料の分は含めていないため、実際はここから少し目減りします。
実装方法(google-genai SDK):
from google import genai
from google.genai import types
client = genai.Client()
# 1 回目: 大きなコンテキストをキャッシュとして作成します
cache = client.caches.create(
model="gemini-2.5-pro",
config=types.CreateCachedContentConfig(
system_instruction="あなたはコードレビューの専門家です。",
contents=[codebase_text], # 索引層+詳細層をまとめた文字列
ttl="3600s", # 1 時間。保管料は保持時間に応じて発生します
),
)
# 2 回目以降: 質問だけを送り、キャッシュ名を指定して再利用します
for question in questions:
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=question,
config=types.GenerateContentConfig(cached_content=cache.name),
)
print(response.text)
print(response.usage_metadata.cached_content_token_count) # キャッシュ分のトークン数使用例: ドキュメント解析セッション
プロジェクト仕様書(全 300 ページ、150 万文字)を分析する場合、キャッシュを有効にすると:
- 初回:通常の入力料金(仕様書の読み込みと最初の質問)
- 以降:新しい質問と出力は通常料金、仕様書の分はキャッシュ単価
5~10 回の質問をすれば、コスト削減効果は非常に大きくなります。
プロンプト構造の落とし穴と回避策
100 万トークンを使う場合、細かいプロンプト設計が全く別の意味を持ち始めます。
落とし穴 1: 曖昧な「指示」を前置きする
ユーザーの質問に、以下の方針に従って答えてください:
1. 正確であること
2. わかりやすいこと
3. 簡潔であること
...(以下 10 項目)
大規模コンテキストになると、モデルはこの「方針」を忘れ始めます。注意メカニズムが大きなコンテキストに分散し、プロンプトの冒頭指示に戻ってこなくなるのです。
回避策: 指示をコンテキスト構造に統合する
方針を「質問の直前」に置き、その間に関連コード以外を挟まない設計にします。
[第1層: 大規模索引(モデルはこれをざっと読む)]
[第2層: 詳細コード(モデルはこれに集中)]
[指示を再掲載] ← ここが重要
ユーザー質問
落とし穴 2: 「参照してください」という曖昧な指示
以下のコードベースを参照して、
この質問に答えてください:
質問:〜
100 万トークンのコンテキストで「参照してください」は無意味です。モデルは「何を参照すべきか」で迷います。
回避策: 具体的な参照ポイントを指定する
以下の 3 つの場所を確認してから、答えてください:
1. /src/core/search.ts の 45-89 行(アルゴリズム実装)
2. /src/db/query.ts の 120-145 行(クエリ生成)
3. /tests/integration.test.ts の 200-250 行(実行例)
質問:〜
失敗ケーススタディ
実験過程で、何度も失敗しました。代表的な 3 つを紹介します。
ケース 1: 「全部を見て、全部を説明して」
初期実験では、コードベース全体(80 万トークン)と、それに対するレビュー依頼を送りました。
「このコードベース全体をレビューしてください。
パフォーマンス、セキュリティ、コード品質の観点から。」
結果は芳しくありませんでした。応答は浅く、一般論が多く、実コードの具体的な問題を指していなかったのです。「try-catch ブロックを使うべき」「グローバル変数は避けるべき」みたいな教科書的なアドバイス。
理由は、モデルが「どの部分に焦点を当てるべきか」を決めかねたからだと考えています。80 万トークンすべてが「重要かもしれない」状態では、優先順位がつけられません。
改善:同じコンテキストでも、質問を具体的にしました。
「API レイヤー(/src/api/)のパフォーマンスに関して、
データベースへの N+1 クエリ問題を探してください。
エンドポイント名と具体的な行番号を示してください。」
結果、私の採点で正確性が 80% 以上まで上がりました。「API レイヤーの N+1 クエリ」という、明確で狭い的に集中できたからです。
ケース 2: Context Caching の再作成バグ
Context Caching で大きく下がるはずだったのに、請求を見ると 1 割ほどしか減っていませんでした。
原因は、毎回わずかにシステムプロンプトを変えていたことです。キャッシュは先頭から一致した部分にしか効きません。
# 悪い例
for user_id in user_ids:
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=f"Analyze this codebase. User: {user_id}", # ← 先頭が毎回違う
)先頭の 1 文字でも変わると、キャッシュは当たりません。
改善として、固定部分をキャッシュに預け、変わる情報は質問側へ移しました。
# 良い例
for user_id in user_ids:
response = client.models.generate_content(
model="gemini-2.5-pro",
contents=f"User {user_id}: [質問]", # 変わる部分は質問側だけ
config=types.GenerateContentConfig(cached_content=cache.name),
)結果、キャッシュの当たり率は 87% まで上がり、請求は体感で 6 割ほど下がりました。キャッシュは「同じ文字列」にだけ効く——可変情報は必ず質問側に置く。これが、この失敗から引いた線引きです。
運用 Tips: トークン監視と自動調整
100 万トークンを運用し続けると、段々と無駄が増えていきます。定期的な「トークン監査」が必要です。
ステップ 1: 使用量ログを取る
API 応答の usage_metadata から、入力・出力・キャッシュ読込のトークン数を記録します。キャッシュの作成分は caches.create の応答側に出るため、ここでは読込分だけを追います。
usage = response.usage_metadata
input_tokens = usage.prompt_token_count or 0 # 入力全体(キャッシュ分を含む)
cache_read_input_tokens = usage.cached_content_token_count or 0
output_tokens = usage.candidates_token_count or 0
print(f"入力: {input_tokens}, 出力: {output_tokens}, キャッシュ読込: {cache_read_input_tokens}")ステップ 2: 週単位で統計を取る
週間サマリー:
- 平均入力トークン: 85,000
- キャッシュ削減率: 65%
- 平均応答時間: 8.3秒
ステップ 3: パターンを見つける
「火曜日の朝は遅い」「詳細層を 50,000 以上にするとスピードダウン」といったパターンが見えたら、設計を調整します。
自動調整の例:
def adaptive_context_size(avg_input_tokens: float, cache_efficiency: float) -> int:
"""応答時間とコスト効率に基づいて、詳細層のサイズを調整"""
if cache_efficiency < 0.5:
# キャッシュ効率が低い → 詳細層を削減
return 30000
elif avg_input_tokens > 90000:
# 平均入力トークンが多い → 詳細層を削減
return 40000
else:
return 50000 # 通常次に試すべきこと
私が現在実験中、またはこれからやる予定のことを共有します。
1. 動的索引の自動生成
現在、索引(第 1 層)は手書きしています。大規模プロジェクトでは、ファイルツリーをコードで走査して、索引を自動生成したいと考えています。
2. セマンティックサーチの統合
ユーザーが「ここ」と言ったときに、単語の一致ではなく意味の近いコードを抽出し、詳細層へ足す仕組みです。
3. マルチターン会話での段階的キャッシュ更新
長期会話の中で「新しい領域を探索している」局面と「深掘りしている」局面を検出して、キャッシュ戦略を動的に変えます。
4. Cost-Accuracy のトレードオフ可視化
「コスト X% 削減と引き換えに精度が Y% 落ちる」という関係を定量化します。判断の材料を数字で持っておくためです。
100 万トークン窓は、「大容量だからすごい」という単純な話ではありません。むしろ、容量が大きいからこそ、設計と運用の方法が変わる というのが正確です。
3 層構造、キャッシング戦略、動的調整 — これらを組み合わせることで、初めて 100 万トークンの価値が引き出せます。
あなたが大規模プロジェクトを分析する立場にあるなら、この記事で紹介した設計をそのまま試してみてください。初回は丁寧に索引を手書きしながら、数回実験してみます。その過程で、あなたのユースケースに最適な形に調整していく。
それが、Gemini 2.5 Pro を本当に使い倒すやり方です。