故障排查指南
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",token 用完时是 "length"。在 Anthropic Messages API 中,字段是 stop_reason,被截断的值是 "max_tokens"。Anthropic 的停止原因指南(2026 年 10 月 28 日核对)推荐的正是下面这两种修复:提高 max_tokens 或继续响应。
下面是同一种检查的三种形式。OpenAI SDK 的 base URL 是 https:/,Anthropic SDK 的是 https:/(不带 /v1)。max_tokens=200 故意设得很小,这样你可以亲眼看到截断。
一种检查,三种写法
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 tokens | 380K |
| GLM-5.3-Flash | 8,192 tokens | 390K |
| MiniMax-M2.7 | 8,192 tokens | 180K |
一个 9,000 token 的翻译需要一个每回答上限更大的模型——DeepSeek-V4-Flash 每个回答最多可输出 32,768 tokens。对于长代码、长 JSON 或整份文档,deepseek- 的空间最大:每个回答最多 32,768 tokens,一天中任何时段都是同一个价格。请从 CheapTokens 文档和模型页面获取准确的模型 id,因为各提供商的命名方式不同。
一条实用规则:把 max_tokens 设为你愿意接受的最长回答,而不是最大值。你只为模型实际写出的 token 付费,所以回答短时高上限不花任何钱。
第 3 步:中途停下的 JSON#
被截断的 JSON 回复是最麻烦的情况,因为文本看起来没问题,直到解析器撞到结尾。假设你在一次调用中从 40 张发票里提取字段,并要求返回一个数组。模型写了 31 个对象,到达上限,数组始终没有闭合。

不要试图通过添加 ]} 来修复字符串。你会得到合法但缺少记录的 JSON,这比报错更糟。先检查 finish_reason;如果它是 length,要么提高上限并重新运行调用,要么把输入拆成更小的批次。关于强制格式本身,请参阅从 LLM API 获得合法 JSON。
第 4 步:让模型继续#
有时回答确实比任何上限都长:一个书籍章节、一次大规模重构。那就把你已有的内容喂回去,让模型接着写。下面的循环保留它目前已有的文本,请求剩余部分,并在几轮之后放弃,这样 bug 就不会无限空转。
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两个习惯能让这件事保持诚实。返回 True/False 标志,并在 False 时向用户显示警告,而不是把半份文档当作完成品呈现。还要记住,每一轮都会把之前的文本作为输入重新发送,所以继续的回答比一次大回答花费更多。对于结构化数据,优先选择更大的上限或更小的批次,而不是继续。
一个长回答要花多少钱#
假设一个长回答是 2,000 tokens 输入、8,000 tokens 输出。在 DeepSeek-V4-Flash 上,按今天的价格,这个请求花费 $0.00083,而且把输入和输出加在一起算,它比 OpenRouter 更便宜。把数字换成你自己的负载:
成本计算器
一天 50 个长回答
每次请求的输入 token 数: 2,000 · 每次请求的输出 token 数: 8,000 · 每日请求数: 50
| 模型 | 每天 | 每月 |
|---|---|---|
| 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 的余量是防止重跑的廉价保险。一个被截断、需要重新生成的回答要付两次钱。关于账单两侧的背景知识见输入 vs 输出 tokens,带价格的完整模型列表在定价页面。
上线之前:一份简短清单#
清单
代码的截断检查清单



