vLLMのStructured Outputs移行を入力形式から確認する

vLLMのOpenAI互換endpointでJSONを強制するとき、古い記事のguided_jsonをそのまま使うとversionによってrequestが拒否されます。
現在の公式資料を読むと、古いguided系fieldと、現在のstructured_outputs、OpenAI互換のresponse_formatを分けて移行する必要があります。
JSON Schemaを追加することと、抽出値の意味が正しいことを保証することも別です。
旧fieldの場所を検索する
以前のvLLMでは、requestの追加parameterへ次のようなguided_jsonを渡していました。
{
"guided_json": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
現行のStructured Outputs公式資料では、guided_json、guided_regex、guided_choice、guided_grammarなどをlegacy APIとして扱います。
まずrepository、client、prompt template、テストへ残る旧fieldを検索します。
利用中のvLLM versionが、旧fieldを受け付けるかも実際のserverで確認します。
structured_outputsへ移行する
guided_jsonに相当するJSON Schemaは、次のように書きます。
{
"structured_outputs": {
"json": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
}
対応関係は次です。
| 旧field | 現在の指定 |
|---|---|
guided_json |
structured_outputs.json |
guided_regex |
structured_outputs.regex |
guided_choice |
structured_outputs.choice |
guided_grammar |
structured_outputs.grammar |
guided_whitespace_pattern |
structured_outputs.whitespace_pattern |
structural_tag |
structured_outputs.structural_tag |
OpenAI互換clientから送る場合は、通常extra_bodyへvLLM固有の指定を入れます。
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="-",
)
completion = client.chat.completions.create(
model="MODEL_ID",
messages=[
{"role": "user", "content": "名前と年齢を抽出してください。"},
],
extra_body={
"structured_outputs": {
"json": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
},
"required": ["name", "age"],
},
},
},
)
response_formatとの違いを確認する
JSON Schemaだけを使う場合は、OpenAI互換のresponse_formatへ指定する方法もあります。
{
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "person",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"}
},
"required": ["name", "age"]
}
}
}
}
response_formatでは、json_schemaの中へnameと実際のschemaを入れます。
structured_outputsはvLLMの制約指定であり、response_formatはOpenAI互換requestの形式です。
同じSchemaを表現できても、clientやハーネスがどちらを転送するかを確認します。
field名だけを置き換えず、requestの入れ子構造とレスポンスの取り出し方をテストします。
server起動時の設定を確認する
OpenAI互換serverでは、Structured Outputsは通常有効です。
backendを固定したい場合は、起動時にstructured-outputs-config.backendを指定します。
vllm serve MODEL_ID \
--structured-outputs-config.backend=auto
autoでは、requestと利用可能なbackendからvLLMが選択します。
backendの挙動とregex syntaxは、vLLM versionとbackendで変わり得ます。
再現性が必要なら、対応状況を確認して明示的なbackendを選びます。
Structured Outputsが保証しないこと
JSON Schemaは、出力を構文と構造へ合わせる制約です。
入力文へ値が実際に存在することや、業務上の意味が正しいことまでは保証しません。
ageをintegerとして生成できても、入力に書かれていない年齢を正しく推測できるとは限りません。
アプリ側で次の検証を分けます。
- syntax validation: JSONとしてparseできるか確認する
- schema validation: type、必須field、enumを確認する
- semantic validation: 入力との対応、範囲、業務ruleを確認する
- retry decision: 同じinputを再送するか、指示を変えるか決める
Structured Outputsは、semantic validationやretryを代行しません。
LLMの出力は検証前の入力として扱います。
reasoning modelと組み合わせる
reasoning modelでは、推論部分と最終回答を分離するためreasoning parserを使う場合があります。
vLLMの資料には、推論中のStructured Outputsを有効にするenable_in_reasoningがあります。
vllm serve MODEL_ID \
--reasoning-parser MODEL_SPECIFIC_PARSER \
--structured-outputs-config.enable_in_reasoning=True
必要性はmodel、chat template、reasoning parser、vLLM versionで変わります。
JSONが崩れるときは、Schemaだけでなく4つの組み合わせを確認します。
移行チェックリスト
- vLLM versionを固定する
guided_jsonなどの旧fieldを検索するstructured_outputs.jsonまたはresponse_formatへ移すresponse_formatならnameとschemaの入れ子を確認する- modelのchat templateを確認する
- 非reasoning modelで最小JSONを試す
- reasoning modelではparserとの組み合わせを試す
- syntax、schema、semanticの検証を分離する
移行の成功条件は、requestが受け付けられることだけではありません。
期待するSchema、意味検証、失敗時のretry境界まで再現できることを確認します。
