Local / Edge実践ガイド
llama.cppでGBNF・JSON Schemaを使いJSON出力を制約する方法

目次
公式サイトで次に進む
この記事で明示的に扱うソフトだけを表示します。配布・手順は各公式ページで確認してください。
llama.cpp
local-gen.jpがファイルを配布するものではなく、互換性を保証するものでもありません。
LLMの出力をプログラムから利用するとき、まず困るのが「JSONとして読み込めない」出力だ。括弧が足りない、余計な文章が付く、必要なフィールドが崩れる、といった問題がある。
llama.cppでは、GBNF grammarやJSON Schemaを使って、生成できる構文を制約することができる。
ただし、JSONの形を正しくすることと、内容の事実性を保証することは別だ。
GBNFは「どんな文字列を作れるか」を制約する
GBNFは、モデルが生成できる文字列の構造を定義するためのgrammarだ。
たとえば、
{
"name": "...",
"age": 0
}
のような形だけを許可するルールを作れば、括弧やフィールド構造が外れた出力を抑えられる。
nameは文字列、ageは整数、といった構文上の型もルールへ含められる。
CLIでは--grammarや--grammar-fileを使える
llama.cppのGBNF資料では、CLIからgrammarを指定するための--grammarや--grammar-fileが案内されている。
最初は公式の例など小さなgrammarから試し、単純な入力で期待したJSON形式になることを確認する。
いきなり大きな業務用スキーマを入れると、grammarの問題なのかモデルの応答内容の問題なのか切り分けにくい。
JSON Schemaを使う経路もある
llama.cppには、対応する範囲のJSON SchemaからGBNFへ変換する仕組みもある。
ただし、JSON Schemaのすべての機能へ常に対応しているとは限らない。使用するllama.cppのバージョンで、対応範囲や制限を公式資料から確認する。
変換時に警告が出る場合は、それを無視して「Schemaを渡したから完全に制約できた」と考えない。
Schemaは項目の意味までモデルへ教えるわけではない
たとえばageを整数に制約できても、その人物の正しい年齢をgrammarが知っているわけではない。
必要なフィールドが何を意味するのか、どの情報から値を決めるのかは、プロンプト側でも説明する必要がある。
構文の制約 → GBNF / JSON Schema
項目の意味 → プロンプト
値の正しさ → 生成後の検証
と分けると理解しやすい。
生成後はアプリ側でも確認する
最低限、次の二段階に分ける。
- JSONとして読み込め、Schemaに合っているか
- 値がアプリの条件や実データと合っているか
たとえば在庫数を整数として返せても、その商品が実在するか、現在庫を超えた数量ではないか、といった確認は別に必要になる。
わざと意味的に答えにくい入力でも試す
構文が正しければ何でも成功としないために、情報が足りない入力も試しておく。
そのとき、
nullやunknownを表現する必要があるか- エラー用のフィールドを用意するか
- アプリ側で拒否する条件は何か
を決める。
GBNFやJSON Schemaは、出力の「形」を安定させるための仕組みである。 内容まで正しいとみなさず、意味や業務上の条件は別の検証として扱う。
出力の構造ではなく生成のばらつきを調整したい場合は、llama.cppのtemperature・top-p・min-pを確認できる。
まとめ
- GBNFはモデルが生成できる文字列の構文を制約する仕組みで、JSONの形を崩れにくくできる
- llama.cppではgrammarファイルを指定する方法や、対応範囲内のJSON Schemaから制約を作る方法が用意されている
- JSONとして正しい形式になっても、値が事実として正しいことまでは保証されない
- 生成後はJSONの形式確認に加え、アプリ側で値や業務条件も検証する
この記事で扱ったデータ
モデル・GPU・実行ソフトの確認済みデータを、記事とは別に確かめられます。