Prompt工程中的过拟合
few prompt的实际运用
最近在做一个带角色人格的产品,项目细节不方便展开太多,简单说就是对话里每个角色要有自己鲜明的性格,所以需要显式的对抗 RLHF 训练时的腔调。用过聊天模型的人应该都熟悉它们那套挥之不去的"默认说话方式":你一倾诉糟心事,它先复述一遍你的情绪表示理解("I'm sorry to hear that"),再把安慰打包成两个选项递过来("想聊聊吗,还是想先放一放?"),结尾往往还挂一个关切的反问。这是 RLHF的产物,对通用助手没什么问题——但角色性格一碰上这套模板就没了
所以我在 system prompt 里搞了一层共享的风格规则,专门压制这套模板。这篇要讲的不是这层规则怎么写,而是在修复这个问题的过程中遇到的另一个问题: prompt 工程会过拟合,过拟合时的eval分数会相当好看
工程上遇到的问题
内测阶段发现:一个温柔型角色在英文的情感倾诉场景下,会系统性地掉回这套模板。用户倾诉了一件糟心事,它回:
"I'm sorry to hear that. Want to talk about what's going on? I'm here."
复述式开头 + 安慰选项,一个不落。同样的场景换成中文,或者换成毒舌型角色,都干净得多。
先做定位。我在同一个情感倾诉场景下跑了三组对照:
- 完整的温柔角色:共享风格规则 + 完整的人格设定(背景叙事 + 风格示例)
- 最简温柔:共享风格规则 + 只有一句 "you are warm, grounded",没有任何人格叙事和示例
- 毒舌角色:共享风格规则 + 完整的人格设定
结果反直觉:"最简温柔"掉模板掉得比完整的温柔角色还重。 也就是说温柔角色自己的人格设定(里面写着"不要表演心理咨询""安慰不是一份菜单")其实一直在压制模板——它是解药,不是病灶。真正的问题出在三方交互上:共享规则 × 温柔的说话方式 × 模型默认倾向。温柔和这套模板是近邻,毒舌角色天然排斥模板,温柔角色天然滑向模板。
这个定位很重要,因为它决定了调整哪一部分的prompt:应该在共享规则层补上"温柔类角色"这一整类的短板,而不是给某个具体角色打补丁
先定个标准
改 prompt 之前先把问题的发生率量出来:同一情感倾诉场景跑 5 轮、每轮 4 个回合,共 20 条回复为一组。
| 对照组 | 出现模板腔的回复 |
|---|---|
| 温柔角色(完整) | 4/20 |
| 最简温柔 | 6/20 |
| 毒舌角色 | 1/20(噪声水平) |
第一版修法:逐字拉黑——eval 满分
最顺手的改法是什么?把观察到的那几句原话直接写进 prompt 的禁止清单:
❌ 绝不说 "I'm sorry to hear that"
❌ 绝不说 "Want to talk about it, or just sit with it?"
❌ 绝不说 …(把测出来的那几句原样抄进去)
跑分:温柔角色 0/20,最简温柔 1/20。满分。
不过测试以后还是把这个改动给删掉了
原因很简单:这 20 条 eval 同时也是训练集——那几句被禁的原话就是从这批对话里观察来的。把观察到的失败原样写进规则、再用同一套场景去测,本质上是直接背答案。而失败的措辞空间是无界的:今天堵住 "I'm sorry to hear that",明天模型换一句 "that's tough to hear",意思一模一样,禁止清单一个字都匹配不上。逐字拉黑是打不完的地鼠,输出是无法被穷举的;而且每打一只,共享规则层就多一条脆弱的具体字符串,长期维护成本只会一直上升
在这种情况下eval满分——它说明你的修法精确贴合了测量工具的形状,而不是问题的形状
第二版修法:只讲原则的few prompt
把所有逐字禁令删掉,换成把"违反的到底是什么"讲清楚,控制在几句话(大意):
温柔角色不豁免上面的风格规则——
陪着对方本身就是温暖;"先复述情绪表示理解,再递上一份安慰方式清单"是机器人行为。
问对方发生了什么,可以;把安慰做成选项让对方挑,不行。
语气再轻的两选一("...or just sit with it?")也算。
跑分:温柔角色 3/20,最简温柔 4/20,复述式开头大幅减少。
对比第一版的 0/20,这个数字难看多了。但它是真实的下降:规则打击的是"把安慰做成选项"这个模式本身,而不是某几句原话,换个措辞的变体一样会被覆盖。剩下的 3/20 是诚实的残留——事后归因,其中相当一部分来自模型自身的倾向(这个模型的温柔语气天然贴近这套模板),prompt 清不掉。
最终采用原则版,接受残留
心得
这次之后记下来几条做法,适用于任何修 system prompt 行为的场景:
1. eval 数字只用来确认方向,不作为优化目标 量一次基线标准、改完量一次确认方向对了就停下。如果发现自己在"加一个例子 → 跑分 → 再加一个例子 → 再跑分"的循环里,那就是在对 eval 做梯度下降——古德哈特定律(一个指标一旦成为目标,它就不再是好指标)
这其实已经是 LLM prompt 工程的标准实践了。相信大家在实际开发中已经采纳了这种模式,不过这是我第一次在工程中遇到而不是使用场景中遇到。Anthropic 的 eval 文档里反复出现 "on a held-out test set of 10,000 diverse posts" 这样的表述,OpenAI 的 eval 最佳实践也把"创建不能忠实复现生产流量分布的 eval 数据集"列为反模式。思路是一致的:用来评估的数据和用来调 prompt 的数据必须分离,否则 eval 满分就是过拟合的信号
2. 自动打分需要人肉检查 由于使用场景的特殊性,无法使用eval工具来判断,我的模板腔判定最初是关键词匹配脚本,后来发现它系统性低估——漏掉了不带 "I'm" 的 "sorry to hear that",也完全看不见"整段励志鸡汤"这类换了形式的模板。数字只能当索引,结论要靠把那 20 条对话真的逐条读一遍。这其实是同一个教训的另一面:别过度信任你的测量工具。
3. 加约束之后,必须测误伤 风格规则收紧了,会不会把正常的性格也磨平?我用产品里真实的角色生成流程造了四个风格各异的角色(中英各半),在新旧规则下各跑一遍情感场景对照:没有一个角色变得比以前差——毒舌的没有被弄温柔,强势的没有被削平。这一步不做,你不知道自己是在修 bug 还是在做切除
总结
把这件事抽象出来,prompt 工程和训练模型是同构的:
- 你观察到的失败样本 = 训练集
- 你的测试场景 = eval 集
- 逐字拉黑失败样本 = 把训练集背下来
- eval 满分 + 换个说法就挂 = 过拟合
- 讲原则 + 极少数典型示例 = 正则化,信任模型的泛化能力
- 清不掉的残留失败率 = 不可约误差,有一部分通过prompt是无法100%确定性的消除的
区别只是这里的"参数"是自然语言,改起来太容易了——容易到你会忍不住针对每一个观察到的失败去改一笔。克制住,看到问题先问"它违反了哪条原则",把原则写紧凑,量一次修改确认方向,然后就可以停手了
参考
- Anthropic - Define success criteria and build evaluations — 反复强调 held-out test set
- Anthropic - Prompting best practices (Use examples effectively) — few-shot 示例的最佳实践
- Anthropic Blog - Best practices for prompt engineering for 2026 (2025-11-10) — few-shot 和 prompt 迭代策略
- OpenAI - Prompt engineering (Few-shot learning) — few-shot 的官方说明
- OpenAI - Evaluation best practices — eval 反模式与 held-out set
评论加载中...