Carpe Diem

備忘録

Function callingのJSON Schemaをpydanticで生成する

背景

Function callingJSONを定義する際はJSON Schemaを用いますが、JSON Schemaは覚えることが多く不慣れだと非常に扱いにくいです。

pydnaticを使うとクラス定義から簡単にJSON Schemaを生成できるので、PythonJSON Schemaを利用する際はおすすめです。

環境

  • python v3.11.8
  • pydantic v2.6.1
  • openai v1.12.0

要件

例えば次のようなJSONがFunction callingで返ってほしいとします。

{
    "ingredients": [
        {
            "ingredient": "人参",
            "amount": "1本"
        },
        {
            "ingredient": "豚ひき肉",
            "amount": "100g"
        }
    ],
    "cookwares": ["フライパン", "ボウル"],
    "instructions": ["材料を切ります。", "材料を炒めます。"]
}

課題

Function callingでの定義

先程のJSONを作るようなJSON Schemaを用意しようとすると、次のように非常に冗長になります。

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "type": "object",
  "properties": {
    "ingredients": {
      "type": "array",
      "items": [
        {
          "type": "object",
          "properties": {
            "ingredient": {
              "type": "string"
            },
            "amount": {
              "type": "string"
            }
          },
          "required": [
            "ingredient",
            "amount"
          ]
        },
        {
          "type": "object",
          "properties": {
            "ingredient": {
              "type": "string"
            },
            "amount": {
              "type": "string"
            }
          },
          "required": [
            "ingredient",
            "amount"
          ]
        }
      ]
    },
    "cookwares": {
      "type": "array",
      "items": [
        {
          "type": "string"
        },
        {
          "type": "string"
        }
      ]
    },
    "instructions": {
      "type": "array",
      "items": [
        {
          "type": "string"
        },
        {
          "type": "string"
        }
      ]
    }
  },
  "required": [
    "ingredients",
    "cookwares",
    "instructions"
  ]
}

解決方法

pydanticを使う

そこでpydanticを使って、次のようにクラス表現にします。

class Ingredient(BaseModel):
    ingredient: str = Field(description="材料", examples=["豚ひき肉"])
    amount: str = Field(description="分量", examples=["300g"])


class Recipe(BaseModel):
    ingredients: list[Ingredient]
    cookwares: list[str] = Field(description="調理器具", examples=["フライパン"])
    instructions: list[str] = Field(description="手順", examples=[["材料を切ります。", "材料を炒めます。"]])

非常に分かりやすいです。

ここでJSON Schemaをparametersに渡しますが、model_json_schema()関数を呼び出すだけOKです。

RECIPE_FUNCTION = {
    "name": "output_recipe",
    "description": "レシピを出力する",
    "parameters": Recipe.model_json_schema(),
}

そしてChat Completions APIにFunction callingとして渡します。

        response = client.chat.completions.create(
            model="gpt-3.5-turbo",
            temperature=0,
            messages=messages,
            functions=[RECIPE_FUNCTION],
            function_call={"name": RECIPE_FUNCTION["name"]},
        )
        response_message = response.choices[0].message
        function_call_args = response_message.function_call.arguments

        recipe = json.loads(function_call_args)
        print(recipe)

結果

期待通りJSONの形で返ってきました。

{
   "ingredients":[
      {
         "ingredient":"合いびき肉",
         "amount":"300g"
      },
      {
         "ingredient":"玉ねぎ",
         "amount":"1個"
      },
      {
         "ingredient":"パン粉",
         "amount":"大さじ2"
      },
      {
         "ingredient":"牛乳",
         "amount":"大さじ2"
      },
      {
         "ingredient":"",
         "amount":"1個"
      },
      {
         "ingredient":"ケチャップ",
         "amount":"大さじ2"
      },
      {
         "ingredient":"ウスターソース",
         "amount":"大さじ1"
      },
      {
         "ingredient":"",
         "amount":"適量"
      },
      {
         "ingredient":"こしょう",
         "amount":"適量"
      }
   ],
   "cookwares":[
      "ボウル",
      "フライパン"
   ],
   "instructions":[
      "1. 玉ねぎをみじん切りにします。",
      "2. ボウルに合いびき肉、玉ねぎ、パン粉、牛乳、卵、ケチャップ、ウスターソース、塩、こしょうを入れてよく混ぜます。",
      "3. ハンバーグの形に成形します。",
      "4. フライパンにオリーブオイルを熱し、ハンバーグを焼きます。",
      "5. 両面に焼き色がついたら蓋をして中まで火を通します。",
      "6. ハンバーグが焼けたら完成です。"
   ],
   "in_english":"Hamburger Steak"
}

UI

Streamlitを使ってUIを用意すれば簡単にそれらしいレシピ画面が作れます。

まとめ

Function callingのJSON Schemaをpydanticで生成することで、運用しやすい形でJSON Schemaを管理できます。

参考