nikkie-ftnextの日記

イベントレポートや読書メモを発信

Google AIのGemini APIで出力をJSON形式で構造化するには(google-generativeai、google-genai)

はじめに

メダリスト「一番上手になりたい」、泣いた😭 nikkieです。

OpenAIのGPTではおなじみのJSON形式出力1
「Geminiでどうやるんだろう」と手を動かしました。
今回はGoogle AI篇です(Vertex AIは登場しません)2

目次

ドキュメントより

ライブラリによって方法が異なると理解しました。

google-generativeai

ai.google.dev

JSONを生成」より、モデルにJSONスキーマを提供する方法は2つ

どちらのアプローチも、Gemini 1.5 Flash と Gemini 1.5 Pro の両方で機能します。

cookbookより

google-genai

https://googleapis.github.io/python-genai/#json-response-schema

Gemini 2.0 Flashに渡しているのは、PydanticのBaseModelを継承したモデルです。
またJSONスキーマの辞書を手で書いてもよさそうでした

動作確認スクリプト

Google AI Studioで払い出したAPIキーを、環境変数GOOGLE_API_KEYに指定しています。

  • uv 0.5.17 (Homebrew 2025-01-10)
    • スクリプトにinline script metadataを書き、uv runで実行しています
  • Python 3.12.5
  • google-generativeai 0.8.3
  • google-genai 0.5.0

google-generativeaiでプロンプト・Gemini 1.5

これ、ちょっと欲しい形と違いました
マークダウンのコードブロックで返ってきています。
ほしいのはコードブロックの中身なんですよね。
プロンプト調整の余地がありそうです(正規表現で抜き出せないこともないかな)

※実際はバッククォートのみです(バックスラッシュはエスケープの意図です)

\`\`\`json
[
  {
    "recipe_name": "Chocolate Chip Cookies",
    "ingredients": [
      "1 cup (2 sticks) unsalted butter, softened",
      "1 cup granulated sugar",
      "1 cup packed brown sugar",
      "2 teaspoons pure vanilla extract",
      "2 large eggs",
      "3 cups all-purpose flour",
      "1 teaspoon baking soda",
      "1 teaspoon salt",
      "2 cups chocolate chips"
    ]
  },
  # 省略
  {
    "recipe_name": "Snickerdoodles",
    "ingredients": [
      "1 cup (2 sticks) unsalted butter, softened",
      "1 3/4 cups granulated sugar",
      "2 large eggs",
      "2 teaspoons cream of tartar",
      "1 teaspoon vanilla extract",
      "3 cups all-purpose flour",
      "2 teaspoons ground cinnamon",
      "1/4 teaspoon salt",
      "Coarse granulated sugar for rolling"

    ]
  }
]
\`\`\`

google-generativeaiでTypedDict・Gemini 1.5

JSONで得られました!

[
  {
    "ingredients": [
      "1 cup (2 sticks) unsalted butter, softened",
      "1 1/2 cups granulated sugar",
      "1 cup packed brown sugar",
      "2 large eggs",
      "2 teaspoons vanilla extract",
      "3 cups all-purpose flour",
      "1 teaspoon baking soda",
      "1 teaspoon salt",
      "1 cup chocolate chips"
    ],
    "recipe_name": "Chocolate Chip Cookies"
  },
  // 省略
  {
    "ingredients": [
      "1/2 cup (1 stick) unsalted butter, softened",
      "1/2 cup shortening",
      "1 cup granulated sugar",
      "1/2 cup packed brown sugar",
      "1 large egg",
      "1 teaspoon vanilla extract",
      "2 1/4 cups all-purpose flour",
      "1 teaspoon baking soda",
      "1/2 teaspoon salt",
      "1/2 cup oatmeal"
    ],
    "recipe_name": "Oatmeal Cookies"
  }
]

google-genaiでPydanticのBaseModel・Gemini 2.0

JSONで得られました!
ただし複数のレシピの例はPydanticのValidationError3が解決できず、ドキュメントの例を使いました。

{
  "capital": "Washington, D.C.",
  "continent": "North America",
  "gdp": 25460000000000,
  "name": "United States",
  "official_language": "English",
  "population": 331000000,
  "total_area_sq_mi": 3797000
}

終わりに

Google AIのGemini APIJSON出力を得るには以下の方法があります。

  • google-generativeaiでTypedDictを渡す(Gemini 1.5)
    • プロンプトエンジニアリングでもいけるかもしれません(ドキュメント記載が再現しなかったので、私はTypedDictにしたいと思いました)
  • google-genaiでPydanticのBaseModelを継承したモデルを渡す(Gemini 2.0)

もしかするとgoogle-generativeaiからGemini 2.0や、google-genaiからGemini 1.5も叩けるかもしれませんが、今回の検証はここまでとします(方法が1つは見つかったということで)

Vertex AIについては別で書こうと考えていますが、なんと異なる方法なんですよ...


  1. https://platform.openai.com/docs/guides/structured-outputs/json-mode
  2. Gemini APIGoogle AIとVertex AIの双方から利用できます
  3. list[Recipe]を持たせたRecipesクラスを導入したのですが、 https://errors.pydantic.dev/2.10/v/extra_forbidden が案内されます。Issueを見たところlistが未サポートで対応中とのこと ref: https://github.com/googleapis/python-genai/issues/60#issuecomment-2577440301