◉GEMINI LABEN
●TTS — Gemini 3.8 Flash TTS と Flash-Lite TTS が GA(9/22)。声の複製に対応するのは Flash だけで、Lite に渡すと 400 が返ります●2.5 — 2.5 系モデルのアクセスは、過去に利用実績のあるユーザーに限定されました(9/18)。廃止ではなく、新規は 3.5 Flash-Lite か 3.8 Flash へ●9/30 — gemini-omni-flash-preview の提供終了まで残り2日、10/2 には gemini-2.5-flash-image も停止。後者の実務上の差し替え先は gemini-3.1-flash-image です●AUTH — 「Workspace の Enterprise アカウントだけ Gemini CLI の認証が通らない」という報告が続いています。個人アカウントとの切り分けが焦点●NEW — 3.8 Flash-Lite TTS への置き換えで、案内音声の声を選び直した記録●429 — 新規プロジェクトが Free tier 表示なのに limit: 0 で 429 が返る。最初の1時間で確かめる点を整理しています●TTS — Gemini 3.8 Flash TTS と Flash-Lite TTS が GA(9/22)。声の複製に対応するのは Flash だけで、Lite に渡すと 400 が返ります●2.5 — 2.5 系モデルのアクセスは、過去に利用実績のあるユーザーに限定されました(9/18)。廃止ではなく、新規は 3.5 Flash-Lite か 3.8 Flash へ●9/30 — gemini-omni-flash-preview の提供終了まで残り2日、10/2 には gemini-2.5-flash-image も停止。後者の実務上の差し替え先は gemini-3.1-flash-image です●AUTH — 「Workspace の Enterprise アカウントだけ Gemini CLI の認証が通らない」という報告が続いています。個人アカウントとの切り分けが焦点●NEW — 3.8 Flash-Lite TTS への置き換えで、案内音声の声を選び直した記録●429 — 新規プロジェクトが Free tier 表示なのに limit: 0 で 429 が返る。最初の1時間で確かめる点を整理しています
記事一覧/API / SDK
◈ API / SDK/2026-06-26上級

Gemini API のセーフティフィルタが正当な応答を黙って落とすとき — 全切りせず誤検知だけを救う運用メモ

本番のGemini APIで正当なプロンプトがSAFETYでブロックされる誤検知を、全カテゴリ無効化に逃げずに扱う運用メモ。入力ブロックと出力ブロックの切り分け、誤検知率の計装、カテゴリ別の段階的リカバリまでを実装で整理します。

gemini-api286safety-filter2production105observability11error-handling8

✦ プレミアム記事

セーフティフィルタの相談で一番多いのは「どう全部切るか」ですが、本番で困るのはたいてい逆です。全部は切れない、切りたくない、それでも正当なリクエストが時々黙って落ちる。落ちたことに気づくのも遅れる。finishReason が SAFETY で返り、response.text を読もうとした瞬間に例外が飛んで、ユーザーには空のカードだけが残ります。

私自身、個人開発の傍らで4つの技術ブログを自動投稿で回している関係で、無人で走るバッチがこれに引っかかったことがあります。題材として渡したコード断片やエラーメッセージのごく一部が DANGEROUS_CONTENT 寄りに判定され、生成が途中で止まる。人がいないので、翌朝ログを見るまで誰も気づきません。このメモは、そのとき「全カテゴリ OFF にして終わり」にせず、誤検知だけを拾って安全側に戻すために組んだ仕組みの記録です。

まず「どちらでブロックされたか」を1行で確定する

セーフティフィルタは入力(プロンプト)と出力(生成結果)の両方を見ます。この二つは原因も対処もまったく別物なのに、ログでは同じ「ブロックされた」に見えてしまうのが厄介な点です。最初にやるべきは、どちらで落ちたかを毎回機械的に記録することです。

入力でブロックされた場合は候補(candidate)が一つも生成されず、情報は prompt_feedback.block_reason に入ります。出力でブロックされた場合は候補は生成されますが、finish_reason が SAFETY になり本文が空になります。新しい google-genai SDK では、この切り分けは次のように書けます。

from google import genai
from google.genai import types
 
client = genai.Client(api_key="YOUR_GEMINI_API_KEY")
 
def classify_block(resp):
    """ブロックの発生箇所を入力/出力/なしで返す"""
    pf = getattr(resp, "prompt_feedback", None)
    if pf and getattr(pf, "block_reason", None):
        return "INPUT_BLOCKED", str(pf.block_reason)
 
    if not resp.candidates:
        # 候補ゼロかつ prompt_feedback も無いレアケース
        return "NO_CANDIDATE", "unknown"
 
    cand = resp.candidates[0]
    if cand.finish_reason == types.FinishReason.SAFETY:
        return "OUTPUT_BLOCKED", "SAFETY"
 
    return "OK", str(cand.finish_reason)

この関数を本番の生成呼び出しの直後に必ず通し、INPUT_BLOCKED か OUTPUT_BLOCKED かをそのままログのフィールドにします。これだけで、後から「入力プロンプトを直すべき案件」と「出力側の閾値を見直すべき案件」を混ぜずに数えられるようになります。経験上、この区別をしていないログは「ブロックが多い」までしか言えず、次の一手が打てません。

誤検知を「カテゴリ別の率」で見えるようにする

閾値を緩めるかどうかを勘で決めると、たいてい緩めすぎるか、怖くて何もできないかのどちらかに振れます。判断材料は、どのカテゴリが、どのくらいの確率で、どれだけブロックに寄与しているかという実測値です。

各候補とプロンプトフィードバックには safety_ratings が付き、要素ごとに category・probability(NEGLIGIBLE / LOW / MEDIUM / HIGH)・blocked(真偽)が入ります。これをそのまま構造化ログに落とし、カテゴリ別に集計します。

from collections import Counter
 
def extract_ratings(resp):
    """入力側・出力側の safety_ratings を平坦化して返す"""
    rows = []
    pf = getattr(resp, "prompt_feedback", None)
    if pf and getattr(pf, "safety_ratings", None):
        for r in pf.safety_ratings:
            rows.append(("input", str(r.category), str(r.probability), bool(r.blocked)))
    for cand in (resp.candidates or []):
        for r in (cand.safety_ratings or []):
            rows.append(("output", str(r.category), str(r.probability), bool(r.blocked)))
    return rows
 
def summarize(logged_rows):
    """蓄積した rows からカテゴリ別の誤検知傾向を出す"""
    blocked = Counter()
    medium_plus = Counter()
    for _side, cat, prob, was_blocked in logged_rows:
        if was_blocked:
            blocked[cat] += 1
        if prob in ("MEDIUM", "HIGH"):
            medium_plus[cat] += 1
    return blocked, medium_plus

ここで重要なのは、blocked の件数だけでなく MEDIUM 止まり(ブロックには至らないが境界に近い)の分布も一緒に見ることです。MEDIUM が特定カテゴリに偏って積み上がっているなら、そのカテゴリは「いまは耐えているが、入力がわずかに変われば落ちる」予備軍です。本番で突然ブロックが増える事故は、たいていこの予備軍が閾値をまたいだ瞬間に起きます。率で持っておくと、事故になる前に気づけます。

なお probability はあくまで安全方針上の確からしさであって、出力内容の正しさとは別物です。ここを取り違えて「LOW だから内容も安全」と読むと判断を誤ります。フィルタが見ているのは方針適合であって事実性ではありません。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦入力ブロック(prompt_feedback)と出力ブロック(finish_reason=SAFETY)を最初の1行で切り分けるログ設計
✦誤検知率をカテゴリ別に集計し、緩めてよい閾値を勘ではなく実測で決める計装コード
✦全カテゴリOFFに逃げず、原因カテゴリだけを段階的に緩めて安全側に倒すリカバリ関数
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Gemini Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥2,480 の永久アクセス
メンバーシップを見る →

関連記事

◈ API / SDK2026-07-04
Gemini API の英語出力に日本語が『たまに』混ざるとき — 混入率を計測して段階的に締める運用メモ
英語出力を指示したGemini APIが、100回に数回だけ日本語を混ぜてくる——この『たまに』を止められない本当の理由と、混入率をSLOとして計測し、段階的リカバリで本番品質まで締める運用パターンを実装コード付きで整理します。
◈ API / SDK2026-05-23
Gemini API × Sentry でLLMエラー追跡とプロンプト失敗を観測する本番運用パイプライン
Sentry と Gemini API を組み合わせ、プロンプトの破綻や安全フィルタ発火、トークン超過といった LLM 特有の失敗を本番で観測するパイプラインを構築します。クライアントラッパー、成功に見える失敗を捕捉するインスペクタ、PII を守る before_send まで実装します。
◈ API / SDK2026-05-02
Gemini API を個人アプリに組み込む時の 7 つの設計判断 — エラー設計から品質モニタリングまで
Gemini API を個人アプリに本番導入した時に直面する設計判断を、私自身の運用記録から 7 つに絞ってまとめました。コストとレイテンシのトレードオフ、エラー時のフォールバック設計、品質モニタリングまで、教科書には載っていない実戦的な判断軸を共有します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます