見出し画像

JSONスキーマの書き方が分からないからChatGPTに書かせてみた。


OpenAIのAPIで、Structured Outputsという機能が使えるようになったので試してみました。
必ず決まったJSONデータの形式で返答してくれるようで、非常に便利そうです。

PLAYGROUNDの中でも使えるになっていて、今回はそれを使います。

JSONスキーマの内容を書くようですが、上のExamplesに数学問題の解答ののサンプルデータがあります。

試しに、これで算数の問題を解いてもらいます。

そうすると以下のようなJSONデータで返答されます。
途中の計算過程が一貫性ある配列になっています。
これはプログラマーからすると凄く扱いやすいですね。

{
  "steps": [
    {
      "explanation": "Let the number of years ago be x. At that time, the father's age would have been 50 - x years, and the daughter's age would have been 22 - x years.",
      "output": "父の年齢: 50 - x, 娘の年齢: 22 - x"
    },
    {
      "explanation": "We need to find when the father's age was 5 times the daughter's age. So we set up the equation: 50 - x = 5 * (22 - x).",
      "output": "50 - x = 5 * (22 - x)"
    },
    {
      "explanation": "Expand the equation: 50 - x = 110 - 5x.",
      "output": "展開すると: 50 - x = 110 - 5x"
    },
    {
      "explanation": "Simplify the equation to solve for x: 50 - x = 110 - 5x.",
      "output": "50 - x = 110 - 5x"
    },
    {
      "explanation": "First, add 5x to both sides to get: 50 + 4x = 110.",
      "output": "50 + 4x = 110"
    },
    {
      "explanation": "Next, subtract 50 from both sides to get: 4x = 60.",
      "output": "4x = 60"
    },
    {
      "explanation": "Finally, divide both sides by 4 to solve for x: x = 15.",
      "output": "x = 15"
    }
  ],
  "final_answer": "15年前、父の年齢が娘の年齢の5倍でした。"
}

ここで数学返答のスキームを一度見てみます。

しかしJSONスキーマという型の定義があるようですが使ったことないのでサッパリです。
どのように書いているのか、なんとなく分かりそうですが分かりません。

という事で解読したり理解するためにも、一度GPTに作らせてみる事にしました。

先にJSONデータのサンプルを作って、サンプルを元にGPTにJSONスキーマを定義させてみる事にしました。

具体的には、
キャラクターのプロフィールのJSONデータを用意して、
そのデータからJSONスキーマを定義してもらいます。

これでサンプルJSONデータができました。
このデータをもとにJSONスキームを書いてもらいます。

これでJSONスキーマが生成されたので、PLAYGROUNDで試してみます。

エラーが出ました。
なんとなく違う気がしていましたが、やはりプロパティ名が違うようです。
ということで先ほどの数学のサンプルをお手本に書き直してもらいます。

再生成されました。
今度こそ、それっぽい形になりました。

以下が生成された全文です。
かなり長くて読むのが大変です。(読まなくてもいいです。)

{
  "name": "character_profile",
  "strict": true,
  "schema": {
    "type": "object",
    "properties": {
      "name": {
        "type": "string"
      },
      "age": {
        "type": "integer"
      },
      "gender": {
        "type": "string"
      },
      "occupation": {
        "type": "string"
      },
      "school": {
        "type": "string"
      },
      "background": {
        "type": "object",
        "properties": {
          "family": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string"
              },
              "secrets": {
                "type": "string"
              },
              "parents": {
                "type": "object",
                "properties": {
                  "profession": {
                    "type": "string"
                  },
                  "social_status": {
                    "type": "string"
                  }
                },
                "required": [
                  "profession",
                  "social_status"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "status",
              "secrets",
              "parents"
            ],
            "additionalProperties": false
          },
          "environment": {
            "type": "object",
            "properties": {
              "technology": {
                "type": "string"
              },
              "feelings": {
                "type": "string"
              }
            },
            "required": [
              "technology",
              "feelings"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "family",
          "environment"
        ],
        "additionalProperties": false
      },
      "personality": {
        "type": "object",
        "properties": {
          "traits": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "attitude": {
            "type": "string"
          },
          "risk_taking": {
            "type": "string"
          }
        },
        "required": [
          "traits",
          "attitude",
          "risk_taking"
        ],
        "additionalProperties": false
      },
      "skills": {
        "type": "object",
        "properties": {
          "technology": {
            "type": "string"
          },
          "specialization": {
            "type": "string"
          },
          "academic": {
            "type": "string"
          },
          "extracurricular": {
            "type": "string"
          },
          "combat": {
            "type": "string"
          }
        },
        "required": [
          "technology",
          "specialization",
          "academic",
          "extracurricular",
          "combat"
        ],
        "additionalProperties": false
      },
      "appearance": {
        "type": "object",
        "properties": {
          "hair": {
            "type": "string"
          },
          "eyes": {
            "type": "string"
          },
          "clothing": {
            "type": "string"
          },
          "details": {
            "type": "string"
          },
          "height": {
            "type": "string"
          },
          "physical_ability": {
            "type": "string"
          }
        },
        "required": [
          "hair",
          "eyes",
          "clothing",
          "details",
          "height",
          "physical_ability"
        ],
        "additionalProperties": false
      },
      "role_in_story": {
        "type": "object",
        "properties": {
          "importance": {
            "type": "string"
          },
          "skills_usage": {
            "type": "string"
          },
          "impact": {
            "type": "string"
          }
        },
        "required": [
          "importance",
          "skills_usage",
          "impact"
        ],
        "additionalProperties": false
      },
      "internal_conflict": {
        "type": "object",
        "properties": {
          "fear": {
            "type": "string"
          },
          "ideals_vs_reality": {
            "type": "string"
          },
          "decision": {
            "type": "string"
          }
        },
        "required": [
          "fear",
          "ideals_vs_reality",
          "decision"
        ],
        "additionalProperties": false
      }
    },
    "required": [
      "name",
      "age",
      "gender",
      "occupation",
      "school",
      "background",
      "personality",
      "skills",
      "appearance",
      "role_in_story",
      "internal_conflict"
    ],
    "additionalProperties": false
  }
}

これを張り付けると先ほどのようなエラーが出なかったので
もう一度キャラ生成を試してみます。

またエラーがでました。
どうも年齢の部分に"minimum"という余計なプロパティがあるのでこれを削除してみます。
"hair": { "type": "string" }のように、ひとつのプロパティに対してtypeのみを定義しないといけないようです。

これでうまくいきました。
さらに他のキャラも生成します。

こんな感じで常に同じ形状のJSONデータで返答されます。

冒頭で話したようにデータ形状の一致していると、これは凄い安心感ありますし、プログラマー的には色んな用途を考えてワクワクするところです。

RPGとかでキャラとかモンスターのローグライク自動生成とか出来そうです。町やアイテムなどなんでも出来そう。

今回はサンプルJSONデータからJSONスキーマを作るのに一回で完璧には無理でしたが、
模範JSONデータをナレッジに渡しておいて、JSONスキーマを生成するGPTsやAssistantsを作ってしまってもいいかもしれない。

プロフィールのように項目が多くなると、先ほど挙げたようにJSONスキームが以上に長くなり、そのまま手書きで書こうとするとケアレスミスが増える可能性が高くなるので、スキーマを改変する際も出来るだけLLMにさせたほうがよさそうだ。
とりあえず生成していると、だんだんと目が慣れてきてスキーマの書き方がわかってきた。


いいなと思ったら応援しよう!