LangChain学习二:提示-实战(上半部分)
自定义示例选择器它的英文名叫做few shot examples:就像我们教小朋友一样,比如教小朋友分类水果,先给他演示一下水果怎么分类的,红色的放哪一个框框,白色的放哪一个框框,然后在给它一个新的水果,小朋友根据你教的示范,就会自己去分类了
具体在上一节的2.5有介绍
具体步骤如下
from langchain.prompts.example_selector.base import BaseExampleSelector
from typing import Dict, List
import numpy as np
class CustomExampleSelector(BaseExampleSelector):
def __init__(self, examples: List[Dict[str, str]]):
self.examples = examples
def add_example(self, example: Dict[str, str]) -> None:
"""为密钥添加要存储的新示例。"""
self.examples.append(example)
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""根据输入选择要使用的示例。 你可以在这里写你自己的算法,我这里就表示随机从examples里拿两个示例,replace表示不会重复"""
return np.random.choice(self.examples, size=2, replace=False)
examples = [
{"foo": "1"},
{"foo": "2"},
{"foo": "3"}
]
# 初始化示例选择器。
example_selector = CustomExampleSelector(examples)
#选择示例
example_selector.select_examples({"foo": "foo"})
# -> array([{'foo': '2'}, {'foo': '3'}], dtype=object)
# 将新示例添加到示例集
example_selector.add_example({"foo": "4"})
print(f"======查看现在有哪些示例\n{example_selector.examples}\n")
# -> [{'foo': '1'}, {'foo': '2'}, {'foo': '3'}, {'foo': '4'}]
# 选择示例
llm_example=example_selector.select_examples({"foo": "foo"})
print(f"======选择示例\n{llm_example}\n")
总长度是由max_length控制的,如果我们输入的长一些,就会少从examples 拿一些,输入短,则反之
from langchain import PromptTemplate, FewShotPromptTemplate
# 首先,创建少数快照示例的列表。
from langchain.prompts import LengthBasedExampleSelector
examples = [
{"word": "开心", "antonym": "悲伤"},
{"word": "高", "antonym": "低"},
]
# 接下来,我们指定模板来格式化我们提供的示例。
# 为此,我们使用“PromptTemplate”类。
example_formatter_template = """
单词: {word}
反义词: {antonym}\n
"""
example_prompt = PromptTemplate(
input_variables=["word", "antonym"],
template=example_formatter_template,
)
#我们将使用' LengthBasedExampleSelector '来选择示例。
example_selector = LengthBasedExampleSelector(
# 这些是可供选择的例子。
examples=examples,
#这是用于格式化示例的PromptTemplate。
example_prompt=example_prompt,
# 这是格式化示例的最大长度。
# 长度由下面的get_text_length函数测量。
max_length=25,
)
# 我们现在可以使用' example_selector '来创建' FewShotPromptTemplate '。
dynamic_prompt = FewShotPromptTemplate(
# We provide an ExampleSelector instead of examples.
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="单词: {input}\n反义词:",
input_variables=["input"],
example_separator="",
)
# We can now generate a prompt using the `format` method.
print(dynamic_prompt.format(input="大"))
这个是上一节举的例子,当然上一节没有提到现在的示例添加新的示例方法
#您也可以将示例添加到示例选择器中。
new_example = {"word": "大", "antonym": "小"}
dynamic_prompt.example_selector.add_example(new_example)
print(dynamic_prompt.format(input="多"))
这种示例选择器基于与输入之间的边际相关性来选择示例。它计算每个示例与输入之间的相关性,并选择具有最高相关性的示例作为回答。
这种方法适用于输入和示例之间有很强相关性的情况,例如问答系统中的问题和答案。
这里我们要借助一个类MaxMarginalRelevanceExampleSelector
MaxMarginalRelevanceExampleSelector:基于哪些示例与输入最相似
以及优化多样性
的组合选择示例。
这里我们用m3e-base作为向量化引擎
下载
from modelscope.hub.snapshot_download import snapshot_download
local_dir_root = "/root/autodl-tmp/models_from_modelscope"
snapshot_download('Jerry0/m3e-base', cache_dir=local_dir_root)
from langchain.prompts.example_selector import MaxMarginalRelevanceExampleSelector
from langchain.vectorstores import FAISS
from langchain.embeddings import OpenAIEmbeddings
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
embeddings = HuggingFaceEmbeddings(
model_name = "/root/autodl-tmp/models_from_modelscope/Jerry0/m3e-base",
model_kwargs = {'device': 'cuda'})
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
#这是许多创建反义词的假装任务的例子。
examples = [
{"input": "开心", "output": "悲伤"},
{"input": "发呆", "output": "兴奋"},
{"input": "高", "output": "底"},
{"input": "精力充沛的", "output": "无精打采"},
{"input": "晴天", "output": "雨天"},
{"input": "天上", "output": "地下"},
]
example_selector = MaxMarginalRelevanceExampleSelector.from_examples(
#这是可供选择的示例列表。
examples,
#这是用于生成用于测量语义相似性的嵌入的嵌入类。
embeddings,
#这是VectorStore类,用于存储嵌入并进行相似性搜索。
FAISS,
#这是要生成的示例数。
k=2
)
mmr_prompt = FewShotPromptTemplate(
#我们提供了ExampleSelector而不是示例。
example_selector=example_selector,
example_prompt=example_prompt,
prefix="给出每个输入的反义词",
suffix="Input: {adjective}\nOutput:",
input_variables=["adjective"],
)
# 输入是一种感觉,所以应该选择快乐/悲伤的例子作为第一个
print(mmr_prompt.format(adjective="快乐"))
其实是对1.4的补充和优化
我们需要借助一个NGramOverlapExampleSelector的类,然后根据ngram重叠得分选择
和排序
示例.
该得分表示示例与输入的相似程度
ngram重叠得分是一个介于0.0
和1.0
之间的浮点数
。
选择器允许设置阈值得分。 ngram重叠得分小于或等于阈值的示例将被排除。默认情况下,阈值设置为-1.0
,因此不会排除任何示例
,只会对它们进行重新排序。
将阈值设置为0.0将排除
具有与输入无ngram重叠的示例
pip install nltk
from langchain.prompts import PromptTemplate
from langchain.prompts.example_selector.ngram_overlap import NGramOverlapExampleSelector
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
# 创建模板
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="输入: {input}\n输出: {output}",
)
#示例集合:这些是虚构翻译任务的例子:英语转化为葡萄牙语
examples = [
{"input": "See Spot run.", "output": "Ver correr a Spot."},
{"input": "My dog barks.", "output": "Mi perro ladra."},
{"input": "Spot can run.", "output": "Spot puede correr."},
]
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template="Input: {input}\nOutput: {output}",
)
example_selector = NGramOverlapExampleSelector(
# 以下是可供选择的示例。
examples=examples,
# 这是用于格式化示例的PromptTemplate。
example_prompt=example_prompt,
# 这是选择器停止的阈值。
# 默认情况下,它设置为-1.0。
threshold=-1.0,
#对于负阈值:
#Selector按ngram重叠分数对示例进行排序,不排除任何示例。
#对于大于1.0的阈值:
#选择器排除所有示例,并返回一个空列表。
#对于等于0.0的阈值:
#Selector根据ngram重叠分数对示例进行排序,
#并且排除与输入没有ngram重叠的那些。
)
dynamic_prompt = FewShotPromptTemplate(
# We provide an ExampleSelector instead of examples.
example_selector=example_selector,
example_prompt=example_prompt,
prefix="提供每个Input的西班牙语翻译",
suffix="Input: {sentence}\nOutput:",
input_variables=["sentence"],
)
#一个与“Spot can run”有较大ngram重叠的示例输入
#与“我的狗叫”没有重叠
print(dynamic_prompt.format(sentence="Spot can run fast."))
new_example = {"input": "Spot plays fetch.", "output": "Spot juega a buscar."}
example_selector.add_example(new_example)
print(dynamic_prompt.format(sentence="Spot can run fast."))
我们可以看到 他确实进行了排序,我们的问题是,Spot跑的飞快
而且第一个是Spot可以跑,第二个看见Spot跑,第三个Spot在玩游戏
第四个:我的狗再叫
第三第四个很明显不符合,所以在最后
排除
具有与输入无ngram重叠的示例example_selector.threshold=0.0
print(dynamic_prompt.format(sentence="Spot can run fast."))
example_selector.threshold=0.09
print(dynamic_prompt.format(sentence="Spot can play fetch."))
1.0 + 1e-9 的结果是 1.000000001,即在 1.0 的基础上增加了一个非常小的数 1e-9。这种写法通常是为了解决在计算机中浮点数运算可能产生的精度问题。
在这段代码中,将 example_selector.threshold 的值设为 1.0+1e-9,其实就是设置一个非常接近于 1.0,但又比它略大一点点的阈值。这样做可能会使得更多的示例被选择,因为在相似度计算中可能存在一些舍入误差或计算误差,导致某些本来应该被选择的示例未能被选中。
example_selector.threshold=1.0+1e-9
print(dynamic_prompt.format(sentence="Spot can play fetch."))
最大边际相关性 ExampleSelector 和 相似度 ExampleSelector 都是示例选择器,它们的区别在于选择示例的方法不同。
相似度 ExampleSelector 则使用文本相似度度量来选择最相关的示例。它不仅考虑了输入和示例之间的相关性,还考虑了它们之间的相似度。具体而言,它计算输入和示例之间的相似度,然后选择与输入最相似的示例作为回答。这种方法适用于输入和示例之间没有直接的相关性,但它们在语义或形式上非常相似的情况,例如聊天机器人对话中的语句。
最大边际相关性 ExampleSelector:
相似度 ExampleSelector:
总结:
最大边际相关性 ExampleSelector 关注输入与示例之间的相关性
,而相似度 ExampleSelector 则重点考虑它们之间的相似度
。两种选择器在选择示例时的侧重点不同,适用于不同的应用场景和数据特征。
说白了就是通过找到嵌入与输入具有最大余弦相似度
的示例,然后迭代地添加它们,同时筛选它们与已选择示例的接近程度来实现这一目的。
其实这里《LangChain学习一:模型-实战》中文本嵌入
有介绍,这里我们在复习一下
就是说从很多的示例集中,我们通过向量的方式去找到示例里和我们提的问题语义相近的内容作为示例,然后在给大模型,这里就不啰嗦介绍了,那一节里介绍的比较全
语言模型输出的是文本。但是很多时候,您可能想要获得的信息不仅仅是文本。这就是输出解析器的用处。输出解释器就是结构化语言模型的响应类。
1.定义一个类,继承BaseModel,实现如下两种方法
或者实现以下这个方法
假定为语言模型的响应
)和一个提示(假定为生成此类响应的提示
),然后将其解析成某种结构。提示在很大程度上是提供的,以防OutputParser希望以某种方式重试或修复输出,并需要提示信息来执行此操作。声明
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field, validator
from typing import List
from langchain.chat_models import ChatOpenAI
初始化模型
model=ChatOpenAI(
streaming=True,
verbose=True,
temperature=0,
# callbacks=[callback],
openai_api_key='none',
openai_api_base='http://localhost:20000/v1',
model_name="Qwen-7B-Chat"
)
定义输出解释器类
#定义所需的数据结构。
class Joke(BaseModel):
setup: str = Field(description="制造笑话的问题")
punchline: str = Field(description="解决这个笑话的答案")
#您可以使用Pydantic轻松添加自定义验证逻辑。
@validator('setup')
def question_ends_with_question_mark(cls, field):
if field[-1] != '?':
raise ValueError("形式不正确的问题!")
return field
实例化
#设置一个解析器+将指令注入到提示模板中。
parser = PydanticOutputParser(pydantic_object=Joke)
定义进入大模型的提示词模板partial_variables为输出解释器的模板
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
实例化模板
joke_query = "告诉我一个笑话"
_input = prompt.format_prompt(query=joke_query)
print(f"=============_input:{_input}\n\n\n")
print(f"=============_input.to_string():{_input.to_string()}\n\n\n")
output = model([HumanMessage(content=_input.to_string())])
print(f"=============output:{output}\n\n\n")
送入输出解释器并且返回
根据模型的不同,其实有时候是不遵循指令遵从的,所以返回给我们的output不是一个标准的json字符串,所以我们这里造一个。写到这里,我想说的是输出解释器就是个笑话。
joke_data = {
"setup": "Why did the chicken cross the road?",
"punchline": "To get to the other side!"
}
import json
text = json.dumps(joke_data)
fin_out = parser.parse(text)
print(f"=============fin_out:{fin_out}\n\n\n")
申明
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
定义了我们想要接收的响应模式。
response_schemas = [
ResponseSchema(name="answer", description="用户问题的答案"),
ResponseSchema(name="source", description="用于回答用户问题的来源,应该是一个网站")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
我们现在得到了一个字符串,其中包含响应应如何格式化的指令,然后我们将其插入到我们的提示中。
format_instructions = output_parser.get_format_instructions()
prompt = PromptTemplate(
template="尽可能最好地回答用户的问题.\n{format_instructions}\n{question}",
input_variables=["question"],
partial_variables={"format_instructions": format_instructions}
)
_input = prompt.format_prompt(question="中国的首都是?")
print(f"=============_input:{_input}\n\n\n")
output = model([HumanMessage(content=_input.to_string())])
print(f"=============output:{output}\n\n\n")
fin_out=output_parser.parse(output.content)
print(f"=============fin_out:{fin_out}\n\n\n")
对话模型也可以用以下
prompt = ChatPromptTemplate(
messages=[
HumanMessagePromptTemplate.from_template("尽可能最好地回答用户的问题.\n{format_instructions}\n{question}")
],
input_variables=["question"],
partial_variables={"format_instructions": format_instructions}
)
_input = prompt.format_prompt(question="中国的首都是?")
print(f"=============_input:{_input}\n\n\n")
output = model([HumanMessage(content=_input.to_string())])
print(f"=============output:{output}\n\n\n")
fin_out=output_parser.parse(output.content)
print(f"=============fin_out:{fin_out}\n\n\n")