跳到主要内容

少样本腐化:为什么昨天的示例会拖累今天的模型

· 阅读需 11 分钟
Tian Pan
Software Engineer

我合作过的一个团队曾有一个 JSON 提取提示词,其中包含 11 个手工调优的 few-shot 示例。在之前的模型上,这些示例将精确匹配准确率提升了 6 个百分点。模型升级后,同样的 11 个示例反而让准确率下降了 2 个百分点。没有人更改过提示词。没有人更改过评估集。这些示例就是失效了——而且更糟的是,它们开始产生误导。

这种退化并不是新模型的 bug。它是提示词本身的一种“腐化”模式。每当团队在迁移模型版本时将提示词视为固定资产,这种现象就会出现。Few-shot 示例并不是提示词独立的一部分,它们是“模型-提示词对(model-prompt pair)”的一部分。在不重新评估另一方的情况下迁移其中一方,会产生任何绑定在单一模型版本上的评估套件都无法捕捉到的退化。

新模型并不存在的失败模式

Few-shot 示例通常是针对特定模型的特定弱点选择的。资深工程师注意到模型在 JSON 输出中总是带有尾随逗号。于是他们添加了一个展示正确格式的示例。他们注意到当字段名不明确时,模型会混淆 DD/MM 和 MM/DD 格式的日期。于是他们又添加了一个示例。到提示词发布时,这些示例就是团队在评估过程中捕捉到的失败模式的压缩历史。

当模型升级时,这些失败模式会发生转变。新一代模型具有不同的格式先验、不同的指令遵循行为以及不同的盲点。上个季度修复了真实 bug 的尾随逗号示例,现在正在纠正一个新模型根本不存在的失败模式——而且模型接触该示例并非没有代价。它消耗上下文 token,引入模型不需要的风格偏见,最糟糕的是,它可能会导致过度纠正:一个已经能正确处理 JSON 的模型可能会被引导做出古怪的格式选择,因为这些演示暗示任务的重点是“格式”而非实际语义。

这种情况有一个被记录的版本叫做 few-shot 崩溃(few-shot collapse):随着你增加示例,性能会攀升到顶峰然后下降。在某些模型上,下降趋势非常剧烈——据测算,当示例数量超过最佳平衡点时,Gemma 7B 的准确率从 77.9% 骤降至 39.9%。其背后的机制已广为人知:较长的上下文会降低对实际任务的注意力,夹在长提示词中间的示例会被忽略,而与模型预训练先验相矛盾的演示会创建模型过度索引的模式。这些机制都不会在意你的示例在上个季度表现有多好。

OpenAI 一直在直接告诉人们这一点。针对较新的推理模型的官方指南指出,“清晰的指令和定义明确的约束通常比添加示例更有效”,并且当任务需要大量推理时,few-shot 提示词反而会降低性能。Decoder 社区将 OpenAI 的迁移建议总结为:将新模型视为“需要调优的新模型系列,而不是掉头就换(drop-in)的替代品”,并且“在开始迁移时使用全新的基准,而不是沿用旧提示词栈中的每一条指令”。Few-shot 示例是旧提示词栈中最沉重的部分,也是团队最不愿意删除的部分。

针对单个示例的效用审计

严酷的事实是,在模型升级后,“提示词整体是否退化?”并不是一个足够有用的问题。尽管整体评估结果可能持平或略微向好,但 few-shot 块中的单个示例可能正在默默地造成损害。你需要的是一种针对单个示例的贡献度度量,并将其作为每次模型迁移的一部分来运行。

这种机制描述起来很简单,但操作起来却枯燥乏味。对于提示词中的每个示例 EiE_i

  • 在保留 EiE_i 的情况下运行评估集。记录得分。
  • 在移除(消融) EiE_i 的情况下运行评估集。记录得分。
  • 两者的差值(delta)就是 EiE_i 在该模型上的效用。

贡献度为正的示例保留。贡献度为零或负的示例被删除。在新模型上新产生帮助的示例——也许是之前处于边缘但在新模型上终于发挥作用的示例——则从候选池中晋升。

在实践中有三个细节至关重要。首先,评估集必须足够大,以确保单个示例的信号不是噪声。一个只有 50 个案例的评估集几乎无法为每个示例提供统计效力;你至少需要几百个案例,这样 1-2 个点的偏差才能真正与采样抖动区分开来。其次,你必须使用确定性设置(temperature 为 0,以及 API 暴露的固定种子)运行,或者运行多次试验并取平均值——否则同一个示例在不同次的审计中测量结果会不同。第三,示例之间可能会发生相互作用:移除一个示例可能会改变另一个示例的表现,因为模型是将它们作为序列读取的。第一轮逐一消融示例的审计会捕捉到主要影响,但在 few-shot 块确实在发挥重要作用的提示词上,进行第二轮成对消融示例是值得的。

审计的输出不是一个简单的“赞”。它是一个表格:包含每个示例、每个模型版本以及评估差值。那个表格会告诉你哪些示例减弱了,哪些示例反转了正负号,以及哪些示例变得更强了。没有它,你就是在瞎猜。

溯源:将每个示例与其对应的评估案例绑定

生产环境提示词中的大多数少样本示例(few-shot examples)都没有记录其存在的理由。它们是在某次特定的调试过程中被添加的,而唯一记得原因的人已经离职了。当这种历史信息缺失时,审计工作就会变得充满敌意:负责迁移的工程师不得不去质疑一个他们并不理解其价值的示例,而最稳妥的做法往往是将其保留。

解决这一问题的纪律就是“溯源”(provenance)。提示词中的每个少样本示例都应该在存储中与其要解决的动机评估案例(eval case)绑定。审阅者在查看提示词时,应该能看到“这个示例的存在是因为测试案例 #47”,而不是“Phil 在去年春天添加了它”。当审计报告显示该示例在新模型上的贡献变为负值时,审阅者可以调出测试案例 #47,在没有该示例的情况下在新模型上运行,并直接确认:该示例原本要修正的错误模式已经消失了。

这并非理论上的账目记录,而是让清理(pruning)变得安全的关键。如果没有溯源,删除一个示例感觉就像拆除“切斯特顿围栏”(Chesterton's fence)——没人知道它为什么被放在那里,所以谨慎的直觉是留着它。有了溯源,删除操作就有了一个具体的、可衡量的依据:“无论是否存在此示例,测试案例 #47 在新模型上都能通过,因此该示例已不再发挥作用。”这一说法是可以被验证的。这样,你就可以毫无顾虑地移除示例。

其实现方式并不深奥。可以建立一个少样本库,每个条目都包含以下字段:它要解决的评估案例 ID、首次验证它的模型版本、添加日期以及最近的审计结果。有些团队将这些信息放在提示词旁边的 YAML 文件中;有些则将其放在存储评估集的向量数据库中,并建立显式的交叉引用。格式并不重要,重要的是这份纪律。

单调增长陷阱

如果没有审计和溯源,一种预见的失败模式就是单调增长。示例不断堆积。每次模型迁移都会添加新的示例,以修复新模型暴露出的新边缘情况。而旧的示例一个都没有被移除,因为没人能证明它们是有害的。

经过两三次模型升级后,少样本区块就成了修正案的沉积层——有些仍然起支撑作用,许多已经过时,还有一些甚至起反作用。提示词变得越来越长,延迟在悄悄增加,单次调用的成本也在上升。而输出质量并没有提升,甚至可能在悄然下降。团队无法察觉,因为他们只测量整体指标,而整体指标掩盖了单个示例带来的损害。

在团队文化层面,这意味着没人觉得自己有权删除示例。添加示例是一种防御性行为——你看到了一个糟糕的输出,写了一个纠偏演示,然后发布了它。而删除示例感觉就像是在自找回归错误(regression)。审计正是为了扭转这种不对称性:手中掌握了经过测量的负贡献数据,删除示例就变成了理所当然的行为,而保留它反而需要理由。如果没有审计,提示词只会无休止地膨胀。

对于那些还没有准备好建立完整审计机制的团队,有一个更简单的启发式方法:为少样本区块设置一个硬上限。五个示例、六个示例,或者任何团队能够辩护的数量。当需要添加一个新示例时,必须删减一个旧示例来腾出空间。这种强制的权衡防止了堆积,即使没有针对每个示例的精确测量。这虽然不如审计精准,但远比单调增长要好。

少样本示例是模型-提示词对的一部分

更深层的架构教训是,少样本示例不能脱离模型独立进行版本管理。包含少样本示例的提示词在任何实际意义上都不具备跨模型版本的可移植性。这些示例是针对特定模型的行为进行微调的,它们编码了关于模型会犯什么错以及它对演示如何反应的假设。在不对目标模型重新评估示例的情况下固定提示词,只是名义上的迁移。

这对评估套件的启示是:“提示词通过评估”是一个特定于模型的结论。如果评估套件仅针对开发时的模型测试提示词,就会错过提示词在下一个模型上失效的时刻。捕捉这一点的纪律是“跨版本评估”:相同的提示词、相同的评估集,同时在当前模型和候选模型上运行,并分别测量每一侧的单示例贡献。这些增量变化会告诉你哪些示例可以保留,哪些应该丢弃,哪些需要重写。

能更早捕捉到这一点的纪律是将提示词和模型视为一个版本化的配对:prompt-v37 + model-2026-04prompt-v37 + model-2026-09 是不同的产物,其中一个的评估结果不能转移到另一个上。生产部署应该同时固定两者,变更日志应该记录两者,升级过程应该将这一配对作为一个整体重新验证。尤其是少样本区块,它是模型和提示词耦合最紧密的地方,因此也是其中之一发生变化时最先失效的地方。

做得好的团队并不会停止使用少样本示例。他们会保留它们——用于示例效果确实优于指令的情况,这种情况是真实存在且不会消失的。他们不再做的是将少样本区块视为永久资产。他们对待它的方式,就像对待系统中任何其他依赖于模型行为的部分一样:在底层模型每次变动时,都进行仪表化监测、审计、清理和重新验证。能在这一过程中幸存下来的示例,才是真正对得起它们消耗的 token 的示例。除此之外的一切都是腐朽,而腐朽会不断累积。

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