GEMINI LABEN
CLI — 本日6/18、Gemini CLIとGemini Code Assist IDE拡張がAI Pro/Ultra・無料個人利用向けにリクエスト提供を終了。後継はAntigravity CLIですFLASH — Gemini 3.5シリーズが始動し3.5 Flashが提供開始。エージェントとコーディング向けにフロンティア級、長時間タスクに強いと説明されていますDEEPTHINK — Gemini 3 Deep ThinkがGoogle AI Ultra向けに展開中。数学・科学・論理・多段推論の最上位モードですAPP — GeminiアプリにDaily Brief、再設計UI、AI動画モデルGemini Omni、個人AIエージェントGemini Sparkが加わりましたDESIGN — 新デザイン言語Neural Expressiveで、よりリッチな視覚出力とモダリティ間の素早い切替に向けて再構築されていますULTRA — Google AI Ultraはモデル最上位アクセス・Deep Research・Veo 3動画生成・100万トークンコンテキストを束ねるプレミアム枠ですCLI — 本日6/18、Gemini CLIとGemini Code Assist IDE拡張がAI Pro/Ultra・無料個人利用向けにリクエスト提供を終了。後継はAntigravity CLIですFLASH — Gemini 3.5シリーズが始動し3.5 Flashが提供開始。エージェントとコーディング向けにフロンティア級、長時間タスクに強いと説明されていますDEEPTHINK — Gemini 3 Deep ThinkがGoogle AI Ultra向けに展開中。数学・科学・論理・多段推論の最上位モードですAPP — GeminiアプリにDaily Brief、再設計UI、AI動画モデルGemini Omni、個人AIエージェントGemini Sparkが加わりましたDESIGN — 新デザイン言語Neural Expressiveで、よりリッチな視覚出力とモダリティ間の素早い切替に向けて再構築されていますULTRA — Google AI Ultraはモデル最上位アクセス・Deep Research・Veo 3動画生成・100万トークンコンテキストを束ねるプレミアム枠です
記事一覧/API / SDK
API / SDK/2026-06-18上級

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

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

gemini-api242structured-output16anyofdiscriminated-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 のサブセットをサポートしており、anyOfenumrequiredproperty_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/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

API / SDK2026-05-20
Gemini API 構造化出力スキーマを本番アプリで安全に進化させる設計記録
個人開発のアプリ事業でGemini APIのJSON出力スキーマを変えるとき、何を契約として守り、何を段階的に切り替えたか。Dual-EmitパターンとSunsetプロトコルの実装記録です。
API / SDK2026-06-13
夜間ログを毎朝3分で読み切る — Gemini API で Daily Brief 風の運用ダイジェストを自作した記録
Daily Brief に着想を得て、夜間の運用ログを毎朝1通のダイジェストに変えるパイプラインを自作しました。収集・構造化・要約・配信の4段構成と実測トークン数、障害の朝でも配信を止めないフォールバック設計までの実装記録です。
API / SDK2026-05-30
Gemini API の多段パイプラインに『残り時間予算』を伝播させる設計 — 4 秒 SLA を守りながら品質を段階的に落とす実装メモ
個人開発のヘルプ検索機能で多発した DEADLINE_EXCEEDED を、リクエスト単位の締め切り予算を埋め込み・検索・生成の各段に伝播させる設計で解消した記録です。残り時間に応じて maxOutputTokens を縮め、フォールバック予算を確保する実装を TypeScript でまとめています。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →