AI Agent白手起家30: 动态示例选择器之根据长度选择 Few Shot 示例
纲要
- 静态示例与动态示例
- 静态示例:示例固定在模板中,无法随任务或上下文长度调整
- 动态示例:根据任务需求和上下文窗口动态选择最合适的示例子集
- 示例选择器核心概念
LengthBasedExampleSelector:基于长度动态选择示例,控制最终提示词的总长度- 工作原理:根据
max_length和已占用的提示词长度,自动截取示例列表
- 关键组件
examples:示例列表(输入-输出对)example_prompt:示例格式化模板FewShotPromptTemplate:组合前缀、示例和后缀的完整提示词模板
- 示例:Python 脚本,展示长短输入下示例数量的自适应变化
引言
在上一篇关于 Few Shot 提示词工程的文章中,我们介绍了如何通过静态示例让模型学会自定义规则。然而在实际应用中,大模型的上下文窗口是有限且宝贵的资源。如果示例过多或输入过长,就可能超出窗口限制,甚至引入与当前任务无关的噪声。
LangChain 提供了示例选择器(Example Selector)来解决这一问题,其中 LengthBasedExampleSelector 是最简单直接的一种:它根据最终提示词的长度,动态决定使用哪些示例以及使用多少示例,确保提示词始终控制在合理长度内。
静态示例 vs 动态示例
在传统 Few Shot 中,示例是“写死”在模板里的:无论输入什么任务,所有示例都会被完整地拼接到提示词中。这种方式存在两个问题:
- 浪费上下文窗口:大量示例可能占用过多 token,挤压实际任务的空间。
- 引入无关信息:示例组中包含与当前任务不相关的例子,反而干扰模型输出。
动态示例选择器则允许我们准备一个大而全的示例池,在运行时根据输入长度和上下文窗口,只挑选合适的示例子集注入提示词。
| 特性 | 静态示例 | 动态示例 |
|---|---|---|
| 示例调整 | 不可变 | 根据输入长度动态截取 |
| 上下文利用 | 可能浪费或溢出 | 精确控制在窗口内 |
| 实现复杂度 | 低 | 中(使用示例选择器) |
| 适用场景 | 示例少且固定 | 示例多、输入长度变化大 |
根据长度动态选择示例的工作原理
LengthBasedExampleSelector 的核心逻辑是:在格式化提示词时,先计算前缀(系统指令)和后缀(用户问题)的长度,然后用剩余的 max_length 空间,从示例列表中从头开始依次添加示例,直到下一个示例会导致总长度超出限制为止。
这样,较长的输入会自动压缩示例数量,避免超出上下文窗口。
其处理流程可用下图表示:
由于示例是按顺序添加的,因此将最重要、最通用的示例放在列表前面是一个最佳实践,可以保证它们在大部分情况下都会被选中。
示例代码
以下代码基于 LangChain 实现了一个反义词示例的 LengthBasedExampleSelector,并演示了输入较短和较长时,最终提示词中示例数量的动态变化。
首先安装依赖:
pip install langchain langchain-core
完整代码:
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_core.example_selectors import LengthBasedExampleSelector
# 1. 准备反义词示例池
examples = [
{"input": "happy", "output": "sad"},
{"input": "高兴", "output": "悲伤"},
{"input": "sunny", "output": "gloomy"},
{"input": "up", "output": "down"},
{"input": "大", "output": "小"},
]
# 2. 定义示例格式化模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="原词: {input}\n反义词: {output}"
)
# 3. 创建长度选择器
# max_length 控制整个提示词的最大字符数(用于演示,设得较小)
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=100, # 限制最终提示词不超过100个字符
)
# 4. 构建动态 Few Shot 提示词模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入词的反义词:",
suffix="原词: {input}\n反义词:",
input_variables=["input"],
)
# 5. 测试:输入较短时,可以容纳更多示例
short_input = "hot"
print("=== 短输入(hot)===")
print(dynamic_prompt.format(input=short_input))
print()
# 6. 测试:输入非常长时,示例会被自动压缩
long_input = "非常" * 30 # 构造一个很长的输入
print("=== 长输入(" + "非常"*30 + ")===")
print(dynamic_prompt.format(input=long_input))
运行后会观察到,短输入时,最终提示词中包含了多个示例(如 happy、高兴、sunny 等);而长输入时,由于用户问题本身就占用了大量长度,示例会被压缩到只剩一两个甚至完全没有,确保总长度不超过 max_length。
使用建议与注意事项
- 示例排序很重要:选择器从前往后挑选示例,因此将典型、通用的示例排在前面,能保证在截断时优先保留高质量示例。
- 合理设置
max_length:该值应略小于模型的实际上下文窗口长度(以字符或 token 计),为回答预留一定空间。若使用 token 分词器,可以自定义get_text_length函数以 token 数计算长度。 - 配合其他选择器:当仅按长度选择不够智能时,LangChain 还提供了
SemanticSimilarityExampleSelector等基于语义相似度的选择器,可以实现“选择与输入最相关的示例”,后续文章将详细介绍。
总结
动态示例选择器是 Few Shot 提示工程走向实用化的关键一环。LengthBasedExampleSelector 以其简单高效的机制,保证了提示词长度始终处于可控范围内,非常适合输入长度波动较大或示例池丰富的场景。
结合良好的示例排序策略,可以在不增加额外计算开销的前提下,显著提升大模型应用的稳定性和响应质量。
更多推荐

所有评论(0)