トラブルシューティングガイド
AI の回答が途中で切れた? finish_reason "length" と max_tokens を修正する
API の回答が文の途中で止まる、あるいは壊れた JSON が返る? finish_reason を確認し、上限内で max_tokens を上げ、継続ステップを追加しましょう。

このページの内容
スクリプトは実行され、モデルも応答したのに、関数が行の途中で終わっている。あるいは、一見まともな返答なのに JSON パーサーが例外を投げる。多くの場合、モデルは失敗していません。設定した max_tokens の上限(あるいは気づかなかったデフォルト値)に達し、API が単語の途中で生成を止めたのです。
素早い対処法: レスポンスの finish_reason を読みます。"length"(OpenAI 互換 API)であるか、stop_reason が "max_tokens"(Anthropic Messages)であれば、回答は途中で切れています。max_tokens をモデルの出力上限まで引き上げるか、モデルに続きを要求してください。このページの残りでは、その両方をコピー&ペーストできるコードで示します。
なぜこうなるのかを平易な言葉で知りたい場合は、まず AI の回答が文の途中で止まる理由 をお読みください。このページはデバッグ向けの解説です。
途中で切れた回答は画面上でどう見えるか#
症状は依頼内容によって異なりますが、原因は同じです。以下を、あなたが見たものと照らし合わせてみてください:
ペアを合わせる
症状とその考えられる原因を照合する
左の項目をタップしてから、そのペアをタップ
- 関数が
return resuで終わっているコードの回答が行の途中で max_tokens に達した json.loadsが "Unterminated string" や "Expecting ',' delimiter" で失敗するJSON が途中で切れた- 翻訳がテキストの前半で止まる出力上限が翻訳より小さい
- 返答は完全で、フィールドが
stopになっているモデルは自力で完了した。別のところを調べる
最後の行が重要です。finish_reason が stop なら、上限は問題ではありません。max_tokens を上げても何も変わりません。
ステップ 1: 教えてくれるフィールドを読む#
すべてのレスポンスは、生成がなぜ終わったかを示します。OpenAI 互換のレスポンスでは choices[0]. です。モデルが完了したときは "stop"、トークンを使い果たしたときは "length" になります。Anthropic Messages API ではフィールドは stop_reason で、途中で切れたときの値は "max_tokens" です。Anthropic の stop-reason ガイド(2026年10月28日確認)は、まさに以下の2つの修正方法を推奨しています: max_tokens を上げるか、レスポンスを継続するかです。
同じチェックを3つの形式で示します。ベース URL は OpenAI SDK では https:/、Anthropic SDK では https:/(/v1 なし)です。max_tokens=200 は意図的に小さくしてあるので、切れる様子を自分の目で確認できます。
1つのチェックを3通りで
Python(OpenAI SDK)
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
resp = client.chat.completions.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
max_tokens=200,
messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)
choice = resp.choices[0]
if choice.finish_reason == "length":
print("Cut off at the token limit")
print(choice.message.content)
print(resp.usage.completion_tokens)Python(Anthropic SDK)
import os
import anthropic
client = anthropic.Anthropic(
base_url="https://api.cheaptokens.dev",
api_key=os.environ["CHEAPTOKENS_API_KEY"],
)
msg = client.messages.create(
model="deepseek-ai/DeepSeek-V4-Flash-0731",
max_tokens=200,
messages=[{"role": "user", "content": "Write a Python class for a todo list with tests."}],
)
if msg.stop_reason == "max_tokens":
print("Cut off at the token limit")
print(msg.content[0].text)curl
curl https://api.cheaptokens.dev/v1/chat/completions \
-H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-ai/DeepSeek-V4-Flash-0731",
"max_tokens": 200,
"messages": [{"role": "user", "content": "Write a Python class for a todo list with tests."}]
}'返ってくる JSON の "finish_ を探してください。
診断として機能すると、このペアが見えます: 低い上限では length、余裕のある上限では stop になります。

ステップ 2: max_tokens を上げる。ただし上限を超えないこと#
明らかな修正は、より大きな数値にすることです。注意点: すべてのモデルには回答に対する独自の上限があります。それを超える値を設定しないでください。上限はモデルごとに異なるので、まず下の表を確認してください。これらは CheapTokens における回答ごとの上限です(事実は2026年10月28日確認):
テーブルを右にスクロールしてください →
| モデル | 回答ごとの最大出力 | コンテキスト |
|---|---|---|
| DeepSeek-V4-Flash | 32,768 トークン | 380K |
| GLM-5.3-Flash | 8,192 トークン | 390K |
| MiniMax-M2.7 | 8,192 トークン | 180K |
9,000 トークンの翻訳には、回答ごとの上限がより大きいモデルが必要です — DeepSeek-V4-Flash は回答ごとに最大 32,768 トークンまで対応します。長いコード、長い JSON、あるいは文書全体には、deepseek- が最も余裕があります: 回答ごとに最大 32,768 トークン、1日のどの時間帯でも同一価格です。プロバイダーによって名前の付け方が異なるため、正確なモデル ID は CheapTokens ドキュメント と モデルページ から取得してください。
実用的なルール: max_tokens は受け入れられる最長の回答に設定し、最大値にはしないことです。料金はモデルが実際に書き出したトークンに対して支払うので、回答が短ければ高い上限でもコストはかかりません。
ステップ 3: 途中で止まる JSON#
途中で切れた JSON の返答は最も厄介なケースです。なぜなら、パーサーが末尾に到達するまでテキストは正しく見えるからです。1回の呼び出しで40枚の請求書からフィールドを抽出し、配列を要求したとします。モデルは31個のオブジェクトを書き、上限に達し、配列は閉じられません。

]} を追加して文字列を修復しようとしないでください。レコードが欠けた有効な JSON が得られ、エラーよりも悪くなります。まず finish_reason を確認し、length なら、上限を上げて呼び出しを再実行するか、入力をより小さなバッチに分割してください。フォーマット自体を強制する方法については、LLM API から有効な JSON を得る をご覧ください。
ステップ 4: モデルに続きを書かせる#
回答がどの上限よりも本当に長いこともあります: 書籍の一章、大規模なリファクタリングなどです。その場合は、ここまで得られたものを返して、モデルに続きを要求します。以下のループは、これまでのテキストを保持し、残りを要求し、数ラウンド後に諦めるので、バグで無限に回り続けることはありません。
def complete(base_messages, model, max_tokens=4096, rounds=4):
text = ""
for _ in range(rounds):
messages = list(base_messages)
if text:
messages += [
{"role": "assistant", "content": text},
{"role": "user", "content": "Continue exactly where you stopped. Do not repeat anything."},
]
r = client.chat.completions.create(model=model, max_tokens=max_tokens, messages=messages)
choice = r.choices[0]
text += choice.message.content or ""
if choice.finish_reason != "length":
return text, True # finished on its own
return text, False # still cut after all roundsこの手法を誠実に保つための2つの習慣。True/False フラグを返し、False のときにユーザーへ警告を表示すること。文書の半分を完成品として提示しないことです。また、各ラウンドはそれまでのテキストを入力として再送するため、継続した回答は1回の大きな回答よりコストがかかることを覚えておいてください。構造化データには、継続よりも大きな上限か小さなバッチを優先してください。
長い回答のコスト#
長い回答が入力 2,000 トークン、出力 8,000 トークンだとします。DeepSeek-V4-Flash では、そのリクエストは本日の価格で $0.00083 かかり、入力と出力を合計すると、OpenRouter より安くなります。数値を自分の負荷に合わせて変えてください:
コスト計算機
長い回答50件の1日
リクエストあたりの入力トークン数: 2,000 · リクエストあたりの出力トークン数: 8,000 · 1日あたりのリクエスト数: 50
| モデル | 1日あたり | 1か月あたり |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.018 | $0.54 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.0415 | $1.25 |
| MiniMax-M2.7$0.05$0.15 | $0.065 | $1.95 |
要点: このシナリオでは出力側が支配的なので、max_tokens の余裕は再実行に対する安価な保険です。切れた回答を再生成すると二重に支払うことになります。請求の両側面についての背景は 入力トークンと出力トークン に、価格付きの全モデル一覧は 料金ページ にあります。
出荷前に: 短いチェックリスト#
チェックリスト
コードのための切り詰めチェックリスト



