Token Counting

如何确定 Few-Shot Prompt 的示例数量,又不浪费上下文

Few-shot prompting 可以提高输出一致性、格式遵循和边界案例处理能力,但也可能悄悄占用真实用户输入与模型回答所需要的上下文空间。

真正该问的不是“Few-shot Prompt 到底应该放几个示例?”因为不存在适用于所有模型和任务的固定数字。更实用的问题是:

在保留足够生产输入和输出空间的前提下,能够通过质量测试的最小示例集合是什么?

可靠的方法是:从少量、差异明确的示例开始,统计完整请求的 Token,再只为已经确认的失败模式增加示例。本文提供一套预算公式、0/1/3/5 示例测试阶梯、压缩规则,以及可以通过 TokenTest 对目标端点进行比较的测试模板。

简短答案:从少量示例开始,让每个示例证明自己的价值

可以先按下面的顺序测试:

  1. 先运行没有示例的 zero-shot 版本。
  2. 加入一个能够展示目标格式的标准示例。
  3. 测试三个示例,分别覆盖常规场景、边界场景和容易混淆的场景。
  4. 只有当三个示例仍然存在可重复的错误时,再测试五个示例。
  5. 最终保留能够满足质量、延迟和 Token 预算要求的最小版本。

这是一套测试顺序,不代表所有模型都必须使用三个或五个示例。Anthropic 当前的 multishot 指南针对 Claude 提到,三个到五个示例通常可以改善结果,并建议示例保持相关、多样、清晰和结构一致。Google 的 few-shot 指南同样强调代表性示例,并指出最佳数量会随任务变化。OpenAI 的 Prompt Engineering 指南则建议使用多样的输入输出示例,让模型从 Prompt 中学习任务模式。

最终生产决策仍然应该来自你实际使用的模型、端点、语言与请求结构测试。

为什么 Few-Shot Prompt 示例很容易变贵

一个示例通常不只有“示例内容”,还会重复包含:

如果一个完整示例需要 220 个输入 Token,五个示例可能增加约 1,100 个 Token。此时还没有计算消息包装、工具定义、检索内容、真实用户请求和输出预留。

可见文字也不等于完整请求。OpenAI 的 Token Counting 指南区分了纯文本输入和包含 messages、tools、files 等结构的请求级统计。Anthropic 提供 Messages 请求的 Token Counting 接口,Gemini 也提供面向具体模型请求内容的 Token 统计方法。不同 Provider 的统计方式并不完全相同,所以字符数不能替代生产环境的 Token 预算。

不要用固定示例数量,要用完整请求预算

先使用下面的公式:

可用于示例的 Token =
  上下文上限
  - 固定请求 Token
  - 生产输入 Token
  - 检索与工具 Token
  - 输出预留
  - 安全余量

各部分应包含:

预算部分 需要统计的内容
上下文上限 实际模型与端点对应的限制
固定请求 Token System Prompt、策略、响应 Schema、消息包装、稳定工具定义
生产输入 Token 有代表性的高分位真实输入,而不是最短测试样本
检索与工具 Token RAG 片段、函数定义、工具结果、图片或文件元数据、对话历史
输出预留 完整有用回答、结构化输出,以及需要考虑的 reasoning/thinking 行为
安全余量 多语言膨胀、超长边界输入、Tokenizer 或请求结构变化

只有最后剩下的空间,才属于 Few-Shot Prompt 示例。

示例预算表

假设团队为某种请求结构批准了 8,000 Token 的内部工作预算。这个数字只是工程预算示例,不是任何模型的规格。

组成部分 Token
System Prompt 与 Schema 900
生产用户输入 1,700
检索与工具 1,200
输出预留 2,400
安全余量 600
可用于示例 1,200

如果每个完整输入输出示例需要 260 Token,四个示例约为 1,040 Token,可以放入预算;五个示例约为 1,300 Token,会超出预算。结论不是“四个最好”,而是“四个是当前预算最多能容纳的数量;如果一个或三个已经通过测试,就应该保留更少的版本”。

统计每个新增示例的增量成本

不要只单独计算示例文本。应该反复统计同一个完整请求:

T0 = 0 个示例时的完整请求
T1 = 1 个示例时的完整请求
T3 = 3 个示例时的完整请求
T5 = 5 个示例时的完整请求

增量_1 = T1 - T0
增量_3 = T3 - T1
增量_5 = T5 - T3

同时记录质量和 Token:

版本 输入 Token 任务通过率 格式通过率 边界错误 是否保留
Zero-shot 实测 实测 实测 记录错误 基线
1 个示例 实测 实测 实测 记录错误 有价值才保留
3 个示例 实测 实测 实测 记录错误 明显更好才保留
5 个示例 实测 实测 实测 记录错误 确有必要才保留

最终应该选择通过发布标准的最小版本。如果五个示例消耗更多上下文,却没有修复新的错误,就删除多余示例。

按覆盖范围选择示例,而不是追求数量

三个几乎重复的示例,通常不如三个教学目的不同的示例。

一个紧凑的示例集合可以覆盖:

  1. 标准场景:最常见的请求和准确的输出结构。
  2. 边界场景:空值、歧义、超长输入或临界分类。
  3. 对比场景:两个看起来相似、但应该产生不同输出的输入。

只有当第四或第五个示例代表了现有集合无法解决、且在生产中反复出现的错误时,才加入它。

增加示例前,先完成这句话:

这个示例存在的目的,是教模型如何 ________。

如果答案与另一个示例相同,就应该合并或删除。

在不丢失教学作用的情况下压缩示例

压缩的目标不是让示例变得晦涩,而是删除不负责“教行为”的 Token。

1. 删除重复指令

把共享规则统一放在示例区块之前。不要在每个示例里重复“只返回有效 JSON”或完整分类列表。

2. 缩短输入,但保留决策边界

保留真正改变答案的短语、字段或上下文,删除装饰性背景信息。

3. 直接展示生产输出格式

如果生产环境要求 JSON,就展示 JSON。不要浪费 Token 输出应用永远不会接受的解释段落。

4. 使用一致的标签和分隔符

稳定结构能减少无意变化。Anthropic 的指南也明确建议保持结构一致,并把 XML 风格标签作为区分示例的一种方式。

5. 删除模型不需要的解释

很多示例只需要正确输出,并不需要解释为什么正确。只有当推理方式本身就是任务要求时,才保留 rationale。

压缩前后对比

冗长版本:

示例 1
下面是一条客户支持消息。请分析这条消息,并从完整分类列表中确定正确类别。你必须返回一个 JSON 对象。

客户消息:同一个月度订阅被扣了两次费用,我希望退回重复扣款。

正确答案:正确类别是 billing_duplicate_charge,因为客户明确表示一个订阅发生了两次扣费。

压缩版本:

<example>
Input: 同一个月度订阅扣款两次,请退回重复扣款。
Output: {"category":"billing_duplicate_charge"}
</example>

压缩版本仍然保留了决策边界和生产输出结构,同时删除了重复策略与解释。

可直接测试的 Few-Shot Prompt 模板

使用下面的模板比较不同示例数量。每次只修改 <examples> 区块,其他请求保持一致。

请把客服消息分类为以下一个类别:
billing_duplicate_charge, billing_refund_pending, account_login, product_bug。

只返回 JSON:
{"category":"category_name"}

<examples>
<example>
Input: 同一个月度订阅扣款两次,请退回重复扣款。
Output: {"category":"billing_duplicate_charge"}
</example>

<example>
Input: 退款上周已经批准,但银行卡还没有收到。
Output: {"category":"billing_refund_pending"}
</example>

<example>
Input: 密码重置成功,但登录时新密码仍然被拒绝。
Output: {"category":"account_login"}
</example>
</examples>

Input: 仪表盘有数据,但导出按钮生成的 CSV 是空的。
Output:

建立四个版本:

使用计划上线的模型和端点运行每个版本。在 TokenTest 中,不要只相信本地文本估算,而要对比端点返回的 usage 证据。TokenTest 当前公开产品检查包括输入 Token 单调性、总 Token 一致性、输出 Token 合理性、缓存 Token 证据,以及端点支持时的 thinking/reasoning Token 证据。TokenTest 首页也明确说明测试时输入的 API Key 不会被存储。

如果需要更完整的流程,可以继续阅读如何建立一套 Token 感知的 Prompt 审查流程生产级 LLM 应用的上下文窗口规划开发者指南

在增加示例前保护输出空间

Few-Shot Prompt 示例与模型回答共享容量。不要让更大的示例区块悄悄压缩任务真正需要的输出。

先定义输出预留:

然后再把示例放入剩余空间。如果新增示例会占用受保护的输出空间,可以:

  1. 压缩现有示例,
  2. 用更强的示例替换较弱示例,
  3. 针对当前请求动态检索示例,
  4. 或者在端点和成本允许时提高已批准的请求预算。

中英文必须分别测试

不要假设英文示例集合与中文翻译会使用相同 Token。Tokenization 会受到模型、Tokenizer、标点、文字系统、JSON 转义和中英文混排影响。

每个本地化请求都应该单独统计和评估。为什么中文 Prompt 的 Token 预算可能和英文不同解释了为什么不能依赖一个固定换算倍数。

本地化也会改变示例质量。直译可能破坏原示例用于教学的歧义、语气或分类边界,因此需要同时重新验证 Token 和行为。

什么时候改用动态示例

静态 Few-Shot 区块简单,但每个请求都会重复发送同一组示例。以下情况更适合动态选择:

可以检索少量相关示例,但检索结果本身也必须纳入完整请求统计。动态选择能减少重复上下文,但错误检索也可能加入无关或相互矛盾的示例。

上线前检查清单

最终原则:每个示例都必须“支付租金”

当 Few-Shot Prompt 示例能够避免明确错误时,它们就有价值;当它们重复同一经验、保留无关文字,或挤占真实输入与有效输出时,它们就是浪费。

从零开始。用一个示例教格式。用少量多样化示例覆盖已经确认的失败模式。每次统计完整请求,并保留能够通过测试的最小集合。

开始一次 TokenTest 端点评测,在下一次 Prompt 修改进入生产环境前,对比 0、1、3、5 个示例的请求结构。

参考资料