Local / Edge実践ガイド

llama.cppでGBNF・JSON Schemaを使いJSON出力を制約する方法

llama.cppでGBNF・JSON Schemaを使いJSON出力を制約する方法
目次

公式サイトで次に進む

この記事で明示的に扱うソフトだけを表示します。配布・手順は各公式ページで確認してください。

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
項目の意味 → プロンプト
値の正しさ → 生成後の検証

と分けると理解しやすい。

生成後はアプリ側でも確認する

最低限、次の二段階に分ける。

  1. JSONとして読み込め、Schemaに合っているか
  2. 値がアプリの条件や実データと合っているか

たとえば在庫数を整数として返せても、その商品が実在するか、現在庫を超えた数量ではないか、といった確認は別に必要になる。

わざと意味的に答えにくい入力でも試す

構文が正しければ何でも成功としないために、情報が足りない入力も試しておく。

そのとき、

  • nullunknownを表現する必要があるか
  • エラー用のフィールドを用意するか
  • アプリ側で拒否する条件は何か

を決める。

GBNFやJSON Schemaは、出力の「形」を安定させるための仕組みである。 内容まで正しいとみなさず、意味や業務上の条件は別の検証として扱う。

出力の構造ではなく生成のばらつきを調整したい場合は、llama.cppのtemperature・top-p・min-pを確認できる。

まとめ

  • GBNFはモデルが生成できる文字列の構文を制約する仕組みで、JSONの形を崩れにくくできる
  • llama.cppではgrammarファイルを指定する方法や、対応範囲内のJSON Schemaから制約を作る方法が用意されている
  • JSONとして正しい形式になっても、値が事実として正しいことまでは保証されない
  • 生成後はJSONの形式確認に加え、アプリ側で値や業務条件も検証する
同じテーマから

この記事で扱ったデータ

モデル・GPU・実行ソフトの確認済みデータを、記事とは別に確かめられます。

サイト内検索