Chain链
概述
为开发更复杂的应用程序,需要使用Chain来链接LangChain中的各个组件和功能,包括模型之间的链接以及模型与其他组件之间的链接。
链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。 链其实可以被视为LangChain中的一种基本功能单元。
分类
LangChain中提供了很多种类型的预置链,目的是使各种各样的任务实现起来更加方便、规范。
LangChain支持两种类型的链:
1.使用LCEL构建的链,LangChain提供了一个更高级别的构造方法,实际上所有工作都是使用LCEL构建链。
2.[遗留]通过从继承自遗留Chain类构建的链,这些链独立于LCEL而存在。
如何使用:
访问LangChain文档,搜索链名称,查看具体用法。
LCEL Chains:链构造器
说明:
链构造器:这是链的构造函数,返回 LCEL 可运行对象的方法。可查看API文档了解更多信息。
函数调用:确定是否需要调用OpenAI函数。
其他工具:在链中使用了哪些其他工具(如果有的话)。
以下是一个包含所有 LCEL 链构造器的表格
链构造器 | 函数调用 | 其他工具 | 使用场景 |
---|---|---|---|
create_stuff_documents_chain | 将文档列表收集并格式化成一个提示,然后传递给LLM。LLM将传递所有文档,所以请确保提示适合LLM的上下文窗口。 | ||
create_openai_fn_runnable | √ | 使用OpenAI函数调用来有选择性地构建输出响应。可以传递多个函数供其调用,但不一定要调用这些函数。 | |
create_structured_output_runnable | √ | 可以使用OpenAI函数调用来强制LLM以某个函数进行响应。只能传入一个函数,并且链将始终返回此响应。 | |
load_query_constructor_runnable | 可以用来生成查询。需指定允许的操作列表,然后将自然语言查询转换为这些允许的操作的可运行对象。 | ||
create_sql_query_chain | SQL数据库 | 从自然语言构建 SQL 数据库的查询 | |
create_history_aware_retriever | Retriever 检索器 | 该链接将收集对话历史记录,然后将其用于生成传递给底层检索器的搜索查询。 | |
create_retrieval_chain | Retriever 检索器 | 该链接将接收用户查询,然后传递给检索器以获取相关文档。随后,将这些文档(以及原始输入)传递给LLM以生成响应。 |
Legacy Chains:遗留链
说明:
链:链的名称,或构造方法的名称。如果是构造方法,这将返回一个Chain子类。
函数调用:是否需要OpenAI函数调用。
其他工具:链中使用的其他工具。
以下是一个包含所有遗留链的表格
链 | 函数调用 | 其他工具 | 使用场景 |
---|---|---|---|
APIChain | Requests Wrapper 请求包装器 | 该链使用LLM将查询转换为API请求,执行请求并获取响应,最后将该响应传递给LLM进行处理。 | |
OpenAPIEndpointChain | OpenAPI规范 | 该链类似于APIChain,专注于与API进行交互。 主要区别在于它针对OpenAPI端点的易用性进行了优化。 | |
ConversationalRetrievalChain | Retriever 检索器 | 该链可以用于与文档进行对话。它接受用户提出的问题和可能包含的对话历史记录。如果有对话历史记录,它会使用LLM将对话重写为查询后发送给检索器。接着,获取相关文档并将它们和对话传递给LLM生成响应。 | |
StuffDocumentsChain | 该链会获取文档列表,将它们格式化为提示后传递给LLM。它传递所有文档,需确保适用于LLM的上下文窗口。 | ||
ReduceDocumentsChain | 该链会通过迭代减少文档数量来组合文档。将文档分组后传递至LLM中处理,获取响应后再继续进行操作,直到能够将所有内容传递给最终的LLM调用。适用于处理大量文档,并希望LLM并行执行时。 | ||
MapReduceDocumentsChain | 该链会首先通过LLM传递每个文档,然后使用ReduceDocumentsChain来减少文档数量。在与ReduceDocumentsChain相同的情况下非常有用,但会在尝试减少文档之前进行初始LLM调用。 | ||
ConstitutionalChain | 该链会回答问题,然后根据提供的宪法原则尝试完善答案,以确保答案符合这些原则。可用来强制链的答案遵循指定的原则。 | ||
LLMChain | LLMChain是最基础也是最常见的链 | ||
ElasticsearchDatabaseChain | Elasticsearch实例 | 该链将自然语言问题转换为Elasticsearch查询,执行查询后总结响应。适用于向Elasticsearch数据库提出自然语言问题时使用。 | |
FlareChain | 这是FLARE的实现,一种高级检索技术,主要用作一种探索性高级检索方法。 | ||
ArangoGraphQAChain | Arango图 | 该链利用自然语言构建Arango查询,针对图数据库执行该查询,并将结果传递回LLM进行响应。 | |
GraphCypherQAChain | 使用 Cypher 查询语言的图 | 该链根据自然语言构建Cypher查询,针对图数据库执行查询,然后将结果传递回LLM进行响应。 | |
FalkorDBGraphQAChain | Falkor数据库 | 该链根据自然语言构建FalkorDB查询,针对图数据库执行查询,然后将结果传递回LLM进行响应。 | |
HugeGraphQAChain | HugeGraph | 该链使用自然语言构造HugeGraph查询,对图数据库执行查询,然后将结果传递回LLM进行响应。 | |
KuzuQAChain | Kuzu图 | 该链根据自然语言构建Kuzu Graph查询,对图数据库执行查询,再将结果传递回LLM进行响应。 | |
NebulaGraphQAChain | Nebula图 | 该链根据自然语言构造Nebula Graph查询,对图数据库执行查询,然后将结果传递回LLM进行响应。 | |
NeptuneOpenCypherQAChain | Neptune图 | 该链使用自然语言构建Neptune Graph查询,执行查询后将结果传递回LLM进行响应。 | |
GraphSparqlChain | 适用于SparQL的图 | 该链根据自然语言构造SPARQL查询,执行查询后将结果传递回LLM进行响应。 | |
LLMMath | 该链将用户问题转换为数学问题,然后执行它(使用 numexpr) | ||
LLMCheckerChain | 该链使用第二个LLM调用来验证初始答案,并在初始LLM调用上添加额外的验证层时选择此选项。 | ||
LLMSummarizationChecker | 该链使用一系列LLM调用创建摘要,以确保准确性。当更关注准确性而不是速度/成本时,可以在正常摘要链上使用这种方法。 | ||
create_citation_fuzzy_match_chain | √ | 使用 OpenAI 函数调用来回答问题并引用其来源。 | |
create_extraction_chain | √ | 使用 OpenAI 函数调用从文本中提取信息。 | |
create_extraction_chain_pydantic | √ | 使用OpenAI函数调用将文本信息提取到Pydantic模型中,它与Pydantic的集成比create_extraction_chain更紧密。 | |
get_openapi_chain | √ | OpenAPI 规范 | 使用 OpenAI 函数调用来查询 OpenAPI |
create_qa_with_structure_chain | √ | 使用OpenAI函数调用通过文本进行问答并以特定格式进行响应 | |
create_qa_with_sources_chain | √ | 使用 OpenAI 函数调用来回答带有引文的问题 | |
QAGenerationChain | 从文档创建问题和答案。用于生成问题/答案对以评估检索项目 | ||
RetrievalQAWithSourcesChain | Retriever | 对检索到的文档进行问答,并引用来源。当希望答案在文本响应中包含来源时,请使用此选项。在load_qa_with_sources_chain上使用此选项,以便在链的一部分中获取相关文档而不是传递它们。 | |
load_qa_with_sources_chain | Retriever | 对传入的文件进行问答,并引用来源。当希望答案在文本响应中带有来源时,请使用此选项。如果想直接传递文档而不依赖检索器获取它们,请使用这种方法而不是RetrievalQAWithSources。 | |
RetrievalQA | Retriever | 该链首先进行检索步骤以获取相关文档,然后将这些文档传递给LLM以生成响应。 | |
MultiPromptChain | Retriever | 该链在多个提示之间路由输入。当有多个潜在提示可用于响应,且只想路由到一个提示时,请使用此选项。 | |
MultiRetrievalQAChain | 该链在多个检索器之间路由输入。当有多个潜在的检索器可获取相关文档,并且只希望路由到一个检索器时,请使用此选项。 | ||
EmbeddingRouterChain | 该链使用嵌入相似性来路由传入查询 | ||
LLMRouterChain | 该链使用 LLM 在潜在选项之间进行路由 | ||
load_summarize_chain | 用于进行摘要和总结的链 | ||
LLMRequestsChain | 该链根据用户输入构造一个URL,获取数据,然后汇总响应。相较于APIChain,这个链更加通用,不专注于单一API规范。 |
链的基本使用
LLMChain是最基础也是最常见的链。LLMChain结合了语言模型推理功能,并添加了PromptTemplate和Output Parser等功能,将模型输入输出整合在一个链中操作。它利用提示模板格式化输入,将格式化后的字符串传递给LLM模型,并返回LLM的输出。这样使得整个处理过程更加高效和便捷。
未使用Chain
当未使用Chain时,Model I/O的实现分为两个部分,提示模板的构建和模型的调用独立处理。
# 导入LangChain中的提示模板
from langchain_core.prompts import PromptTemplate
# 原始字符串模板
template = "猪八戒吃{fruit}?"
# 创建LangChain模板
prompt_temp = PromptTemplate.from_template(template)
# 根据模板创建提示
prompt = prompt_temp.format(fruit='人参果')
# 导入LangChain中的OpenAI模型接口
from langchain_openai import OpenAI
# 创建模型实例
model = OpenAI(temperature=0)
# 传入提示,调用模型返回结果
result = model.invoke(prompt)
print(result)
猪八戒是《西游记》中的一个角色,他是一只贪吃懒惰的猪妖,平时喜欢吃各种美食。在《西游记》中,猪八戒曾经吃过人参果,但并不是真的吃了人参果,而是被唐僧误认为是人参果而被骗吃了。事实上,人参果是一种神奇的果实,可以让人长生不老,但是只有在特定的条件下才能生长,非常稀少。因此,猪八戒并没有真的吃到人参果。
使用Chain
当使用Chain链时,代码结构则更简洁。
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 原始字符串模板
template = "猪八戒吃{fruit}?"
# 创建模型实例
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(template))
# 调用LLMChain,返回结果
result = llm_chain.invoke({
"fruit": "人参果"})
print(result)
{
'fruit': '人参果', 'text': '\n\n猪八戒是《西游记》中的一个角色,他是一只贪吃懒惰的猪妖,平时喜欢吃各种美食。在《西游记》中,猪八戒曾经吃过人参果,但并不是真的吃了人参果,而是被唐僧误认为是人参果而被骗吃了。事实上,人参果是一种神奇的果实,可以让人长生不老,但是只有在特定的条件下才能生长,非常稀少。因此,猪八戒并没有真的吃到人参果。'}
使用表达式语言 (LCEL)
LangChain表达式语言,或 LCEL,是一种声明式的方法,可以轻松地将链组合在一起。
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 原始字符串模板
template = "猪八戒吃{fruit}?"
prompt = PromptTemplate.from_template(template)
# 创建模型实例
llm = OpenAI(temperature=0)
# 创建Chain
chain = prompt | llm
# 调用Chain,返回结果
result = chain.invoke({
"fruit": "人参果"})
print(result)
链的调用方式
1.通过invoke方法
通过invoke方法,在调用链的时候,传入一个字典参数。在新、高版本中推荐使用。
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 创建模型实例
template = PromptTemplate(
input_variables=["role", "fruit"],
template="{role}喜欢吃{fruit}?",
)
# 创建LLM
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
# 调用LLMChain,返回结果
# 如果提示模板中包含多个变量,在调用链的时候,可以使用字典一次性输入它们。
result = llm_chain.invoke({
"role": "猪八戒", "fruit": "人参果"})
print(result)
{
'role': '猪八戒', 'fruit': '人参果', 'text': '\n\n猪八戒是一只贪吃懒惰的猪妖,他最喜欢吃的是猪食。人参果是唐僧师徒在取经途中遇到的一种神奇的水果,具有延年益寿的功效,但并非猪八戒的最爱。'}
2.通过predict方法
通过predict方法,将输入键指定为关键字参数
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 创建模型实例
template = PromptTemplate(
input_variables=["role", "fruit"],
template="{role}喜欢吃{fruit}?",
)
# 创建LLM
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
# 调用LLMChain,返回结果
result = llm_chain.predict(role="猪八戒", fruit="人参果")
print(result)
3.通过apply方法
apply方法允许输入列表运行链,一次处理多个输入。
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 创建模型实例
template = PromptTemplate(
input_variables=["role", "fruit"],
template="{role}喜欢吃{fruit}?",
)
# 创建LLM
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
# 输入列表
input_list = [
{
"role": "猪八戒", "fruit": "人参果"}, {
"role": "孙悟空", "fruit": "仙桃"}
]
# 调用LLMChain,返回结果
result = llm_chain.apply(input_list)
print(result)
[{
'text': '\n\n猪八戒是一个贪吃的角色,他喜欢吃各种美味的食物,包括人参果。在《西游记》中,猪八戒曾经在取经路上遇到过人参果树,他非常贪婪地摘下来吃,结果被孙悟空和唐僧发现并教训。虽然人参果具有补气养血的功效,但是对于猪八戒来说,它更像是一种美味的水果,他并不在意它的药用价值。因此,可以说猪八戒是喜欢吃人参果的。'}, {
'text': '\n\n是的,孙悟空非常喜欢吃仙桃。在《西游记》中,他经常会偷吃仙桃,甚至为了吃仙桃而闹出许多笑话和故事。仙桃也是孙悟空的最爱,因为它们具有神奇的功效,可以让他长生不老。'}]
4.通过generate方法
generate方法类似于apply,但它返回一个LLMResult对象,而不是字符串。LLMResult通常包含了在模型生成文本过程中的一些相关信息,例如令牌数量、模型名称等。
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import OpenAI
# 创建模型实例
template = PromptTemplate(
input_variables=["role", "fruit"],
template="{role}喜欢吃{fruit}?",
)
# 创建LLM
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
input_list = [
{
"role": "猪八戒", "fruit": "人参果"}, {
"role": "孙悟空", "fruit": "仙桃"}
]
# 调用LLMChain,返回结果
result = llm_chain.generate(input_list)
print(result)
generations=[[Generation(text='\n\n猪八戒是一个贪吃的角色,他喜欢吃各种美味的食物,包括人参果。在《西游记》中,猪八戒曾经在取经路上遇到过人参果树,他非常贪婪地摘下来吃,结果被孙悟空和唐僧发现并责备他。虽然人参果具有补气养血的功效,但是对于猪八戒来说,它更像是一种美味的水果,他并不在意它的药用价值。因此可以说,猪八戒确实喜欢吃人参果。', generation_info={
'finish_reason': 'stop', 'logprobs': None})], [Generation(text='\n\n是的,孙悟空非常喜欢吃仙桃。在《西游记》中,他经常会偷吃仙桃,甚至为了吃仙桃而闹出许多笑话和故事。仙桃也是孙悟空的最爱,因为它们具有神奇的功效,可以让他长生不老。', generation_info={
'finish_reason': 'stop', 'logprobs': None})]] llm_output={
'token_usage': {
'completion_tokens': 309, 'total_tokens': 343, 'prompt_tokens': 34}, 'model_name': 'gpt-3.5-turbo-instruct'} run=[RunInfo(run_id=UUID('1354a070-6820-4005-9436-af859e65ebc3')), RunInfo(run_id=UUID('7d5d6633-b569-487f-ae18-24a3f4ac21db'))]
5.直接调用链对象
可以直接调用链对象,实际调用对象内部实现的
__call__
方法。在新、高版本中不推荐使用且将被弃用。
注意:
当像函数一样调用一个对象时,它实际上会调用该对象内部实现的
__call__
方法。
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
# 调用LLMChain,返回结果
result = llm_chain({
"role": "猪八戒", "fruit": "人参果"})
print(result)
6.通过run方法
通过run方法,等价于直接调用_call_
函数。在新、高版本中不推荐使用且将被弃用。
# 创建LLMChain
llm_chain = LLMChain(llm=llm, prompt=template)
# 调用LLMChain,返回结果
result = llm_chain.run({
"role": "猪八戒", "fruit": "人参果"})
print(result)