如何控制大模型的输出长度并处理截断?
简化版
输出长度要同时靠任务设计、输出 Schema、max_output_tokens 和停止条件控制。先按分类、抽取、问答、报告等任务设置不同预算,提示模型优先给结论并限制章节或数组数量;收到响应后必须检查停止原因。若因长度截断,结构化结果应判失败并重新生成或缩小范围,长文则用带状态的分段续写,不能把半个 JSON 或半段代码当成功。
详细版
输出预算与输入共享上下文,先计算 可用输出 ≤ 窗口上限 - 实际输入 - 安全余量。任务层再设置较小业务上限,例如分类 32~64 token、字段抽取 256~512、普通解释 800~1500;具体数值要按 tokenizer 和评测校准。
requested_max = min(task_limit,
context_limit - input_tokens - safety_margin,
remaining_cost_budget)
提示中的“简洁一些”不具备硬保证,应给明确结构、字段数和优先级。流式接口累计 token 与字节,允许用户取消;完成时区分正常停止、长度上限、内容过滤、工具调用和错误。长文采用章节计划、逐段生成、每段验收与最终一致性检查,避免无状态地说“继续”造成重复或漂移。
完整版教学
一、长度控制有三种不同目标
上下文硬上限防止请求不可执行,业务长度限制保证用户体验,成本预算限制最坏费用。三者不能只用一个 max_tokens 代替。
客服回答可能要求 200 字内,即使模型还能生成 4000 token 也应停止;代码生成则不能为了短而删掉必要函数。先定义完整性的最低要求,再决定最大长度。
记忆钩子:窗口上限是天花板,业务上限是房间高度,任务完整性是人至少要站得下。
二、输入和输出共享容量
一个 8192 token 窗口,输入 7000,安全余量 192,最多只能预留 8192-7000-192=1000 token。若接口请求输出 2000,可能报错、截输入或在服务端缩小上限。
因此在实际序列化聊天模板、工具定义和检索片段之后重新计数。不能按字符粗估,也不能忘记特殊 token。
输入过长时先减少低价值上下文,而不是无声压缩输出到无法完成。
输入太长被拒绝和输出被截断是两种情况,处理方式见「什么是大模型的上下文窗口?长度越长越好吗?」。
三、按任务建立输出档位
统一上限会让简单任务浪费资源、复杂任务经常截断。可根据历史长度分布和业务要求建立档位,再允许少量动态调整。
| 任务 | 示例上限 | 完整性检查 |
|---|---|---|
| 分类 | 64 token | 标签在枚举内 |
| JSON 抽取 | 512 token | Schema 完整 |
| 普通问答 | 1200 token | 必答点覆盖 |
| 分章节报告 | 每节 1500 token | 章节状态完成 |
示例不是通用标准。中文、代码与不同 tokenizer 的长度分布不同,应使用本业务 P95/P99 数据设置。
四、结构约束比模糊形容词有效
“简洁回答”会因模型和问题变化而波动。更可执行的要求是“先给 3 条结论,每条不超过两句,再给一个 4 行表格”,或用 JSON Schema 限制数组 maxItems 与字段长度。
但提示仍是软约束。字符或字数必须由应用检查,硬上限由生成参数执行。模型偶尔超出时可做一次受控改写,不能无限压缩导致事实丢失。
重要信息应排在前面,避免达到上限时只留下背景、没有结论。
五、停止原因决定响应能否使用
正常结束、命中停止序列、长度耗尽、内容过滤、工具调用和网络错误语义不同。应用不能只看有没有文本。
JSON 在 length 状态下即使能补一个右括号,也可能缺少后续字段;代码可能少清理逻辑;自然语言可能缺结论。因此结构化与高风险任务应判未完成。
日志记录请求上限、实际输出 token、停止原因和解析结果,才能判断截断来自预算太小还是模型冗长。
六、长文使用有状态分段生成
一次生成 10,000 token 报告容易截断、结构漂移和重复。可先生成章节计划,为每节分配目标长度,再逐节提供共享事实与已完成摘要,最后检查跨节一致性。
outline -> section_1(validated)
-> section_2(validated)
-> ... -> consistency pass
每段保存 section_id、covered_points、open_points。续写指令引用状态,而不是只说“继续”,否则模型可能从任意位置重复。
七、流式输出也要硬控制
流式传输改善首字延迟,不会自动减少 token。服务端应累计生成量、支持取消并处理客户端断开;用户关闭页面后若仍后台生成,费用继续产生。
若业务按字符限制,注意 UTF-8 字节、Unicode 字符和 token 不相等。不要在任意字节位置截断,也不要在 JSON 字符串中间切割。
达到业务展示上限时,可停止上游生成或继续生成到安全边界后存为完整 artifact,选择取决于产品需求。
八、用长度—质量—成本曲线校准
在固定评测集上测试 256、512、1024、2048 等上限,比较必答点覆盖、截断率、冗余率、延迟和费用。选择质量达到平台后边际收益低的位置。
例如 512 token 覆盖率 82%,1024 为 94%,2048 为 95%;从 1024 翻倍只提升 1 个百分点,却增加解码时间,1024 可能更合理。
线上按任务类型监控 length 停止率、实际/上限比、用户主动取消率和续写次数。模型升级后长度习惯会改变,需要重新校准。
九、常见误区与追问
- 误区:提示里写“控制在 500 字”就是硬限制。 仍需参数限制和应用计数验证。
- 误区:达到长度上限后补一个括号即可。 内容可能不完整,结构合法不代表任务完成。
- 误区:流式输出天然更省成本。 未取消的流仍会生成同样 token。
- 误区:所有任务用同一个上限最易维护。 长度分布和完整性要求不同,应分档。
- 误区:让模型无限续写可以完成任何长文。 无状态续写会重复、矛盾并继续膨胀上下文。
- 追问:如何判断被截断? 检查 API 停止原因,同时验证结构与任务必答点。
- 追问:输入太长与输出预算冲突怎么办? 先压缩低价值输入、分解任务或换窗口,不能牺牲必要输出。
- 追问:长报告怎样续写? 先定章节,保存已覆盖与待覆盖状态,逐段验收后做一致性检查。
十、加强记忆
输出长度控制记住“先算容量、按任务分档、用结构约束、看停止原因、长文分段”:输入与输出共享窗口,实际计数后为输出预留;分类、抽取和报告用不同上限;提示负责组织,参数负责硬停,验证器负责判断是否完整。出现 length 不把残片当成功,分段续写携带章节状态,最终用长度—质量—成本曲线选择配置。
项目实战落地
项目里怎么做的
《AI智能简历助手》的五个 AI 功能调模型时,都取带元数据的完整响应 chatResponse(),而不是只拿正文的 content(),再用同一个方法检查结束原因:
private void checkNotTruncated(Generation generation) {
// 有的服务商在某些帧里不带元数据,取不到结束原因就不判断
if (generation == null || generation.getMetadata() == null) {
return;
}
// length 表示到达长度上限被迫停止,stop 才是正常说完
if ("length".equalsIgnoreCase(generation.getMetadata().getFinishReason())) {
throw new CustomException("5008", "AI输出到达模型的长度上限被截断了,这次结果不完整。"
+ "通常是简历正文太长导致的,请回到【简历工作台】精简正文(删掉和目标岗位无关的内容)后重试");
}
}
流式的四个功能多做两件事:结束原因只出现在最后一帧,所以用 AtomicReference 一路记着最后一帧的 Generation,整段输出结束后再判断;这个判断放在捕获调用异常的 try 外面。
另外两个项目在「上限定多少」上各有做法:
- 《AI个人学习计划助手》的模型配置表有
max_tokens字段,默认 8000,按全项目单次输出最长的一次调用来定:30 天学习计划要返回 30 条任务,每条 5 个字段;输出越长耗时越长,超时也从 60 秒放宽到 120 秒。检测到截断时提示去把max_tokens调大,并带上当前配置值。 - 《AI Agent旅游行程智能规划平台》按用途分别配置模型:行程编排模型最大输出 32000,偏好抽取模型 2048。截断检查放在「返回内容是否为空」的判断之前。
《AI Agent岗位匹配与求职规划系统》的统一出口 AiChatService 同样取 chatResponse() 检查结束原因,是 length 就报「AI输出达到模型长度上限,结果不完整」;ReAct Agent 取最终结论时也做同样的检查。上限来自模型配置表的 max_tokens,生成求职报告时配置值低于 3000 就临时按 3000 调用;仍被截断时换一份更短的提示词再生成一次,做法见「Agent 如何管理上下文窗口?」。
《AI 多 Agent 长租公寓智能合租匹配平台》的执行器每一轮拿到回复先调 check_truncated:response_metadata 里的 finish_reason 是 length 就报「模型输出超过了最大长度被截断。请到【AI模型配置】调大最大输出 Token,或者缩短输入内容后重试」。最大输出取自每条模型配置的 max_tokens,模型工厂的注释写明它要真下发给模型;预置的四条配置里审核是 1024,其余 2048。
为什么这样取舍
- 截断不算报错。 输出到达上限时服务商返回的 HTTP 状态码还是 200,内容也是正常文本,只是缺了后半截;只拿正文,就分不清「说完了」和「被截断了」。不拦的话,评分会因为 JSON 只有半截而解析失败,改写、面试题这类功能会把半截结果当成正常结果。
- 判断放在
try外面。 截断不是调用失败,而是调用成功但结果不完整;写在try里,会被兜底的catch包成「AI模型流式调用失败」,真正的原因就被盖掉了。 - 截断检查先于判空。 输出被上限吃光时正文也是空的,这时报「返回内容为空,请检查模型名称和 API Key」,会把人引到完全不相干的地方去查。
面试官还会追问
- 流式输出被截断时,前面的内容已经显示在页面上了,这次结果还会存进记录表吗?
- 同样是「太长」,简历正文太长和输出被截断,服务商的反应有什么不同?分别在哪里拦?
学完《AI智能简历助手》,上面这些追问你都会迎刃而解。