给例子:少样本提示
把用户留言分成四类,不给例子答对 18 条,给 4 个例子答对 20 条。讲清楚例子怎么挑、放几个、会带来什么副作用。
- 约 30 分钟
- 难度:入门
- 实测:2026-09-14 deepseek-flash
有些要求很难用文字说清楚。"缺陷"和"使用问题"的边界在哪?用户问"cookie 在重定向之后丢了,是我用法不对吗",算哪一类?你可以写一大段定义,但往往不如直接给几个例子来得快。
给模型看几个"输入 → 输出"的例子,让它照着做,这叫少样本提示(few-shot prompting)。不给例子直接做,叫零样本(zero-shot)。
实验:给用户留言分类
httpx 的维护者每天会收到各种留言。我们想让模型把它们自动分成四类:缺陷、功能建议、使用问题、其他。我准备了 20 条留言,每条都人工标好了正确类别,比如:
TESTS = [
("用 AsyncClient 并发 100 个请求,程序直接卡死,CPU 占满", "缺陷"),
("能不能加一个像 requests 那样的 Session 重试适配器?", "功能建议"),
("怎么给单个请求设置不同的超时时间?", "使用问题"),
("你们的文档网站打不开了", "其他"),
# ……一共 20 条,每类 5 条,完整列表见 code/02-prompting/few_shot.py
]
零样本的提示词只说明分类规则:
ZERO_SHOT = """把用户留言分成以下四类之一:缺陷、功能建议、使用问题、其他。
只输出类别名称。"""
少样本的提示词在后面加了 4 个例子,每类一个,而且这 4 条都不在测试集里:
FEW_SHOT = ZERO_SHOT + """
例子:
留言:调用 client.close() 之后再发请求没有报错,而是静默返回了旧的响应
类别:缺陷
留言:想要一个参数,能在请求失败时自动打印完整的请求和响应
类别:功能建议
留言:base_url 和 url 拼接的规则是什么?结尾的斜杠有没有影响
类别:使用问题
留言:这个项目和 aiohttp 比哪个更好
类别:其他"""
每条留言用 留言:{text}\n类别: 的格式发给模型,温度设为 0,关掉思考,然后和人工标注比对:
def classify(system, text):
response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": system}, {"role": "user", "content": f"留言:{text}\n类别:"}],
temperature=0,
extra_body={"thinking": {"type": "disabled"}},
)
return response.choices[0].message.content.strip()
我运行的结果:
零样本:答对 18/20,格式不对 0 条 []
怎么给单个请求设置不同的超时时间? 标注=使用问题 模型=功能建议
你们的文档网站打不开了 标注=其他 模型=缺陷
少样本:答对 20/20,格式不对 0 条 []
零样本错在哪
看看零样本错的两条。
"怎么给单个请求设置不同的超时时间?"被分成了功能建议。模型大概理解成了"用户想要一个给单个请求设超时的功能"。但这是 httpx 早就支持的用法,用户只是不知道怎么写。
"你们的文档网站打不开了"被分成了缺陷。从字面看,网站打不开确实是个 bug,但我们的"缺陷"指的是 httpx 这个库本身的问题,文档网站属于"其他"。
两个错误都不是模型笨,而是分类标准有模糊地带,而我的规则里没写清楚。给了例子之后,模型从"base_url 和 url 拼接的规则是什么"这个例子里看出了"问怎么用 = 使用问题",从"这个项目和 aiohttp 比哪个更好"看出了"和库本身无关的 = 其他"。例子替我把规则说清楚了。
当然,你也可以不用例子,而是把规则写得更细:"问'怎么做'的算使用问题,哪怕看起来像在要新功能"、"只有 httpx 库本身的行为问题才算缺陷"。这也有效,而且更省词元。实践中两者经常一起用:规则写主干,例子补细节。
例子怎么挑
覆盖每一种输出。四个类别各给一个例子。如果只给了"缺陷"和"使用问题"的例子,模型就不太会输出另外两类。
挑边界上的例子。最有价值的例子不是最典型的,而是最容易分错的。"调用 close() 之后再发请求没有报错"就是个好例子:它听起来像是在问用法,实际上是库的行为有问题。
和测试集分开。例子不能出现在测试集里,否则等于把答案告诉了模型,准确率会虚高。这个实验里的 4 个例子是我另外写的。
格式和真实输入一致。例子用的是"留言:……类别:……"的格式,真实请求也用同样的格式。模型会严格模仿例子的格式,所以例子的格式就是你想要的输出格式。这个实验里零样本和少样本都没有出现格式错误,但当你要求更复杂的输出(比如 JSON)时,一个格式正确的例子能大大减少格式问题。
放几个
一般 3~5 个就够了。例子越多,每次请求的输入越长,花钱越多。好在例子是固定的,放在 system 消息里能命中缓存。
如果效果还不够好,先想想是不是例子挑得不好,而不是急着加数量。10 个相似的例子,不如 3 个覆盖不同边界情况的例子。
例子的副作用
例子很有效,所以也很容易带偏模型。
模型会模仿例子的一切。例子里的回答都很短,模型的回答也会变短;例子都是中文,遇到英文留言它可能还是用中文分类名(这个倒是我们想要的)。你没打算让它模仿的特征,它也会模仿。
模型可能照抄例子的内容。在生成类的任务里(比如写产品描述),如果例子写的都是咖啡,模型写茶叶的描述时也可能冒出咖啡的词。所以例子要多样化,在内容上彼此不同。
顺序和比例有影响。如果 5 个例子里有 4 个是"缺陷",模型会倾向于多分成"缺陷"。各类别的例子数量尽量平衡。
常见问题
例子放在 system 消息里,还是做成多轮对话? 两种都可以。上面的做法是把例子写进 system 消息。另一种写法是把每个例子做成一对 user 和 assistant 消息,放在真正的问题前面,好像模型之前已经这样回答过几次。后一种对模仿格式特别有效,但消息列表会变长,也更难维护。我一般先用 system 消息的写法,格式出问题了再换。
分类的类别很多,比如 30 个,每类一个例子太长了怎么办? 先考虑能不能把类别分成两层,先分大类再分小类。或者只给容易混淆的那几类配例子。更进一步的做法是:先用第 01 模块第 5 课的向量嵌入,从一个大的例子库里找出和当前输入最相似的几个例子,只把它们放进提示词。这叫动态少样本,原理和 04 模块的 RAG 一样。
这个实验说明不了什么
20 条测试数据太少了。18 条和 20 条的差距,换一批数据可能就变成 19 和 20,甚至反过来。这个实验能说明的是"少样本在这类任务上有帮助"的一个具体例子,不能说明"少样本能把准确率提高 10%"。要可靠地比较两个提示词,需要更多的测试数据,这是第 5 课的内容。
练习
- 把少样本提示里的例子减到 2 个(只留"缺陷"和"使用问题"),再运行,看看"其他"和"功能建议"两类的准确率怎么变。
- 故意放 4 个全是"缺陷"的例子,看模型是不是更倾向于分成"缺陷"。
- 自己再写 10 条留言,尽量写那些连你都要想一想才能分对的,加到测试集里重新跑。零样本和少样本的差距变大了还是变小了?
- 不用例子,改为在零样本的规则里补充两条说明("问怎么用的,哪怕看起来像要新功能,也算使用问题"、"文档、社区相关的算其他"),看看能不能达到和少样本一样的效果。
自测
1. 为什么少样本提示里的例子不能出现在测试集里?
例子等于告诉了模型这些输入的正确答案。如果测试集里有同样的输入,模型只要照抄就能答对,测出来的准确率会虚高,不能反映它在新数据上的真实表现。
2. 你给了模型 5 个例子,其中 4 个是"缺陷"。可能会有什么问题?
模型会倾向于把更多留言分成"缺陷",因为例子的比例暗示了"大多数留言都是缺陷"。例子里各个类别的数量应该尽量平衡,每一种输出都至少覆盖到。
3. 挑例子时,应该挑最典型的,还是最容易分错的?
优先挑最容易分错的、处在边界上的例子。典型的例子模型本来就能分对,边界上的例子才能帮它弄清楚模糊地带的规则。
提问与讨论
这一课没看懂的地方,在这里问。看到别人的问题,也欢迎你来回答。
提问 +3 积分,回答别人 +6 积分。内容经审核后公开。
正在加载讨论…