我调了一周 prompt 把团队用崩了——过拟合到 5 个测试 case,prod 全军覆没
Trainer 模式的承诺是「调好一次、复用一年」。我调了一周,A/B 测试 5 个 case 全过——然后 prod 80% 的请求都翻车。我调的是测试,不是模型。
一句话总结:Trainer 的核心是「eval set 必须代表 prod 的真实分布」。我用 5 个自己写的测试 case 调 prompt,过拟合到这 5 个 case 全过——但 prod 跟我那 5 个 case 完全不是同一个分布。
任务源头
2026 年 2 月,我加入一家 AI 公司做 prompt 工程师,负责公司主产品的客服 prompt——一个 GPT-4 驱动的客服机器人,回答用户的售前售后问题。
我入职时 prompt 是这样的:
你是 {公司} 的客服助手。
- 回答用户问题
- 不能承诺退款
- 不能推荐竞品
- 不能谈论价格
当时效果:40% 用户问题答得对,60% 会答非所问或编内容。
我的 KPI 是 3 个月内把”答对率”提到 70%。我打算用 Trainer 模式——改 system prompt + 加 few-shot + A/B 测试。
时间线:7 天调优 + 上线翻车
Day 1-2:收集测试 case。
我从客服历史对话里挑了 5 个 case——都是我觉得”典型的”用户问题:
- “你们支持哪些支付方式?”
- “我的订单什么时候发货?”
- “可以开发票吗?”
- “这个产品适合初学者吗?”
- “能不能便宜点?”
这是我犯的第一个错:我挑 case 时已经偏向了我能答的 case**。
Day 3:写 v2 prompt + 5 个 case 全过。
我在原 prompt 上加了:
你是 {公司} 的客服助手。
- 回答用户问题,**语气友好但专业**
- 不能承诺退款
- 不能推荐竞品
- 不能谈论价格
- **回复长度控制在 50-150 字**
- **结尾必须问"还有什么可以帮您"**
- **不能使用"我们很抱歉"超过 1 次**
- **不能使用 emoji**
跑 5 个 case:
[case 1] "我们支持微信、支付宝、信用卡、银联。" (87字, 结尾"还有什么可以帮您", 无 emoji, 无抱歉)
[case 2] "您的订单将在 24 小时内发货。" (52字, ...)
[case 3] "可以开发票。下单时备注即可。" (45字, ...)
[case 4] "适合。我们的产品有详细教程。" (50字, ...)
[case 5] "抱歉,价格由系统决定,不便议价。" (42字, ...)
5/5 全过。 我以为这就搞定了。
Day 4:内部 A/B 测试。
我把 v2 prompt 给客服组长试用 1 天。反馈:“比 v1 好多了。”
Day 5:上线到 10% 流量。
上线后 24 小时,用户问题答对率从 40% 掉到 32%——比 v1 还差。
我慌了。看日志,发现 prod 的真实用户问题跟我那 5 个 case 完全不是一回事:
- 60% 用户问题带情绪:“你们骗人""垃圾产品""我投诉 12315”
- 20% 用户问题很具体:“iPhone 15 的充电器接口是 USB-C 吗”(我们的产品跟 iPhone 没关系)
- 10% 用户问题要求超出客服范围:“能帮我写代码吗""能帮我做 plan 吗”
- 5% 用户问题是真正的售后:“我的快递丢了”
- 5% 是其他
而我那 5 个 case,全是”礼貌的标准问题”——连 5% 都不到。
我调了 5 个 case,让 prompt 在那 5 个 case 上变好,但让 prompt 在剩下 95% 的 case 上变差——因为我加了”严格控制”约束(字数、结尾、不能抱歉、不能 emoji),这些约束在情绪化场景里完全失效:
- 用户骂人 → prompt 让 agent 用”友好专业”语气 → agent 答”感谢您的反馈” → 用户更怒
- 用户要求写代码 → prompt 让 agent “回答问题” → agent 答”抱歉我无法帮您” → 用户:“那你有什么用”
- 用户快递丢 → prompt 让 agent 不能承诺退款 → agent 完全不知道怎么处理 → 转人工率 80%
Day 6:紧急回滚。
把 v2 prompt 撤回 v1。当晚数据回到 38% 答对率(接近基线)。
Day 7-10:重新收集 case + 重写。
我这次随机抽样 200 个真实用户对话作为测试集,每个 case 标”答对 / 答错”。
按这个新测试集重写 prompt 3 轮,最后到 65% 答对率——3 周后到 71%,达到 KPI。
但 Day 3 那个 5/5 的 v2 prompt 教训,我一直记着。
决策点反推
错误 1:测试 case 是我”挑的”,不是”抽的”。
我挑 case 的过程已经带了我的偏见——我挑”我能答的 case”。这种 case 集合不代表真实用户。
正确做法:
- 随机抽 200 个真实对话(不挑)
- 覆盖所有场景:礼貌 / 情绪化 / 超出范围 / 真实售后 / 边缘 case
- 按真实分布权重采样(不是平均采样)
错误 2:5 个 case 太少,无法衡量 overfit。
5 个 case 的 prompt 优化几乎肯定过拟合——5 个 case 是”金鱼缸”,prompt 可以完美适配,但 prod 是”大海”。
经验法则:
- < 30 个 case:纯过拟合风险
- 30-100 个 case:勉强
- 100+ case + 真实分布:可信赖
错误 3:我的 prompt 加了”约束”但没加”决策树”。
我加的全是形式约束(字数、结尾、不能抱歉、不能 emoji)。形式约束会让 agent 在异常情况下僵掉——它不知道什么时候该”破例”。
更好的做法:
- 形式约束少一点(不要锁死 agent 的灵活度)
- 加决策树:遇到情绪化用户怎么答、遇到超出范围怎么答、遇到边缘 case 怎么答
- 让 agent 在决策点有多个选项,根据场景选
token 账单
| 项目 | 数值 |
|---|---|
| Day 1-5 调优轮数 | 23 轮 |
| Claude 调用次数 | 156 次(含 5 case × 4 轮 × 7 版本) |
| Input tokens | 0.8 M |
| Output tokens | 0.2 M |
| 调优成本 | $24 |
| 上线翻车期间客户投诉 | +47 条 |
| 紧急回滚 + 重写成本 | +$340(含重新 A/B + 重新上线) |
| KPI 延期 | 3 周 |
$24 调优成本听起来不高,但翻车带来的客服投诉 + 客户流失 + 团队信任损失,是 $340 也补不回来的。
给也想用 Trainer 的朋友的 3 条避坑
避坑 1:测试集必须”随机抽样”而不是”挑 case”。
挑 case = 你的偏见 = 过拟合的种子。你写的 case 永远会偏向你能答的——这不是故意的,是人的本能。
做法:从 prod 日志随机抽 200 条对话,按真实场景比例采样。情绪化的、超出范围的、边缘的,全部要有。
避坑 2:每版 prompt 必须在 hold-out 测试集上验证。
不要在 train set 上验证——那是过拟合的证据。
做法:把测试集分 70% train + 30% hold-out。**只调 train,**最后在 hold-out 上验证。如果 hold-out 表现差,说明过拟合。
避坑 3:少加形式约束,多加决策树。
形式约束(字数、句式、不能用 X)会让 agent 僵化——它无法处理约束之外的场景。
决策树(“如果用户情绪化 → 先共情再解决”)让 agent 有判断能力——它可以根据场景选不同的应对。
反思
Trainer 模式的核心是”评估驱动”——你调什么取决于你怎么评估。
如果你的评估集合是错的(太小、太挑、太干净),你调出来的 prompt 就只会在错的集合上变好。
我这次的失败本质是 eval set 没代表 prod——5 个我自己挑的 case 完全不代表真实用户。
Trainer 的元教训:
不要调你测的东西。测你调的东西。
我之前反过来——测的是我挑的 5 个 case,调的也是这 5 个 case,结果在 prod 完全失效。
现在我团队的新规矩是:任何 prompt 改动必须有 200+ 真实对话测试集 + hold-out 验证 + 3 周灰度。
失败成本:$24 + 47 条投诉 + 3 周延期 + 团队信任 -1。 真正贵的是「我用 5 个 case 调 prompt,然后以为这能代表 prod」。