很多 Prompt 调优建议会说“加 Few-shot”“降低温度”“做 A/B 测试”,但没有回答一个更实际的问题:面对一条效果不好的 Prompt,下一步具体该改什么,怎样知道改对了?

答案不是继续润色文字,而是把 Prompt 当成一段可测试的程序:先定义输入输出契约,用真实失败样本建立评估集,再针对某一种失败做单变量实验。只有离线结果稳定改善,才值得进入线上 A/B。

调优前先判断:问题真的在 Prompt 吗?

以下问题改 Prompt 通常没有用,应该先修上游或下游:

现象 更可能的原因 应优先处理
回答依据错误或缺失 检索没有召回正确文档 RAG 查询、分片、召回和重排序
JSON 经常解析失败 没有结构化输出能力或解析器太脆弱 JSON Schema / Structured Outputs / 解析与重试
工具调用失败 参数校验、权限或工具实现有问题 Tool schema、服务端校验、超时与重试
同一输入每次差异很大 模型采样参数或上下文不稳定 固定模型版本、温度、上下文和工具结果
任务本身需要专业知识 模型或知识源能力不足 补充知识、换模型或人工兜底

Prompt 适合解决的是:任务目标不清楚、规则有歧义、输出遗漏、格式不符合契约、面对边界情况时决策不一致。

用一个具体任务定义“好结果”

假设要从客服消息中抽取退款工单,返回给后端系统。不要从“请你成为专业客服助手”开始,而要先写清楚输出契约:

{
  "intent": "refund | other",
  "order_id": "string | null",
  "reason": "quality | delivery | duplicate_charge | other | null",
  "needs_human": true,
  "reply": "string"
}

再定义每个字段如何判定:订单号不明确时填 null;用户只是在咨询政策时 intentother;出现威胁、欺诈或无法判断的信息时 needs_humantrue。这份契约比“写得更详细的 Prompt”更重要,因为它同时决定了 Prompt、评估脚本和后端处理逻辑。

第一步:建立可复现的基线

先保留当前 Prompt 为 v0,固定模型版本、温度、最大输出长度、工具列表和 RAG 配置。准备 30 到 50 条脱敏的真实输入,不能只挑正常案例,应至少包含:

  • 明确的正常请求;
  • 信息缺失、错别字和口语化表达;
  • 多个意图混在一条消息中;
  • 容易误判的反例,例如“我想了解退款规则”;
  • 需要人工处理的高风险案例。

每条样本记录期望字段、关键事实和允许的回答范围。先跑一次 v0,把结果按失败类型归档,而不是简单记为“回答不好”。

样本 期望 实际结果 失败标签
“订单 A123 重复扣款了” duplicate_charge other 分类错误
“商品有破损,订单号找不到了” order_id: null 编造订单号 事实编造
“退款多久到账?” intent: other 创建退款工单 意图边界错误
“帮我尽快处理” 合法 JSON 缺少 needs_human 格式/字段遗漏

这张失败表就是下一轮调优的待办列表。

第二步:把失败原因映射为动作

失败类型 先检查什么 常见的 Prompt 改动
漏字段、字段类型错误 输出契约是否明确 写出完整 Schema、字段含义和 null 规则
规则误解、意图混淆 规则是否可判定 改成决策条件与优先级,而非抽象要求
边界案例误判 是否缺少代表性反例 增加 1-3 个最有区分度的 Few-shot 示例
编造上下文没有的信息 是否说明信息来源边界 明确“仅使用输入内容;未知则填 null/转人工”
回答啰嗦或语气不当 输出目标是否具体 指定受众、长度、禁用内容和输出模板

不要一次同时加入角色设定、十个示例、几十条规则和新的检索策略。那样即使结果改善,也不知道真正有效的因素是什么,更难排查回归。

第三步:做单变量 Prompt 实验

假设基线显示最多的问题是:模型在没有订单号时会编造订单号。先只改这一件事。

v0:

从用户消息中抽取退款工单,按 JSON 返回。

v1,只新增事实边界:

从用户消息中抽取退款工单,按指定 JSON 返回。

事实边界:只使用用户消息中明确出现的信息。
- 未出现订单号时,order_id 必须为 null。
- 不得猜测或补全订单号、商品或退款原因。

运行同一份评估集,重点看“编造订单号”是否下降,同时检查是否引入了新问题,例如把合法订单号也误判为 null。如果 v1 有改善,再以 v1 为基线处理下一个最常见失败。

何时使用 Few-shot

Few-shot 不是默认配料。它适合规则难以用一句话定义、但输入输出模式很稳定的情况,例如区分“申请退款”和“咨询退款政策”。示例要覆盖最容易混淆的边界,而不是重复展示简单正确样本。

示例:
用户:退款多久能到账?
输出:{"intent":"other", "order_id":null, ...}

用户:订单 A123 重复扣款,请退款。
输出:{"intent":"refund", "order_id":"A123", ...}

示例会占用上下文并增加维护成本。新增示例后,必须把它们之外的样本作为测试集,避免只是在“背题”。

第四步:让评估替代感觉

评估至少要同时看质量、可靠性和成本。对于上面的结构化任务,可定义:

字段准确率 = 正确字段数 / 应评字段数
格式合规率 = 能通过 JSON Schema 校验的输出数 / 总输出数
编造率 = 含输入中不存在的关键事实的输出数 / 总输出数
人工转交准确率 = needs_human 判断正确数 / 总样本数
成本 = 输入 token + 输出 token + 工具调用成本
延迟 = P50 / P95 端到端耗时

将评估集分为两部分:开发集用于迭代,保留集只在候选版本确定后评估。否则反复针对同一批样本调 Prompt,会高估真实收益。

一份最小版本记录就足够开始:

版本 唯一改动 字段准确率 格式合规率 编造率 P95 延迟 结论
v0 基线 82% 91% 12% 1.2s 当前线上版本
v1 增加未知值规则 86% 93% 3% 1.2s 保留
v2 增加两个边界示例 89% 95% 2% 1.4s 进入灰度

数字应来自实际数据。示例表只展示记录格式,不应被当作目标承诺。

离线对比通过后,才做线上 A/B

离线评估证明“在已知样本上更好”,不能保证线上更好。上线时将用户或会话稳定地分配到对照组 v0 和实验组 v2,避免同一个会话在中途切换 Prompt。

线上指标必须绑定业务结果,例如工单一次完成率、人工介入率、用户再次追问率、投诉率,以及成本和延迟。不要只比较模型自评分。

开始前写下放量和回滚条件,例如:

灰度比例:10%
放量条件:任务完成率不低于 v0,人工介入率下降至少 5%,P95 延迟增加不超过 10%
回滚条件:高风险错误率上升、投诉率异常,或任何安全规则被绕过
观察窗口:覆盖至少一个完整业务周期,并达到预先计算的样本量

如果样本量很小或高风险错误不能接受,不应为了“做 A/B”而放量。可以扩大离线评估、采用人工复核,或只在低风险流量中验证。

一次完整调优的工作清单

1. 写出输入、输出 Schema、未知值和人工转交规则。
2. 固定模型与运行参数,保存当前 Prompt 作为 v0。
3. 收集真实样本,标注期望结果和失败标签。
4. 跑 v0,按失败类型排序,选影响最大的一个问题。
5. 只针对这一个问题修改 Prompt,形成 v1。
6. 在开发集和保留集上比较 v0/v1 的质量、成本与延迟。
7. 记录唯一改动和结果;无改善就回退并换假设。
8. 离线稳定胜出后,小流量 A/B,并按预先定义的门槛放量或回滚。
9. 把线上新失败样本持续补入评估集。

总结

Prompt 调优的交付物不是一段“更聪明”的提示词,而是一套可复现的任务契约、评估集、版本记录和上线判定标准。先定位失败,再做单变量实验,最后让真实指标决定是否上线。