同じ長い前提文を毎回モデルに送っていて、「このトークン代、積み重なると馬鹿にならないな」と感じたことはないでしょうか。私はあります。2014年から個人でアプリを作り続けていて、AdMob の収益とサーバー費用を毎月にらみながら運営してきたので、わずかな単価でも「呼び出し回数 × 単価」で効いてくる感覚が体に染みついています。
コンテキストキャッシュは、まさにその「毎回同じ前提を送る」無駄を消すための仕組みです。ただ、公式の料金ページの見出しには大きく書かれていない落とし穴があって、設定を一つ忘れると、節約するつもりが逆に固定費を積み上げてしまいます。今日はその回避のしかたを、実際のコードと一緒に書き留めておきます。
キャッシュには「保管している間ずっと」課金される
まず一番大事なところから。明示的なコンテキストキャッシュ(explicit cache)は、保管しているだけで時間あたり課金されます。Gemini API の場合、キャッシュ保管は概ね 100万トークンあたり 1 時間 1 ドル前後です。
ここで計算してみます。100万トークン分のキャッシュを作って、使い終わったあと削除を忘れて放置したとします。
- 1時間あたり 約 1 ドル
- 24時間放置すると 約 24 ドル
- 一週間気づかなければ 約 168 ドル
呼び出していなくても、です。これが「節約のために入れたのに請求が増えていた」というパターンの正体です。トークン入力代を下げても、消し忘れた保管料がそれを上回ってしまえば本末転倒になります。
私はアプリの広告まわりで「使っていないのに毎月引かれている費用」に何度か泣かされてきたので、この手の固定費には反射的に警戒します。キャッシュは「作ったら必ず消す」までを一つの動作だと考えるのが安全です。
最小トークン数を下回ると、キャッシュは静かに効かない
もう一つ、ドキュメントを流し読みすると見落としやすい挙動があります。コンテキストキャッシュには有効になる最小トークン数があり、それを下回ると黙って無効化されます。
- Gemini 2.5 Flash: おおむね 1,024 トークン以上
- Gemini 2.5 Pro: おおむね 4,096 トークン以上
つまり、短いシステムプロンプトをキャッシュしたつもりでも、しきい値に届いていなければキャッシュは作られず、毎回フルで課金されます。エラーが出るわけではないので、「キャッシュしたはずなのに料金が下がらない」と首をかしげることになります。私も最初、短い指示文をキャッシュしようとして「効いていない」と勘違いしました。キャッシュにするのは、PDF やマニュアル、長い仕様書のように確実にしきい値を超える大きな前提だけにするのが現実的です。
実際のコード:作る・使う・必ず消す
新しい Google Gen AI SDK での基本形です。ポイントは ttl を短めに設定し、最後に必ず delete まで通すことです。
from google import genai
from google.genai import types
client = genai.Client() # 環境変数 GEMINI_API_KEY を読み込む
# 1. 長い前提(ここでは大きめのPDF)をキャッシュ化する
# ttl を 600 秒にして「最大でも10分で自然消滅」させておく
cache = client.caches.create(
model="gemini-2.5-flash",
config=types.CreateCachedContentConfig(
contents=[
types.Part.from_uri(
file_uri="gs://example-bucket/long-spec.pdf",
mime_type="application/pdf",
),
],
system_instruction="あなたはこの仕様書に基づいて回答するアシスタントです。",
display_name="spec-cache",
ttl="600s", # 設定しないと既定は1時間。短くするほど保管料は減る
),
)
# 2. キャッシュを参照して生成する(前提を毎回送らなくてよい)
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="第3章の要点を3つ挙げてください。",
config=types.GenerateContentConfig(cached_content=cache.name),
)
print(response.text)
# 3. 使い終わったら必ず削除する(ttl が切れる前でも明示削除が確実)
client.caches.delete(name=cache.name)ttl を短くしておけば、削除コードが何かの例外で飛ばされても、保管料の上限が「最大10分ぶん」に抑えられます。私は「明示削除」と「短い ttl」の二重の保険をいつもかけています。後始末をコードに任せきりにしないのは、宮大工だった祖父たちの「道具は使ったら必ず元の場所へ戻す」という所作を、なぜか作業のたびに思い出すからかもしれません。
消し忘れを防ぐ:キャッシュの棚卸し
それでも消し忘れは起きます。定期的にキャッシュ一覧を確認して、孤児になっているものを掃除する小さなスクリプトを置いておくと安心です。
from google import genai
client = genai.Client()
# 現在残っているキャッシュをすべて表示する
for cache in client.caches.list():
print(cache.name, cache.display_name, "expires:", cache.expire_time)
# 不要と判断したらその場で削除する
# client.caches.delete(name=cache.name)これを CI や日次のバッチで回しておけば、「気づいたら何日も保管料を払っていた」という事故をかなり防げます。アプリ運営で月次の費用明細を必ず見る習慣があったので、API のキャッシュも同じように棚卸しの対象に入れています。
小さい前提なら、自分で抱え込まないという選択
最近の Gemini 3 Flash 系では、明示キャッシュを自分で管理しなくても、繰り返し送られる入力を自動でキャッシュする仕組み(暗黙キャッシュ)が標準で効くようになってきました。条件が合えば入力トークン代が大きく下がります。さらに、即時性が要らない大量処理なら Batch API に回すことで、別途コストを抑えつつレート制限も緩められます。
ここから見えてくる判断基準はシンプルです。大きな固定前提を何度も使い回すなら明示キャッシュ、ただし短い ttl と確実な削除をセットにする。前提が小さい、あるいは扱いを自分で持ちたくないなら、暗黙キャッシュや Batch に寄せる。 どちらが正解ということではなく、節約したい金額と、後始末の手間のどちらを取るかの選択だと感じています。
まずは手元の一番長いプロンプトを一つ選んで、トークン数を数えてみてください。しきい値を超えているなら、上のコードに当てはめて「作る・使う・消す」を一周させてみる。それだけで、毎月の請求書の見え方が少し変わるはずです。