この記事は AI(Claude)が下書きし、運営者が内容を確認して公開しています。
何が困るのか
LLM に「JSON で出力して」と頼んでも、そのまま json.loads に渡せないことがあります。たとえば次のような壊れ方です。
- 末尾カンマ:
{"name": "田中", "age": 20,} - シングルクォートで囲まれている
-
```のコードブロックで囲まれている - 出力が途中で切れている
- 構文は正しいが、必須項目がない、型が違う(
"age": "20"のように数値が文字列になっている)
JSONDecodeError だけでは、LLM に何を直させればよいかが伝わりにくいです。そこで、JSON の構文・型・必須項目などを検証し、日本語のエラー説明と、LLM にそのまま再送できる修正指示テキストを返す API を作りました。
検証は JSON Schema に基づくルールベースで、LLM は使っていません。同じ入力なら結果は毎回同じです。
この API で何が返るか
エンドポイントは POST https://oxbox-json-validator.fly.dev/validate です。以下は公開中の API で実際に取得したレスポンスです。
1. 構文エラー(末尾カンマ)
リクエスト本文:
{"json":"{\"name\": \"田中\", \"age\": 20,}"}
レスポンス:
{"valid":false,"error_count":1,"errors_truncated":false,"errors":[{"code":"json_syntax","path":"$","message":"末尾にカンマ(,)が余分に付いている可能性があります(JSON では不可)。(1行目 26文字目付近)","fix":"} や ] の直前のカンマを取り除いてください。","position":{"line":1,"column":26}}],"retry_prompt":"直前に出力されたJSONに問題がありました。次の点をすべて修正し、JSONだけを出力し直してください。\n1. } や ] の直前のカンマを取り除いてください。","usage":{"used":1,"limit":100,"month":"2026-11"}}
原因と位置(1行目 26文字目付近)、直し方が返ります。
2. スキーマ違反(必須項目なし・型違い)
リクエスト本文:
{"json":"{\"age\": \"20\"}","schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"age":{"type":"integer"}}}}
レスポンス:
{"valid":false,"error_count":2,"errors_truncated":false,"errors":[{"code":"required","path":"$","message":"$ に必須項目 \"name\" がありません。","fix":"$ に \"name\" を追加してください。","position":null},{"code":"type","path":"$.age","message":"$.age の型が違います。整数である必要がありますが、文字列が渡されました。","fix":"$.age を整数の値に直してください。","position":null}],"retry_prompt":"直前に出力されたJSONに問題がありました。次の点をすべて修正し、JSONだけを出力し直してください。\n1. [$] $ に \"name\" を追加してください。\n2. [$.age] $.age を整数の値に直してください。","usage":{"used":2,"limit":100,"month":"2026-11"}}
エラーが複数ある場合も、retry_prompt に番号付きでまとまります。エラーは最大50件まで返り、超えた分は errors_truncated: true になります。
3. 正常
同じスキーマで {"name": "田中", "age": 20} を送った場合です。
{"valid":true,"error_count":0,"errors_truncated":false,"errors":[],"retry_prompt":null,"usage":{"used":3,"limit":100,"month":"2026-11"}}
retry_prompt は不合格のときだけ入り、合格時は null です。
主なエラー種別(code)
json_syntax(構文)、type、required、additional_properties、enum、const、minimum/maximum 系、min_length/max_length、pattern、format、min_items/max_items、unique_items、one_of_*/any_of/not などに対応しています。エラー文は日本語が既定で、lang に "en" を指定すると英語になります。
使い方
1. API キーを申し込む
無料 API キーの申込フォーム から申し込みます。キーは運営者が手動で発行し、メールで送ります。通常2営業日以内です。
2. cURL で試す
BASE_URL=https://oxbox-json-validator.fly.dev
curl -X POST "$BASE_URL/validate" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"json": "{\"name\": \"太郎\", \"age\": \"20\"}",
"schema": {
"type": "object",
"properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
"required": ["name", "age"],
"additionalProperties": false
}
}'
認証は X-API-Key: <キー> のほか、Authorization: Bearer <キー> でも行えます。
リクエストの項目は次のとおりです。
| 項目 | 型 | 説明 |
|---|---|---|
json |
文字列(必須) | 検証したい JSON 文字列(LLM の出力そのまま) |
schema |
オブジェクト(任意) | JSON Schema。省略すると構文のみ検証。外部 $ref は非対応 |
lang |
"ja" / "en"(任意) |
エラー文の言語。既定は "ja"
|
json には、パース済みのオブジェクトではなく文字列のまま渡します。
3. Python で試す
import requests
BASE_URL = "https://oxbox-json-validator.fly.dev"
schema = {"type": "object",
"properties": {"name": {"type": "string"}, "age": {"type": "integer"}},
"required": ["name", "age"]}
llm_output = '{"name": "太郎", "age": "20"}' # LLM が返した文字列
res = requests.post(
f"{BASE_URL}/validate",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"json": llm_output, "schema": schema},
timeout=10,
)
data = res.json()
if not data["valid"]:
# LLM にそのまま再送できる修正指示
print(data["retry_prompt"])
4. エージェントの再試行ループに組み込む
retry_prompt を LLM への次の入力に足すだけで、再試行ループが作れます。以下は上の Python 例を土台にした組み込みの一例です。call_llm はお使いの LLM 呼び出し関数に置き換えてください(ここでは仮の関数です)。
import json
import requests
BASE_URL = "https://oxbox-json-validator.fly.dev"
def validate(llm_output: str, schema: dict) -> dict:
res = requests.post(
f"{BASE_URL}/validate",
headers={"X-API-Key": "YOUR_API_KEY"},
json={"json": llm_output, "schema": schema},
timeout=10,
)
res.raise_for_status() # 401 / 413 / 422 / 429 などはここで例外になる
return res.json()
def generate_json(prompt: str, schema: dict, max_retries: int = 3) -> dict:
current_prompt = prompt
for _ in range(max_retries + 1):
llm_output = call_llm(current_prompt) # 仮の関数: LLM の出力文字列を返す
data = validate(llm_output, schema)
if data["valid"]:
return json.loads(llm_output)
# 直前の出力と修正指示を付けて、もう一度依頼する
current_prompt = (
prompt
+ "\n\n直前の出力:\n" + llm_output
+ "\n\n" + data["retry_prompt"]
)
raise RuntimeError("規定回数内に有効な JSON を得られませんでした")
注意点です。
- 検証を1回呼ぶごとに、無料枠(月100件)を1件消費します。
max_retriesは小さめにしておくと安心です。 - JSON が不正でも HTTP ステータスは 200 で返ります。合否は
validで判定してください。 - 429 にはレート制限(
rate_limited、Retry-After付き)と月間上限超過(quota_exceeded)があります。ループ内で扱いを分けてください。
制限と注意
- 提供プラン: 現在は無料プラン(月100件)のみです。有料プランは準備中です。
- サイズ上限: 1リクエスト 100,000 バイトまでです。超えると 413 になります。
-
レート制限: 1キーあたり毎分60回です。超えると 429(
rate_limited、Retry-After付き)になります。 -
正規表現(pattern)の制限: スキーマの
pattern/patternPropertiesは、1つ256文字・1スキーマ20個までです。超えると 422PATTERN_LIMIT_EXCEEDEDになります。また、検証には2秒の時間制限があり、超えると中断されて 422VALIDATION_TIMEOUTになります。patternを単純にし、入れ子の繰り返しは避けてください。 -
スキーマの制限: 入れ子が深すぎるスキーマや不正なスキーマは 422 になり、利用回数は消費しません。外部
$refは非対応です。 - 提供していない機能: バッチ処理、Webhook、JSON の自動修復は提供していません。
- ペイロードは保存しません: 送られた JSON とスキーマは保存せず、ログに残るのは利用件数だけです。ただし、個人情報・機密情報・認証情報を含む JSON は送らないでください。
- 初回リクエストが遅いことがあります: Fly.io(東京リージョン)でホスティングしており、アクセスがないと停止し、最初のリクエストで起動します。そのため初回は数秒かかることがあります。クライアントのタイムアウトには余裕を持たせてください。
まとめ
LLM が出す JSON の「壊れ方」を、原因・位置・直し方つきの日本語で返し、retry_prompt を LLM に戻せるようにする API です。構文エラーとスキーマ違反を1回の呼び出しで検証でき、エージェントの再試行ループに組み込めます。無料枠は月100件です。気になった方は、キーを申し込んで試してみてください。
リンク・運営者情報
- サービス(LP 兼 API ドキュメント): https://oxbox-json-validator.fly.dev
- 無料 API キーの申込フォーム: https://forms.gle/YiEwnqcToAfxR62P6
- 利用規約: https://oxbox-json-validator.fly.dev/terms
- プライバシーポリシー: https://oxbox-json-validator.fly.dev/privacy
- 運営者: OXBOX(連絡先: takanozaki.1021@gmail.com)