LLMのAPI SchemaではJSON Schemaがよく使用されている。例えばFunction CallingのAPI定義(バリデーション)は、JSON Schemaを使って送受信するデータを定義する。

JSONにはJSON Type Definition(JTD)というRFCで標準化された型定義ルールがあるが、あまり使われてない気がする。JSON Schemaの方が汎用性が高く、JTDでできなくてJSON Schemaではできることの方が多いので、今やJTDを使う理由がない。JTDの方がシンプルなので、バリデーションの実行時間が早い、くらい?

と思ってJTDの仕様を見ていたら、Tagged unionが表現できることを知る。例えば、描画オブジェクトの circlesquare があったとして、バリデーションは以下のように書く。

{
  "type": "object",
  "properties": {
    "shape": {
      "discriminator": "type",
      "mapping": {
        "circle": {
          "properties": {
            "radius": { "type": "number" }
          },
          "required": ["radius"]
        },
        "square": {
          "properties": {
            "sideLength": { "type": "number" }
          },
          "required": ["sideLength"]
        }
      }
    }
  },
  "required": ["shape"]
}

以下のようなJSONは受け付けられる。

{ "shape": { "type": "circle", "radius": 5 } }
{ "shape": { "type": "square", "sideLength": 10 } }

以下の値は、型としては合っていても、不正となる。

{ "shape": { "type": "equilateral_triangle", "length": 3 } }

Pythonだとjsontypedef/json-typedef-pythonでバリデーションができる。

import jtd

schema = {
    "type": "object",
    "properties": {
        "shape": {
            "discriminator": "type",
            "mapping": {
                "circle": {
                    "properties": {
                        "radius": {"type": "number"}
                    },
                    "required": ["radius"]
                },
                "square": {
                    "properties": {
                        "sideLength": {"type": "number"}
                    },
                    "required": ["sideLength"]
                }
            }
        }
    },
    "required": ["shape"]
}
data = data = {"shape": {"type": "circle", "radius": 5}}

jtd.validate(schema, data)  # return List[jtd.ValidationError]

全体をチェックするコードはGistに上げた。

JSON Schemaで同等のバリデーションをするのは、以下かな。Copilotは「同じ」と大小判を押していたが、自信はない。python-jsonschema/jsonschemaを使っている。

import jsonschema

schema = {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
        "shape": {
            "type": "object",
            "properties": {
                "type": {
                    "type": "string",
                    "enum": ["circle", "square"]
                }
            },
            "required": ["type"],
            "oneOf": [
                {
                    "properties": {
                        "type": {"const": "circle"},
                        "radius": {"type": "number"}
                    },
                    "required": ["type", "radius"],
                    "additionalProperties": False
                },
                {
                    "properties": {
                        "type": {"const": "square"},
                        "sideLength": {"type": "number"}
                    },
                    "required": ["type", "sideLength"],
                    "additionalProperties": False
                }
            ]
        }
    },
    "required": ["shape"],
    "additionalProperties": False
}


data = data = {"shape": {"type": "circle", "radius": 5}}

## require to catch jsonschema.SchemaError and jsonschema.ValidationError 
jsonschema.validate(data, schema)

同じく、全体をチェックするコードをGistに上げた。

JSON Schemaはjson-schema.orgで議論・公開されている。現在のバージョンは2020-12。