11月13日、TheGenAIGirl(code, community, and GenAI)が「Stop Burning LLM Tokens Just to Run an if Statement」と題した記事を公開した。LLMをルーティングや条件分岐に使う際のトークン無駄遣い問題と、それを解消するGemini Decisions APIの実践的な使い方について解説している。
「ifのためにLLMを起こすな」という問題意識
バックエンドでLLMを使っていると、こんな処理が散見される。
# チケットの部署振り分けにLLMを使う例(アンチパターン)
response = llm.generate("このチケットはbillingかshippingどちらに属しますか?")
if response == "billing":
send_to_billing_queue()
数兆トークンを学習した大規模モデルを、"billing" の一単語を吐かせるだけのために起動している。記事はこの構造に3つの問題を指摘する。
- 出力トークンのコスト:どんなに短いJSONでも、自己回帰的な生成にはレイテンシとトークン課金が発生する。
- 確率が見えない:
"billing"と出力されても、それが99%の確信なのか51%の僅差なのかがわからない。ヒューマンエスカレーション(人間への判断委譲)の閾値を設計できない。 - 巨大プロンプトのスパゲッティ化:15個のルールを1つのプロンプトに詰め込むと、エッジケースを修正するたびに別の箇所が壊れる。
Gemini Decisions APIとは何か
gemini-decision-1 はこの層に特化して設計されたモデルだ。テキスト生成ではなく、クローズドエンドな質問に対するキャリブレーション済み確率を返す。最大の特徴は 出力トークン数がゼロ(total_output_tokens: 0)である点だ。
APIの呼び出しは client.decisions.create で行い、共有 input(テキスト・画像・音声・動画・PDF)と、1〜1000個の独立した questions を渡す。
質問には3種類のプリミティブがある。
| 型 | 用途 | 返り値 |
|---|---|---|
noul |
Yes/No判定(No / Unknown / Low の略) | 確率(0.01〜0.99)+信頼度 |
choice |
択一選択(2〜255択) | 選択肢キー+全確率マップ |
score |
順序付きルーブリック評価 | 期待インデックス(例: 1.84)+各レベル確率 |
noul という型名は元記事の説明によると "No/Unknown/Low" を表す略称で、Yes寄りなら1.0、No/不明/Low寄りなら0.0に近い値を取る設計だ。
すべての質問は1回のフォワードパスで並列処理され、入力トークンは質問数に関わらず1回分しかカウントされない。
まず動かしてみる:ゼロトークンの分類
SDKのインストールから始める。
pip install -U "google-genai>=3.2.0"
Eコマースの返品理由が「製品の欠陥」か「好みの問題」かを判定する例:
from google import genai
client = genai.Client(http_options={"api_version": "v1beta"})
result = client.decisions.create(
model="gemini-decision-1",
input="Customer returned the sweater because they preferred green over blue.",
questions={
"is_defect": {
"type": "noul",
"instructions": "Is the customer reporting a product defect?",
"criteria": {
"true_description": "The product itself is broken, damaged, or faulty.",
"false_description": "Preference, sizing, shipping, or pricing complaint.",
},
},
},
)
answer = result.answers["is_defect"]
print(f"Probability (NOUL): {answer.noul:.4f}")
print(f"Confidence Score: {answer.confidence:.4f}")
print(f"Output Tokens: {result.usage.total_output_tokens}")
結果:
Question: is_defect
- Probability (NOUL): 0.0115
- Confidence Score: 0.9885
- Output Tokens: 0
「欠陥でない」確率が98.85%の信頼度で返ってくる。出力トークンはゼロ。
注意点:noul の値域は必ず 0.01 〜 0.99 に収まる設計になっている。answer.noul > 0.99 という閾値を書いても永遠にマッチしない。
マルチモーダル対応:画像・音声・動画を一括評価
Decisions APIの真価は、テキスト以外のモダリティを同一のフォワードパスでまとめて評価できる点にある。保証クレームの審査を例に、JSON・画像・音声・動画の4種類を同時に評価するコードが紹介されている。
result = client.decisions.create(
model="gemini-decision-1",
input=[
{"type": "text", "text": json.dumps(claim_metadata) + "\n"},
{"type": "image", "data": photo_b64, "mime_type": "image/jpeg", "resolution": "medium"},
{"type": "audio", "uri": voice_note_url, "mime_type": "audio/mp3"},
{"type": "video", "uri": demo_video_url, "mime_type": "video/mp4", "resolution": "low"},
],
questions={
"photo_shows_damage": {"type": "noul", ...},
"audio_describes_leak": {"type": "noul", ...},
"video_shows_hazard": {"type": "noul", ...},
"evidence_consistent": {"type": "score", ...},
"triage_queue": {"type": "choice", ...},
},
)
記事が示す条件分岐の実装で重要なのが評価順序だ。
if a["evidence_consistent"].score < 1.0:
decision = "fraud_or_mismatch"
elif a["video_shows_hazard"].noul > 0.50:
decision = "safety_escalation"
elif a["evidence_consistent"].score >= 1.7 and a["triage_queue"].confidence >= 0.80:
decision = a["triage_queue"].choice
else:
decision = "manual_review"
evidence_consistent(証拠の整合性)を先にチェックしているのが肝だ。 video_shows_hazard を先に判定すると、無関係な焚き火動画をアップロードしただけで安全性エスカレーションが発火してしまう。整合性を確認してから各シグナルを見ることで、不正クレームを適切に fraud_or_mismatch へ振り分けられる。
実装前に押さえる6つの注意点
記事は本番投入前の「フィールドノート」として以下を挙げている。
- 質問キーはモデルに見えない。
"is_defect"のような辞書キーはあくまで結果マッピング用。質問の内容は必ずinstructionsに書く。 choiceのキーはUTF-8バイト順でソートされる。"none_of_the_above"は"shipping"より先に並ぶため、位置依存のキャッチオールとして使わないこと。"other"を明示的な説明付きで使うか、noulと組み合わせる。scoreのルーブリックは線形に保つ。指数スケール($1, $10, $100)のような基準を使うと期待値が歪む。- 否定形より肯定形の質問を使う。「Goldでないか?」ではなく「Goldか?」と聞いて、コード側で
1 - answer.noulを計算する。 - 数値計算や日付比較はPythonで済ませてから渡す。
age_days <= 30のような判定はコードで処理し、結果だけinputに含める。 - MIMEタイプは正確に指定する。
application/octet-streamではHTTP 400が返る。
Structured Outputsとの使い分け
記事内では「構造化出力(Structured Outputs)とどう使い分けるか」の比較表も掲載されている。元記事の比較をまとめると、主な判断軸は以下のとおりだ。
| 比較軸 | Decisions API | Structured Outputs |
|---|---|---|
| 出力トークン | ゼロ | 生成量に応じて課金 |
| 主なユースケース | 分類・ルーティング・評価 | 要約・文章生成・抽出 |
| 確率の取得 | キャリブレーション済みで直接取得可 | 通常は取得不可 |
| 質問の並列処理 | 1フォワードパスで最大1000問 | 原則1リクエスト1出力 |
Decisions APIはクローズドエンドな評価・分類・ルーティングに適しており、自由記述の要約や文章生成が必要な場面はStructured Outputsが引き続き適切だ。
試せるコードはすべてGoogle ColabのNotebookで公開されている。APIキーはGoogle AI Studioで取得できる。LangChain、LiteLLM、Pydantic AIなど主要フレームワークへの統合も順次展開中だ。
詳細はStop Burning LLM Tokens Just to Run an if Statementを参照していただきたい。