検証ステップに Gemini 3 Deep Think を入れた翌週、API の請求見込みが普段の約3倍になっていました。
私は個人開発で4つの技術ブログ(Dolice Labs)の記事生成を自動化しているのですが、生成した記事を公開前に「事実の言い過ぎがないか」「コード例が壊れていないか」を機械的にチェックする工程を持っています。これまでは Flash に採点させていました。2026年6月にあった更新で Deep Think が API からも一部呼べるようになったので、「採点の精度を上げたい」と単純に差し替えてみたのです。
結果は精度こそ上がったものの、コストの上がり方が予想を超えていました。Deep Think は答えを出す前に長い推論を回します。その推論トークンが、入力・出力とは別に請求へ効いてきます。検証は1日に何十件も走るので、1件あたりの差が積み上がって効いてくるわけです。
この記事は、その膨張を thinking_level とコストガードレールで抑え込み、最終的に Flash との二段構えに落ち着くまでの実装記録です。Deep Think を「賢いけれど高い道具」として、必要な場面にだけ使うための線引きを共有します。
なぜ Deep Think の検証は高くつくのか
通常のモデル呼び出しでは、コストはおおむね「入力トークン + 出力トークン」で見積もれます。ところが Deep Think のような深い推論モデルは、最終的な回答を書く前に内部で長い思考を展開します。この思考も計算であり、課金対象です。
検証タスクは特にこれと相性が悪いと感じました。「この記事に誇張表現はありますか」という問いは、Deep Think からすると考えがいのある問題に見えてしまい、勝手に深く考え込みます。出力は「OK」か「要修正」の短い判定で十分なのに、そこへ至るまでの思考が膨らむのです。
つまり Deep Think の検証コストは、出力の短さからは想像できません。見えにくい推論トークンこそが主役で、ここを制御しない限り単価は安定しません。
thinking_level で推論の深さに上限を作る
最初に効いたのは、推論の深さそのものに上限を設けることでした。Gemini 3 系では thinking_level で思考の度合いを指定できます。検証のように「正解が短く、判断基準が明確」なタスクでは、最大まで考えさせる必要はありません。
from google import genai
from google.genai import types
# GEMINI_API_KEY を環境変数から読み込みます
client = genai.Client()
def verify_article (article_text: str ) -> str :
"""記事を検証し、JUDGE: OK / JUDGE: REVISE のどちらかを含む短い結果を返します。"""
prompt = (
"あなたは技術記事の事実確認を行う校閲者です。"
"次の記事に、誇張表現・壊れたコード例・明らかな事実誤りがあるかを判定してください。"
"問題がなければ 'JUDGE: OK'、修正が必要なら 'JUDGE: REVISE' を1行目に書き、"
"理由を3行以内で続けてください。 \n\n --- \n " + article_text
)
response = client.models.generate_content(
model = "gemini-3-deep-think" ,
contents = prompt,
config = types.GenerateContentConfig(
# 検証は深く考えすぎる必要がないため low に固定します
thinking_config = types.ThinkingConfig( thinking_level = "low" ),
# 出力も短く保ち、無駄な長文を防ぎます
max_output_tokens = 200 ,
),
)
return response.text
print (verify_article( "(ここに記事本文)" ))
# 期待される出力例:
# JUDGE: REVISE
# 3つ目のコード例で client.generate() を呼んでいますが、正しくは client.models.generate_content() です。
thinking_level を high のままにしていたのが、コスト膨張のいちばん大きな原因でした。low に落とすと、検証の正答率はほとんど変わらないのに、1件あたりの思考トークンが大きく減りました。深く考えてほしい設計・数学の問題と、短い判定を返すだけの検証とでは、必要な思考量がまるで違うのです。
私はこの場面では low を既定にして、後述する「灰色のケース」だけ high に上げる運用を好んでいます。
検証に渡すコンテキストを削る
次に効いたのが、入力そのものを薄くすることでした。最初の実装では、記事本文に加えて執筆ガイドライン全文や過去記事の抜粋まで一緒に渡していました。「材料が多いほど正確に判定してくれるはず」という思い込みです。
実際には逆でした。コンテキストが長いほど Deep Think はそれを読み込み、関連を考え、思考が伸びます。判定に本当に必要なのは「記事本文」と「判定基準を数行に圧縮したルール」だけでした。
# ❌ 重い: ガイドライン全文(数千トークン)を毎回同梱していました
RULES_FULL = open ( "writing_guidelines_full.md" ).read()
# ✅ 軽い: 判定に必要な観点だけを箇条書きに圧縮します
RULES_COMPACT = (
"判定観点: (1) 数値や実績の誇張がないか "
"(2) コード例が構文として成立しているか "
"(3) 公式仕様と矛盾する断定がないか"
)
def build_prompt (article_text: str ) -> str :
return f " { RULES_COMPACT }\n\n 以下の記事を判定してください。 \n --- \n{ article_text } "
判定基準をルールブックのまま渡すのではなく、検証に効く観点だけを数行へ要約してから渡す。これだけで入力トークンと、それに引きずられる思考トークンの両方が下がりました。検証の文脈では「情報を足す」より「判断軸を絞る」ほうが、精度とコストの両面で有利だと感じています。
コスト天井ガードレールを実装する
thinking_level と入力削減で単価は下がりましたが、それでも「想定外に長い記事が来たとき」や「呼び出し回数が跳ねたとき」に備えて、上限そのものを仕組みで持っておきたくなりました。検証は自動で走るので、暴走したときに気づくのが請求のタイミングでは遅すぎます。
そこで、1回の検証で消費したトークンを usage_metadata から読み取り、日次の予算に対して累積を見張るガードレールを挟みました。
import time
# 1日あたりの検証コスト上限(USD)。超えたら Deep Think を止めます。
DAILY_BUDGET_USD = 2.0
# 概算用の単価(実際の料金表に置き換えてください)。
# 推論トークンは出力側のレートで概算しています。
PRICE_PER_1K_INPUT = 0.0003
PRICE_PER_1K_OUTPUT = 0.0025
_spent_today = 0.0
def estimate_cost (usage) -> float :
in_tok = usage.prompt_token_count or 0
# 思考トークンと出力トークンを合算して出力レートで概算します
out_tok = (usage.candidates_token_count or 0 ) + (usage.thoughts_token_count or 0 )
return (in_tok / 1000 ) * PRICE_PER_1K_INPUT + (out_tok / 1000 ) * PRICE_PER_1K_OUTPUT
def verify_with_guardrail (article_text: str ):
global _spent_today
if _spent_today >= DAILY_BUDGET_USD :
# 予算超過時は Deep Think を使わず、安価な Flash にフォールバックします
return flash_fallback(article_text), "fallback"
response = client.models.generate_content(
model = "gemini-3-deep-think" ,
contents = build_prompt(article_text),
config = types.GenerateContentConfig(
thinking_config = types.ThinkingConfig( thinking_level = "low" ),
max_output_tokens = 200 ,
),
)
_spent_today += estimate_cost(response.usage_metadata)
return response.text, "deep-think"
def flash_fallback (article_text: str ):
r = client.models.generate_content(
model = "gemini-3.5-flash" ,
contents = build_prompt(article_text),
config = types.GenerateContentConfig( max_output_tokens = 200 ),
)
return r.text
ここで大事なのは、thoughts_token_count(思考トークン)を見積もりに必ず含めることです。出力トークンだけを見て予算を組むと、Deep Think では実際の消費を大きく下振れして見積もってしまい、ガードレールが用をなしません。見えにくいトークンほど明示的に数える。これが Deep Think を運用に乗せるうえでの肝でした。
二段構えにする — Flash で一次判定、Deep Think は灰色だけ
最終的にいちばん効いたのは、すべてを Deep Think に通すのをやめたことです。検証対象の大半は、Flash でも自信を持って「OK」と判定できる素直な記事でした。Deep Think の出番は、Flash が「OK とも REVISE とも言い切れない」と感じる灰色のケースだけで十分だったのです。
そこで、まず Flash に判定と一緒に確信度を返させ、確信度が低いときだけ Deep Think に昇格させる流れにしました。
import json
def first_pass_flash (article_text: str ) -> dict :
"""Flash で一次判定し、判定と確信度(0.0-1.0)を JSON で返します。"""
prompt = (
build_prompt(article_text)
+ " \n\n 結果を JSON で返してください。"
'{"judge": "OK|REVISE", "confidence": 0.0-1.0}'
)
r = client.models.generate_content(
model = "gemini-3.5-flash" ,
contents = prompt,
config = types.GenerateContentConfig(
response_mime_type = "application/json" ,
max_output_tokens = 120 ,
),
)
return json.loads(r.text)
def verify_two_stage (article_text: str ):
first = first_pass_flash(article_text)
# 確信度が高ければ Flash の判定を採用し、Deep Think は呼びません
if first[ "confidence" ] >= 0.8 :
return first[ "judge" ], "flash"
# 灰色のケースだけ Deep Think に昇格させます
result, source = verify_with_guardrail(article_text)
judge = "OK" if "JUDGE: OK" in result else "REVISE"
return judge, source
この構成にしてから、Deep Think に回る件数は全体の2割ほどに落ち着きました。精度を担保したい本当に難しいケースにだけ高い道具を使い、残りは安い道具で素早く捌く。当たり前のようでいて、最初は「精度を上げたいから全部 Deep Think で」と考えてしまっていたので、ここに気づくまでに少し回り道をしました。
二段構えの考え方そのものは、別の記事Flash で生成し、迷ったときだけ Deep Think に検証させる二段パイプライン でも扱っています。どのワークロードから新しいモデルへ移すかという観点はGemini 3.5 Flash GA をどこから差し替えるか が参考になります。
踏んだ3つの落とし穴
1. thinking_level を指定し忘れて既定の深さで走っていた
thinking_config を渡さずに呼ぶと、モデルが既定の深さで思考します。検証のような軽い判定では、これが過剰になりがちです。明示的に low を指定しないと「考えなくていい場面で考えてしまう」ので、まず指定漏れを疑うとよいです。
2. 思考トークンを請求見積もりから落としていた
usage_metadata の thoughts_token_count を見ずに、出力トークンだけで日次コストを計算していた時期がありました。これだとガードレールが甘くなり、実際の請求と乖離します。Deep Think を使うなら、思考トークンを必ず見積もりに足してください。
3. JSON 出力を Deep Think に強く縛りすぎた
二段構えにする前、Deep Think 側にも厳密な JSON スキーマを要求していたのですが、深い推論の途中経過とスキーマ整形がぶつかり、ときどき壊れた JSON が返りました。Deep Think には自然文で短く判定を返させ、機械可読な整形は Flash 側に任せるほうが安定しました。
実際に効いた数値
切り替え前後で、検証1件あたりの平均コストはおよそ3分の1になりました。内訳としては、thinking_level を low に下げた効果、入力コンテキストを削った効果、そして二段構えで Deep Think の呼び出し件数を全体の約2割に絞った効果が積み重なっています。精度は、Flash 単独のときに見逃していた「もっともらしいが誤ったコード例」を拾えるようになり、むしろ上がりました。
数字以上に大きかったのは、コストの上限が読めるようになったことです。日次予算とガードレールがあるので、検証件数が増えても請求が暴走しない。自動化を安心して回せる状態になったことが、いちばんの収穫でした。
Deep Think を検証に組み込もうとしている方は、まず thinking_level="low" を明示し、thoughts_token_count を見積もりに入れた小さなガードレールを1つ書くところから始めてみてください。そこを押さえるだけで、コストの読みやすさが大きく変わります。