🦙
llama.cppにおけるJSON Schemaパーサーの調査と仕様
llama.cppプロジェクトに実装されている、JSON Schemaを解析し、それに基づいてGBNF(GGML BNF)形式の文法を生成する機能についての調査結果をまとめます。
1. 機能の概要
llama.cppは、JSON SchemaをGBNF文法に変換する機能を持ちます。これにより、大規模言語モデル(LLM)の出力を、指定されたJSON構造に厳密に準拠させることが可能になります。
この機能はcommon/json-schema-to-grammar.cppファイル内のjson_schema_to_grammar関数によって提供されています。
2. 主要な実装コンポーネントと処理の流れ
変換処理の中心はcommon/json-schema-to-grammar.cppに実装されているSchemaConverterクラスです。
-
エントリーポイント:
json_schema_to_grammar関数が変換プロセスの入り口となり、nlohmann::ordered_json型のJSON Schemaオブジェクトを受け取ります。 -
中心的なクラス:
SchemaConverterクラスが実際の変換ロジックを担います。
2.1. 再帰的なスキーマ解釈 (visitメソッド)
JSON Schemaの解釈は、SchemaConverter::visit メソッドによって再帰的に行われます。
-
visitメソッドは、JSON Schemaの断片(ノード)を受け取ります。 -
if-else if構文を使い、$ref,oneOf,type,propertiesなどのキーワードを優先順位に従って評価し、処理を分岐させます。 - オブジェクトのプロパティや配列の要素など、入れ子になったスキーマに対しては、
visitメソッド自身を再度呼び出すことで再帰的に解釈を進めます。 - 再帰は、
"type": "string"のようなプリミティブ型に到達した時点で停止し、あらかじめ定義された基本ルールが返されます。
2.2. 参照の解決 (resolve_refsメソッドと$defsの扱い)
スキーマ内の参照はresolve_refsメソッドによって解決されます。
-
$ref:"$ref"キーワードを見つけると、その参照先(内部または外部)のスキーマを解決します。 -
$defs:"$defs"キーワード自体を直接処理するロジックはありません。代わりに、"$ref": "#/$defs/my_definition"のようなJSONポインター形式の参照をresolve_refsメソッドが解釈することで、$defsセクションで定義されたスキーマが間接的にサポートされます。
3. サポートされるJSON Schemaキーワード
SchemaConverterがGBNF文法を生成する際に解釈するキーワードは以下の通りです。
schema_type (typeキー) |
関連する子要素 (キーワード) | 説明 |
|---|---|---|
| (型を問わない) | $ref |
最優先。参照先のスキーマを解決する。 |
| (型を問わない) |
oneOf, anyOf, allOf
|
複数のスキーマを組み合わせるための論理演算子。 |
| (型を問わない) | const |
特定の定数値と完全に一致することを要求する。 |
| (型を問わない) | enum |
値が列挙リスト内のものであることを要求する。 |
object |
properties, additionalProperties, required
|
オブジェクトの構造(プロパティ、必須項目、拡張性)を定義する。 |
array |
items, prefixItems, minItems, maxItems
|
配列の要素のスキーマや個数を定義する。 |
string |
pattern, format, minLength, maxLength
|
文字列の正規表現、フォーマット、長さを定義する。 |
integer / number
|
minimum, maximum, exclusiveMinimum, exclusiveMaximum
|
数値の範囲を定義する。 |
4. サポートされないJSON Schemaキーワード
JSON Schemaの仕様にはドキュメンテーションやメタデータのためのフィールドも含まれますが、これらはGBNFの構文定義に直接関係しないため、SchemaConverterでは無視されます。
具体的には、以下のフィールドは文法生成のロジックで考慮されません。
titledescriptiondefaultexamples
これらのフィールドは、あくまでスキーマの可読性や開発者向けの補助情報として扱われます。
関連記事
llama.cppで構造化出力がどのように処理されるかを調査しました。
構造化出力の効率的な利用方法を探るため、複数のLLMを対象に5つの異なる指示形式を比較します。
Discussion