深入 LangChain示例选择器核心:详解 Length、Similarity、MMR 与 Ngram 示例选择策略

文章目录

    • [1. 概念](#1. 概念)
      • [1.1 什么是示例选择器?](#1.1 什么是示例选择器?)
      • [1.2 选择策略](#1.2 选择策略)
    • [2. Length 选择器 - 根据长度选择](#2. Length 选择器 - 根据长度选择)
      • [2.1 概念](#2.1 概念)
      • [2.2 通俗理解](#2.2 通俗理解)
      • [2.3 使用场景](#2.3 使用场景)
      • [2.4 代码示例](#2.4 代码示例)
      • [2.5 关键参数](#2.5 关键参数)
    • [3. Similarity 选择器 - 根据语义相似性选择](#3. Similarity 选择器 - 根据语义相似性选择)
      • [3.1 概念](#3.1 概念)
      • [3.2 通俗理解](#3.2 通俗理解)
      • [3.3 工作原理](#3.3 工作原理)
      • [3.4 使用场景](#3.4 使用场景)
      • [3.5 代码示例](#3.5 代码示例)
      • [3.6 关键参数](#3.6 关键参数)
      • [3.7 向量存储说明](#3.7 向量存储说明)
    • [4. MMR 选择器 - 最大边际相关性](#4. MMR 选择器 - 最大边际相关性)
      • [4.1 概念](#4.1 概念)
      • [4.2 通俗理解](#4.2 通俗理解)
      • [4.3 与 Similarity 的区别](#4.3 与 Similarity 的区别)
      • [4.4 工作原理](#4.4 工作原理)
      • [4.5 使用场景](#4.5 使用场景)
      • [4.6 代码示例](#4.6 代码示例)
      • [4.7 关键参数](#4.7 关键参数)
    • [5. Ngram 选择器 - N-gram 重叠](#5. Ngram 选择器 - N-gram 重叠)
      • [5.1 概念](#5.1 概念)
      • [5.2 什么是 N-gram?](#5.2 什么是 N-gram?)
      • [5.3 通俗理解](#5.3 通俗理解)
      • [5.4 与 Similarity 的区别](#5.4 与 Similarity 的区别)
      • [5.5 使用场景](#5.5 使用场景)
      • [5.6 代码示例](#5.6 代码示例)
      • [5.7 重叠度计算示例](#5.7 重叠度计算示例)
      • [5.8 关键参数](#5.8 关键参数)
    • [6. 四种选择器对比总结](#6. 四种选择器对比总结)
    • [7. 实际应用建议](#7. 实际应用建议)
      • [7.1 如何选择合适的选择器?](#7.1 如何选择合适的选择器?)
      • [7.2 组合使用](#7.2 组合使用)
      • [7.3 性能对比](#7.3 性能对比)
    • [8. 常见问题](#8. 常见问题)
      • [8.1 为什么需要示例选择器?](#8.1 为什么需要示例选择器?)
      • [8.2 如何调试选择器?](#8.2 如何调试选择器?)
      • [8.3 向量数据库存储在哪里?](#8.3 向量数据库存储在哪里?)
      • [8.4 如何使用纯内存模式?](#8.4 如何使用纯内存模式?)
    • [9. 完整示例对比](#9. 完整示例对比)
      • [9.1 相同输入,不同选择器的结果](#9.1 相同输入,不同选择器的结果)
    • [10. 关键要点](#10. 关键要点)
    • [12. 参考资料](#12. 参考资料)

1. 概念

1.1 什么是示例选择器?

一旦我们有了示例数据集,就需要考虑提示中应该有多少个示例。关键的权衡是,更多的示例通常会提高性能,但更大的提示会增加成本和延迟。超过某个阈值,太多示例可能会开始混淆模型。

找到正确数量的示例在很大程度上取决于模型、任务、示例的质量以及成本和延迟限制。有趣的是,模型越好,它需要精准的示例就越少。但其实,最佳的方法是使用不同数量的示例进行一些实验。

若此时我们有【大量】的示例数据集。对于大模型来说,就没必要全部使用与参考。我们需要有一种方法可以根据给定的输入,从数据集中选择示例。

在 LangChain 中,示例选择器就可以帮我们从一组【示例的集合】中根据具体策略选择正确的【示例子集】构建少样本提示。

1.2 选择策略

LangChain 提供了四种主要的示例选择策略:

  1. Length(长度):根据指定【长度】内可以容纳的数量选择示例
  2. Similarity(语义相似性):使用输入和示例之间的【语义相似性】来决定选择哪些示例
  3. MMR(最大边际相关性):使用输入和示例之间的【最大边际相关性】来决定要选择哪些示例
  4. Ngram(N-gram 重叠):使用输入和示例之间的【ngram 重叠】来决定要选择哪些示例

这些其实都是自然语言处理(NLP)里的相似性衡量问题。


2. Length 选择器 - 根据长度选择

2.1 概念

LengthBasedExampleSelector 根据指定的【长度限制】来选择示例。它会计算每个示例的长度,然后选择能够在长度限制内容纳的示例。

2.2 通俗理解

就像打包行李箱:

  • 你有一个固定大小的行李箱(max_length)
  • 你有很多衣服要带(examples)
  • 你会按顺序往里装,直到装不下为止

2.3 使用场景

  • 当你的 Prompt 有长度限制时(比如 API 限制)
  • 当你想控制成本时(更短的 Prompt = 更便宜)
  • 当你的示例长度差异很大时

2.4 代码示例

python 复制代码
# 根据长度决定的示例选择器
from langchain_core.example_selectors import LengthBasedExampleSelector
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate

# 一些例子方便测试
examples = [
    {"input": "1$2", "output": "0.5"},
    {"input": "2$3", "output": "0.6666666666666666"},
    {"input": "3$6", "output": "0.5"},
]

# 提示词模版
template_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)

# 创建一个根据长度选择示例的选择器
example_selector = LengthBasedExampleSelector(
    examples=examples,
    example_prompt=template_prompt,
    max_length=5,  # 最大长度为5个单词
    # 默认函数
    # get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ",x))
    # 也可自定义
)

# 少样本提示模版
few_shot_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=template_prompt,
    input_variables=["input"],
    prefix="请根据以下示例回答问题:",
    suffix="问题:{input}\n回答:",
)

# 调用
result = few_shot_prompt.invoke({"input": "4$8"})
print(result.text)

输出示例

复制代码
请根据以下示例回答问题:

Input: 1$2
Output: 0.5

问题:4$8
回答:

2.5 关键参数

  • examples:示例列表
  • example_prompt:示例的格式模板
  • max_length:最大长度限制(单词数)
  • get_text_length:自定义长度计算函数(可选)

3. Similarity 选择器 - 根据语义相似性选择

3.1 概念

SemanticSimilarityExampleSelector 使用【语义相似性】来选择示例。它会将输入和所有示例转换为向量(embeddings),然后计算相似度,选择最相似的示例。

3.2 通俗理解

就像找相似的朋友:

  • 你说"我喜欢小狗"
  • 系统会找到"小猫"、"小兔子"这些相似的动物示例
  • 而不会选择"苹果"、"汽车"这些不相关的

3.3 工作原理

  1. 文本 → 向量:使用嵌入模型将文本转换为向量

    复制代码
    "小狗" → [0.23, -0.45, 0.67, ...] (768维向量)
  2. 存储到向量数据库:使用 Chroma 等向量数据库存储

    复制代码
    Chroma 保存:文本 + 向量 + 元数据
  3. 查询时计算相似度

    • 输入"小象" → 转成向量
    • 在 Chroma 中搜索最相似的向量
    • 返回最相似的 k 个示例

3.4 使用场景

  • 当你的示例有明确的语义含义时
  • 当你想根据输入的含义选择相关示例时
  • 当你有大量示例需要智能筛选时

3.5 代码示例

python 复制代码
# 基于语义相似性的示例选择器
from langchain_core.example_selectors import SemanticSimilarityExampleSelector
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_chroma import Chroma
from langchain_google_genai import GoogleGenerativeAIEmbeddings

# 制造例子基于语义相似性的
examples = [
    {"input": "小狗", "output": "狗小"},
    {"input": "小猫", "output": "猫小"},
    {"input": "苹果", "output": "果果"},
    {"input": "橙子", "output": "子子"},
    {"input": "巧克力", "output": "巧克力"},
]

# 模版
template_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)

# 创建一个根据语义相似性选择示例的选择器
semantic_selector = SemanticSimilarityExampleSelector.from_examples(
    # 示例列表
    examples,
    # 谷歌嵌入模型
    GoogleGenerativeAIEmbeddings(model="gemini-embedding-001"),
    # 向量存储
    Chroma,
    k=2,  # 生成示例个数
)

# 实例化模版few_shot_prompt
few_shot_prompt = FewShotPromptTemplate(
    example_selector=semantic_selector,
    example_prompt=template_prompt,
    input_variables=["input"],
    prefix="请根据以下示例回答问题:",
    suffix="问题:{input}\n回答:",
)

# 调用 - 输入"小象"会选择"小狗"、"小猫"这些相似的动物
result = few_shot_prompt.invoke({"input": "小象"})
print(result.text)

输出示例

复制代码
请根据以下示例回答问题:

Input: 小狗
Output: 狗小

Input: 小猫
Output: 猫小

问题:小象
回答:

3.6 关键参数

  • examples:示例列表
  • embeddings:嵌入模型(如 GoogleGenerativeAIEmbeddings)
  • vectorstore:向量存储(如 Chroma)
  • k:返回的示例数量

3.7 向量存储说明

Chroma 是一个向量数据库,默认情况下:

  • 存储位置 :本地磁盘(会在当前目录创建 chroma 文件夹)
  • 也支持内存模式:可以配置为纯内存存储

好处

  • 首次运行:计算向量并保存
  • 后续运行:直接读取,速度更快

4. MMR 选择器 - 最大边际相关性

4.1 概念

MaxMarginalRelevanceExampleSelector 使用【最大边际相关性】来选择示例。它在相似性和多样性之间取得平衡。

4.2 通俗理解

就像组建一个多元化的团队:

  • 你想找"开心"相关的词
  • 普通相似性选择器会给你:快乐、高兴、喜悦(都很相似)
  • MMR 会给你:快乐(相似)、悲伤(对比)、苹果(多样性)

目的:避免选择过于重复的示例,增加示例的多样性。

4.3 与 Similarity 的区别

特性 Similarity 选择器 MMR 选择器
选择标准 只看相似度 相似度 + 多样性
示例特点 可能都很相似 相似但有多样性
使用场景 需要高度相关的示例 需要相关但不重复的示例
示例 快乐、高兴、喜悦 快乐、悲伤、苹果

4.4 工作原理

  1. 先获取相似的fetch_k=5 先获取 5 个最相似的示例
  2. 再选择多样的k=3 从这 5 个中选择多样性高的 3 个
  3. 平衡算法:MMR 算法确保既相关又不重复

4.5 使用场景

  • 当你的示例中有很多相似的内容时
  • 当你想避免示例过于单一时
  • 当你想让 LLM 看到更全面的示例时

4.6 代码示例

python 复制代码
# 基于mmr的示例选择器(最大边际相关性)
from langchain_core.example_selectors import MaxMarginalRelevanceExampleSelector
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_chroma import Chroma
from langchain_google_genai import GoogleGenerativeAIEmbeddings

# 制造例子 - 注意有很多相似的情感词
examples = [
    {"input": "快乐", "output": "开心"},
    {"input": "高兴", "output": "愉快"},
    {"input": "喜悦", "output": "欢乐"},
    {"input": "悲伤", "output": "难过"},
    {"input": "伤心", "output": "痛苦"},
    {"input": "苹果", "output": "水果"},
    {"input": "香蕉", "output": "水果"},
    {"input": "汽车", "output": "交通工具"},
]

# 模版
template_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)

# 创建一个根据基于语义相似性为基础的mmr选择示例的选择器
semantic_selector = MaxMarginalRelevanceExampleSelector.from_examples(
    # 示例列表
    examples,
    # 谷歌嵌入模型
    GoogleGenerativeAIEmbeddings(model="gemini-embedding-001"),
    # 本地基于内存的向量存储
    Chroma,
    k=3,  # 最终返回3个示例
    fetch_k=5,  # 先获取5个最相似的,再从中选择多样性高的3个
)

# 实例化模版few_shot_prompt
few_shot_prompt = FewShotPromptTemplate(
    example_selector=semantic_selector,
    example_prompt=template_prompt,
    input_variables=["input"],
    prefix="请根据以下示例回答问题:",
    suffix="问题:{input}\n回答:",
)

# 调用 - 输入"开心"会选择相似但多样的示例
result = few_shot_prompt.invoke({"input": "开心"})
print(result.text)

输出示例

复制代码
请根据以下示例回答问题:

Input: 快乐
Output: 开心

Input: 伤心
Output: 痛苦

Input: 香蕉
Output: 水果

问题:开心
回答:

分析

  • 选择了"快乐"(最相似)
  • 选择了"伤心"(情感词但相反,增加多样性)
  • 选择了"香蕉"(完全不同类别,增加多样性)

4.7 关键参数

  • examples:示例列表
  • embeddings:嵌入模型
  • vectorstore:向量存储
  • k:最终返回的示例数量
  • fetch_k:先获取的候选示例数量(应该 > k)

5. Ngram 选择器 - N-gram 重叠

5.1 概念

NGramOverlapExampleSelector 使用【N-gram 重叠】来选择示例。它计算输入和示例之间的 n-gram 重叠度,选择重叠度最高的示例。

5.2 什么是 N-gram?

N-gram 是连续的 n 个字符或词的序列:

示例:"我很喜欢苹果"

  • 1-gram(unigram):我、很、喜、欢、苹、果
  • 2-gram(bigram):我很、很喜、喜欢、欢苹、苹果
  • 3-gram(trigram):我很喜、很喜欢、喜欢苹、欢苹果

5.3 通俗理解

就像找共同话题:

  • 你说"我很喜欢橙子"
  • 系统会找包含"喜欢"的示例:"喜欢苹果"、"喜欢香蕉"
  • 因为它们有共同的 n-gram:"喜欢"

5.4 与 Similarity 的区别

特性 Similarity(语义相似性) Ngram(字符重叠)
匹配方式 理解语义含义 字符串匹配
技术 向量嵌入 + 余弦相似度 字符串重叠计算
示例 "快乐" 匹配 "高兴" "喜欢苹果" 匹配 "喜欢香蕉"
优点 理解同义词 速度快,不需要模型
缺点 需要嵌入模型 不理解语义

5.5 使用场景

  • 当你的示例有明显的关键词重叠时
  • 当你不需要深度语义理解时
  • 当你想要快速匹配时(不需要嵌入模型)

5.6 代码示例

python 复制代码
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_community.example_selectors import NGramOverlapExampleSelector

# 使用简单的中文词组示例,便于观察 n-gram 重叠选择效果
examples = [
    {"input": "快乐", "output": "开心"},
    {"input": "悲伤", "output": "难过"},
    {"input": "愤怒", "output": "生气"},
    {"input": "i like apple", "output": "喜欢水果"},
    {"input": "喜欢吃香蕉", "output": "喜欢水果"},
    {"input": "喜欢汽车", "output": "喜欢交通工具"},
]

example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="问题:{input}\n回答:{output}",
)

# NGramOverlapExampleSelector 根据 n-gram 重叠度选择和排序示例
selector = NGramOverlapExampleSelector(
    examples=examples,
    example_prompt=example_prompt,
    threshold=0.0,  # 只返回有 n-gram 重叠的示例
)

few_shot_prompt = FewShotPromptTemplate(
    example_selector=selector,
    example_prompt=example_prompt,
    input_variables=["input"],
    prefix="请根据以下示例回答问题:\n",
    suffix="\n问题:{input}\n回答:",
)

# 测试:输入"i like"会选择包含"i like"的示例
result = few_shot_prompt.invoke({"input": "i like"})
print(result.text)

输出示例

复制代码
请根据以下示例回答问题:

问题:i like apple
回答:喜欢水果

问题:i like
回答:

分析

  • 输入"i like"与"i like apple"有最高的 n-gram 重叠
  • 所以选择了这个示例

5.7 重叠度计算示例

输入 :"喜欢橙子"
2-gram:喜欢、欢橙、橙子

示例1 :"喜欢苹果"
2-gram :喜欢、欢苹、苹果
重叠 :喜欢(1个)
重叠度:1/5 = 0.2

示例2 :"喜欢吃香蕉"
2-gram :喜欢、欢吃、吃香、香蕉
重叠 :喜欢(1个)
重叠度:1/6 = 0.17

示例3 :"快乐"
2-gram :快乐
重叠 :0个
重叠度:0

结果:选择"喜欢苹果"和"喜欢吃香蕉"

5.8 关键参数

  • examples:示例列表
  • example_prompt:示例格式模板
  • threshold:重叠度阈值
    • -1.0:返回所有示例(按重叠度排序)
    • 0.0:只返回有重叠的示例
    • 1.0:只返回完全匹配的示例

6. 四种选择器对比总结

选择器 选择依据 优点 缺点 使用场景
Length 长度限制 简单、可控成本 不考虑相关性 有长度限制时
Similarity 语义相似度 理解语义、智能 需要嵌入模型 需要语义匹配时
MMR 相似度+多样性 避免重复、全面 计算复杂 需要多样化示例时
Ngram 字符重叠 快速、无需模型 不理解语义 有明显关键词时

7. 实际应用建议

7.1 如何选择合适的选择器?

  1. 有长度限制 → 使用 Length
  2. 需要语义理解 → 使用 Similarity
  3. 避免示例重复 → 使用 MMR
  4. 关键词匹配 → 使用 Ngram

7.2 组合使用

你也可以组合使用多个选择器:

python 复制代码
# 先用 Similarity 选出相关的
# 再用 Length 控制长度
# 最后用 MMR 增加多样性

7.3 性能对比

选择器 速度 准确性 成本
Length ⭐⭐⭐⭐⭐ ⭐⭐ 免费
Similarity ⭐⭐⭐ ⭐⭐⭐⭐ 需要嵌入模型
MMR ⭐⭐ ⭐⭐⭐⭐⭐ 需要嵌入模型
Ngram ⭐⭐⭐⭐ ⭐⭐⭐ 免费

8. 常见问题

8.1 为什么需要示例选择器?

当你有大量示例时:

  • 全部使用 → 成本高、延迟大、可能混淆模型
  • 手动选择 → 不智能、不灵活
  • 使用选择器 → 自动、智能、高效

8.2 如何调试选择器?

python 复制代码
# 查看选择了哪些示例
selected = selector.select_examples({"input": "测试输入"})
for ex in selected:
    print(f"{ex['input']} -> {ex['output']}")

8.3 向量数据库存储在哪里?

默认情况下,Chroma 会在当前目录创建 chroma 文件夹(也可基于内存创建的,上面写的例子就是被当做内存的了):

复制代码
d:\trae开发\langchain-learn\示例选择器\
├── chroma/  ← 自动创建的向量数据库
├── length.py
├── Similarity.py
├── MMR.py
└── ngram.py

8.4 如何使用纯内存模式?

python 复制代码
semantic_selector = SemanticSimilarityExampleSelector.from_examples(
    examples,
    embeddings,
    Chroma,
    k=2,
    vectorstore_kwargs={
        "persist_directory": None  # None 表示纯内存
    }
)

9. 完整示例对比

9.1 相同输入,不同选择器的结果

输入:"我喜欢水果"

Length 选择器

复制代码
选择前 2 个示例(不管相关性)

Similarity 选择器

复制代码
选择:苹果、橙子(水果相关)

MMR 选择器

复制代码
选择:苹果(水果)、汽车(多样性)

Ngram 选择器

复制代码
选择:喜欢苹果、喜欢香蕉(包含"喜欢")

10. 关键要点

  1. 示例选择器的核心作用:从大量示例中智能选择最合适的子集
  2. 四种策略各有特点:根据实际需求选择
  3. Similarity 和 MMR 需要嵌入模型:会产生额外成本
  4. Length 和 Ngram 不需要模型:速度快、免费
  5. MMR 是 Similarity 的升级版:增加了多样性考虑
  6. 实际应用中可以组合使用:发挥各自优势

12. 参考资料

相关推荐
qq_5470261793 小时前
LangChain 输出解析器(OutputParser)
langchain
JaydenAI3 小时前
[RAG在LangChain中的实现]根据数据格式选择文档加载器和文本分割器
python·langchain·ai编程
java资料站1 天前
LangChain 中文入门教程
langchain
QWsin1 天前
【LangGraph Server】 LangGraph Server是什么?
人工智能·langchain·langgraph·langsmith
米小虾1 天前
从对话到行动:AI Agent 架构演进与工程实践指南
人工智能·langchain·agent
Csvn1 天前
🌟 LangChain 30 天保姆级教程 · Day 4|用 LLMChain 把 Prompt 和 LLM 打包成可复用组件!
langchain
coderlin_1 天前
langchain 基础
microsoft·langchain
QC·Rex1 天前
国产大模型应用实践:从 0 到 1 搭建企业级 AI 助手
人工智能·langchain·大语言模型·rag·企业应用·ai 助手
轻舟行71 天前
langchain从入门到入土 (一)langchain的历程及应用场景
人工智能·python·langchain