0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini 3.8 Flash を GA 翌日に実測、課金される出力トークンの73%が思考ぶんだった

0
Posted at

はじめに

Gemini 3.8 Flash が 2026-09-02 に一般提供(GA)されました。導入価格は入力 $0.75 / 出力 $3.75 per 1M tokens で、Flash ティアとしては強気の性能をうたっています。

ただ、価格表の「出力 $3.75」をそのまま予算計算に使うと足りません。Gemini API の料金表は出力価格の見出しを "Output price (including thinking tokens)" と書いており、モデルが内部で使った思考トークンも出力として課金されます。実際に叩いてみると、返ってきた本文よりも思考ぶんのほうが多いという結果になりました。

対象読者は、Gemini API をコード生成やエージェントのバックエンドに組み込んでいて、モデルを 3.7 Flash から 3.8 Flash に切り替えるか検討している開発者です。GA 直後のモデルを本番に入れるときの注意点もあわせて扱います。

計測はすべて 2026-09-03 JST にクラウド環境から generativelanguage.googleapis.com/v1beta を直接叩いて取得したものです。ベンチマークスコアの引用ではなく、同一プロンプトを投げて usageMetadata を読んだ実測値です。

実測サマリー(gemini-3.8-flash / gemini-3.7-flash)

同じコード生成プロンプト(入力 93 トークン)を各モデルに 5 回ずつ投げた結果です。

項目 gemini-3.8-flash gemini-3.7-flash
成功回数 4 / 5(1 回は 503) 5 / 5
レイテンシ 中央値 2,996 ms 3,469 ms
レイテンシ 最小 / 最大 2,858 / 3,179 ms 3,160 / 3,833 ms
思考トークン 中央値 425 487
思考トークン 最小 / 最大 305 / 486 457 / 674
本文トークン 中央値 153 148
課金される出力に占める思考の割合 73.5% 76.7%
入力トークン 93 93

3.8 Flash は 3.7 Flash より 中央値で 473 ms 速く、思考トークンも 62 トークン少なく 済んでいます。同じ答えを出すまでの内部処理が軽くなっている方向です。

いっぽうで、どちらのモデルも 課金対象の出力トークンの 4 分の 3 前後が思考ぶん でした。「本文 153 トークンぶんの料金」と見積もると、実際の請求は 3.8 倍になります。

計測に使ったコード

usageMetadatathoughtsTokenCount を読むだけです。SDK を入れずに標準ライブラリで書けます。

import json, os, time, urllib.request

KEY = os.environ["GEMINI_API_KEY"]
URL = "https://generativelanguage.googleapis.com/v1beta/models/{m}:generateContent"

PROMPT = (
    "次の要件の Python 関数を書いてください。説明は不要でコードのみ。\n"
    "- 関数名 merge_ranges(ranges: list[tuple[int,int]]) -> list[tuple[int,int]]\n"
    "- 閉区間のリストを受け取り、重なる区間をマージして開始位置昇順で返す\n"
    "- 空リストは空リストを返す\n"
    "- 隣接(end+1 == next start)はマージしない"
)

def call(model: str, generation_config: dict | None = None) -> dict:
    body = {"contents": [{"parts": [{"text": PROMPT}]}]}
    if generation_config:
        body["generationConfig"] = generation_config
    req = urllib.request.Request(
        URL.format(m=model),
        data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json", "x-goog-api-key": KEY},
    )
    started = time.time()
    with urllib.request.urlopen(req, timeout=180) as res:
        data = json.loads(res.read())
    usage = data.get("usageMetadata", {})
    return {
        "ms": int((time.time() - started) * 1000),
        "input": usage.get("promptTokenCount"),
        "thoughts": usage.get("thoughtsTokenCount", 0),
        "output": usage.get("candidatesTokenCount"),
        "total": usage.get("totalTokenCount"),
    }

print(call("gemini-3.8-flash"))
# => {'ms': 2858, 'input': 93, 'thoughts': 364, 'output': 159, 'total': 616}

thoughtsTokenCount は既定のレスポンスに含まれています。フラグを立てる必要はありません。ここを読まずに candidatesTokenCount だけで原価を計算していると、請求書を見てから気づくことになります。

課金トークンの内訳を図にすると

1 リクエストで料金が発生する場所は 3 か所ありますが、単価が同じなのは思考と本文だけです。

中央値で計算すると 1 リクエストあたり、入力が 93 × $0.75 / 1M ≈ $0.00007、出力が 578 × $3.75 / 1M ≈ $0.00217 です。コストの 97% が出力側 で、そのうち 73.5% が画面に出ない思考ぶんです。

導入価格は 2026-12-31 までで、2027-01-01 から入力 $1.50 / 出力 $7.50 に倍増します。年明け以降を見込んだ予算では、この計算をそのまま 2 倍してください。

思考トークンは設定で消せる(ただし段階は限られる)

generationConfig.thinkingConfig で思考を抑えられます。同じプロンプトで 4 通り試した結果が次のとおりです。

設定 HTTP 思考トークン 本文トークン 合計トークン
指定なし(既定) 200 330 148 571
thinkingLevel: "LOW" 200 0 159 252
thinkingBudget: 0 200 0 159 252
thinkingLevel: "MINIMAL" 400

LOW を指定すると思考トークンが 0 になり、合計トークンが 571 から 252 へ 56% 減りました。今回のような仕様が固まった関数を書かせるタスクでは、出力コードの内容に差はありませんでした。

MINIMAL は 400 が返ります。エラーメッセージは実測で次のとおりでした。

{
  "error": {
    "code": 400,
    "message": "Thinking level MINIMAL is not supported for this model. Please retry with other thinking level.",
    "status": "INVALID_ARGUMENT"
  }
}

公式の Thinking guide でも 3.8 Flash がサポートするのは low / medium / high の 3 段階とされており、実測のエラーと整合します。他の Gemini 3 系モデル向けに書いた設定を使い回すと、ここで落ちます。モデル ID を切り替えるときは thinkingLevel の値が新モデルで受理されるか、最小のリクエストで先に確認しておくと安全です。なお thinkingLevel の 4 段階そのものの比較は別記事(gemini-3-thinking-level-bench)で扱っているので、本記事では 3.8 Flash で受理される値の確認にとどめます。

注意点として、LOW を指定した回のレイテンシは 8,699 ms と既定(2,796 ms)より遅くなる回がありました。思考を切ればいつでも速くなるわけではなく、コスト削減の効果のほうが確実です。

GA 翌日のモデルは 503 を返すことがある

もうひとつ、切り替えの前に知っておきたい挙動があります。generateContent が 503 を返す回が混ざりました。

{
  "error": {
    "code": 503,
    "message": "This model is currently experiencing high demand. Spikes in demand are usually temporary. Please try again later.",
    "status": "UNAVAILABLE"
  }
}

十分な間隔をあけて投げた試行だけを数えると、3.8 Flash は 11 回中 3 回が 503、3.7 Flash は 6 回中 0 回でした。試行回数が少ないので割合そのものは目安ですが、同じ時間帯・同じ API キーで 3.7 Flash が一度も落ちなかった のに対し 3.8 Flash だけが落ちた、という差は再現しました。いずれも 1 回リトライすれば通っています。

紛らわしいのは、モデルの存在確認は成功する点です。

確認方法 結果
GET /v1beta/models models/gemini-3.8-flash が一覧に出る(version 3.0 / 入力 1,048,576 / 出力 65,536)
POST .../gemini-3.8-flash:countTokens 200(497 ms)
POST .../gemini-3.8-flash:generateContent 200 と 503 が混在

models.list にも出るし countTokens も通るので、疎通確認だけを見ていると「使える」と判断してしまいます。メタデータ側と推論側で可用性が別物になっているので、ヘルスチェックは実際に生成させる呼び出しで行ってください。

リトライとフォールバックの実装

503 は一時的なので、指数バックオフで再試行し、それでも駄目なら 3.7 Flash に落とす形が現実的です。INVALID_ARGUMENT(400)はリトライしても直らないので、再試行の対象から外します。

import json, time, urllib.error, urllib.request

RETRYABLE = {429, 500, 502, 503, 504}

def generate(model: str, body: dict, retries: int = 3):
    req = urllib.request.Request(
        URL.format(m=model),
        data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json", "x-goog-api-key": KEY},
    )
    for attempt in range(retries + 1):
        try:
            with urllib.request.urlopen(req, timeout=180) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            if e.code not in RETRYABLE or attempt == retries:
                raise
            time.sleep(2 ** attempt)  # 1, 2, 4 秒

def generate_with_fallback(body: dict):
    try:
        return generate("gemini-3.8-flash", body)
    except urllib.error.HTTPError as e:
        if e.code not in RETRYABLE:
            raise
        return generate("gemini-3.7-flash", body)

フォールバック先を 3.7 Flash にしても、入力トークン数と thinkingConfig の受け付け方は同じでした。呼び出し側のコードを分岐させずに済みます。

なお、間隔を空けずに連続で投げると 429 が返ります。1 秒間隔で 15 回ずつ流したところ、3.8 Flash が 15 回中 6 回、3.7 Flash が 15 回中 1 回 429 でした。これは無料枠の分あたりリクエスト数の制限で、モデルの可用性とは別の話です。スループットを測りたい場合は、有料ティアに切り替えるか間隔を広げてください。

3.7 Flash から乗り換えるかの判断材料

今回の実測から言えることを整理します。

観点 結論
レイテンシ 3.8 Flash が中央値で 14% 速い
思考トークン 3.8 Flash が中央値で 13% 少ない(同じ単価なのでそのままコスト差)
出力品質 今回の仕様固定タスクでは差なし。判断材料にならない
安定性 GA 翌日時点では 3.8 Flash のほうが 503 を返しやすい
設定の互換性 thinkingLevel: MINIMAL は 3.8 Flash で 400。移行時に要確認

速度とトークン効率では 3.8 Flash が優位ですが、その差は数十パーセントで、思考トークンを LOW で切ったときの 56% 減のほうが効きます。まずはモデルを固定したまま thinkingConfig を見直し、そのうえでリトライとフォールバックを用意してから切り替える、という順序が無難です。

タスクの性質によっては思考を切ると品質が落ちるので、切ってよいかどうかは自分のプロンプトで確かめてください。今回のように出力仕様が決まりきっているタスクは、思考を切る効果が大きい側の代表例です。

まとめ

  • Gemini API の出力課金には思考トークンが含まれ、今回の実測では課金される出力の 73.5%(3.8 Flash)が思考ぶんだった
  • thinkingLevel: "LOW" または thinkingBudget: 0 で思考トークンは 0 になり、合計トークンが 56% 減った。MINIMAL は 3.8 Flash では 400 になる
  • GA 翌日の 3.8 Flash は generateContent が 503 を返す回が混ざった。models.listcountTokens は通るため、疎通確認だけでは判別できない
  • 503 は 1 回のリトライで通ったので、指数バックオフと 3.7 Flash へのフォールバックを用意しておけば実用できる

原価計算をするときは candidatesTokenCount ではなく thoughtsTokenCount との合計を見てください。これを外すと見積もりが 4 分の 1 になります。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?