回答说到一半停住,最后一句还悬着;或者明明让它写五条,只给了两条。这不是模型「不想写了」,几乎总是某个边界先到了。截断分两类:参数层面的硬限制,和模型自己判断「说完了」。两种情况看着一样,处理路径完全不同——所以第一步不是改参数,而是先分清属于哪一种。
先判断:是被截断,还是本来就答完了
最直接的证据是 finish_reason(有的客户端叫 stop_reason)。值是 length 或 max_tokens,说明撞到了输出上限;值是 stop,说明模型认为已经写完——此时内容不完整属于提示词问题,不是长度问题。客户端不显示这个字段时就看断点:断在句子中间是硬截断,断在完整条目之后,多半是模型自己收的尾。
第二个证据是数字。把输入与输出的 token 数记下来,和上下文窗口对一遍。很多「截断」其实是输入太长先吃掉了窗口,留给输出的只剩几百个 token,断点看着毫无规律。
先看 finish_reason,再谈调参——否则你调的每一个旋钮都可能与真正的原因无关。
三个最常见的原因
第一,max_tokens 设得太小。不少客户端默认只有 1024 甚至 512,写长文、出代码时很容易不够。第二,上下文窗口被输入占满:整份文档或整段历史一起塞进去,输出自然没空间。第三,单次输出本身有上限,某些模型即使窗口很大,一次也只能生成几千 token,调 max_tokens 也突破不了。
还有一种容易误判的情况:流式输出中途断了。连接断了、网关超时了、客户端卡住了,表现都是「话没说完」。这类问题在 finish_reason 里看不到 length,日志里却能看到连接被提前关闭。改成非流式再打一次,如果完整,就是链路问题。
排查顺序:从最便宜的动作开始
建议的顺序:先复现,原样再发一次确认非偶发;再看 finish_reason;然后算 token 余量;最后才动参数。把 max_tokens 调大最省事,但它只对第一种原因有效。窗口是总量,输入多了,max_tokens 设得再大也塞不进去——这也是有人反复调参却没好转的原因。
确认输入过长之后,方向是压缩而不是加额度:把历史对话做摘要、把文档切块再逐段处理、把示例精简到必要的一两个。接口统一走 https://api.aigcbreeze.cn,选模型时把上下文长度当硬指标看,别只盯着单价——省下的差价往往抵不上一次返工。
长回答的正确做法是分段,不是硬顶
需要几千字的输出时,指望一次请求顶满本身就脆弱。更稳的做法是按大纲分段写:先要目录、确认结构,再逐段生成,每段单独保存。这样单次输出可控,断了也只损失一段。
续写要注意衔接:把已有内容的最后两三段一起带上,并明确「不要重复已有内容,直接从断点继续」。只说一句「继续」,它常会从头再讲一遍,或者接得前言不搭后语——上下文不完整时,模型只能猜你写到哪了。
常见问题
把 max_tokens 调到很大有风险吗?
它约束的是上限,不是实际用量,设大并不会因此多扣费;但一次生成过长的回答耗时明显增加,也更容易碰上超时或连接中断。合理做法是按任务给略高于预期的值,比如预计 2000 token 就设 3000,而不是一律拉满。
同一个提示词,为什么有时完整有时被截断?
常见于输出长度不稳定的任务,比如让它「尽量详细」。模型每次展开的程度不同,写得长的那次就容易撞上限。把要求量化——「分五点,每点不超过 80 字」——比「详细一点」稳定得多,也便于估算余量。
结构化输出(JSON)被截断怎么办?
JSON 被截断几乎总会在解析时报错,因为括号没闭合。除了控制长度,还可以要求它先输出精简字段、长文本留空,再单独请求填充;或者改用支持结构化输出的接口,让格式由接口保证。