背景
Function callingでJSONを定義する際はJSON Schemaを用いますが、JSON Schemaは覚えることが多く不慣れだと非常に扱いにくいです。
pydnaticを使うとクラス定義から簡単にJSON Schemaを生成できるので、PythonでJSON 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を管理できます。