Prompt学习二:如何让输出更稳定

上一篇我主要在讲一件事:Prompt 不是聊天技巧,而是结构化输入设计。这个视角解决了“怎么把任务说清楚”的问题,但真正把模型接进系统后,很快就会遇到第二个问题:明明已经说清楚了,输出为什么还是不稳定?

这也是我在继续学习 Prompt 稳定性这部分内容时最有感的一点。很多 Prompt 不是不能用,而是只能“偶尔答对”。对于 AI 应用工程来说,这种状态其实离可上线还很远。你真正需要的,不是某一次回答惊艳,而是同类输入下,模型能持续给出同类结果。

“能用”不等于“可接入系统”

在工程场景里,Prompt 最大的问题往往不是模型完全不会,而是它每次都差一点:

  • 这次字段全了,下次漏一个
  • 这次语气正常,下次风格跑偏
  • 这次是 JSON,下次多加一段解释
  • 这次分类合理,下次边界样本判断漂移

这种“不完全错,但总不完全一样”的状态,才是最难处理的。因为下游系统最怕的不是单次错误,而是没有稳定预期

所以这部分内容的核心,不是再学几个 Prompt 技巧,而是学会一件更工程化的事:

如何逐步收窄模型的输出空间,让结果更稳定、更可验收。

Zero-shot 应该是默认起点

更合理的做法,是把 zero-shot 当成默认起点,而不是把 few-shot 当成“高级模式”。

原因很简单:如果一个任务在 zero-shot 下就能稳定完成,说明你的任务描述本身已经足够清楚。这个时候继续堆示例,收益未必高,维护成本反而会增加。

换句话说,zero-shot 最适合拿来做两件事:

  1. 验证任务边界是否清晰
  2. 建立最小可用版本的 Prompt

例如我们想从用户反馈中提取问题信息,一个很自然的 zero-shot 写法可能是:

1
2
3
4
请分析下面这条用户反馈,并提取关键信息。

反馈:
App 打开很慢,而且首页改版后我找不到会员入口了。

这时候模型很可能能答出一些内容,但输出通常不稳定:

  • 这次提到“性能问题”和“导航问题”
  • 下次可能改成“用户体验差”和“功能入口不清晰”
  • 有时会总结,有时会解释,有时还会扩展建议

所以 zero-shot 的意义不是“够不够高级”,而是帮助你看到:现在的任务描述到底有多大自由度。

Few-shot 不是装饰,而是约束

我以前会下意识把 few-shot 理解成“给几个例子,让模型学一学”。但现在我更愿意把它理解成:

当任务已经描述清楚,但输出空间仍然太大时,用示例去收窄模型的可接受行为。

这时候示例的作用就不再是“举例说明”,而更像是在定义:

  • 应该用什么字段
  • 哪些判断边界算合理
  • 输出风格应该落在哪个范围
  • 什么样的答案才叫“符合预期”

还是上面的用户反馈场景,如果改成 few-shot,可能会更像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
你是一名用户反馈分析助手。

请根据输入反馈,输出以下字段:
- issue_type
- severity
- summary

示例1:
反馈:支付完成后页面一直转圈,没有成功提示。
输出:
{
"issue_type": "支付流程异常",
"severity": "high",
"summary": "支付结果反馈缺失,用户无法确认是否成功"
}

示例2:
反馈:字体有点小,但还能用。
输出:
{
"issue_type": "界面可读性",
"severity": "low",
"summary": "字体偏小,影响部分用户阅读体验"
}

现在请分析:
反馈:App 打开很慢,而且首页改版后我找不到会员入口了。

加了 few-shot 之后,模型会更倾向于:

  • 使用同一套字段
  • 使用接近的抽象粒度
  • 给出一致的 severity 风格
  • 用更稳定的 summary 句式

所以 few-shot 解决的不是“模型知不知道”,而是“模型会不会稳定地按你希望的方式知道”。

示例最重要的不是数量,而是边界

对 few-shot 来说,一个很重要的判断是:示例不是越多越好,关键在于边界样本选得好不好

如果你给的全是“理想样本”,模型学到的往往只是表面形式;但在真实场景里,真正麻烦的是边界输入:

  • 情绪强烈但信息不完整
  • 同时包含两个问题类型
  • 描述模糊,严重程度不好判断
  • 反馈很短,几乎没上下文

这些时候,few-shot 的价值才真正体现出来。

因为示例不只是告诉模型“正确答案长什么样”,更是在告诉它:

当事情不那么标准时,你也应该怎么做。

结构化输出不是格式美化,而是系统边界

这里还有一个非常重要的点,就是“格式控制”。

一开始很容易把它理解成:

  • 用 JSON 比较整齐
  • 用列表比较清楚
  • 用表格比较好看

但如果站在工程角度看,结构化输出的意义根本不是排版,而是:

让输出结果能够被程序稳定消费、比较、校验和接入后续流程。

比如同样是分析用户反馈:

不加格式约束

1
请分析下面这条反馈,并告诉我有什么问题。

模型可能返回:

1
这个反馈主要涉及两个方面。第一,应用启动较慢,属于性能体验问题;第二,首页改版后会员入口难找,属于信息架构和导航问题。整体来看,这会影响用户留存。

这段话人可以读懂,但程序很难稳定解析。

加结构化输出约束

1
2
3
4
5
6
请分析下面这条反馈,并严格输出 JSON:
{
"issue_types": [""],
"severity": "",
"summary": ""
}

这时候你得到的结果,不只是更规整,而是:

  • 更容易做自动处理
  • 更容易做批量评估
  • 更容易比对不同 Prompt 版本
  • 更容易接到下游系统里

也就是说,structured output 不是“更漂亮”,而是“更可依赖”。

一个更实用的判断标准:先 zero-shot,必要时再 few-shot

这里最值得坚持的一条方法论是:

先用 zero-shot 验证任务是否清晰,只有当输出空间仍然过大时,再用 few-shot 去收窄行为。

这个顺序很重要,因为它更符合工程上的成本意识。

如果一开始就堆示例,很容易掩盖真正的问题:

  • 是任务没定义清楚?
  • 还是边界没说清?
  • 还是输出协议不明确?
  • 还是模型本来就需要行为样本来校准?

zero-shot 更像是基线方案,few-shot 更像是校准手段。

这样理解之后,你就不会把 few-shot 当成“更高级的 Prompt”,而会把它当成:

在必要时才引入的控制成本。

我现在会怎么验收一个 Prompt 是否更稳定

如果把这部分内容真正落到工程实践里,“Prompt 好不好”的判断标准也应该随之变化。

以前更容易关注单次结果看起来是不是聪明、是不是漂亮;现在我会更关注下面这些问题:

  • 同类输入下,输出结构是否一致?
  • 关键信息会不会时有时无?
  • 边界样本会不会漂移?
  • 下游程序能不能稳定消费结果?
  • 如果换一个人继续使用这个 Prompt,结果还能不能维持可用?

如果这些问题答不上来,那这个 Prompt 可能只是“看起来能用”,还远远谈不上稳定。

结语

第一篇我更关注“怎么把 Prompt 写清楚”,第二篇我更关注“怎么让 Prompt 少发挥一点”。

对 AI 应用工程师来说,写 Prompt 的关键,不是让模型尽情发挥,而是让输出尽量收敛。zero-shot 帮你验证任务定义是否清楚,few-shot 帮你校准行为边界,格式约束帮你把结果变成可以接进系统的输出。

如果说第一篇是在建立输入接口,那这一篇更像是在收紧接口契约:让模型不只是能答,而是能稳定地按你希望的方式回答。

参考文档

  • Prompt Engineering Guide - 零样本提示
  • Prompt Engineering Guide - 少样本提示
  • Prompt Engineering Guide - 提示词示例
  • Prompt Engineering Guide - 提示词要素