纲要
- 动态示例选择进阶:从长度到语义
- 核心概念
SemanticSimilarityExampleSelector:基于语义相似度从示例池中选择与输入最相关的示例- 向量嵌入(Embedding)与向量数据库(如
Chroma) - 余弦相似度:衡量向量空间距离的常用算法
- 关键流程:准备示例 → 定义嵌入模型和向量数据库 → 创建选择器 → 组合 Few Shot 提示词模板 → 调用 LLM
- 涉及组件:
langchain_core.example_selectors、langchain_community.embeddings、langchain_community.vectorstores - 代码演示:完整可运行的 Python 脚本,使用
FakeEmbeddings模拟嵌入过程,无需外部 API Key
引言
在上一篇文章中,我们介绍了根据长度动态截取示例的 LengthBasedExampleSelector,它虽然能够控制上下文窗口的大小,但选择逻辑过于机械:只是从列表头部开始依次添加示例,完全不考虑示例与当前输入的相关性。如果示例池里混杂了中文和英文、天气和情绪等多种场景,长度选择器很可能给出与当前任务毫不相干的例子。
LangChain 提供了另一种更智能的选择器------SemanticSimilarityExampleSelector,它利用文本嵌入技术,将输入和示例都映射到向量空间,然后通过计算余弦相似度,从示例池中选出语义最接近的 Top-K 个示例。这种方式可以轻松实现跨语言、跨领域的精准匹配,让 Few Shot 提示真正"看懂"输入的内容。
工作原理
其本质是检索增强生成(RAG)在提示词构建阶段的应用,整体流程如下:
#mermaid-svg-TCsjSKKM1Ak4xioH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TCsjSKKM1Ak4xioH .error-icon{fill:#552222;}#mermaid-svg-TCsjSKKM1Ak4xioH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TCsjSKKM1Ak4xioH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TCsjSKKM1Ak4xioH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TCsjSKKM1Ak4xioH .marker.cross{stroke:#333333;}#mermaid-svg-TCsjSKKM1Ak4xioH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TCsjSKKM1Ak4xioH p{margin:0;}#mermaid-svg-TCsjSKKM1Ak4xioH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster-label text{fill:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster-label span{color:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster-label span p{background-color:transparent;}#mermaid-svg-TCsjSKKM1Ak4xioH .label text,#mermaid-svg-TCsjSKKM1Ak4xioH span{fill:#333;color:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH .node rect,#mermaid-svg-TCsjSKKM1Ak4xioH .node circle,#mermaid-svg-TCsjSKKM1Ak4xioH .node ellipse,#mermaid-svg-TCsjSKKM1Ak4xioH .node polygon,#mermaid-svg-TCsjSKKM1Ak4xioH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TCsjSKKM1Ak4xioH .rough-node .label text,#mermaid-svg-TCsjSKKM1Ak4xioH .node .label text,#mermaid-svg-TCsjSKKM1Ak4xioH .image-shape .label,#mermaid-svg-TCsjSKKM1Ak4xioH .icon-shape .label{text-anchor:middle;}#mermaid-svg-TCsjSKKM1Ak4xioH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TCsjSKKM1Ak4xioH .rough-node .label,#mermaid-svg-TCsjSKKM1Ak4xioH .node .label,#mermaid-svg-TCsjSKKM1Ak4xioH .image-shape .label,#mermaid-svg-TCsjSKKM1Ak4xioH .icon-shape .label{text-align:center;}#mermaid-svg-TCsjSKKM1Ak4xioH .node.clickable{cursor:pointer;}#mermaid-svg-TCsjSKKM1Ak4xioH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TCsjSKKM1Ak4xioH .arrowheadPath{fill:#333333;}#mermaid-svg-TCsjSKKM1Ak4xioH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TCsjSKKM1Ak4xioH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TCsjSKKM1Ak4xioH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TCsjSKKM1Ak4xioH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TCsjSKKM1Ak4xioH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TCsjSKKM1Ak4xioH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster text{fill:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH .cluster span{color:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TCsjSKKM1Ak4xioH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TCsjSKKM1Ak4xioH rect.text{fill:none;stroke-width:0;}#mermaid-svg-TCsjSKKM1Ak4xioH .icon-shape,#mermaid-svg-TCsjSKKM1Ak4xioH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TCsjSKKM1Ak4xioH .icon-shape p,#mermaid-svg-TCsjSKKM1Ak4xioH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TCsjSKKM1Ak4xioH .icon-shape .label rect,#mermaid-svg-TCsjSKKM1Ak4xioH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TCsjSKKM1Ak4xioH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TCsjSKKM1Ak4xioH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TCsjSKKM1Ak4xioH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 示例池
向量化并存入Chroma
用户输入
向量化
在Chroma中做相似度搜索
返回Top-K最相似示例
组合进Few Shot提示词模板
- 向量化:使用嵌入模型(Embedding Model)将文本转换为高维向量。
- 存储:将示例向量存入向量数据库(本文使用轻量级的 Chroma)。
- 查询:将用户输入也向量化,通过余弦相似度计算与各示例向量的距离,返回距离最近(即语义最相似)的 K 个示例。
完整可运行代码
为了让你无需任何外部 API Key 即可运行,本示例使用 FakeEmbeddings 模拟向量嵌入。实际项目中只需替换为真实的嵌入模型(如 OpenAI 或 HuggingFace 模型),代码结构完全一致。
首先安装依赖:
bash
pip install langchain langchain-core langchain-community chromadb
核心代码:
python
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_community.embeddings.fake import FakeEmbeddings
from langchain_community.vectorstores import Chroma
# 1. 准备示例池(反义词任务)
examples = [
{"input": "happy", "output": "sad"},
{"input": "高兴", "output": "悲伤"},
{"input": "sunny", "output": "gloomy"},
{"input": "晴朗", "output": "阴沉"},
{"input": "big", "output": "small"},
]
# 2. 定义示例格式化模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="原词: {input}\n反义词: {output}"
)
# 3. 创建语义相似度示例选择器
# 使用 FakeEmbeddings 模拟嵌入,size 参数指定向量维度
embedding_model = FakeEmbeddings(size=128)
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples=examples,
embeddings=embedding_model,
vectorstore_cls=Chroma, # 使用 Chroma 作为向量数据库
k=1, # 每次选择最相似的 1 个示例
)
# 4. 构建动态 Few Shot 提示词模板
dynamic_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入词的反义词:",
suffix="原词: {input}\n反义词:",
input_variables=["input"],
)
# 5. 测试:输入英文情绪词,期望选中情绪相关示例
print("=== 输入: worried ===")
print(dynamic_prompt.format(input="worried"))
print()
# 6. 测试:输入中文天气词,期望选中天气相关示例
print("=== 输入: 晴朗 ===")
print(dynamic_prompt.format(input="晴朗"))
运行后可以看到,worried 会匹配到情绪相关的示例(如 happy/sad),晴朗 则匹配到天气相关的示例(如 sunny/gloomy 或 晴朗/阴沉)。注意 FakeEmbeddings 使用随机向量,因此结果可能略有随机性;若换成真实嵌入模型,匹配会非常精准。
与长度选择器的对比
| 特性 | 长度选择器 (LengthBasedExampleSelector) |
语义选择器 (SemanticSimilarityExampleSelector) |
|---|---|---|
| 选择依据 | 示例字符数 + 顺序 | 输入与示例的语义相似度 |
| 能否跨语言 | 不支持 | 支持(嵌入模型通常跨语言) |
| 计算开销 | 极低 | 较高(需向量化和相似度搜索) |
| 适用场景 | 示例少且顺序重要 | 示例多、任务多变、要求高相关性 |
使用建议
- 嵌入模型选型 :对中文任务推荐使用
text-embedding-ada-002(OpenAI)或开源的BAAI/bge-large-zh,它们对中英文混合场景支持良好。 - Top-K 设置 :
k值一般设为 1~3,过多示例可能引入噪声。可根据实际效果调整。 - 向量数据库:Chroma 适合原型开发和小规模数据;生产环境可考虑 FAISS、Pinecone、Milvus 等。
- 示例池维护:定期更新示例池并重新生成向量,以保持与最新任务的语义一致性。
总结
SemanticSimilarityExampleSelector 将检索增强生成(RAG)的向量检索思路应用于提示词工程,让 Few Shot 示例的选择从"机械截断"进化为"智能检索"。
它充分利用了嵌入模型捕捉语义的能力,无论输入是中文还是英文,都能从庞大的示例池中精准捞出最有参考价值的样例,大幅提升 Few Shot 的命中率和回答质量。