LangChain之链的认识(上)

905 阅读14分钟

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_chainSQL数据库从自然语言构建 SQL 数据库的查询
create_history_aware_retrieverRetriever 检索器该链接将收集对话历史记录,然后将其用于生成传递给底层检索器的搜索查询。
create_retrieval_chainRetriever 检索器该链接将接收用户查询,然后传递给检索器以获取相关文档。随后,将这些文档(以及原始输入)传递给LLM以生成响应。

Legacy Chains:遗留链​

说明:

链:链的名称,或构造方法的名称。如果是构造方法,这将返回一个Chain子类。

函数调用:是否需要OpenAI函数调用。

其他工具:链中使用的其他工具。

以下是一个包含所有遗留链的表格

链​函数调用其他工具使用场景
APIChainRequests Wrapper 请求包装器该链使用LLM将查询转换为API请求,执行请求并获取响应,最后将该响应传递给LLM进行处理。
OpenAPIEndpointChainOpenAPI规范该链类似于APIChain,专注于与API进行交互。 主要区别在于它针对OpenAPI端点的易用性进行了优化。
ConversationalRetrievalChainRetriever 检索器该链可以用于与文档进行对话。它接受用户提出的问题和可能包含的对话历史记录。如果有对话历史记录,它会使用LLM将对话重写为查询后发送给检索器。接着,获取相关文档并将它们和对话传递给LLM生成响应。
StuffDocumentsChain该链会获取文档列表,将它们格式化为提示后传递给LLM。它传递所有文档,需确保适用于LLM的上下文窗口。
ReduceDocumentsChain该链会通过迭代减少文档数量来组合文档。将文档分组后传递至LLM中处理,获取响应后再继续进行操作,直到能够将所有内容传递给最终的LLM调用。适用于处理大量文档,并希望LLM并行执行时。
MapReduceDocumentsChain该链会首先通过LLM传递每个文档,然后使用ReduceDocumentsChain来减少文档数量。在与ReduceDocumentsChain相同的情况下非常有用,但会在尝试减少文档之前进行初始LLM调用。
ConstitutionalChain该链会回答问题,然后根据提供的宪法原则尝试完善答案,以确保答案符合这些原则。可用来强制链的答案遵循指定的原则。
LLMChainLLMChain是最基础也是最常见的链
ElasticsearchDatabaseChainElasticsearch实例该链将自然语言问题转换为Elasticsearch查询,执行查询后总结响应。适用于向Elasticsearch数据库提出自然语言问题时使用。
FlareChain这是FLARE的实现,一种高级检索技术,主要用作一种探索性高级检索方法。
ArangoGraphQAChainArango图该链利用自然语言构建Arango查询,针对图数据库执行该查询,并将结果传递回LLM进行响应。
GraphCypherQAChain使用 Cypher 查询语言的图该链根据自然语言构建Cypher查询,针对图数据库执行查询,然后将结果传递回LLM进行响应。
FalkorDBGraphQAChainFalkor数据库该链根据自然语言构建FalkorDB查询,针对图数据库执行查询,然后将结果传递回LLM进行响应。
HugeGraphQAChainHugeGraph该链使用自然语言构造HugeGraph查询,对图数据库执行查询,然后将结果传递回LLM进行响应。
KuzuQAChainKuzu图该链根据自然语言构建Kuzu Graph查询,对图数据库执行查询,再将结果传递回LLM进行响应。
NebulaGraphQAChainNebula图该链根据自然语言构造Nebula Graph查询,对图数据库执行查询,然后将结果传递回LLM进行响应。
NeptuneOpenCypherQAChainNeptune图该链使用自然语言构建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_chainOpenAPI 规范使用 OpenAI 函数调用来查询 OpenAPI
create_qa_with_structure_chain使用OpenAI函数调用通过文本进行问答并以特定格式进行响应
create_qa_with_sources_chain使用 OpenAI 函数调用来回答带有引文的问题
QAGenerationChain从文档创建问题和答案。用于生成问题/答案对以评估检索项目
RetrievalQAWithSourcesChainRetriever对检索到的文档进行问答,并引用来源。当希望答案在文本响应中包含来源时,请使用此选项。在load_qa_with_sources_chain上使用此选项,以便在链的一部分中获取相关文档而不是传递它们。
load_qa_with_sources_chainRetriever对传入的文件进行问答,并引用来源。当希望答案在文本响应中带有来源时,请使用此选项。如果想直接传递文档而不依赖检索器获取它们,请使用这种方法而不是RetrievalQAWithSources。
RetrievalQARetriever该链首先进行检索步骤以获取相关文档,然后将这些文档传递给LLM以生成响应。
MultiPromptChainRetriever该链在多个提示之间路由输入。当有多个潜在提示可用于响应,且只想路由到一个提示时,请使用此选项。
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)