【AI大模型】LangChain框架:示例选择器与输出解析器携手,编织NLP高效精准之网

在这里插入图片描述



前言

    本文将聚焦于LangChain框架中的两大璀璨明珠——示例选择器输出解析器,深入探讨它们如何与Prompt精妙结合,共同编织出提升NLP任务效率与准确性的精密网络。示例选择器通过提供精心挑选的示例来增强Prompt,有效引导大型语言模型(LLM)生成更加符合预期的高质量输出。而输出解析器则负责将LLM产生的非结构化文本输出转换为结构化数据,为后续处理和分析提供便利。文章将详细介绍多种类型的示例选择器和输出解析器,并通过实际案例展示它们如何与Prompt协同工作,共同提升NLP任务的效率和准确性。


一、示例选择器

1.介绍及应用

    LangChain 的示例选择器是一个用于从一组示例中动态选择部分示例以构建提示(prompt)的重要组件。在LangChain中,有多种不同类型的示例选择器,包括自定义样例选择器、长度样例选择器、MMR样例选择器、n-gram重叠度样例选择器和相似度样例选择器。以下是这些示例选择器的简要介绍和格式清晰的回答:

1. 自定义样例选择器

  • 允许用户根据自己的业务逻辑和需求来定义样例的选择方式
  • 至少需要实现两个方法:add_example(用于添加新示例)和select_examples(基于输入变量返回一个样例列表)。
  • 用户可以通过继承BaseExampleSelector类并实现所需的方法来创建自定义样例选择器。

2. 长度样例选择器

  • 根据输入的长度来选择要使用的示例。对于较长的输入,它会选择较少的示例,而对于较短的输入,则会选择更多示例。
  • 这在构建提示时需要控制上下文窗口长度时特别有用。
  • 使用时,可以导入LengthBasedExampleSelector类,并传入示例列表来创建长度样例选择器。

3. MMR样例选择器

  • MMR(Maximum Marginal Relevance)是一种常用于信息检索和推荐系统的排序算法,也适用于样例选择。
  • MMR样例选择器通过计算示例与输入之间的相关性以及示例之间的不相似性来选择样例。
  • 具体的实现细节可能因库或框架而异,但通常用户需要指定计算相关性和不相似性的方法。

4. n-gram重叠度样例选择器

  • 基于输入和示例之间的n-gram重叠度来选择样例。
  • n-gram是一种用于表示文本中连续n个词或字符的序列的模型。
  • 通过计算输入和示例之间的n-gram重叠度,可以选择与输入内容最相关的样例。

5.相似度样例选择器

  • 基于输入和示例之间的相似度来选择样例。
  • 相似度可以使用各种方法来计算,如余弦相似度、Jaccard相似度等。
  • 选择与输入内容最相似的样例可以帮助语言模型更好地理解prompt,并给出更准确的回答。

2.自定义示例选择器

    示例选择器是一个接口或抽象类,定义了选择示例的通用方法。根据输入或上下文选择最合适的示例,以提高模型的性能或适应特定的应用场景。

ExampleSelector 必须实现两个方法:

  1. add_example 方法,接受一个示例并将其添加到 ExampleSelector
  2. select_examples 方法,接受输入变量(用于用户输入)并返回要在 few shot prompt 中使用的示例列表。

案例:AI点评姓名

from langchain.prompts import PromptTemplate
from langchain_community.llms import Tongyi

from langchain.prompts.example_selector.base import BaseExampleSelector
import numpy as np

names = [
  "张伟", "张静", "张磊", "张敏", "张涛",
  "王强", "王芳", "王刚", "王艳", "王超",
  "李娜", "李明", "李静", "李杰", "李浩",
  "刘波", "刘洋", "刘婷", "刘伟", "刘芳"
]
# 示例选择器
class ExampleSelector(BaseExampleSelector):
    def __init__(self, example):
        self.examples = example
    def add_example(self, example):
        self.examples.append(example)
    def select_examples(self, input_v):
        return list(filter(lambda e: e.startswith(input_v), names))
        # return np.random.choice(self.examples,size=5)

selector = ExampleSelector(names)
temp = "你是一个玄学大师,我给你一些姓名,你帮我做一些点评,这些姓名如下:{names}"
pt = PromptTemplate.from_template(temp)
prompt = pt.format(names=selector.select_examples("李"))
llm = Tongyi()
ret = llm.invoke(prompt)
print(ret)

在这里插入图片描述

3.基于长度的示例选择器

    这个示例选择器基于长度来选择要使用的示例,特别适用于当构建的提示可能超出上下文窗口长度的情况。它能智能调整:对于较长的输入,会选择较少的示例以避免超出长度限制;而对于较短的输入,则会选择更多的示例以提供更丰富的信息。

案例:对输入内容取反

from langchain.prompts import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector
from langchain_community.llms import Tongyi

# 这是一个虚构任务的许多示例,用于创建反义词。
examples = [
    {"input": "面包", "output": "馒头"},
    {"input": "冰棍", "output": "雪糕"},
    {"input": "面条", "output": "粉丝"},
    {"input": "粥", "output": "汤"},
    {"input": "可口可乐", "output": "百事可乐"},
]


example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)
example_selector = LengthBasedExampleSelector(
    # 这些是可供选择的示例。
    examples=examples,
    # 这是用于格式化示例的PromptTemplate。
    example_prompt=example_prompt,
    # 这是格式化示例的最大长度。
    # 长度由下面的get_text_length函数测量。
    max_length=25,
    # 这是用于获取字符串长度的函数,用于确定要包含的示例。
    # 如果未指定,则提供一个默认值。
    # get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ", x))
)
dynamic_prompt = FewShotPromptTemplate(
    # 我们提供一个ExampleSelector而不是示例。
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="给出每个输入的反义词",
    suffix="输入: {adjective}\n输出:",
    input_variables=["adjective"],
)

# 一个输入较小的示例,因此它选择所有示例。
# print(dynamic_prompt.format(adjective="big"))
prompt1 = dynamic_prompt.format(adjective="big")

# 一个输入较长的示例,因此它只选择一个示例。
long_string = "big and huge and massive and large and gigantic and tall and much much much much much bigger than everything else"
# print(dynamic_prompt.format(adjective=long_string))
prompt2 = dynamic_prompt.format(adjective=long_string)

# 您还可以将示例添加到示例选择器中。
new_example = {"input": "big", "output": "small"}
dynamic_prompt.example_selector.add_example(new_example)
# print(dynamic_prompt.format(adjective="enthusiastic"))
prompt3 = dynamic_prompt.format(adjective="enthusiastic")

llm = Tongyi()
# ret = llm.invoke(prompt1)
print('输入:big')
print(llm.invoke(prompt1))
print('---'*20)
print("输入:",long_string)
print(llm.invoke(prompt2))
print('---'*20)
print("输入:enthusiastic")
print(llm.invoke(prompt3))

在这里插入图片描述

4.基于最大边际相关性(MMR)的示例选择器

MaxMarginalRelevanceExampleSelector根据示例与输入之间的相似性以及多样性进行选择。 它通过找到与输入具有最大余弦相似度的嵌入示例,并在迭代中添加它们,同时对已选择示例的接近程度进行惩罚来实现这一目标。

  • MMR 是一种在信息检索中常用的方法,它的目标是在相关性和多样性之间找到一个平衡
  • MMR 会首先找出与输入最相似(即余弦相似度最大)的样本
  • 然后在迭代添加样本的过程中,对于与已选择样本过于接近(即相似度过高)的样本进行惩罚
  • MMR 既能确保选出的样本与输入高度相关,又能保证选出的样本之间有足够的多样性
  • 关注如何在相关性和多样性之间找到一个平衡

案例:得到输入的反义词

from langchain.prompts import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import MaxMarginalRelevanceExampleSelector
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.llms import Tongyi

# 假设已经有这么多的提示词示例组:
examples = [
    {"input": "高兴", "output": "悲伤"},
    {"input": "乐观", "output": "悲观"},
    {"input": "光明", "output": "黑暗"},
    {"input": "干燥", "output": "湿润"},
    {"input": "善良", "output": "邪恶"},
    {"input": "复杂", "output": "简单"},
    {"input": "安静", "output": "喧闹"}
]

# 构造提示词模板
example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="原词:{input}\n反义:{output}"
)

# 调用MMR
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
    # 传入示例组
    examples,
    # 使用阿里云的dashscope的嵌入来做相似性搜索
    DashScopeEmbeddings(),
    # 设置使用的向量数据库是什么
    FAISS,
    # 结果条数
    k=2,
)

# 使用小样本提示词模版来实现动态示例的调用
dynamic_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="给出每个输入词的反义词",
    suffix="原词:{word}\n反义:",
    input_variables=["word"]
)
prompt = dynamic_prompt.format(word="快乐")
# print(dynamic_prompt.format(word="快乐"))

llm = Tongyi()
ret = llm.invoke(prompt)
print(ret)

在这里插入图片描述

5.基于n-gram重叠的示例选择器

NGramOverlapExampleSelector 是一个根据ngram重叠得分选择和排序示例的工具。这个得分是一个介于0.0和1.0之间的浮点数(包含0.0和1.0)。此选择器允许用户设置一个阈值得分,得分小于或等于此阈值的示例将被排除。默认阈值设为-1.0,意味着不会排除任何示例,只会对它们进行重新排序。若将阈值设为0.0,则会排除与输入没有ngram重叠的示例。

6.综合案例

    假设我们正在构建一个基于LangChain的文本生成系统,该系统需要根据用户输入的关键词生成相关的故事或描述。 为了提高生成的准确性和相关性,我们决定使用示例选择器来动态选择最佳的示例,这些示例将被包含在Prompt中以引导模型进行生成。

- 1>示例选择器选择
    在LangChain中,有多种示例选择器可供选择,每种都有其特定的用途和优势。在这个案例中,我们假设选择了以下两种示例选择器:

  1. LengthBasedExampleSelector:基于输入的长度来选择示例。当输入较长时,选择较少的示例以避免Prompt过长;当输入较短时,选择更多的示例以提供更多的上下文。
  2. SemanticSimilarityExampleSelector:基于输入与示例之间的语义相似性来选择示例。这种方法能够确保选择的示例与输入高度相关,从而提高生成的准确性。

- 2>案例步骤

  1. 定义示例列表
    首先,我们需要定义一个包含多个示例的列表。每个示例都应该是一个包含输入和输出的字典
  2. 创建PromptTemplate
    接下来,我们创建一个PromptTemplate,该模板将用于格式化输入和输出示例,以构建最终的Prompt
  3. 创建示例选择器
    然后,我们创建之前提到的两种示例选择器。这里,为了简化案例,此处仅展示如何使用LengthBasedExampleSelector,还可以根据需要添加SemanticSimilarityExampleSelector
  4. 动态选择示例并构建Prompt
    最后,当用户输入关键词时,使用示例选择器动态选择示例,并使用PromptTemplate构建最终的Prompt。

    通过结合使用PromptTemplate和示例选择器(如LengthBasedExampleSelectorSemanticSimilarityExampleSelector),我们能够根据输入动态选择最佳的示例,并将它们包含在Prompt中以引导模型进行生成。这种方法能够提高生成的准确性和相关性,使生成的文本更加符合用户的期望。

- 3>代码实现

from langchain_core.prompts import PromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector
from langchain_community.llms import Tongyi


# 示例
examples = [
    {"input": "好天气", "output": "与好天气相关的故事或描述"},
    {"input": "坏天气", "output": "与坏天气相关的故事或描述"},
]

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

#
length_based_selector =LengthBasedExampleSelector(
    # 这些是可供选择的示例。
    examples=examples,
    # 这是用于格式化示例的PromptTemplate。
    example_prompt=prompt_template,
    # 这是格式化示例的最大长度。
    # 长度由下面的get_text_length函数测量。
    max_length=25,
    # 这是用于获取字符串长度的函数,用于确定要包含的示例。
    # 如果未指定,则提供一个默认值。
    # get_text_length: Callable[[str], int] = lambda x: len(re.split("\n| ", x))
)

user_input = "春天的天气特别好,我特别喜欢,写一个关于春天的文章"  # 假设用户输入的关键词
selected_examples = length_based_selector.select_examples(input_variables={"key":user_input})

prompt = prompt_template.format(
    input=user_input,
    output="\n".join([example["output"] for example in selected_examples])
)

# 将prompt传递给模型进行生成
print(prompt)
tongyi = Tongyi()
ret = tongyi.invoke(prompt)
print(ret)

在这里插入图片描述


二、输出解析器

1.介绍

输出解析器是一种专门用于结构化语言模型响应的类。其核心功能通过两个主要方法实现:

  1. 获取格式指令(get_format_instructions): 此方法返回一个字符串,详细说明了语言模型输出的期望格式。
  2. 解析(parse): 此方法接受一个字符串作为输入(该字符串假设为语言模型的响应),并将其解析成某种结构化形式。

此外,输出解析器还可以选择实现一个额外的方法:

  • 带提示解析(parse_with_prompt): 此方法除了接受语言模型的响应字符串外,还接受一个提示字符串。提示字符串主要用于在解析过程中提供额外信息,帮助解析器更好地结构化输出,尤其是在需要利用提示中的信息来修复或完善输出时。

综上所述,输出解析器的主要作用是帮助将语言模型的响应转换为更加结构化和易于处理的形式。

2.列表解析器

    列表解析器是输出解析器的一种,专门用于处理大型语言模型生成的列表数据。它能够将逗号或其他分隔符分隔的文本字符串解析为Python列表或其他数据结构,便于后续的程序处理和分析。通过简单的接口,用户可以轻松地将语言模型的输出转换为所需的数据结构。

from langchain_core.output_parsers import CommaSeparatedListOutputParser,NumberedListOutputParser
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate
from langchain_community.llms import Tongyi


# 1.定义输出解析器:列表解析器
output_parser = CommaSeparatedListOutputParser()
# 2.获取格式指令
format_instructions = output_parser.get_format_instructions()

# 3.创建并格式化提示
promptTemplate = PromptTemplate(
    template="列出五种{subject}。\n{format_instructions}",
    input_variables=["subject"],
    partial_variables={"format_instructions": format_instructions},
)

prompt=promptTemplate.format(subject="水果")
print(prompt)

# 4.调用模型并获取响应
tongyi = Tongyi()
ret = tongyi.invoke(prompt)
print(ret)
print(type(ret))

# 5.使用输出解析器解析模型响应
parsed_list = output_parser.parse(ret)
print(parsed_list)  # eg:输出: ['苹果', '香蕉', '橙子', '葡萄', '梨']

在这里插入图片描述

3.日期时间解析器

    输出解析器中的日期时间解析器是一种专门用于处理大型语言模型(LLMs)生成的日期和时间数据的工具。它的主要作用是将LLMs输出的文本字符串(如日期和时间的自然语言表达) 解析为 Python中的datetime对象或其他日期时间格式,以便于程序进行进一步的日期时间计算、比较或格式化等操作。

from langchain_community.llms import Tongyi
from langchain.prompts import PromptTemplate
from langchain.output_parsers.datetime import DatetimeOutputParser

# 1、定义输出解析器:日期时间解析器
output_parser = DatetimeOutputParser()
# 2、获取格式指令
format_instructions = output_parser.get_format_instructions()
print("获取格式指令:\n",format_instructions)


template = """请按下面要求回答问题:
{question}
{format_instructions}"""

# 3、定义模板
promptTemplate = PromptTemplate.from_template(
    template,
    partial_variables={"format_instructions": output_parser.get_format_instructions()},
)

# 4、生成prompt
prompt = promptTemplate.format(question="新中国成立的日期是什么?")
print(prompt)

# 5、调用大模型并获取响应
llm= Tongyi()
output = llm.invoke(prompt)
print(output)

# 6、使用输出解析器解析模型响应
# output_parser.parse(output)
print("使用输出解析器解析模型响应 ",output_parser.parse(output))

在这里插入图片描述

4.枚举解析器

    输出解析器中的枚举解析器是一种专门用于处理大型语言模型(LLMs)输出中预定义值集合的工具。枚举(Enum)是一种数据类型,它包含了一组命名的常量值,这些值在程序中是固定的,不能改变。 在LLMs的应用场景中,有时模型的输出应该是这组预定义值之一,以确保数据的准确性和一致性。这时,枚举解析器就派上了用场。

from langchain.output_parsers.enum import EnumOutputParser
from enum import Enum

# 定义一个枚举类Colors,用于列举颜色选项
class Colors(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

# 创建EnumOutputParser实例,指定Colors枚举作为解析目标
parser = EnumOutputParser(enum=Colors)

# 示例:解析字符串"red",预期输出Colors枚举中对应的RED成员
print(parser.parse("red"))
# -> <Colors.RED: 'red'>

# 包括空格也可以
print(parser.parse(" green"))
# -> <Colors.GREEN: 'green'>

# 包括\n\t也可以
print(parser.parse("blue\n\t"))
# -> <Colors.BLUE: 'blue'>

# 其他字符不可以
# print(parser.parse("blue?"))
# -> <Colors.BLUE: 'blue'>

在这里插入图片描述

5.Pydantic 解析器

    输出解析器中的 Pydantic解析器 是一种专门用于处理大型语言模型(LLMs)或其他数据源输出的工具,它利用Pydantic库的数据验证和解析能力来确保输出数据符合预期的数据模型。
    Pydantic 是一个基于Python类型注解的数据验证和设置管理工具,特别适合于构建复杂的数据模型并自动验证数据的有效性和完整性。

案例:讲个笑话

from typing import List

from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator

from langchain_community.llms import Tongyi


# 定义一个笑话模型,用于规范和解析语言模型生成的笑话
class Joke(BaseModel):
    joke: str = Field(description="设置笑话的问题部分",title="笑话")
    answer: str = Field(description="解答笑话的答案部分")


# 设置一个解析器 + 将指令注入到提示模板中。
# 初始化PydanticOutputParser解析器,用于将语言模型的输出解析为Joke模型
parser = PydanticOutputParser(pydantic_object=Joke)
# 打印解析器的格式化指令,用于指导语言模型生成符合格式的输出
print(parser.get_format_instructions())
print("="*50)

# 创建一个提示模板,用于格式化输入的问题,并注入解析器的格式化指令
promptTemplate = PromptTemplate(
    template="回答用户的问题:\n{format_instructions}\n{query}\n",
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)

# 使用提示模板格式化一个关于笑话的问题
prompt = promptTemplate.format(query="讲一个中文的笑话。")
print(prompt)
print("="*50)

# 使用通义大模型生成符合提示要求的笑话
llm = Tongyi()
ret = llm.invoke(prompt)
print(ret)

在这里插入图片描述

案例:订单案例

如下代码中,order_data验证Order模型失败时触发 ValidationError

from pydantic import BaseModel,Field,ValidationError
class Order(BaseModel):
    product_id: int = Field(...,gt=0)
    quantity: int = Field(...,gt=0,le=100)
    payment_method: str = Field(...,pattern=r'^(cash|credit_card|bank_transfer)$')

# 订单数据示例,用于验证Order模型
order_data = {
    "product_id": -123, #不符合规则,必须大于0
    "quantity": 50,
    "payment_method": "cash"
}

try:
    order = Order(**order_data)
    print(order)
except ValidationError as e:
    print(e.errors())

在这里插入图片描述


在这里插入图片描述

  • 34
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 41
    评论
评论 41
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

么凹猫'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值