跳到主要内容

过度规格化系统提示词的质量税

· 阅读需 10 分钟
Tian Pan
Software Engineer

大多数工程团队在第一次收到账单暴涨时都会发现同一件事:他们的系统提示词已经悄悄增长到4,000个token的精心指令,而模型也悄悄开始忽略其中一半。解决方法很少是添加更多指令,几乎总是删除它们。

追求面面俱到的本能是可以理解的。更多约束感觉像是更多控制。但随着系统提示词膨胀,存在一种可量化的质量下降——而且它与成本的复合方式在造成损害之前并不明显。研究一致发现,在大约3,000个输入token处准确率开始下降,远在达到任何名义上的上下文限制之前。模型不会拒绝遵守;它只是开始以难以查明的方式表现不佳。

本文的目的是使这种退化变得可见,理解其发生原因,并建立一套不需要寄希望于"不会出问题"的精简规范。

为什么注意力是预算,而非保证

"更多指令=更多控制"的直觉之所以失效,是因为transformer注意力机制的实际工作方式。输入中的每个token都会关注其他每个token——模型在为每个位置计算整个上下文的加权相关性。一个添加了2,000个token的系统提示词不只是增加了2,000个token的上下文;它稀释了分配给其他所有内容的相对注意力权重。

"迷失在中间"效应已有充分记录:模型对上下文开头和结尾的信息显示出强烈的回忆能力,但对中间部分的回忆则明显较弱。在长系统提示词中,这意味着精心编写的位于中间位置的指令,相比顶部的样板文字和用户消息前的最后一句话,会被降低优先级。你写了十二段;模型只加权了其中两段。

还有注意力分散问题。即使LLM能识别出某些上下文是不相关的,也很容易被其误导。包含相邻边缘情况描述的系统提示词——出于防御性目的添加的,"以防万一"——会主动降低常见情况下的性能。模型不只是忽略这些部分;它处理这些部分的方式会在最终输出分布中引入噪声。

实际阈值很重要:研究表明推理性能退化从大约3,000个输入token开始。一个使用1,500词系统提示词的团队很可能已经超过这个阈值。而大多数生产系统提示词并不是1,500词——它们经过数月的补丁叠加迭代,已经积累成3,000+个token,没有明确的负责人。

你没有衡量的非线性成本

系统提示词的计算成本与token数量不是线性关系。标准transformer注意力随序列长度呈O(n²)缩放。将提示词加倍大约会使注意力计算四倍。在推理规模上,从2,000个token增长到4,000个token的系统提示词在预填充阶段的成本不是两倍——而是大约四倍。

预填充阶段是模型在生成响应之前处理所有输入token的阶段。你的系统提示词在每次请求时都处于预填充阶段。70B模型在128K token上下文中的40GB HBM占用不是假设——这是你的基础设施提供商在向你收费的,分摊到各请求中。Token计数工具使原始计数可见;这些token背后的二次方计算通常是不可见的。

对于多轮应用,成本进一步复合。系统提示词、对话历史和检索上下文都共享同一个上下文窗口。每个都独立增长。结果是一个在演示中看起来没问题、但在真实对话历史达到十轮时在生产中崩溃的上下文预算。

约60%使用LLM API的团队报告超出预期成本——而低效的token使用始终被认定为主要原因。提示词缓存解决了其中一些问题(重复预填充可降低90%以上的成本),但缓存臃肿的提示词只是让你更便宜地运行一个更差的模型。

诊断提示词肥胖

提示词肥胖本身不是关于长度的——一个用于复杂文档分析任务的3,000 token提示词可能是合理的。问题是长度与价值不成比例。一个有用的诊断从三个问题开始:

这里有哪些从未经过测试的指令? 大多数系统提示词包含为应对某一事件而添加的指令——某人曾经看到的一种失败模式,并决定预防性地避免。这些一次性约束无法泛化。它们为其他所有请求增加噪声,而解决问题的方式可能更简单。

哪些指令在语义上重叠? 提示词肥胖通常表现为同义反复:同一约束以四种不同方式表达了四次,因为每次添加感觉都在强调不同的东西。模型处理全部四个;结果是对那种特定行为的过度加权,以牺牲其他所有内容为代价。

完成每项任务的token比率是多少? 如果你能用80%更少的token以相同质量解决同一任务,你就有20%不必要的开销——但测试是实证的,而不是直觉的。系统化衡量此指标的团队报告可以实现40-46%的token减少而不损失任务保真度。

混淆矩阵驱动的分析在这里很有用:查看你的失败案例并对它们进行分类。如果大多数失败有共同模式,你需要有针对性的添加。如果失败是分散的,你可能有注意力稀释问题——太多等权重的约束阻止模型正确地确定优先级。

编辑规范

系统提示词演进的默认模式是累加式的:某事失败,添加一条新指令。精简需要刻意地以相反的方式运行这个过程,这感觉很冒险,因为你在删除因某些原因而添加的东西。

起始启发式是孤立并独立测试每条指令。如果你有一条约束说"始终用正式英语回应,避免缩写,不使用随意的语言,保持专业语气,写作如同在向董事会级别的受众演讲"——那是五条可以是一条的指令。在其他任何事情之前,在语义层面合并冗余。

第二个启发式是从新提示词的最小值开始。从产生可接受结果的基线开始,然后仅为解决具体识别出的失败而添加约束。这与防御性预规格化相反,产生的提示词更短、更准确。每个添加的约束都应通过反事实测试:删除它是否会导致可测量的回退?

对于现有的臃肿提示词,最安全的方法是渐进式删除而不是整体重写:

  • 每次删除一个子句
  • 在代表性评估集上运行提示词(即使20个例子在大多数情况下也能发现回退)
  • 如果性能保持或改善则保留删除;如果不行则恢复

这很慢,但能建立关于哪些指令是承重的实证知识。大多数团队发现,不到一半的指令实际上在起作用。

语义摘要是另一个杠杆:跨多个句子的指令通常可以压缩为一个而不损失。"你是一个总是将清晰度置于全面性之上的有用助手,从不提供未经确认的信息,避免推测性陈述,并在存在不确定性时承认"变成"优先考虑清晰度;适当地进行规避。"测试一下。通常有效。

长度何时合理

并非所有长系统提示词都有问题。区分因素是长度是功能性的还是防御性的。

功能性长度包括模型实际需要的数据:参考文档、模式定义、必需的格式规范、模型无法推断的枚举值。这些内容赚取其token,因为省略它们是错误的。

防御性长度是所有为防止假设失败而添加的内容:关于未发生的边缘情况的指令、模型默认遵循的策略的阐述、多种形式的约束重复。这是需要削减的类别。

实际测试:如果你删除了一条指令,你是否需要在生产中捕获失败才能知道它丢失了?如果是,该指令可能是承重的。如果你不确定是否会被注意到——删除它并找出答案。

具有检索功能的结构良好的提示词始终优于试图预先加载模型可能需要的所有内容的单块提示词。"包含所有内容"的本能优化了覆盖率,但以牺牲精确性为代价。检索方法推迟到运行时上下文——而模型处理运行时上下文比处理静态过度规格化要好。

测量思维

没有测量的提示词精简是猜测。使其工作的团队将提示词变更视为代码变更:可测试、有版本、有明确指标。

最小可行的测量设置是20-50个代表性输入及预期输出的评估集。运行当前提示词,记录通过率。做出改变。重新运行。增量告诉你改变是否有帮助或有害。这不需要复杂的工具——电子表格和手动审查周期足以停止猜测。

要跟踪的指标是每个通过输出的token,而不仅仅是token数量。一个失败更多的更短提示词不是改进。一个在相同或更好通过率下成本更低的更短提示词才是目标。

专业版本增加了自动化回退检测,并随时间跟踪提示词质量——一种在每次变更时运行的"提示词CI"。大多数团队还没到那里,但即使是这个的非正式版本也比替代方案好,替代方案是发现三个月前的提示词变更导致了生产中不可见的质量回退。

默认应该是对添加的怀疑

围绕系统提示词的工程文化倾向于积累。每个修复都是一次添加;每个新用例都是一个新段落。随着时间推移,结果是一个成本超过应有水平、性能低于表面水平的提示词。

提示词精简的规范既是技术性的,也是组织性的。从技术上讲,它需要评估基础设施来知道何时删除是安全的。从组织上讲,它需要以与代码复杂性同等的严肃性对待提示词膨胀——这种东西默认会积累,如果不积极管理就会降低系统质量。

当某事出错时添加更多指令的本能是可以理解的。但在下一条指令被追加之前,更好的问题是:现有的哪条指令未能覆盖这个情况,为什么?更多时候,答案不是你需要更多——而是你所拥有的在一次做太多事情。

Let's stay in touch and Follow me for more thoughts and updates