【LangChain】示例选择器(Example Selectors)实战指南

🎬 博主简介:

前言
少样本提示(Few-shot Prompting)是提升大语言模型性能最有效的手段之一。通过给模型提供几个 “输入 - 输出” 示例,我们可以清晰地告诉模型应该做什么、怎么做、以什么格式输出,从而大幅减少模型 “胡说八道” 的概率,获得更稳定、更符合预期的结果。然而,当我们的示例库越来越大时,一个棘手的问题出现了:如果把所有示例都塞进提示词,会迅速耗尽模型的上下文窗口,导致 API 调用失败,过多的冗余示例会增加 token 成本,还可能混淆模型,反而降低输出质量,不同的查询需要不同的示例,一刀切的示例选择方式效率极低。这正是示例选择器(Example Selectors) 要解决的问题。它能根据给定的输入,从大量示例集中动态、智能地选择最相关的示例子集,在保证提示效果的同时,严格控制上下文长度和调用成本。本文将系统讲解 LangChain 中四大核心示例选择器的原理、适用场景,并提供完整的实战代码和源码解读。
一. 什么是示例选择器?
示例选择器是 LangChain 中专门用于优化少样本提示的核心组件。它的工作流程非常清晰:
- 接收用户的查询输入
- 根据预设的策略,从预定义的示例集中筛选出最相关的 N 个示例
- 将筛选后的示例格式化为模型可理解的消息格式
- 与用户查询一起构建最终的提示词
它为我们带来了四个核心价值:
- 控制上下文长度:避免因示例过多导致的上下文窗口溢出
- 提升提示质量:只保留与查询最相关的示例,减少冗余信息干扰
- 降低 API 成本:更少的 token 意味着更低的调用费用
- 提高响应准确性:精准的示例能更好地引导模型输出正确结果
在这里插入图片描述
二. 四大核心示例选择器原理解析
LangChain 内置了四种常用的示例选择器,分别适用于不同的业务场景。下面我们逐一拆解它们的核心原理和适用边界。
2.1 按长度选择(LengthBasedExampleSelector)
核心原理:根据用户输入文本的长度动态调整示例数量。输入越短,选择的示例越多;输入越长,选择的示例越少。
设计逻辑:当用户输入本身已经很长时,留给示例的 token 空间就很小了,此时应该减少示例数量以避免上下文溢出;反之,短输入可以搭配更多示例来提升效果。
适用场景:对上下文长度有严格限制、token 成本敏感的简单任务。
核心参数:
max_length:提示词的最大长度(默认按单词数统计)get_text_length:自定义文本长度计算函数

2.2 按语义相似性选择(SemanticSimilarityExampleSelector)
核心原理:将所有示例和用户查询都通过嵌入模型转换为高维向量,然后计算查询向量与所有示例向量之间的余弦相似度,选择相似度最高的 k 个示例。
关键依赖:
- 嵌入模型(Embeddings):将文本转换为语义向量
- 向量数据库(VectorStore):高效存储和检索向量
适用场景:大多数少样本提示任务,尤其是需要语义理解而非简单关键词匹配的场景。
核心参数:
k:最终返回的示例数量embeddings:嵌入模型实例vectorstore_cls:向量数据库类

2.3 按最大边际相关性选择(MaxMarginalRelevanceExampleSelector)
解决的问题:纯语义相似性选择有一个致命缺陷 —— 返回的结果往往高度同质化,信息冗余严重。例如,查询 “苹果的价格” 时,可能返回的都是关于红富士苹果价格的示例,而缺少其他品种的信息。
核心原理:MMR(Maximum Marginal Relevance)算法是一种重新排序技术,它在选择示例时同时兼顾相关性和多样性:
- 首先从向量库中获取
fetch_k个最相似的示例(广撒网) - 然后从这
fetch_k个示例中,迭代选出既与查询相关、又与已选示例差异最大的k个示例
适用场景:RAG(检索增强生成)、推荐系统、文档摘要等需要结果多样性的场景。
核心参数:
k:最终返回的示例数量fetch_k:初步筛选的候选示例数量(通常fetch_k > k)lambda_mult:控制相关性和多样性的权重(0~1,默认 0.5,值越大越偏向相关性)


2.4 按 Ngram 重叠选择(NGramOverlapExampleSelector)
核心原理:计算用户查询和每个示例之间的ngram 重叠度(连续 n 个词的匹配数量),然后按重叠度从高到低排序示例。
特点:
- 基于表面形式匹配,不理解语义
- 速度极快,无需嵌入模型和向量数据库
- 对同义词、一词多义等情况处理能力差
适用场景:关键词匹配、代码搜索、精确查询等不需要深度语义理解的场景。
核心参数:
threshold:重叠度阈值,控制是否排除示例-1.0:按重叠度排序,不排除任何示例0.0:排除与查询没有任何 ngram 重叠的示例>1.0:排除所有示例
n:ngram 的长度(默认 2)


三. 实战落地:从零实现四大示例选择器
3.1 环境准备
首先安装所需的依赖包:
pip install langchain langchain-openai langchain-chroma nltk langchain-community python-dotenv
然后配置 OpenAI API Key(推荐使用.env文件管理):
from dotenv import load_dotenv
load_dotenv() # 加载.env文件中的环境变量
3.2 按长度选择示例实战
我们以 “反义词生成” 任务为例,演示 LengthBasedExampleSelector 的使用:
from langchain_core.example_selectors import LengthBasedExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 1. 定义反义词示例集
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
# 2. 定义单个示例的格式化模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 3. 初始化长度示例选择器
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=25, # 提示词的最大长度(单词数)
)
# 4. 构建动态少样本提示模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector, # 使用选择器而非固定examples
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
# 测试1:短输入
print("=== 短输入测试 ===")
print(dynamic_prompt.invoke({"adjective": "big"}).to_string())
# 测试2:长输入
print("\n=== 长输入测试 ===")
long_input = "非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常 非常大"
print(dynamic_prompt.invoke({"adjective": long_input}).to_string())
源码解读:
max_length=25表示整个提示词(前缀 + 所有示例 + 后缀)的单词数不能超过 25- 默认的长度计算函数是按空格和换行符分割文本,统计单词数量
- 当输入过长时,选择器会自动从后往前截断示例,直到满足长度限制

3.3 按语义相似性选择示例实战
from langchain_chroma import Chroma
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
# 1. 复用之前的反义词示例集
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 2. 初始化语义相似示例选择器
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples=examples,
embeddings=OpenAIEmbeddings(model="text-embedding-3-small"), # 嵌入模型
vectorstore_cls=Chroma, # 使用Chroma内存向量数据库
k=1, # 只返回最相似的1个示例
)
# 3. 构建少样本提示模板
similar_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
# 测试:查询"worried"(担心的)
print("=== 语义相似性测试 ===")
print(similar_prompt.invoke({"adjective": "worried"}).to_string())
源码解读:
from_examples方法会自动将所有示例转换为向量,并存储在 Chroma 向量数据库中- 当调用
invoke时,选择器会先将查询转换为向量,然后在向量库中搜索最相似的 k 个示例 - 输出结果会显示最相似的示例是 “happy → sad”,因为它们都表示情绪状态,语义最接近


3.4 按最大边际相关性选择示例实战
from langchain_chroma import Chroma
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAIEmbeddings
examples = [
{"input": "happy", "output": "sad"},
{"input": "joyful", "output": "miserable"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
]
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 初始化MMR示例选择器
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
examples=examples,
embeddings=OpenAIEmbeddings(model="text-embedding-3-small"),
vectorstore_cls=Chroma,
k=2, # 最终返回2个示例
fetch_k=4, # 先获取4个最相似的候选
)
mmr_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
# 测试:查询"cheerful"(快乐的)
print("=== MMR测试 ===")
print(mmr_prompt.invoke({"adjective": "cheerful"}).to_string())
源码解读:
fetch_k=4表示先从向量库中获取 4 个最相似的示例- 然后 MMR 算法会从这 4 个示例中选出 2 个既相关又多样的示例
- 输出结果会包含 “happy → sad” 和 “joyful → miserable” 吗?不,MMR 会避免选择过于相似的示例,可能会返回 “happy → sad” 和 “energetic → lethargic”,这样既相关又有多样性

3.5 按 Ngram 重叠选择示例实战
from langchain_community.example_selectors import NGramOverlapExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 1. 定义翻译示例集
examples = [
{"input": "See Spot run.", "output": "看见Spot跑。"},
{"input": "My dog barks.", "output": "我的狗叫。"},
{"input": "Spot can run.", "output": "Spot可以跑。"},
]
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
# 2. 初始化NGram示例选择器(threshold=-1,不排除任何示例)
example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0,
)
# 3. 构建少样本提示模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的中文翻译",
suffix="Input: {sentence}\nOutput:",
input_variables=["sentence"],
)
# 测试:查询"Spot can run fast."
print("=== NGram重叠测试(threshold=-1) ===")
print(dynamic_prompt.invoke({"sentence": "Spot can run fast."}).to_string())
# 测试:threshold=0.0,排除无重叠的示例
example_selector.threshold = 0.0
print("\n=== NGram重叠测试(threshold=0) ===")
print(dynamic_prompt.invoke({"sentence": "Spot can run fast."}).to_string())
源码解读:
- 对于查询 “Spot can run fast.”,与示例 “Spot can run.” 的 2gram 重叠度最高(“Spot can” 和 “can run”)
- 当
threshold=0.0时,与查询没有任何 ngram 重叠的示例会被排除 - 输出结果会按重叠度从高到低排序示例


四. 核心考点与选型指南
4.1 四大选择器对比表
| 选择器类型 | 核心原理 | 依赖 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| Length | 输入文本长度 | 无 | 简单快速,无额外成本 | 不考虑语义,效果一般 | 严格控制 token,简单任务 |
| Similarity | 余弦相似度 | 嵌入模型 + 向量库 | 语义匹配,效果好 | 结果同质化,有嵌入成本 | 大多数少样本任务 |
| MMR | 相关性 + 多样性 | 嵌入模型 + 向量库 | 兼顾相关和多样,减少冗余 | 计算稍复杂 | RAG、推荐系统、文档摘要 |
| NGram | 连续词重叠 | nltk | 速度极快,无嵌入成本 | 表面匹配,不理解语义 | 关键词匹配、代码搜索 |
4.2 选型建议
- 优先选择 SemanticSimilarity:对于大多数需要语义理解的少样本任务,它的效果最好
- 需要多样性选 MMR:在 RAG、推荐等场景中,MMR 能有效避免结果同质化,提升答案的全面性
- 成本敏感选 Length/NGram:如果对 token 成本和响应速度要求极高,且任务不需要深度语义理解,可以选择这两种
- 精确匹配选 NGram:对于代码搜索、关键词查询等场景,NGram 的表面匹配效果反而更好
🍓 我是草莓熊 Lotso!若这篇技术干货帮你打通了学习中的卡点:
👀 【关注】跟我一起深耕技术领域,从基础到进阶,见证每一次成长
❤️ 【点赞】让优质内容被更多人看见,让知识传递更有力量
⭐ 【收藏】把核心知识点、实战技巧存好,需要时直接查、随时用
💬 【评论】分享你的经验或疑问(比如曾踩过的技术坑?),一起交流避坑
🗳️ 【投票】用你的选择助力社区内容方向,告诉大家哪个技术点最该重点拆解
技术之路难免有困惑,但同行的人会让前进更有方向~愿我们都能在自己专注的领域里,一步步靠近心中的技术目标!
结语:示例选择器是 LangChain 中优化少样本提示的关键组件,它解决了 “示例太多上下文溢出、示例太少效果不好” 的核心矛盾。在实际开发中,建议先从 SemanticSimilarity 开始尝试,然后根据业务需求逐步优化。如果发现返回的示例过于同质化,可以切换到 MMR;如果对成本和速度有严格要求,可以考虑 Length 或 NGram。未来,随着大模型上下文窗口的不断扩大,示例选择器的作用不会减弱,反而会更加重要。它将在复杂 Agent、多轮对话、长上下文 RAG 等场景中发挥越来越关键的作用,帮助我们构建更高效、更智能的 AI 应用。
✨把这些内容吃透超牛的!放松下吧✨ ʕ˘ᴥ˘ʔ づきらど
更多推荐


所有评论(0)