平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“langchain采用自定义example selector示例解析”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。
简介
理解这一步时,在之前的文章里,我们提到了能够在跟大模型交互的时候,给大模型提供一些具体的例子内容,便于大模型从这些内容中拿到想要的答案。这种便于的机制在langchain中叫做FewShotPromptTemplate。
从实现思路看,若例子内容少的话,其实无所谓,我们能够把所有的例子都发送给大语言模型进行处理。
理解这一步时,但是如果例子太多的话,每次都发送如此多的内容,会让我们的钱包承受不住。毕竟那些第三方的大语言模型是按token收费的。
怎么办呢? 能不能找到一个经济又有效的方法来完成我们的工作呢?
答案就是采用example selector。
采用和自定义example selector
结合项目来看,我们回想一下在采用FewShotPromptTemplate的时候,实际上是能够同时传入example_selector和examples。
prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
suffix="Question: {input}",
input_variables=["input"]
)
落到代码里,这里我们采用了一个example_selector,那么什么是example_selector呢?
落到代码里,从名字上看他的主要作用就是从给定的examples中选择需的examples出来,提供给大模型采用,从而减少会话的token数目。
实际处理时,langchain中提供了这样的example_selector的实现,我们先来看下它的基础类的定义是怎么样的:
class BaseExampleSelector(ABC):
"""Interface for selecting examples to include in prompts."""
@abstractmethod
def add_example(self, example: Dict[str, str]) -> Any:
"""Add new example to store for a key."""
@abstractmethod
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""Select which examples to use based on the inputs."""
实际处理时,能够看到BaseExampleSelector继承自ABC,同时且定义了两个需实现的抽象方法。
实际处理时,一个方法叫做add_example。目的是向selector中添加一个example。
落到代码里,一个方法叫做select_examples,主要目的就是根据input,从examples中找出要select出来的内容。
那么什么是ABC呢?
实际处理时,ABC当然就是你了解到的ABC,但是他还有一些额外的含义。ABC的全称叫做Abstract Base Class,也叫做抽象基类。常用来在Python程序中新建抽象基类。
从实现思路看,他提供了一些@abstractmethod,@abstarctproperty这些装饰方法,来表明具体类的特征。
在这个场景下,所以,如果我们想自定义一个ExampleSelector,只需继承自BaseExampleSelector,随后实现这两个抽象方法即可。
langchain中的ExampleSelector实现
落到代码里,除了自定义实现之外,langchain已经为我们提供了几个常用的ExampleSelector实现,一起来看看吧。
LengthBasedExampleSelector
在这个场景下,LengthBasedExampleSelector是根据example的长度来进行选择的选择器。
我们看下它的具体实现:
def add_example(self, example: Dict[str, str]) -> None:
"""Add new example to list."""
self.examples.append(example)
string_example = self.example_prompt.format(**example)
self.example_text_lengths.append(self.get_text_length(string_example))
在这个场景下,add_example的逻辑是先把example添加到examples这个list中。
落到代码里,随后采用example_prompt对example进行格式化,得到最后的输出。
落到代码里,最后再把最后输出的text长度添加到example_text_lengths数组中。
def select_examples(self, input_variables: Dict[str, str]) -> List[dict]:
"""Select which examples to use based on the input lengths."""
inputs = " ".join(input_variables.values())
remaining_length = self.max_length - self.get_text_length(inputs)
i = 0
examples = []
while remaining_length > 0 and i < len(self.examples):
new_length = remaining_length - self.example_text_lengths[i]
if new_length < 0:
break
else:
examples.append(self.examples[i])
remaining_length = new_length
i += 1
return examples
理解这一步时,select_examples方法实际上就是用max_length减去输入text的长度,随后再去匹配example_text的长度,匹配一个减去一个,最后得到特定长度的examples。
理解这一步时,这个selector的最主要作用就是防止耗尽context window。因为对于大多数大语言模型来说,用户的输入是有长度限制的。
若超出了输入长度,会产生意想不到的结果。
这个selector采用起来很轻松,下面是具体的例子:
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}",
)
example_selector = LengthBasedExampleSelector(
examples=examples,
example_prompt=example_prompt,
max_length=25,
)
SemanticSimilarityExampleSelector和MaxMarginalRelevanceExampleSelector
这两个selector是根据相似度来进行example的查找的。
结合项目来看,其中MaxMarginalRelevanceExampleSelector是SemanticSimilarityExampleSelector的字类,他是对SemanticSimilarityExampleSelector进行了一些算法上的优化。所以这里我们把他们两个放在一起介绍。
落到代码里,这两个selector和之前介绍的selector有所不同。因为他们用到了向量数据库。
落到代码里,向量数据库是干什么用的呢?它的主要目的是把输入转换成各种向量随后存储起来。向量数据库能够便于的进行输入相识度的计算。
我们先来看下他们的add_example方法:
def add_example(self, example: Dict[str, str]) -> str:
"""Add new example to vectorstore."""
if self.input_keys:
string_example = " ".join(
sorted_values({key: example[key] for key in self.input_keys})
)
else:
string_example = " ".join(sorted_values(example))
ids = self.vectorstore.add_texts([string_example], metadatas=[example])
return ids[0]
理解这一步时,这个方法先把example的key加入到input_keys中,随后进行排序。最后借助调用vectorstore的add_texts,把key和value加入到向量数据库中。
在这个场景下,这两个selector的add_example都是一样的。只有select_examples的方法不同。
从实现思路看,其中SemanticSimilarityExampleSelector调用了vectorstore的similarity_search方法来实现相似度的搜索。
实际处理时,而MaxMarginalRelevanceExampleSelector则是调用vectorstore的max_marginal_relevance_search方法来实现搜索的。
两者的搜索算法不太一样。
因为采用了向量数据库,所以他们的调用方法和其他的也不太一样:
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "energetic", "output": "lethargic"},
{"input": "sunny", "output": "gloomy"},
{"input": "windy", "output": "calm"},
]
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
# 使用的ebeddings
OpenAIEmbeddings(),
# 向量数据库
Chroma,
# 要返回的数目
k=1
)
NGramOverlapExampleSelector
落到代码里,最后一个要介绍的是NGramOverlapExampleSelector。这个selector采用的是ngram 重叠矩阵来选择相似的输入。
具体的实现算法和原理这里就不介绍了。大家有兴趣的能够自行探索。
这个selector也不需采用向量数据库。
采用起来是这样的:
example_selector = NGramOverlapExampleSelector(
examples=examples,
example_prompt=example_prompt,
threshold=-1.0,
)
这里有个不太一样的参数叫做threshold。
对于负阈值:Selector按ngram重叠分数对示例进行排序,不排除任何示例。
对于大于1.0的阈值:选择器排除所有示例,同时得到一个空列表。
对于等于0.0的阈值:选择器根据ngram重叠分数对示例进行排序,同时且排除与输入没有ngram重叠的那些。
总结
实际处理时,有了这些selector我们就能够在提供的examples中进行特定的选择,随后再把选择的结果输入给大语言模型。从而有效的减少token的浪费。
结合项目来看,以上就是langchain采用自定义example selector示例解析的详细内容,更多关于langchain自定义example selector的资料请关注脚本之家其它相关文章!
- 大语言模型的开发利器langchainan安装采用更快入门学习
- langchain Prompt大语言模型采用技巧详解
- Javascript版Langchain入门教程解析
- ChatGLM 集成LangChain工具详解
- LangChain简化ChatGPT工程复杂度采用详解
- Langchain集成管理prompt功能详解
- langchain中LLM模型采用详解
