トップ > JSON・構造化出力が壊れる
Ollama で JSON が途中で切れる:num_ctx 4096 の天井を見分けて直す
この記事で分かることdone_reason が length で、入力+出力がちょうど 4,096 なら文脈長の天井。/v1 では num_ctx が効かない
結論:Ollama で長い入力を渡したとき、構造化出力(JSON)が途中で切れてパースに失敗し、done_reason が length になっているなら、まず 入力トークン+出力トークンの合計が `num_ctx`(既定 4096)にちょうど張り付いていないか を確認してください。私たちの環境では、合計がぴったり 4,096 で打ち切られていました。num_ctx を広げると同じ入力で完結しました。OpenAI 互換の /v1 に num_ctx を送っても効きませんでした。
対象読者:Ollama の format(JSON スキーマ)で構造化出力を使っていて、入力が長いときだけ JSONDecodeError(EOF)が出る人。
症状の見分け方
次の 3 つが同時に出ていたら、出力上限ではなく 文脈長(num_ctx)の天井 を疑います。
- JSON が閉じないまま終わり、パースで失敗する(EOF)
- 応答の
done_reason(OpenAI 互換ならfinish_reason)がlength - 応答の
prompt_eval_count + eval_countがnum_ctxと同じ値(4096 など)
さらに、prompt_eval_count が 自分が送った入力より明らかに小さい 場合、入力側も黙って切られています。私たちの再現では、6,302 トークン相当の入力が 2,050 トークンしか読まれていませんでした。
検証した条件
| 項目 | 値 |
|---|---|
| ランタイム | Ollama 0.34.0(Windows 11) |
| モデル | qwen3.5:9b |
| GPU | RTX 5070 Ti(16GB) |
| 生成設定 | temperature 0、think 無効 |
| 入力 | 320 項目の一覧を 1 件ずつ要約させる指示(同一の入力を 3 回とも使用) |
| 出力形式 | JSON スキーマ(items 配列、各要素に id と summary、すべて required) |
結果
| 呼び出し方 | num_ctx | 入力 | 出力 | 合計 | 終了理由 | JSON |
|---|---|---|---|---|---|---|
ネイティブ /api/chat | 4096 | 2,050 | 2,046 | 4,096 | length | 壊れた |
ネイティブ /api/chat | 16384 | 6,302 | 6,568 | 12,870 | stop | 読めた |
OpenAI 互換 /v1(num_ctx 16384 を送信) | 無視された | 2,050 | 2,046 | 4,096 | length | 壊れた |
所要時間は順に 26.3 秒、65.0 秒、23.8 秒でした。
自分の環境で確かめる
ネイティブ API の応答に含まれる数値を見るだけで判定できます。次の Python は、Ollama を localhost:11434 で動かしている前提です(モデル名と入力は自分のものに置き換えてください)。
import json, urllib.request
body = {
"model": "qwen3.5:9b",
"stream": False,
"format": your_json_schema, # 使っている JSON スキーマ
"messages": [{"role": "user", "content": your_long_prompt}],
"options": {"num_ctx": 4096}, # いまの値
}
req = urllib.request.Request("http://localhost:11434/api/chat",
data=json.dumps(body).encode(),
headers={"Content-Type": "application/json"})
d = json.loads(urllib.request.urlopen(req).read())
total = d["prompt_eval_count"] + d["eval_count"]
print(d["done_reason"], d["prompt_eval_count"], d["eval_count"], total)
# length で、total が num_ctx と同じなら文脈長の天井
直し方
- ネイティブ API(`/api/chat`・`/api/generate`)を使い、`options.num_ctx` を広げる。 上の結果の 2 行目がこれです。入力と、欲しい出力の長さの合計より大きい値にします。
- OpenAI 互換 `/v1` を使うクライアントでは、リクエストで `num_ctx` を渡しても効かない(上の 3 行目で確認)。サーバー側の設定(環境変数
OLLAMA_CONTEXT_LENGTHや Modelfile のPARAMETER num_ctx)で広げる方法が紹介されていますが、私たちは未検証です(出典:RepoFold、openclaw の報告)。 - 出力そのものを小さく設計する。 私たちのシステムでは、記事全体を JSON で書き直させていた呼び出し(qwen3.5:35b-a3b、Ollama 0.33.3)が入力 3,136+出力 960=4,096 で切れていました。直す文だけを返させる形に変えたところ、同じ記事で 2,195+318 トークンで完結しました。
当てはまらない条件・未検証の範囲
- 確かめたのは Ollama 0.34.0 と qwen3.5:9b の組み合わせ(最後の例のみ qwen3.5:35b-a3b / 0.33.3)です。他のランタイムやモデルでは確かめていません。
num_ctxを広げると、モデルによってはメモリ使用量や速度が変わります。私たちの qwen3.5:35b-a3b では 4096 から 16384 に広げても速度はほぼ同じでしたが、一般化はできません。- 合計が
num_ctxに届いていないのに切れる場合は、別の原因(出力上限num_predictなど)です。この記事の対象外です。
以前の版からの訂正
以前この記事では、原因を「審査役に書かせる指摘が多すぎて出力が長くなるため」と説明していました。その後の実測で、主因は入力と出力の合計が文脈長 4,096 に達することだと分かったので、全面的に書き直しました。
この記事はローカル LLM が下書きし、記載の数値・固有名は実測ログとの機械照合を通したものです。人による全文の確認はしていません。数値は本文中に示した実測条件でのものです。