文本生成任务里,输出在句子中间突然停住,往往不是模型本身不会继续写,而是两个参数在背后提前叫停:最大输出长度Max Tokens和停止序列Stop Sequences。排查时可以先看返回对象里的finish_reason,再核对参数配置,通常几分钟就能定位。下面先把两个参数的作用边界说清楚,再给不同接口的检查方法。

Max Tokens为什么最容易导致截断
Max Tokens在不同服务里的叫法不太一样。OpenAI旧接口使用max_tokens,Hugging Face Transformers常用max_new_tokens,vLLM也用max_tokens,而部分国产模型API使用max_output_tokens。名字虽不同,作用都是限制模型本次生成的token数量。只要生成的token数达到这个阈值,推理就会停止,此时返回的finish_reason通常是length。这个值不包括输入prompt,但要注意某些框架中max_length指的是输入加输出的总长度,容易混淆。
看到输出被截断时,第一件事不应是重试,而是打印返回的完成原因。如果finish_reason是length,说明是长度上限到了。例如下面这段OpenAI调用,max_tokens设置为50,模型刚开始总结就被迫停止。
import openai
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "请完整总结这篇技术文章"}],
max_tokens=50,
stop=None
)
print(response["choices"][0]["finish_reason"]) # 输出 length
print(response["choices"][0]["message"]["content"])
这段代码的finish_reason为length,提示token预算耗尽。需要注意的是,token并不等于单词或汉字数量。英文中一个单词可能被切分成多个token,中文也类似。一个汉字通常是一到两个token,具体取决于分词器。所以不要用字符串长度去推算max_tokens是否足够,应该参考官方分词工具或直接看接口返回的usage字段。
如果确认是长度限制导致,建议把max_tokens调到512、1024甚至更高。但调大之前要确认模型的上下文窗口。对于上下文只有4096的模型,如果输入已经占用了3000多token,输出空间自然很小。此时可以压缩提示词、减少历史对话轮次,或换用上下文更大的模型。不要只盯着max_tokens本身,输入长度和输出长度是共用上下文的。
Stop Sequences误触发同样常见
停止序列是另一类截断原因。它的设计目的是让模型遇到指定字符串就停止生成,例如在对话场景中把用户下一轮的开头当作停止词。但如果停止词设置得太短或太普通,正常内容里一旦出现该片段,模型就会提前收尾。比如把stop设置为换行符,模型在输出列表、代码或多行说明时,第一行结束后马上停止。把stop设置为单个句号,遇到小数点、缩写或网址中的点也会截断。
排查时要注意接口返回的finish_reason是否为stop。如果是stop,再去检查stop参数里每一个字符串。建议把停止序列写成长一些、不容易在正文中自然出现的模式,例如两个换行加特殊标记,或者只在明确需要分隔的地方使用。下面是一个更稳妥的停止序列配置:
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "列出计划"}],
max_tokens=500,
stop=["\n\n###", "用户:"]
)
print(response["choices"][0]["finish_reason"])
这里用两个换行加井号避免单个换行误触发,同时保留中文对话中下一轮开头的停止标记。多个停止序列中,只要模型生成过程中出现任意一个匹配片段,就会停止。匹配规则通常是精确子串匹配,大小写敏感,不同服务端可能会在停止后移除该字符串或保留它,具体表现需要看接口文档。
此外,有些模型的特殊token也会被当成停止序列。例如<|endoftext|>、<|im_end|>或</s>,如果模板没有正确过滤,这些token出现在输出中就会触发结束。排查时可以把stop参数和tokenizer的特殊token一并打出来核对。
用返回字段区分截断原因
多数推理接口会返回finish_reason字段,它可以明确区分截断类型。length代表达到最大输出token,stop代表命中停止序列,content_filter代表被内容安全过滤。流式输出场景中,最后一个chunk才会带上finish_reason,很多人只读取前面的content片段,没有等到结束事件,容易把正常流式响应误判为截断。下面这段流式调用会收集所有增量内容,并读取最终的完成原因。
stream = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "解释流式输出"}],
max_tokens=300,
stream=True
)
collected = []
finish_reason = None
for chunk in stream:
delta = chunk["choices"][0].get("delta", {})
if "content" in delta:
collected.append(delta["content"])
if chunk["choices"][0].get("finish_reason"):
finish_reason = chunk["choices"][0]["finish_reason"]
print("".join(collected))
print(finish_reason)
拿到finish_reason后,处理思路就很清晰:如果是length,优先调大max_tokens并检查上下文;如果是stop,逐个检查停止序列;如果是content_filter,说明内容触发安全策略,需要改写提示词或检查输出内容。
在Transformers中,同样可以通过生成的token数量判断是否达到max_new_tokens。例如生成了256个新token且结尾没有eos_token_id,很可能就是长度截断。可以打印outputs.shape或者比对最后一个token是否为结束token。
其他容易被忽略的截断因素
除了Max Tokens和Stop Sequences,还有几个参数和习惯会让输出看起来像中途截断。第一个是max_length和max_new_tokens的混用。部分框架的max_length表示输入加输出总长度,如果只看到输出很短,可能是因为输入已经占用了大部分预算。第二个是提示词中无意间包含了停止词或过短约束。比如系统消息里写只输出十个字,或者要求简洁回答,模型可能主动提前结束。第三个是重复惩罚设置过高,模型生成重复片段后概率分布变得不稳定,也可能出现意外终止。
对Transformers用户来说,建议同时显式设置eos_token_id和pad_token_id,避免模型把填充token当成结束标志。下面是一个相对完整的generate调用:
from transformers import AutoModelForCausalLM, AutoTokenizer
model = AutoModelForCausalLM.from_pretrained("your-model-path")
tokenizer = AutoTokenizer.from_pretrained("your-model-path")
inputs = tokenizer("请写一份会议纪要", return_tensors="pt")
outputs = model.generate(
**inputs,
max_new_tokens=512,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id,
do_sample=True,
temperature=0.7,
top_p=0.9
)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
这段代码中max_new_tokens只限制新增token,不会把输入长度算进去。设置eos_token_id和pad_token_id后,模型遇到真正的结束token才会停止。若仍然截断,可以打印outputs的形状,确认新token数量是否达到了512。
排查顺序和调参建议
遇到推理中途截断,建议按固定顺序检查:先看finish_reason,再看max_tokens或max_new_tokens的值,然后检查stop序列,接着确认输入输出总长度是否超限,最后看流式响应是否读取完整。这个顺序可以避免把时间浪费在重试和换模型上。
- 如果finish_reason为length,调大max_tokens或max_new_tokens,并压缩输入。
- 如果finish_reason为stop,逐个删除停止序列测试,找出误触发的字符串。
- 如果流式输出没有finish_reason,等待最后一个chunk或检查框架是否需要手动关闭。
- 如果使用Transformers,确认eos_token_id、pad_token_id是否配置正确。
- 如果输出仍然偏短,检查系统提示词中是否存在字数限制或简略要求。
调参时不要只增大一个值。合理的组合是:max_tokens设置在512到1024之间,停止序列使用较长的唯一模式,采样参数不要设置得太极端。对于需要完整长文输出的场景,可以去掉单个标点或换行这类停止词,改用两个换行或特定标记,这样能明显降低误截断概率。
Max TokensStop Sequences推理截断修改时间:2026-09-23 14:16:39