◉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-18上級

Gemini の構造化出力で入力種別ごとに型を変える — anyOf 判別ユニオンの実運用メモ

種類の違う入力を1つのエンドポイントで分類すると、フラットなスキーマは null だらけになります。anyOf 判別ユニオンで種別ごとに型を切り替え、Pydantic / Zod で安全に受け取る実装をまとめました。

gemini-api286structured-output26anyofdiscriminated-unionindie-development4

✦ プレミアム記事

きっかけは、個人開発で回しているレビュー集約の小さなバッチでした。App Store と Google Play のレビュー、サポート問い合わせメール、そして稀に届く返金依頼を、ひとつの分類エンドポイントにまとめて流していたのですが、出力が安定しないのです。レビューには rating が、返金依頼には order_id が必要なのに、全部を1枚のフラットなスキーマで受けていたため、フィールドの大半が optional になり、Gemini が「埋められそうなところを埋める」挙動で order_id にレビュー本文の一部を入れてくる、ということが起きていました。

問題はモデルの賢さではなく、こちらの渡したスキーマが「どの種別なのか」をモデルに決めさせる構造になっていなかったことでした。種別を先に確定させ、その種別に応じてフィールドを切り替える——つまり判別可能なユニオン(discriminated union)を anyOf で表現すれば、この曖昧さはかなり消えます。今回はその設計と、Pydantic / Zod で安全に受け取るところまでの実装メモです。モデルは本日 GA となった gemini-3.5-flash に固定して検証しています。

フラットな1枚スキーマが null だらけになる理由

最初に組んでいたスキーマは、考えうる全フィールドを並べただけのものでした。

{
  "type": "object",
  "properties": {
    "category": { "type": "string" },
    "rating": { "type": "integer" },
    "summary": { "type": "string" },
    "order_id": { "type": "string" },
    "reason": { "type": "string" },
    "urgency": { "type": "string" }
  }
}

この形は一見すると無難ですが、運用に乗せると次の問題が出ます。種別ごとに必要なフィールドが違うのに、required を厳しくできません。レビューに order_id は不要で、返金依頼に rating は無意味です。すべてを optional にすると、今度はモデルが「空欄を嫌って」無関係なフィールドを埋め始めます。私の手元では、返金依頼として処理すべき入力のうち約 15% で rating に推測値が入り、order_id が空のまま流れてくるケースが目立ちました。

下流のコードはこの曖昧さを吸収するために if category == "refund" and order_id is None のような分岐だらけになり、しかもその分岐は「モデルが正しく category を入れてくれた」前提に乗っています。category 自体が信用できないのに、です。

フラット型と判別ユニオン型の違いを整理すると、次のようになります。

観点フラット1枚スキーマanyOf 判別ユニオン
必須フィールド種別差を表現できず全部 optional になりがち種別ごとに required を厳密に指定できる
誤入力無関係なフィールドを埋めやすいその種別に無いフィールドは構造上存在しない
下流の分岐category を信用した手書き if が増える判別子で型が確定し網羅性チェックが効く
検証部分的にしか効かないPydantic / Zod の判別ユニオンがそのまま使える

anyOf 判別ユニオンという考え方

判別ユニオンは、各バリアントに「種別を表す1つのフィールド(判別子)」を持たせ、その値で型を一意に決められるようにしたものです。OpenAPI / JSON Schema では anyOf でバリアントを並べ、各バリアントの判別子フィールドを「単一値だけを許す enum」にします。kind: ["app_review"] のように許容値を1つに絞ると、モデルはそのバリアントを選んだ瞬間に kind の値が確定し、こちらは kind を見るだけで型を切り分けられます。

Gemini の responseSchema は OpenAPI のサブセットをサポートしており、anyOf・enum・required・property_ordering あたりは実用範囲で使えます。ここで効いてくるのが property_ordering です。生成は前から順に進むため、判別子を先頭に置くと、モデルは「まず種別を決めてから、その種別のフィールドを埋める」順序になります。私の検証では、判別子を末尾に置いた場合と先頭に置いた場合で、無関係フィールドの混入率が体感で大きく変わりました。判別子は必ず required かつ property_ordering の先頭に置く、というのが実運用での結論です。

✦

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

この記事の続きを読む

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

この記事で得られること
✦responseSchema を anyOf で書くときの実際に通るサブセットと、判別子(discriminator)をモデルに必ず先頭で確定させる property_ordering の指定
✦Python(Pydantic 判別ユニオン)と TypeScript(Zod discriminatedUnion)で受け取り、検証失敗時に1回だけ修復する実装コード
✦未知種別を握りつぶさず DLQ に流す設計と、判別ユニオンが 3.5 Flash でトークンとレイテンシに与えた実測の差
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

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

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

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

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

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

関連記事

◈ API / SDK2026-05-20
Gemini API 構造化出力スキーマを本番アプリで安全に進化させる設計記録
本番アプリで Gemini API の JSON 出力スキーマを変えるとき、何を契約として守り、何を段階的に切り替えるか。旧版と新版を同時に返す Dual-Emit パターン、旧スキーマ利用箇所を見積もる compatibility checker、Sunset プロトコルの実装記録です。
◈ API / SDK2026-08-22
メディエーショングループが20を超えたとき、設定の抜けをどう見つけるか
広告メディエーションのグループが増えると、設定の抜けや型のずれが静かに溜まります。設定を1枚の表に正規化して機械でずれを確定させ、判断が必要なセルだけを Gemini に渡す分担にした手順をまとめました。
◈ API / SDK2026-07-13
responseSchema で $ref が使えず詰まったら — responseJsonSchema で再帰スキーマを本番で扱う
Gemini の responseSchema は OpenAPI サブセットで $ref/$defs が使えず、共有定義も再帰も表現できません。responseJsonSchema へ移行し、多言語フィールドの再利用とカテゴリ木の再帰を本番で扱う実装をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます