返回博客

AI 入门指南

为什么 AI 答案会在句子中间停止?max_tokens、上下文窗口和 finish_reason 详解

在句子中间停止的 AI 答案触发了限制。读取 finish_reason,区分 max_tokens 与上下文窗口已满,并检查每个模型的输出限制。

作者:CheapTokens Team发布于 更新于 6 分钟阅读

深色封面,带有 cheaptokens 标志:方框中写着 max_tokens、finish_reason 和 length,标题为“答案在句子中间被截断?”
本页内容
  1. 截断答案的三个限制
  2. 先读 finish_reason
  3. 修复 1:提高 max_tokens
  4. 修复 2:上下文窗口已满时
  5. 修复 3:分部分获取长答案
  6. 当 finish_reason 为 stop 时
  7. 按答案长度选择模型

回复在句子中间戛然而止,几乎总是被某个限制截断了。模型并没有丢失思路;是请求没有足够的空间了。响应会说明是哪个限制:读取 finish_reason。length 表示触发了限制。stop 表示模型自行结束。

有三件事决定答案能有多长。一旦你知道是哪一个限制卡住了你,修复只需一分钟。

截断答案的三个限制#

  • max_tokens 是你在请求中设置的上限。当答案达到该上限时,生成就会停止,即使停在单词中间。DeepSeek 的 API 参考将其定义为补全中生成的最大 token 数。
  • 模型的输出限制 是该模型单次答案的上限。它属于模型本身,因此请将 max_tokens 保持在该值或以下。我们各模型的数值见下表。
  • 上下文窗口 是整个交互的空间:你的输入和答案加在一起。DeepSeek 的参考说明,输入和生成的 token 合计受模型上下文长度限制。

如果 token 对你来说还是个模糊概念,请先阅读 What is a token in AI?。本页假设你知道它是什么。

先读 finish_reason#

聊天响应中的每个 choice 都带有一个 finish_reason。紧挨着它,usage 告诉你输入和输出了多少 token。

下面是一个被截断的答案与一个已完成答案的对比。请求问的是“用一句简短的话说明什么是 token?”,第一次将 max_tokens 设为 16,然后设为 200。

示例仅作说明,并非真实记录的响应:你的计数和额外字段会有所不同。

json
{
  "choices": [
    {
      "message": { "role": "assistant", "content": "A token is a small piece of text that a model reads and writes, usu" },
      "finish_reason": "length"
    }
  ],
  "usage": { "prompt_tokens": 21, "completion_tokens": 16, "total_tokens": 37 }
}
json
{
  "choices": [
    {
      "message": { "role": "assistant", "content": "A token is a small piece of text that a model reads and writes, usually a word or part of a word." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 21, "completion_tokens": 24, "total_tokens": 45 }
}

在第一个响应中,completion_tokens 为 16,正好等于上限,文本以“usu”结尾。在第二个响应中,模型在 24 个 token 处停止,远低于 200 的上限,因为它已经完成了。

要弄清发生了什么,请按顺序执行以下步骤:

  1. 查看 finish_reason。 如果是 stop,答案就是完整的。模型选择结束,或触发了你设置的停止序列。如果是 length,则答案被某个限制截断,下一步会告诉你具体是哪一个。还存在其他值,例如 content_filter 和 tool_calls;两者都不是长度问题。
  2. 将 completion_tokens 与你发送的 max_tokens 进行比较。 如果它们相等或几乎相等,说明你触发了 max_tokens。
  3. 如果 completion_tokens 远低于 max_tokens 且 prompt_tokens 很大, 则很可能是上下文窗口已满。DeepSeek 的参考对两种情况都使用 length,因此仅凭该字段无法区分它们;第 2 步中的比较就是区分方法。Anthropic 和 Z.ai 的文档将第二种情况单独命名为 model_context_window_exceeded。查看你自己响应中的该字段,看看你得到的是哪个名称。

以上三种描述均来自开发者文档,核查于 2026 年 10 月 20 日:DeepSeek、Anthropic 和 Z.ai。

你看到的情况 finish_reason 原因 该怎么办
被截断,completion_tokens 等于 max_tokens length 达到了 max_tokens 提高 max_tokens,或要求分部分给出答案
被截断,completion_tokens 远低于 max_tokens,输入很长 length(某些 API 中为 model_context_window_exceeded) 上下文窗口已满 缩短输入或历史记录,或选择窗口更大的模型
完整但比你期望的短 stop 模型已完成,或触发了停止序列 要求更多细节;检查 stop 参数
没有文本,或被拒绝 content_filter 响应被过滤 改写请求
没有文本,而是工具请求 tool_calls 模型想要调用工具 运行工具并将结果发回

修复 1:提高 max_tokens#

最常见的原因也是最容易修复的。自己设置 max_tokens,这样你就知道适用的是哪个上限。不要依赖默认值。

python
from openai import OpenAI

client = OpenAI(base_url="https://api.cheaptokens.dev/v1", api_key="YOUR_API_KEY")

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Write a detailed guide to setting up a home network."}],
    max_tokens=16000,  # example value: stay at or below the model's output limit
)

choice = resp.choices[0]
print(choice.finish_reason, resp.usage.completion_tokens)

这展示了调用的形式。这里的密钥以 YOUR_API_KEY 形式传入;不要把真实密钥留在你分享的代码中。

提高数值只在模型自身限制以内才有帮助。以下是 CheapTokens 上的限制,核查于 2026 年 10 月 20 日,对照 CheapTokens 模型列表:

模型 模型 ID 每次答案的最大输出 上下文
DeepSeek-V4-Flash (0731) deepseek-ai/DeepSeek-V4-Flash-0731 32,768 tokens 380K
GLM-5.3-Flash zai-org/GLM-5.3-Flash 8,192 tokens 390K
MiniMax-M2.7 MiniMaxAI/MiniMax-M2.7 8,192 tokens 200K

开发者会为自己的 API 发布各自的限制。例如,Z.ai 列出 GLM-5 系列最高支持 128K 输出。这些不是 GLM-5.3-Flash 在 CheapTokens 上的限制,后者每次答案为 8,192 tokens。对于你在 CheapTokens 上构建的任何东西,请使用上表。

修复 2:上下文窗口已满时#

在聊天中,每条消息都会重新发送整个历史记录,因此输入每轮都会增长。上下文窗口必须容纳该历史记录加上新答案。如果仅历史记录就几乎占满整个窗口,那么留给回复的空间就所剩无几。

这些窗口很大,一个简短的问题不会填满它们。长聊天和粘贴的文档会。有帮助的做法:

  • 为新任务开启新聊天,而不是继续旧聊天。
  • 让模型总结到目前为止的对话,只把摘要带下去。
  • 删除不再需要的旧消息和附件。
  • 如果输入本身很大,选择窗口最大的模型。在我们的模型中,GLM-5.3-Flash 以 390K 居首。它的输出限制是 8,192 tokens,因此请分部分要求长结果。

修复 3:分部分获取长答案#

当你想要的答案超过模型的输出限制时,任何设置都无法将其塞进一次回复。请分块要求:当 finish_reason 为 length 时,将部分答案发回,并让模型继续。

python
messages = [{"role": "user", "content": "Write a detailed guide to setting up a home network."}]
parts = []

for _ in range(4):  # example limit: at most 4 rounds
    resp = client.chat.completions.create(
        model="zai-org/GLM-5.3-Flash",
        messages=messages,
        max_tokens=8000,  # example value: below this model's output limit
    )
    choice = resp.choices[0]
    parts.append(choice.message.content)
    if choice.finish_reason != "length":
        break
    messages.append({"role": "assistant", "content": choice.message.content})
    messages.append({"role": "user", "content": "Continue exactly where you stopped. Don't repeat anything."})

text = "".join(parts)

同样的提醒:这是该模式的示例,并非真实记录的运行。注意各部分之间的衔接处。模型有时会在连接处重复一句话或重新开始一个列表。

每一轮都会将到目前为止的所有内容作为输入重新发送,并且每个输出 token 都会计费。长答案比短答案花费更多。下面是一个 8,000 token 的答案,输入为 2,000 token,这适用于表中的每个模型:

模型 费用 OpenRouter
GLM-5.3-Flash $0.00036 $0.00069
DeepSeek-V4-Flash $0.00083 $0.0013
MiniMax-M2.7 $0.0013 $0.0102

总计 2,000 个输入 token 和 8,000 个输出 token。每 100 万 token 的价格为当前价格,会随我们的资费一同变动;OpenRouter 列显示相同工作负载的参考价格。

当 finish_reason 为 stop 时#

stop 表示模型结束了答案。如果它比你期望的更早结束,那不是限制问题,更大的 max_tokens 也不会有任何改变。说明你想要什么:长度、章节、详细程度。还要检查请求是否设置了 stop 参数;出现在答案中的停止序列会立即结束它。

按答案长度选择模型#

从你需要的答案出发。如果它适合 8,192 tokens,MiniMax-M2.7(MiniMaxAI/MiniMax-M2.7)可以在 CheapTokens 上处理。它的输出比 OpenRouter 参考价格更便宜,计费为 $0.15(每 1M 输出 tokens)。价格在任何时段都相同,没有高峰时段,并且 API 兼容 OpenAI 和 Anthropic。

以下是每个模型的当前价格列表:

模型 上下文 输入 $/1M 输出 $/1M OpenRouter 输入 OpenRouter 输出
GLM-5.3-Flash reasoning 390K $0.02 $0.04 $0.0413 $0.0758
DeepSeek-V4-Flash 380K $0.015 $0.10 $0.0475 $0.1568
MiniMax-M2.7 180K $0.05 $0.15 $0.30 $1.20

价格以每 100 万 token 的美元金额计算。OpenRouter 数据仅供参考,可能会有变动。

如果你还没有发送过请求,Your first API request with CheapTokens 会带你走一遍。关于什么是 token 以及为什么输入和输出分开计数,请参阅 What is a token in AI?。

每个模型的端点、模型 ID 和限制都在 CheapTokens docs 中。