🦙
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
クラスが実際の変換ロジックを担います。
visit
メソッド)
2.1. 再帰的なスキーマ解釈 (JSON Schemaの解釈は、SchemaConverter::visit
メソッドによって再帰的に行われます。
-
visit
メソッドは、JSON Schemaの断片(ノード)を受け取ります。 -
if-else if
構文を使い、$ref
,oneOf
,type
,properties
などのキーワードを優先順位に従って評価し、処理を分岐させます。 - オブジェクトのプロパティや配列の要素など、入れ子になったスキーマに対しては、
visit
メソッド自身を再度呼び出すことで再帰的に解釈を進めます。 - 再帰は、
"type": "string"
のようなプリミティブ型に到達した時点で停止し、あらかじめ定義された基本ルールが返されます。
resolve_refs
メソッドと$defs
の扱い)
2.2. 参照の解決 (スキーマ内の参照は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
では無視されます。
具体的には、以下のフィールドは文法生成のロジックで考慮されません。
title
description
default
examples
これらのフィールドは、あくまでスキーマの可読性や開発者向けの補助情報として扱われます。
関連記事
llama.cppで構造化出力がどのように処理されるかを調査しました。
構造化出力の効率的な利用方法を探るため、複数のLLMを対象に5つの異なる指示形式を比較します。
Discussion