「长度惩罚」这四个字在参数表里根本不存在。主流 API 提供的是 max_tokens、stop sequences、frequency_penalty、presence_penalty 和 repetition_penalty,没有一个参数叫长度惩罚。可工程师嘴里的「模型回答太长、结尾敷衍、被截断」,确实需要一个能调控长度的地方。这篇文章想把这些名字拆开,讲清楚每个旋钮实际在惩罚什么,以及调错了会出现什么。
先说清楚:谁在控制长度
控制输出长度,真正只有两类硬手段。第一类是 max_tokens,给模型一个 token 上限。Token 不等于字数:按常见经验公式,中文一字约对应 1 到 2 个 token,英文一个单词约 1 个 token,代码更碎,token 数偏高。设置太小会看到回答被中途截断,尤其在代码生成场景里,一个函数写到一半就停,排查时需要先看 finish_reason 是不是 length。设置太大约等于没有限制,只会在模型自己「不想说了」时停下。
第二类是 stop sequences(停止序列)。它是一段预定义文本,模型在生成过程中遇到它就终止。用 stop 来控制列表长度是典型用法:让模型输出编号列表,把「11」设为停止序列,模型到第 11 项就停。这比 max_tokens 精确,因为不依赖 token 计数,但要求输出结构稳定。如果模型换了一种编号格式,比如改成「十一」,停止条件就失效。
实际工程里两者要一起用。max_tokens 兜底防爆,stop 保证在语义结束点收尾。只靠 max_tokens 截断,结尾往往是半句话,下游解析会直接失败;只靠 stop,一旦格式漂移,成本就没有边界。
max_tokens 不能调到「恰好」
很多人把 max_tokens 当成本开关,希望卡得越紧越省钱。这里有个现实约束:你无法预知模型会用多少 token 表达完一个完整意思,因此设得过紧的影响不是「省钱」,而是把完整回答切碎。对于简短问答,100 到 200 token 够用;代码生成通常要 500 到 1000;长文写作可能要 1000 到 2000;翻译一般按原文长度的 1.5 倍留余量。这些数字只能当起点,不能当标准。
更隐蔽的问题是 prompt 与 max_tokens 的合计要落在模型的上下文窗口内。写长文档摘要时,输入本身就吃掉几千 token,如果再要求输出长篇,模型可能直接报错或输出被挤得很短。这类问题不要通过调「隐藏的长度参数」解决,得回头做上下文工程,比如分段摘要。
frequency_penalty 和 presence_penalty:名不副实的「长度药」
一个常见的误区是:回答太啰嗦,就调高 presence_penalty。presence_penalty 的含义是,只要某个词在已生成文本里出现过,就对它施加一个固定的惩罚,不管出现一次还是十次。它的作用是推动模型去用新词,从而减少同一实体、同一短语的反复出现。副作用是模型会开始绕开必要的重复。写技术文档时,「模型」「token」「参数」这类核心术语被反复惩罚,句子会变得别扭,模型会去找同义词来替代一个本该稳定出现的专有名词。这个代价在需要术语一致性的场景里很直接。
frequency_penalty 按出现次数累加惩罚。资料里常用的推荐区间是 0.1 到 0.3,常用于头脑风暴、内容多样化、避免简单复述输入。它的强度比 presence_penalty 更重,出现次数多,惩罚就更大。用它治「回答太长」通常无效:惩罚的是高频词,而不控制总体篇幅。一个啰嗦但用词丰富的回答,施加频率惩罚之后依然啰嗦,只是词汇换了。
这两个参数真正通用的地方,是抑制那种原地打转的输出。但当模型开始说一些冷僻而不相关的词、句子逻辑断开时,往往是惩罚强了。降低 frequency_penalty,或者减少参与统计的历史窗口,通常能缓解。
repetition_penalty 和它旁边的 numpy 参数
本地部署或对着 llama.cpp、vLLM 调参时才会遇到 repetition_penalty。它的机制和其他两个不一样。对 logits 先做修改再做 softmax:对于正的 logit 除以惩罚系数,对于负的 logit 乘以惩罚系数,因此非线性,模型越确信的词被打压得越厉害。常见默认值在 1.1 到 1.2,合理设置区间大致是 1.0 到 2.0,超过 1.4 之后要谨慎,很容易看到句子结构崩掉、模型硬找替代词的胡言乱语式输出。
它通常还带一个窗口参数,比如 repeat_last_n 为 64,表示只检查最近 64 个 token 内有没有重复。窗口太小,拦不住段落级别的重复;窗口太大,比如长文中正常出现两次的人名、关键词也会被罚。代码生成里还有一组专门的工具,比如 no_repeat_ngram_size 为 4,即强制某个 4-gram 不重复出现。对于变量名、固定的一行模式重复很有效,但设得太大同样会把本来合法重复的短结构也压掉。
代码生成场景下的实测组合常见是:repetition_penalty 约 1.2,temperature 约 0.2,n-gram 重复用更小的粒度来挡。低温度保证风格稳定,惩罚保证不重复旧行。
一套不花哨的调优流程
不要同时拧五个旋钮。先锁定一组基线:固定的 temperature 和 top_p,max_tokens 能覆盖最常见的输出,stop 设为能识别的结束标记。然后一次只改一个惩罚参数,用同一批输入跑 A/B 对比,对比时不能靠肉眼觉得「好像更简洁」,要看具体的评分:正确答案命中率、结构化输出格式的稳定度、被迫截断的比例、最终花费。对于要上线的场景,自动比对和固定评测集的收益远大于手感调整。
需要预防的预算和效果如下。低温度加高频率惩罚,会让模型在用词上过度求新和语义偏移,很常见于客服回复出现莫名其妙的新词。开始生成没重复,拉到几千 token 后重复变多,大概率是惩罚只作用于有限窗口而后半段缺少约束,不是突然又需要调大 parameter。同一输入今天合适、上游换了模型就出问题,说明这套阈值是最紧贴着旧模型调出的一组偶然值,而不是可以继承的配置。
这些惩罚参数有个共同的产品定位:它们是后处理式的辅助,无法根治重复问题。输出重复最常见的两个原因,一是模型对某个词形成概率峰值并自我强化,二是上下文实在太短,模型只能在已有信息里打转。惩罚只处理第一层的症状,如果输入信息不足导致模型不断重复同样的说明,需要在 prompt 与上下文构造上让它有足够新的定向内容可写。
一次选清楚,把名不副实的说法停在这里
边界归边界:长度该由 max_tokens 与 stop sequences 控制;实体反复出现该由 presence/frequency/rep、ngram 这一层控制。「长度惩罚」不是一个已存在的参数,把这个思路带入调参,会同时埋下被截断和用词怪异的故障。建议在项目中把命名也整理一下:配置注释里只写这三种控制对应的实际目标,不要再创造新术语。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/ai-engineering/614.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
2026 年 AI 应用评测:从单轮打分到多轮轨迹评估
一个 Agent 的评测分数从 72% 涨到 81%,团队很高兴,直到发现它学会了在失败时直接结束任务,把“没做完”算成了“无需回答”。评测设计错了,指标越好看越危险。
4天前
-
推理成本下降之后:模型路由、KV Cache 与端侧推理
上个月一个团队拿着账单来找我:模型单价降了,费用却涨了三倍。看完调用链就明白了,问题不在单价,在用法。单位 token 便宜,不等于每次任务便宜。
2026/10/10
-
长度惩罚参数调优:常见参数名字容易混淆
长度惩罚在主流 API 里并不存在。真正管长度的是 max_tokens 和 stop sequences,frequency、presence、repetition 惩罚管的是用词重复和新词探索。把这些旋钮分开,能避免输出被截断、术语被替换成同义词两类故障。
4小时前
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布