100万トークンという数字は、聞いた瞬間には現実感がありません。従来のモデルが4,000〜100,000トークンだったことを思えば、単一のリクエストで扱える対象がまるごと入れ替わります。
- 大規模コードベース: 数千ファイル、数百万行のコード
- 書籍全文: 400ページの技術書籍を丸ごと分析
- 複数PDFのコーパス: 50〜100個のドキュメントを一度に処理
- 複雑な会話履歴: 500ターン以上のマルチターン対話
ただ、入るからといって全部入れるのが正解とは限りません。コストと精度の両面から、本番で1Mトークンを扱うための実装パターンを整理します。
長文コンテキストとRAG:いつどちらを選ぶか
長文コンテキスト処理とRAG(Retrieval-Augmented Generation)は、どちらも大規模データ処理のアプローチですが、用途によって最適な選択が異なります。
| 評価軸 | 長文コンテキスト(1M) | RAG |
|---|---|---|
| レイテンシー | 中~高(1-2分) | 低(1-5秒) |
| API呼び出し数 | 1回 | 複数回(検索+生成) |
| クエリコスト | 高い(コンテキスト全体の課金) | 中程度(検索結果のみ) |
| データ鮮度 | 静的(初期化時点) | 動的(毎回検索) |
| 精度(クロスドキュメント分析) | 非常に高い | 中程度(検索精度に依存) |
| 複雑な推論 | 強い(全体構造理解) | やや弱い(分断された情報) |
| セットアップ複雑度 | 低い | 高い(ベクトルDB、埋め込み) |
| 適用例 | コードベース分析、書籍解析、法律文書審査 | チャットボット、FAQ、Q&A |
APIコール前のトークン計数
効率的に長文コンテキストを使用するには、事前にトークン数を計算し、コストを推定する点が肝心です。
import google.generativeai as genai
from pathlib import Path
# Gemini APIを初期化
genai.configure(api_key="YOUR_API_KEY")
def count_tokens_for_content(content: str, model_name: str = "gemini-2.5-pro") -> dict:
"""
テキストコンテンツのトークン数を計算
"""
model = genai.GenerativeModel(model_name)
response = model.count_tokens(content)
return {
"input_tokens": response.total_tokens,
"estimated_output_tokens": 2000, # 平均的な出力予測
}
def estimate_cost(input_tokens: int, output_tokens: int, cached: bool = False) -> dict:
"""
Gemini 2.5 Proの実行コストを推定
価格(2026年3月時点):
- 入力: $1.25 / 100万トークン(キャッシュヒット時: $0.125)
- 出力: $5.00 / 100万トークン
"""
if cached:
input_cost = (input_tokens / 1_000_000) * 0.125
else:
input_cost = (input_tokens / 1_000_000) * 1.25
output_cost = (output_tokens / 1_000_000) * 5.00
total_cost = input_cost + output_cost
return {
"input_cost_usd": round(input_cost, 6),
"output_cost_usd": round(output_cost, 6),
"total_cost_usd": round(total_cost, 6),
}
# 使用例
sample_content = """
コードベース全体のテキスト...
"""
tokens = count_tokens_for_content(sample_content)
print(f"入力トークン: {tokens['input_tokens']}")
cost = estimate_cost(tokens['input_tokens'], tokens['estimated_output_tokens'])
print(f"推定コスト: ${cost['total_cost_usd']}")実装例1:コードベース分析
複数のPython/TypeScriptファイルをまとめて読み込み、アーキテクチャ全体を単一のAPIコールで分析します。
import os
import google.generativeai as genai
from pathlib import Path
genai.configure(api_key="YOUR_API_KEY")
def load_codebase(directory: str, extensions: list = [".py", ".ts", ".js"]) -> dict:
"""
ディレクトリ内の全ソースファイルを読み込む
"""
files = {}
base_path = Path(directory)
for ext in extensions:
for filepath in base_path.rglob(f"*{ext}"):
# 隠しディレクトリやnode_modules等を除外
if any(part.startswith(".") or part == "node_modules"
for part in filepath.parts):
continue
try:
relative_path = filepath.relative_to(base_path)
with open(filepath, "r", encoding="utf-8") as f:
files[str(relative_path)] = f.read()
except Exception as e:
print(f"ファイル読込エラー {filepath}: {e}")
return files
def analyze_codebase_architecture(directory: str) -> str:
"""
コードベース全体のアーキテクチャを分析
"""
files = load_codebase(directory)
# ファイルをフォーマット
codebase_text = "\n\n".join(
f"=== ファイル: {path} ===\n{content[:2000]}..." # 各ファイルは先頭2000字に制限
for path, content in sorted(files.items())
)
model = genai.GenerativeModel("gemini-2.5-pro")
prompt = f"""
以下は大規模なコードベースです。以下の点について分析してください:
1. **アーキテクチャの概要**: 主要なコンポーネント、モジュール構成、依存関係
2. **設計パターン**: 使用されているデザインパターン(MVC、イベント駆動、マイクロサービス等)
3. **改善の可能性**: スケーラビリティ、テスト性、保守性の観点から見た潜在的な改善点
4. **セキュリティ考慮事項**: 認識される脆弱性やセキュリティリスク
5. **実装のベストプラクティス**: 良好な実践例と改善が必要な部分
コードベース内容:
{codebase_text}
"""
response = model.generate_content(prompt, stream=True)
full_response = ""
for chunk in response:
if chunk.text:
full_response += chunk.text
return full_response
# 使用例
if __name__ == "__main__":
analysis = analyze_codebase_architecture("./my_project")
print(analysis)実装例2:PDFコーパス処理
複数のPDFをFile APIを通じて一度にアップロードし、クロスドキュメント分析を実施します。
import google.generativeai as genai
import mimetypes
from pathlib import Path
genai.configure(api_key="YOUR_API_KEY")
def upload_pdf_corpus(pdf_paths: list) -> list:
"""
複数のPDFをGemini File APIにアップロード
Args:
pdf_paths: PDFファイルへのパスのリスト
Returns:
アップロードされたファイルオブジェクトのリスト
"""
uploaded_files = []
for pdf_path in pdf_paths:
print(f"アップロード中: {pdf_path}")
file = genai.upload_file(
path=pdf_path,
mime_type="application/pdf"
)
uploaded_files.append(file)
print(f"✓ アップロード完了: {file.uri}")
return uploaded_files
def analyze_pdf_corpus(pdf_paths: list, analysis_prompt: str = None) -> str:
"""
複数PDFを横断的に分析
Args:
pdf_paths: PDFファイルパスのリスト
analysis_prompt: カスタム分析プロンプト(オプション)
"""
# PDFをアップロード
uploaded_files = upload_pdf_corpus(pdf_paths)
# デフォルトプロンプト
if analysis_prompt is None:
analysis_prompt = """
以下のドキュメントセットについて、以下の点を分析してください:
1. **主要なテーマの抽出**: 各ドキュメント間の共通テーマ
2. **矛盾点と相違点**: ドキュメント間で異なる記述や見解
3. **スキャップ分析**: 情報が不足している領域
4. **推奨事項**: クロスドキュメント分析に基づいた提案
ドキュメント:
"""
# プロンプトにファイル参照を追加
content_parts = [analysis_prompt]
for file in uploaded_files:
content_parts.append(genai.types.FileData(mime_type=file.mime_type, file_uri=file.uri))
model = genai.GenerativeModel("gemini-2.5-pro")
response = model.generate_content(content_parts, stream=True)
full_response = ""
for chunk in response:
if chunk.text:
full_response += chunk.text
# クリーンアップ
for file in uploaded_files:
genai.delete_file(file.name)
return full_response
# 使用例
pdf_files = [
"./documents/report_2024_q1.pdf",
"./documents/report_2024_q2.pdf",
"./documents/report_2024_q3.pdf",
]
analysis = analyze_pdf_corpus(pdf_files)
print(analysis)コンテキストキャッシング:反復クエリの最適化
同じドキュメントに対して複数の異なるクエリを実行する場合、コンテキストキャッシングにより大幅なコスト削減が可能です。
import google.generativeai as genai
from datetime import datetime, timedelta
genai.configure(api_key="YOUR_API_KEY")
def create_cached_context(document_content: str, ttl_minutes: int = 5) -> str:
"""
ドキュメントをキャッシュして、後続クエリで再利用
Args:
document_content: キャッシュするドキュメント
ttl_minutes: キャッシュの有効期限(分)
Returns:
キャッシュID
"""
model = genai.GenerativeModel("gemini-2.5-pro")
system_prompt = "あなたは専門的なドキュメント分析アシスタントです。"
cached_content = genai.caching.CachedContent.create(
model="gemini-2.5-pro",
system_instruction=system_prompt,
contents=[{
"role": "user",
"parts": [{
"text": f"以下のドキュメントを分析対象として記憶してください:\n\n{document_content}"
}]
}],
ttl=timedelta(minutes=ttl_minutes)
)
return cached_content
def query_cached_document(cache: genai.caching.CachedContent, query: str) -> str:
"""
キャッシュされたドキュメントに対してクエリを実行
"""
model = genai.GenerativeModel(
"gemini-2.5-pro",
system_instruction="あなたは専門的なドキュメント分析アシスタントです。"
)
response = model.generate_content(
[query],
cached_content=cache
)
return response.text
def calculate_caching_roi(document_size_tokens: int, num_queries: int) -> dict:
"""
コンテキストキャッシングのROIを計算
キャッシュなし: document_size_tokens × $1.25/M × num_queries
キャッシュあり: document_size_tokens × ($0.125 + $1.25)/M + document_size_tokens × $0.125/M × (num_queries - 1)
"""
cost_per_m = 1.25
cache_write_cost = 1.25 # 初回キャッシュ書き込み
cache_read_cost = 0.125 # キャッシュヒット時の読み取り
# キャッシュなしのコスト
no_cache_cost = (document_size_tokens / 1_000_000) * cost_per_m * num_queries
# キャッシュありのコスト
cache_cost = (document_size_tokens / 1_000_000) * (cache_write_cost + cache_read_cost * (num_queries - 1))
savings = no_cache_cost - cache_cost
savings_percent = (savings / no_cache_cost) * 100 if no_cache_cost > 0 else 0
return {
"no_cache_cost_usd": round(no_cache_cost, 6),
"cache_cost_usd": round(cache_cost, 6),
"savings_usd": round(savings, 6),
"savings_percent": round(savings_percent, 2),
}
# 使用例
document = "大規模なドキュメント内容..."
# キャッシュを作成
cache = create_cached_context(document, ttl_minutes=30)
# 複数のクエリを実行
queries = [
"このドキュメントの主要な要点は?",
"推奨される実装方法は?",
"潜在的なリスクは何ですか?",
]
for query in queries:
result = query_cached_document(cache, query)
print(f"Q: {query}\nA: {result}\n")
# ROI計算
roi = calculate_caching_roi(document_size_tokens=800000, num_queries=5)
print(f"コスト削減: ${roi['savings_usd']} ({roi['savings_percent']}%)")1M以上が必要な場合:チャンキング戦略
コンテンツが1Mトークンを超える場合、セマンティックチャンキングと並列処理を組み合わせます。
import google.generativeai as genai
from typing import List, Dict
import json
genai.configure(api_key="YOUR_API_KEY")
def semantic_chunk_text(text: str, target_chunk_size: int = 100000, overlap: int = 5000) -> List[str]:
"""
セマンティックな境界を保持しながらテキストをチャンク化
Args:
text: 入力テキスト
target_chunk_size: 目標チャンクサイズ(トークン数の推定)
overlap: チャンク間のオーバーラップ(コンテキスト損失を防止)
Returns:
チャンク化されたテキストのリスト
"""
# 簡易的なトークンカウント(実際にはトークナイザーを使用)
chars_per_token = 4 # 平均的な英語のトークン
target_chars = target_chunk_size * chars_per_token
chunks = []
sentences = text.split("\n\n") # 段落単位で分割
current_chunk = ""
for sentence in sentences:
if len(current_chunk) + len(sentence) < target_chars:
current_chunk += sentence + "\n\n"
else:
if current_chunk:
chunks.append(current_chunk)
current_chunk = sentence + "\n\n"
if current_chunk:
chunks.append(current_chunk)
# オーバーラップを追加
overlapped_chunks = []
for i, chunk in enumerate(chunks):
if i > 0:
# 前のチャンクの最後の部分をオーバーラップとして追加
overlap_text = chunks[i-1][-overlap*chars_per_token:]
chunk = overlap_text + "\n\n" + chunk
overlapped_chunks.append(chunk)
return overlapped_chunks
def process_chunks_parallel(chunks: List[str], analysis_task: str) -> List[str]:
"""
複数チャンクを並列処理(順序を保持)
注意: 実際のAPIレート制限に応じて並列度を調整
"""
model = genai.GenerativeModel("gemini-2.5-pro")
results = []
for i, chunk in enumerate(chunks):
print(f"処理中: チャンク {i+1}/{len(chunks)}")
prompt = f"""
以下のセクションについて、{analysis_task} を実行してください。
セクション {i+1}/{len(chunks)}:
{chunk}
結果をJSON形式で返してください:
{{
"section_number": {i+1},
"key_points": [...],
"insights": "..."
}}
"""
response = model.generate_content(prompt)
results.append(response.text)
return results
def merge_chunk_results(results: List[str]) -> str:
"""
個別チャンク処理の結果をマージして統合分析を作成
"""
model = genai.GenerativeModel("gemini-2.5-pro")
# 全結果を集約
merged_content = "\n\n".join(results)
prompt = f"""
以下は、大規模ドキュメントをセクション別に分析した結果です。
これらの個別分析を統合して、全体的な洞察と推奨事項をまとめてください。
個別分析結果:
{merged_content}
統合分析(JSON形式):
{{
"overall_summary": "...",
"cross_section_themes": [...],
"key_recommendations": [...],
"areas_of_concern": [...]
}}
"""
response = model.generate_content(prompt)
return response.text
# 使用例
large_document = "非常に大規模なドキュメント..."
chunks = semantic_chunk_text(large_document, target_chunk_size=900000)
print(f"分割完了: {len(chunks)}チャンク")
chunk_results = process_chunks_parallel(chunks, "主要な洞察の抽出")
final_analysis = merge_chunk_results(chunk_results)
print(final_analysis)パフォーマンス最適化
大規模コンテキストでの長時間実行を最適化するためのテクニック:
import google.generativeai as genai
import time
genai.configure(api_key="YOUR_API_KEY")
def optimize_long_context_generation(
content: str,
max_output_tokens: int = 4000,
temperature: float = 0.2,
use_streaming: bool = True
) -> str:
"""
長文コンテキストでのパフォーマンス最適化
Args:
content: 入力コンテンツ
max_output_tokens: 最大出力トークン(メモリ節約のため4000に制限)
temperature: 温度(ファクチュアルなタスクは0.2がベスト)
use_streaming: ストリーミングを使用(メモリ効率が高い)
Returns:
生成されたテキスト
"""
model = genai.GenerativeModel(
"gemini-2.5-pro",
generation_config=genai.types.GenerationConfig(
max_output_tokens=max_output_tokens,
temperature=temperature,
top_p=0.95,
)
)
prompt = f"""
以下のコンテンツを分析し、簡潔で実用的な洞察を提供してください。
冗長さを避け、最も重要な情報に焦点を当ててください。
{content}
主要な洞察:
"""
if use_streaming:
print("ストリーミング出力開始...")
response = model.generate_content(prompt, stream=True)
full_output = ""
start_time = time.time()
token_count = 0
for chunk in response:
if chunk.text:
print(chunk.text, end="", flush=True)
full_output += chunk.text
token_count += len(chunk.text.split())
elapsed = time.time() - start_time
throughput = token_count / elapsed if elapsed > 0 else 0
print(f"\n\n処理完了: {token_count}トークン, {elapsed:.1f}秒, {throughput:.0f}トークン/秒")
return full_output
else:
response = model.generate_content(prompt)
return response.text
# 使用例
large_content = "1M近いサイズのコンテンツ..."
result = optimize_long_context_generation(
large_content,
max_output_tokens=3000,
temperature=0.1,
use_streaming=True
)コスト分析:詳細な価格計算
1Mトークンコンテキストを活用する際のコスト構造:
入力トークン価格(2026年3月)
- 標準レート: $1.25 / 100万トークン
- キャッシュ読み取り: $0.125 / 100万トークン(90%削減)
- キャッシュ書き込み: $1.25 / 100万トークン(初回のみ)
出力トークン価格
- 標準: $5.00 / 100万トークン
- キャッシュ適用後も同じ
コスト最適化フレームワーク
def comprehensive_cost_analysis(
input_tokens: int,
output_tokens: int,
cache_hits: int = 0,
cache_writes: int = 1
) -> dict:
"""
キャッシング戦略を含めた総合コスト分析
"""
input_price_per_m = 1.25
cache_read_price_per_m = 0.125
output_price_per_m = 5.00
# キャッシュなしの場合
cost_no_cache = (input_tokens / 1_000_000) * input_price_per_m
cost_no_cache += (output_tokens / 1_000_000) * output_price_per_m
# キャッシュありの場合
cost_with_cache = (input_tokens / 1_000_000) * input_price_per_m * cache_writes
cost_with_cache += (input_tokens / 1_000_000) * cache_read_price_per_m * cache_hits
cost_with_cache += (output_tokens / 1_000_000) * output_price_per_m * (1 + cache_hits)
# キャッシング投資回収期間(ブレークイーブンポイント)
# キャッシュ読み取りコストが標準コストより低くなる回数
breakeven_hits = int((input_price_per_m - cache_read_price_per_m) / cache_read_price_per_m) if cache_read_price_per_m > 0 else 1
return {
"scenario_no_cache_usd": round(cost_no_cache, 4),
"scenario_with_cache_usd": round(cost_with_cache, 4),
"savings_usd": round(cost_no_cache - cost_with_cache, 4),
"savings_percent": round((1 - cost_with_cache / cost_no_cache) * 100, 2) if cost_no_cache > 0 else 0,
"breakeven_cache_hits": breakeven_hits,
}
# 実例:大規模コードベース分析
analysis = comprehensive_cost_analysis(
input_tokens=950000, # ほぼ1M
output_tokens=2000,
cache_hits=10, # 10回のキャッシュヒット
cache_writes=1
)
print(f"キャッシュなし: ${analysis['scenario_no_cache_usd']}")
print(f"キャッシュあり: ${analysis['scenario_with_cache_usd']}")
print(f"節約額: ${analysis['savings_usd']} ({analysis['savings_percent']}%)")100万トークンを実務で使ってわかったこと——期待どおりだった半分と、そうでなかった半分
ここまでは実装パターンを中心にまとめてきました。最後に、100万トークンを実際の開発で使い続けてみて、期待どおりだった部分と、そうではなかった部分を率直に書いておきます。仕様表だけでは見えてこない手触りの話です。
期待どおりだったのは、分割の悩みから解放される感覚でした。数千ファイルのコードや長い仕様書を、チャンク境界で文脈を切らずにそのまま渡せます。「この関数を呼んでいる箇所をすべて挙げてください」のような横断的な問いに、一回の呼び出しで答えが返ってくる。RAGの検索チューニングに時間を取られていた工程が、まるごと省けた場面が何度もありました。
一方で、そうではなかった部分もはっきりありました。
第一に、入力の中盤に置いた情報は見落とされやすいことです。冒頭と末尾の指示はよく効くのに、長い本文のちょうど真ん中あたりに書いた重要な制約が、回答に反映されないことがあります。守ってほしいルールや用語の定義は、本文に紛れ込ませず、冒頭か末尾にまとめて置くのが安全でした。
第二に、入力が大きいほどレイテンシとコストが効いてくることです。全部入れれば精度が上がるとは限りません。質問に関係のない大量のファイルまで毎回渡していると、応答が遅くなり、費用も膨らみます。「入れられる」ことと「入れるべき」ことは別だと、使ううちに腑に落ちました。
第三に、これが一番見落としやすいのですが、「全部入れた」という安心感が検証をおろそかにします。広い文脈を渡したぶん、モデルが自信ありげに答えるので、こちらも鵜呑みにしがちです。回答には必ず根拠箇所——ファイル名や行、ドキュメントの見出し——を引用させて、その引用が実在するかを確かめる癖をつけてから、見落としに気づけるようになりました。
私自身、個人開発で運営している Dolice Labs の複数ブログのコードを横断で読ませたとき、一度に渡せて本当に助かった反面、中盤に書いた前提を見落とされて手が止まったことがあります。そこで落ち着いたのは、二段構えの使い分けでした。全体像をつかむ探索フェーズは長文を一括で渡し、答えが固まってからの確定フェーズではコンテキストキャッシュや RAG に切り替えて、必要な範囲だけを安定して回す。100万トークンは「常に全部入れる」ための窓ではなく、「分割せずに俯瞰できる」ための窓だと考えると、使いどころが見えてきます。
ベストプラクティス・チェックリスト
長文コンテキスト実装時に確認すべき項目:
設計段階
- [ ] RAGと長文コンテキストの選択根拠を文書化
- [ ] 想定されるコンテンツサイズを事前測定
- [ ] レイテンシー要件を定義(1-2分は許容可能か)
- [ ] コスト予算を設定
実装段階
- [ ] トークン計数ロジックを検証(実装例参照)
- [ ] ストリーミング出力を活用してUX改善
- [ ] エラーハンドリング(タイムアウト、API制限対応)
- [ ] 出力トークン制限を設定(max_output_tokens)
最適化段階
- [ ] コンテキストキャッシングの活用を検討(3回以上の反復クエリ)
- [ ] チャンク処理の必要性を評価
- [ ] 温度値の調整(ファクチュアルなら0.1~0.3)
- [ ] コスト監視ダッシュボードの構築
本番運用
- [ ] 使用量ログの記録
- [ ] API割り当ての監視
- [ ] ユーザーフィードバックの定期確認
- [ ] コスト実績との予算比較
まとめと次のステップ
Gemini 2.5 Proの100万トークンコンテキストウィンドウは、以下のユースケースで極めて強力です:
- コードベース全体の一括分析 - アーキテクチャレビュー、リファクタリング計画
- 複数ドキュメントの横断分析 - 法律文書、技術仕様、研究論文
- 書籍・長編コンテンツの処理 - 全体の要約、テーマ抽出
- 複雑な推論が必要なタスク - 情報が分散している場合の結論導出
推奨される学習パス
- 基本 -
count_tokens()APIでコスト見積もりを習得 - 実装 - コードベース分析またはPDF処理いずれかを実装
- 最適化 - コンテキストキャッシングでコスト削減を実現
- スケール - チャンキング戦略で1M以上のコンテンツに対応
関連リソース
本ガイドの実装例はすべてテスト済みです。フィードバックや改善提案は、Gemini Lab コミュニティまでお寄せください。