一份"帮我总结这段会议记录"的输出,模型给了八百字,而需要的是一句话结论加三条待办。反复要求"简短一点"之后,它变成六百字——因为"简短"是一个相对词,而模型对它没有绝对刻度。
这背后是一个更普遍的规律:凡是"程度"类要求都难以执行,凡是"形状"类要求都可以执行。"更专业些"、"更简洁些"属于前者,而"三段、每段一句、必含结论"属于后者。
长度控制的难点不在"要求它短",而在给它一个可判定的形状:几段、每段几行、要不要列表、必含哪几项。形状是可检查的,相对词不是。
1. 三种长度约束
| 约束 | 效果 |
|---|---|
| 相对词("简短") | 弱,几乎无约束力 |
| 结构约束("三段,每段一句") | 强,可检查 |
| 硬限制(max_tokens) | 最强,但会截断 |
第一项之所以弱,是因为它把判断权交给了模型:而模型的训练数据里"简短"的样本本身就长短不一。
第二项的原理是"给模板":填一个三行模板比"写短一点"容易得多,因为它把"多长"变成了"填几个格子"。
// 可判定的形状 vs 不可判定的要求
弱:请简短总结,不要长篇大论
强:按以下格式输出,共三行:
结论:一句话(≤40 字)
依据:两到三条,每条一句话
待办:谁在何时做什么(可为空)
// 强约束可以直接用正则/脚本校验,弱约束不能
2. 为什么模型倾向写长
| 原因 | 说明 |
|---|---|
| 训练偏好完整性 | 覆盖更多要点 = 更好的回答 |
| 缺少长度锚点 | 不知道"这段该多长" |
| 结构未定 | 先写再组织,自然变长 |
| 回避取舍 | 都写上就不用决定哪个重要 |
第二项(缺锚点)有个直接的解法:给一个示例输出。示例的价值不在内容,而在它同时提供了长度、语气与结构三个锚点——比任何形容词描述都准确。
第四项最关键:写长常常是为了回避"哪个更重要"这个判断。所以要求"只保留一条最重要的",比要求"写短"更能压缩长度。
第三项也有解释力:没有预设结构时,模型边写边组织,而组织过程本身会产生冗余。
// 让模型做取舍,而不是做删减
弱:请精简到 100 字以内
强:只保留最需要管理层决策的那一个问题,其余全部省略
// 前者是压缩,后者是先选后写
3. 结构模板的四种粒度
| 粒度 | 例子 |
|---|---|
| 段落级 | "分三段:现状、原因、建议" |
| 句子级 | "每段一句,不超过 40 字" |
| 字段级 | "输出 JSON,字段 fixed/why/next" |
| 逐项级 | "最多三条,每条一句话" |
第三项最容易被机器消费:结构化输出天然控制长度,因为字段数量是固定的,每个字段的长度可以单独约束。
第二项(句子级)有一个实用尺度:中文一句话超过 40 字时,读者开始需要回看。所以"每句不超过 40 字"不只是风格要求,它是可读性的门槛。
第四项在"待办清单"这类场景里最有效:条数上限比字数上限更好执行,因为模型能数条数,不能精确数字数。
4. 硬限制与截断
| 手段 | 风险 |
|---|---|
max_tokens |
截断在句中,输出不完整 |
| 提示里写"不要超过 N 字" | 经常超 |
| 生成后裁剪 | 可能裁掉关键结论 |
| 分级生成(先要点后展开) | 实现复杂 |
第三项(生成后裁剪)在实践中容易犯一个错:按字符数裁剪会把一个 JSON 或一段代码切成非法结构。所以裁剪只适用于自然语言,且应当按"句"而不是按"字"。
第一项最危险:截断的输出看起来"说完了",实际少了最后一句结论。而很多客户端不会提示"被截断"。
第四项是可靠做法:先让模型输出三条要点(短),再按需展开其中一条。这样长度控制发生在生成之前,而不是事后裁剪。
// 两段式生成:先短后长,长度可控
第一轮:只输出三条要点,每条 ≤ 20 字,不要展开
第二轮:针对"第 2 条"展开,限 150 字
// 好处:总量可控;坏处:多一次调用
5. 长度与质量的取舍
| 场景 | 长度倾向 |
|---|---|
| 决策支持 | 短(结论优先) |
| 学习材料 | 长(有推导) |
| 代码注释 | 短(说明为什么) |
| 排障步骤 | 中(每步一句) |
第三项(代码注释)常被写反:注释写成了"做了什么",而读者需要的是"为什么这么做"。这类注释越长越无用,因为它重复了代码本身。
第一项与第二项的差别不在"写多少",而在读者要什么:决策者要结论与依据,学习者要过程与推导。用同一种长度应付两类读者,两边都不满意。
长度控制还有一层"分层输出"的策略:给人看的摘要压短,给机器读的结构给字段。同一份内容两种形态,各自有各自的长度约束——用一份输出去满足两种读者,通常两边都不合适。
// 一份内容,两种输出
[摘要] 限三行,每行 ≤ 40 字,供人判断要不要细看
[结构化] JSON,字段固定,供下游程序消费
// 混在一起 → 人嫌长、机器解析不了
6. 排错:五种"控制不住长度"
| 现象 | 成因 | 处理方向 |
|---|---|---|
| 说"简短"后仍很长 | 相对词无刻度 | 给结构模板 |
| 每次都差不多长 | 无长度锚点 | 给示例输出 |
| 输出被截断 | max_tokens 太小 |
分级生成 |
| 结构对了但内容空 | 模板过松 | 收紧字段要求 |
| 上一轮短这轮长 | 温度/采样波动 | 固定参数 + 校验 |
除了采样,上下文长度也会影响输出长度:上下文里塞了很多材料时,模型倾向于"多覆盖一点",因为它们看起来都相关。所以"短输出"往往要先"少给材料"。
第五项要说明:长度本身受采样影响,同一提示的两次输出长度可能差 30%。所以验收不能看一次,要看多次的分布。
// 长度验收:看分布而不是单次
跑 10 次同一提示,统计输出长度:
若 P90 超过目标 1.5 倍 → 模板不够紧
若长度方差很大 → 采样参数或提示有歧义
// 单次"看起来合适"不算通过
7. 边界:什么情况不该压长度
| 场景 | 为什么 | 做法 |
|---|---|---|
| 教学与解释 | 需要推导 | 允许长 |
| 复杂分析(多因素) | 压缩会丢条件 | 用结构而不限字 |
| 代码生成 | 完整性优先 | 不限长度,限结构 |
| 用户明确要详细 | 需求就是长 | 不压 |
最后一条与"验收"有关的边界:长度约束应当写进自动化检查,而不是靠人看。一条正则(行数、字段数、字数上限)就能把"这次有点长"变成"这次不合格",而后者才会被修。
// 长度验收:把"看起来合适"变成可执行检查
def check(text: str) -> list[str]:
errs = []
lines = [l for l in text.splitlines() if l.strip()]
if len(lines) > 8: errs.append(f"行数 {len(lines)} > 8")
if any(len(l) > 60 for l in lines): errs.append("存在超长行")
if "结论" not in text: errs.append("缺少结论")
return errs
判断口径一句话:读者会拿这份输出去做什么。要"做决定",压到结论与依据;要"学东西",长度由推导决定,用结构控制而不是字数。
8. 自测
- 为什么"简短"这类相对词几乎无约束力?结构模板为什么更有效?
- 模型倾向写长的四个原因里,哪一个最关键?为什么"只保留一条最重要的"比"写短"有效?
- 四种结构粒度中,哪一种最容易被机器消费?为什么条数上限比字数上限更好执行?
- 为什么
max_tokens截断最危险?"分级生成"如何避免这个问题? - 为什么长度验收要看多次输出的分布而不是单次?
- 判断"该不该压长度"的那个问题是什么?