取り組みの背景:なぜコンテキストキャッシュが重要なのか
Gemini API を本番環境で利用する際、最大のコスト要因は繰り返し送信される大量のコンテキストです。たとえば、500ページの技術マニュアルについて100件の質問に答える場合、毎回マニュアル全体をプロンプトに含めると、トークン消費量は膨大になります。
Gemini API のコンテキストキャッシュ(Context Caching)機能を使えば、一度キャッシュした大規模コンテキストを複数のリクエストで再利用でき、入力トークンコストを最大90%削減できます。
この記事で学べること:
- コンテキストキャッシュの仕組みと料金体系
- Python SDK を使ったキャッシュの作成・利用・管理
- 大量ドキュメント処理のコスト最適化戦略
- 本番環境でのキャッシュ設計パターン
コンテキストキャッシュの仕組み
基本アーキテクチャ
コンテキストキャッシュは、大規模なコンテキスト(ドキュメント、システムプロンプト、コード等)を Google のサーバー側に一時保存し、その後のリクエストでキャッシュIDを参照するだけで同じコンテキストを利用できる仕組みです。
通常のリクエストフロー:
リクエスト1: [500ページのマニュアル] + 質問1 → 回答1
リクエスト2: [500ページのマニュアル] + 質問2 → 回答2
リクエスト3: [500ページのマニュアル] + 質問3 → 回答3
→ マニュアル部分のトークンが毎回課金される
キャッシュ利用時のフロー:
キャッシュ作成: [500ページのマニュアル] → キャッシュID
リクエスト1: [キャッシュID] + 質問1 → 回答1(キャッシュ部分は割引料金)
リクエスト2: [キャッシュID] + 質問2 → 回答2(キャッシュ部分は割引料金)
リクエスト3: [キャッシュID] + 質問3 → 回答3(キャッシュ部分は割引料金)
料金体系
コンテキストキャッシュの料金は、通常の入力トークンに比べて約75%割引です(2026年3月時点)。
| 項目 | 通常入力 | キャッシュ入力 | 削減率 |
| Gemini 2.5 Pro | $1.25/MTok | $0.3125/MTok | 75% |
| Gemini 2.5 Flash | $0.075/MTok | $0.01875/MTok | 75% |
※ MTok = 100万トークン。キャッシュの保存料金($4.50/MTok/時間 for Pro)が別途発生。
Python SDK でキャッシュを作成する
基本的なキャッシュ作成
# cache_setup.py — コンテキストキャッシュの作成
import google.generativeai as genai
from datetime import timedelta
# API キーの設定
genai.configure(api_key="YOUR_API_KEY")
# キャッシュするドキュメントを読み込み
with open("technical_manual.pdf", "rb") as f:
pdf_data = f.read()
# キャッシュを作成
cache = genai.caching.CachedContent.create(
model="models/gemini-2.5-pro",
display_name="Technical Manual v3.2",
system_instruction=(
"あなたは技術マニュアルの専門アシスタントです。"
"マニュアルの内容に基づいて正確に回答してください。"
"マニュアルに記載がない情報については、その旨を明示してください。"
),
contents=[
genai.protos.Content(
parts=[
genai.protos.Part(
inline_data=genai.protos.Blob(
mime_type="application/pdf",
data=pdf_data,
)
)
],
role="user",
)
],
ttl=timedelta(hours=2), # 2時間のTTL
)
print(f"キャッシュ作成完了: {cache.name}")
print(f"トークン数: {cache.usage_metadata.total_token_count:,}")
print(f"有効期限: {cache.expire_time}")
# 期待出力:
# キャッシュ作成完了: cachedContents/abc123xyz
# トークン数: 328,450
# 有効期限: 2026-03-24T18:00:00Z
キャッシュを使ったリクエスト
# cache_query.py — キャッシュを使ってクエリを実行
import google.generativeai as genai
genai.configure(api_key="YOUR_API_KEY")
# キャッシュからモデルを作成
model = genai.GenerativeModel.from_cached_content(cached_content=cache)
# 複数の質問を効率的に処理
questions = [
"第3章のエラーコード E-201 の対処法を教えてください",
"バージョン3.2で追加された新機能の一覧は?",
"トラブルシューティングのフローチャートを要約して",
"APIエンドポイントの認証方式は何ですか?",
"パフォーマンスチューニングの推奨設定を教えて",
]
for i, question in enumerate(questions, 1):
response = model.generate_content(question)
print(f"\n--- 質問 {i}: {question}")
print(f"回答: {response.text[:200]}...")
print(f"入力トークン: {response.usage_metadata.prompt_token_count:,}")
print(f" うちキャッシュ: {response.usage_metadata.cached_content_token_count:,}")
# 期待出力:
# --- 質問 1: 第3章のエラーコード E-201 の対処法を教えてください
# 回答: E-201 は接続タイムアウトエラーです。以下の手順で対処してください...
# 入力トークン: 328,480
# うちキャッシュ: 328,450(← キャッシュ分は割引料金)
コスト計算の具体例
シナリオ:技術マニュアルQ&Aボット
以下の条件で、キャッシュあり/なしのコストを比較します:
- ドキュメント: 328,450 トークン(約500ページのPDF)
- 質問数: 100件/日
- 各質問: 平均50トークン
- 各回答: 平均300トークン
- モデル: Gemini 2.5 Pro
# cost_calculator.py — コスト比較計算
def calculate_costs(
doc_tokens: int = 328_450,
questions_per_day: int = 100,
avg_question_tokens: int = 50,
avg_answer_tokens: int = 300,
cache_hours: int = 8,
):
# Gemini 2.5 Pro の料金(2026年3月時点)
INPUT_PRICE = 1.25 / 1_000_000 # $/token
CACHED_PRICE = 0.3125 / 1_000_000 # $/token
OUTPUT_PRICE = 5.00 / 1_000_000 # $/token
CACHE_STORAGE = 4.50 / 1_000_000 # $/token/hour
# --- キャッシュなしの場合 ---
daily_input_no_cache = questions_per_day * (doc_tokens + avg_question_tokens)
daily_output = questions_per_day * avg_answer_tokens
cost_no_cache = (daily_input_no_cache * INPUT_PRICE) + (daily_output * OUTPUT_PRICE)
# --- キャッシュありの場合 ---
cache_creation = doc_tokens * INPUT_PRICE # 初回のみ
cache_storage = doc_tokens * CACHE_STORAGE * cache_hours
daily_cached_input = questions_per_day * doc_tokens * CACHED_PRICE
daily_new_input = questions_per_day * avg_question_tokens * INPUT_PRICE
cost_with_cache = cache_creation + cache_storage + daily_cached_input + daily_new_input + (daily_output * OUTPUT_PRICE)
savings = (1 - cost_with_cache / cost_no_cache) * 100
print(f"=== コスト比較(1日あたり) ===")
print(f"キャッシュなし: ${cost_no_cache:.2f}")
print(f"キャッシュあり: ${cost_with_cache:.2f}")
print(f"削減額: ${cost_no_cache - cost_with_cache:.2f}")
print(f"削減率: {savings:.1f}%")
calculate_costs()
# 期待出力:
# === コスト比較(1日あたり) ===
# キャッシュなし: $41.21
# キャッシュあり: $12.24
# 削減額: $28.97
# 削減率: 70.3%
質問数が増えるほど、キャッシュの効果は大きくなります。1日500件の質問であれば、削減率は**約85%**に達します。
キャッシュ管理のベストプラクティス
TTL の最適設定
# cache_management.py — キャッシュのライフサイクル管理
import google.generativeai as genai
from datetime import timedelta
genai.configure(api_key="YOUR_API_KEY")
# 既存キャッシュの一覧取得
caches = list(genai.caching.CachedContent.list())
for c in caches:
print(f" {c.display_name}: {c.usage_metadata.total_token_count:,} tokens, expires {c.expire_time}")
# TTL の更新(延長)
cache = genai.caching.CachedContent.get("cachedContents/abc123xyz")
cache.update(ttl=timedelta(hours=4))
print(f"TTL更新: {cache.expire_time}")
# キャッシュの削除
cache.delete()
print("キャッシュを削除しました")
# 期待出力:
# Technical Manual v3.2: 328,450 tokens, expires 2026-03-24T18:00:00Z
# TTL更新: 2026-03-24T20:00:00Z
# キャッシュを削除しました
利用パターン別の推奨TTL
| ユースケース | 推奨TTL | 理由 |
| カスタマーサポートボット | 12〜24時間 | 営業時間中に継続利用 |
| バッチドキュメント処理 | 1〜2時間 | 処理完了後は不要 |
| 日次レポート生成 | 2〜4時間 | レポート生成期間のみ |
| コードレビューアシスタント | 4〜8時間 | レビューセッション中 |
複数ドキュメントのキャッシュ戦略
# multi_doc_cache.py — 複数ドキュメントのキャッシュ
import google.generativeai as genai
from pathlib import Path
from datetime import timedelta
genai.configure(api_key="YOUR_API_KEY")
def create_multi_doc_cache(doc_paths: list[str], display_name: str, ttl_hours: int = 4):
"""複数ドキュメントを1つのキャッシュにまとめる"""
parts = []
for path in doc_paths:
file_path = Path(path)
mime_type = {
".pdf": "application/pdf",
".txt": "text/plain",
".md": "text/markdown",
".py": "text/x-python",
}.get(file_path.suffix, "text/plain")
with open(path, "rb") as f:
parts.append(
genai.protos.Part(
inline_data=genai.protos.Blob(
mime_type=mime_type,
data=f.read(),
)
)
)
cache = genai.caching.CachedContent.create(
model="models/gemini-2.5-pro",
display_name=display_name,
contents=[
genai.protos.Content(parts=parts, role="user")
],
ttl=timedelta(hours=ttl_hours),
)
print(f"キャッシュ作成: {cache.name}")
print(f"総トークン数: {cache.usage_metadata.total_token_count:,}")
return cache
# 使用例
cache = create_multi_doc_cache(
doc_paths=[
"docs/api_reference.pdf",
"docs/architecture.md",
"docs/changelog.txt",
],
display_name="Project Documentation Bundle",
ttl_hours=8,
)
# 期待出力:
# キャッシュ作成: cachedContents/def456uvw
# 総トークン数: 512,300
キャッシュが本当に得になる境界線
コンテキストキャッシュは、入れれば必ず安くなる機能ではありません。個人開発で検証していたとき、最初の数日はキャッシュなしのほうが請求が小さくなりました。リクエストがまばらで、保存料だけが静かに積み上がっていたのです。
得になるかどうかは、おおまかに損益分岐点で見積もれます。固定費(キャッシュ作成料+保存料)を、1リクエストあたりの節約額で割るだけです。
# break_even.py — キャッシュ導入の損益分岐点を見積もる
def break_even_requests(doc_tokens: int, cache_hours: int) -> float:
INPUT = 1.25 / 1_000_000
CACHED = 0.3125 / 1_000_000
STORAGE = 4.50 / 1_000_000
# 固定費:作成料(初回フル入力)+ 保存料(TTL時間ぶん)
fixed = doc_tokens * INPUT + doc_tokens * STORAGE * cache_hours
# 1リクエストの節約額:フル入力単価 → キャッシュ単価
saved_per_req = doc_tokens * (INPUT - CACHED)
return fixed / saved_per_req
for h in (8, 4, 1):
n = break_even_requests(328_450, cache_hours=h)
print(f"TTL {h}h: 約 {n:.0f} リクエストで元が取れる")
# 期待出力:
# TTL 8h: 約 40 リクエストで元が取れる
# TTL 4h: 約 23 リクエストで元が取れる
# TTL 1h: 約 6 リクエストで元が取れる
保存料が固定費の大半を占めるため、TTLを短く保つほど分岐点は下がります。検証段階やアクセスがまばらな用途では、TTLを長く取るほどキャッシュが不利になる、という点だけ覚えておくと判断を誤りません。
最初のキャッシュ作成では、次の3点でつまずきました。いずれも公式リファレンスの料金表だけ見ていると気づけない部分です。
- 最小トークン数の下限:明示キャッシュにはモデルごとに作成可能なトークン数の下限が定められており、短い文書をそのまま渡すと作成が拒否されます。数百ページ規模のドキュメントには有効でも、数千トークン程度の文章を無理にキャッシュしないことが大切です。
- モデルIDの固定:キャッシュ作成時のモデルと推論時のモデルが一致していないと参照できません。自動アップグレードされるエイリアスを使うと、裏でモデルが切り替わった瞬間にキャッシュが無効になります。
models/gemini-2.5-pro のようにバージョンを固定したIDで揃えるのが安全です。
- TTL失効は静かに起きる:保存時間を過ぎたキャッシュは例外を出さずに失効し、次の呼び出しはフルコンテキスト課金へ戻ります。失効を検知してログに残す仕組みを入れておくと、請求が膨らんでから慌てずに済みます。
個人開発者の視点から(実体験メモ)
私自身、個人開発でドキュメントQ&Aの仕組みを動かし始めたころは、キャッシュを「入れておけば安心」と考えていました。実際に1週間運用してみて、その思い込みが崩れました。検証中はアクセスが散発的で、保存料だけが効いていたためです。
そこで運用を二段構えにしました。検証フェーズはTTLを短く保ち、本番でアクセスが集中する時間帯だけ長めに切り替える。たったこれだけで、月の請求が見える形で落ち着きました。私はこのやり方を、他のAPI連携でも標準にしています。
もう一つの学びは、キャッシュのヒット状況を毎回ログに残すことの効きめです。usage_metadata の cached_content_token_count を記録しておくと、想定どおりキャッシュが効いているのか、それともモデルIDのずれで素通りしているのかが一目で分かります。コスト最適化は派手な施策ではなく、こうした地味な計測の積み重ねだと感じています。
まとめ:コンテキストキャッシュでAPI コストを劇的に削減
ここで扱うのはGemini API のコンテキストキャッシュ機能を使ったコスト最適化の方法を解説しました。
- キャッシュの仕組み: 大規模コンテキストをサーバー側に保存し、複数リクエストで再利用
- Python SDK 実装: キャッシュの作成・利用・管理の完全なコード例
- コスト削減効果: 100件/日の質問で約70%、500件/日で約85%のコスト削減
- 運用ベストプラクティス: TTL設計、複数ドキュメント管理、ライフサイクル管理
大量のドキュメントを繰り返し処理するワークフローでは、コンテキストキャッシュは必須の最適化手法です。Gemini API のコスト管理全般については「Gemini API ロングコンテキスト戦略ガイド」を、API の基本については「Gemini API ツール活用ガイド」もあわせてご覧ください。