Chains的基本使用
Chain的基本概念
Chain:链,用于将多个组件(提示模板,LLM模型,记忆,工具等)链接起来,形成可复用的工作流
,完成复杂的任务。
Chain的核心思想是通过组合不同的模板化单元,实现比单一组件更强大的功能。比如:
- 将
LLM
与Prompt Template
(提示模板)结合 - 将
LLM
与输出解析器
结合 - 将
LLM
与外部数据
结合,例如用于问答 - 将
LLM
与长期记忆
结合,例如勇敢与聊天历史记录 - 通过将
第一个LLM
的输出作为第二个LLM
的输入,...,将多个LLM按顺序结合在一起
LCEL及其构成
使用LCEL,可以构造出结构最简单的Chain
LangChain表达式语言(LCEL,LangChain Expression Language)是一种声明式的方法,可以轻松地将多个组件链接成AI工作流。它通过Python原生操作符(如管道符|
)将组件链接成可执行流程,显著简化了AI应用的开发。
LCEL的基本构成:提示(Prompt)+ 模型(model)+ 输出解释器(OutputParser)
即:
在这个链条中,用户输入被传递给提示词模板,然后提示模板的输入被传递给模型,然后模型的输出被传递给输出解析器
chain = prompt | model | output_parser chian.invoke("input":"你叫什么名字")
- Prompt :Prompt是一个BasePromptTemplate,这意味着它接受一个模板变量的字典并生成一个
PromptValue
。PromptValue可以传递给LLM(它以字符产作为输入)或ChatModel(它以消息列表作为输入) - Model :将PromptValue传递给model。如果我们的model是一个ChatModel,这意味着它将输出一个
BaseMessage
- OutputParser:将model的输出传递给output_parser,他是一个BaseOutputParser,意味着它可以接收字符串或BaseMessage作为输入
- chain :我们可以使用
|
运算符轻松创建这个Chain。|
运算符在LangChain中用于将两个元素组合在一起 - invoke :所有LCEL对象都实现了
Runnable
协议,保证一致的调用性(invoke
/batch
/stream
)
| 符号类似于shell里面的管道操作符,他将不同的组件链接在一起,将前一个组件的输出作为下一个组件的输入,这就形成了一个AI工作流
Runnable
Runnable是LangChain定义的一个抽象接口(Protocol),它强制要求
所有LCEL组件是西安一组标准方法:
python
class Runnable(Protocol):
def invoke(self, input: Any) -> Any: ... # 单输入单输出
def batch(self, inputs: List[Any]) -> List[Any]: ... # 批量处理
def stream(self, input: Any) -> Iterator[Any]: ... # 流式输出
# 还有其他方法如 ainvoke(异步)等...
任何实现了这些方法的对象都被视为LCEL兼容组件。比如:聊天模型,提示词模板,输出解析器,检索器,代理(智能体)等
每个LCEL对象都实现了Runnable接口,该接口定义了一组公共的调用方法。这使得LCEL对象链也自动支持这些调用成为可能
为什么需要同意调用方式?
假设没有同一协议:
- 提示词渲染用:
.format()
- 模型调用用:
.generate()
- 解析器解析用:
.parse()
- 工具调用用:
.run()
代码会变成:
python
prompt_text = prompt.format(topic = "猫")
model_out = model.generate(prompt_text)
result = parser.paese(model_out)
每个组件调用方式不同,组合时需要手动适配。
LCEL解决方案
通过Runnable
协议统一:
python
# 分步调用
prompt_text = prompt.invoke(topic = "猫")
model_out = model.invoke(prompt_text)
result = parser.invoke(model_out)
# LCEL管道式
chain = prompt | model | parser # 用管道符组合
result = chain.invoke({"topic":"猫"}) # 所有组件同一用invoke
- 一致性:无论组件的功能多复杂(模板/提示词/工具),调用方式完全相同
- 组合性 :管道操作符
|
背后自动处理类型匹配和中间结果的传递
使用举例
例1:没有使用chain
python
# 调用大模型的前置环境配置
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
import os
import dotenv
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
os.environ["OPENAI_BASE_URL"] = os.getenv("OPENAI_BASE_URL")
chat_model = ChatOpenAI(
model = "gpt-4o-mini"
)
# 开始调用
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template(
template = "给我将一个关于{topic}的笑话"
)
parser = StrOutputParser()
prompt_value = prompt_template.invoke({"topic":"卫生纸"})
result = chat_model.invoke(prompt_value)
output = parser.invoke(result)
print(type(output))
print(output)
<class 'str'>
当然可以!这是一个关于卫生纸的笑话:
有一天,有两个卷纸在讨论谁更受欢迎,卫生纸自豪地说:"我每天都被人用来擦屁股,大家都离不开我!"
另一卷纸微笑着说:"那你可真有'屁股'的自信!"
希望这个笑话能让你开心!
例2:使用Chain将提示模板,模型,解析器链接在一起。使用LCEL将不同的组件组合成一个单一的链条
python
prompt_template = PromptTemplate.from_template(
template = "给我将一个关于{topic}的笑话"
)
parser = StrOutputParser()
# 构建链式调用(LCEL语法)
chain = prompt_template | chat_model | parser
output = chain.invoke({"topic":"卫生纸"})
print(type(output))
print(output)
<class 'str'>
当然可以!这是一个关于卫生纸的笑话:
有一天,卫生纸和纸巾在聊天。卫生纸自豪地说:"我最厉害,谁都离不开我!"
纸巾笑着回答:"可你有多久没出门了?你就只在厕所里待着!"
卫生纸不以为然:"那又怎样?我可是'急需'的角色!"
纸巾反击:"但我可是'随时待命'的明星!"
最后,卫生纸叹了口气:"好吧,我们都是'纸'上谈兵!"
希望这个笑话能让你开心!

传统Chain的使用
基础链:LLMChain
使用说明
LCEL之前,最基础也最常见的类型是LLMChain
这个链至少包括一个提示词模板(PromptTemplate),一个语言模型(LLM或者聊天模型)
注意:LLMChain was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use prompt | llm instead。
特点:
- 用于单次问答,输入一个Prompt,输出LLM的响应
- 适合无上下文的简单任务(如翻译,摘要,分类等)
- 无记忆:无法自动维护聊天历史
主要步骤
1.配置任务链:使用LLMChain类将任务与提示词结合,形成完整的任务链
python
chain = LLMChain(llm = chat_model, prompt = prompt_template)
2.执行任务链:使用invoke()等方法执行任务链,并获取生成结果。可以根据需要对输出进行处理和展示。
python
result = chain.invoke(...)
print(reslut)
参数说明
参数名 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
llm |
Union[Runnable[LanguageModelInput,str],Runnable[LanguageModelInput,BaseMessage]] | - | 是 |
要调用的语言模型 |
prompt |
BasePromptTemplate | - | 是 |
要使用的提示对象 |
verbose |
bool | False | 否 | 是否以详细模式运行 。在详细模式下,一些中间日志将被打印到控制台 |
memory |
Optional[BaseMemory | None | 否 | 可选的记忆对象 |
output_parser |
BaseLLMOutputParse | - | 否 | 要使用的输出解释器 ,默认为StrOutputParser |
callbacks | Callbacks | None | 否 | 可选的回调处理器列表或回调管理器。在调用链的生命周期中的不同阶段被调用,从on_chain_start开始,到on_chain_end或on_chain_error结束。自定义链可以选择调用额外的回调方法 |
llm_kwargs | dict | - | 否 | 语言模型的关键字参数字典 |
metadata | Optional[Dict[str, Any]] | None | 否 | 与链相关联的可选元数据。默认为None |
return_final_only | bool | True | 否 | 是否只返回最终解析结果。默认为True |
顺序链-SimpleSequentialChain
顺序链(SequentialChain)允许将多个链顺粗连接起来,每个Chain的输出作为下一个Chain的输入,形成特定场景的流水线(Pipline)
顺序链有两种类型:
- 单个输入/输出:对应着:SimpleSequentialChain
- 多个输入/输出:对应着:SequentialChain
说明
SimpleSequentialChain:最简单的顺序链,多个链串行执行
,每个步骤都有单一
的输入和输出,一个步骤的输出就是下一个步骤的输入,无需手动映射。

使用
例:
python
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import LLMChain
from langchain.chains.sequential import SimpleSequentialChain
chainA_template = ChatPromptTemplate.from_messages([
("system","你是一个网上冲浪高手,善于搜集资料,并保证资料的真实性"),
("human","请你尽可能详细的解释一下:{question}")
])
chainA_chains = LLMChain(
llm = chat_model,
prompt = chainA_template,
verbose = True,
)
chainB_template = ChatPromptTemplate.from_messages([
("system","你非常善于提取文本中的重要信息,并作除简短的总结"),
("human","这是针对一个提问的完整的解释说明内容:{description}"),
("human","请你根据上述说明,尽可能简短的输出重要结论,控制在20字以内")
])
chainB_chains = LLMChain(
llm = chat_model,
prompt = chainB_template,
verbose = True
)
final_chain = SimpleSequentialChain(
chains = [chainA_chains, chainB_chains],
verbose = True,
)
response = final_chain.invoke(input={"input":"如何从读者口中评价《龙族》小说的作者江南"})
print(response)
在这个过程中,因为SimpleSequentialChain
定义的是顺序链,所以在chains
参数中传递的列表要按照顺序来进行传入,即chainA要在chainB之前。同时,在调用时,不再使用chainA中定义的{question}
参数,也不是chainB中定义的{description}
参数,而是要使用input
进行变量的传递。
顺序链-SequentialChain
说明
SequentialChain:更通用的顺序链,具体来说:
多变量支持
:允许不同子链由独立输入/输出变量灵活映射
:需显式定义
变量如何从一个链传递到下一个链。即精准地命名输入关键字和输出关键字,来明确链之间的关系复杂流程控制
:支持分支,条件逻辑(分别通过input_variables
和output_variables
配置输入和输出)

使用
例:
python
from langchain.chains.sequential import SequentialChain
from langchain_core.prompts.chat import ChatPromptTemplate
chainA_prompt = ChatPromptTemplate.from_messages([
("system","你是一个精通各个领域知识的教授"),
("human","请你尽可能详细的解释一下:{konwledge},并且{action}")
])
chainA_chains = LLMChain(
llm = chat_model,
prompt = chainA_prompt,
verbose = True,
output_key = "chainA_chains_key"
)
chainB_prompt = ChatPromptTemplate([
("system","你非常善于提取文本中的重要信息,并做出简短的总结"),
("human","这是针对上一个提问的完整的解释说明内容:{chainA_chains_key}"),
("human","请你根据上述说明,尽可能简短的输出重要结论,请控制在100字以内"),
])
chainB_chains = LLMChain(
llm = chat_model,
prompt = chainB_prompt,
verbose = True,
output_key = "chainB_chains_key"
)
final_chain = SequentialChain(
chains = [chainA_chains, chainB_chains],
input_variables=["konwledge","action"],
output_variables=["chainA_chains_key","chainB_chains_key"],
verbose = True,
)
response = final_chain({
"konwledge":"中国足球为什么踢得烂",
"action":"举一个实际的例子"
})
print(response)
还可以单独输出
python
print(response["chainA_chains_key"])
print(response["chainB_chains_key"])
使用场景
场景:多数据源处理
举例:根据商品名查询-->数据库获取价格,生成促销文案
使用 SimpleSequentialChain(失败)
python
# 假设链1返回{"price":100},链2需要{product:"...",price:...}
# 结构不匹配,无法自动传递
使用SequentialChain(正确)
python
# 查询链
query_chain = LLMChain(
llm = chat_model,
prompt = PromptTemplate.from_template(
"请模拟查询{product}的市场价格,直接返回一个合理的价格数字(如899),不要包含其他任何文字或代码"
),
verbose = True,
output_key = "price"
)
# 文案链
prompt_chain = LLMChain(
llm = chat_model,
prompt = PromptTemplate.from_template(
"为{product}(售价为{price}元)创作一篇50字以内的促销文案,要求突出产品卖点"
),
verbose = True,
output_key = "prompt_text"
)
final_chain = SequentialChain(
chains = [query_chain, prompt_chain],
verbose = True,
input_variables = ["product"],
output_variables = ["price","prompt_text"]
)
result = final_chain.invoke({"product":"华为mate60"})
print(result)
> Entering new SequentialChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
> Entering new SequentialChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
请模拟查询华为mate60的市场价格,直接返回一个合理的价格数字(如899),不要包含其他任何文字或代码
> Finished chain.
> Entering new LLMChain chain...
Prompt after formatting:
> Entering new SequentialChain chain...
> Entering new LLMChain chain...
Prompt after formatting:
请模拟查询华为mate60的市场价格,直接返回一个合理的价格数字(如899),不要包含其他任何文字或代码
> Finished chain.
> Entering new LLMChain chain...
Prompt after formatting:
为华为mate60(售价为5999元)创作一篇50字以内的促销文案,要求突出产品卖点
> Finished chain.
> Finished chain.
{'product': '华为mate60', 'price': '5999', 'prompt_text': '体验华为Mate60,5999元,搭载先进科技,强劲性能,超清影像,续航持久,轻松应对多任务。时尚设计与智能交互,助你畅享生活每一刻!立即拥有,开启智慧新生活!'}
数学链LLMMathChain
LLMMathChain将用户的问题转换为数学问题,然后将数学问题转换为可以使用 Python 的 numexpr 库执行的表达式。使用运行此代码的输出来回答问题。
路由链RounterChain
路由链(RounterChain)用于创建可以动态选择下一条链
的链。可以自动分析用户的需求,任何引导到最适合的链中执行,获取响应并发挥最终结果
比如,我们目前有三类chain,分别对应三种学科的问题解答。我们的输入内容也是与这三种学科对应,但是随机的,比如第一次输入数学问题、第二次有可能是历史问题... 这时候期待的效果是:可以根据输入的内容是什么,自动将其应用到对应的子链中。RouterChain就为我们提供了这样一种能力。

他会首先决定将要传递下去的子链,然后把输入传递给那个链。并且在设置的时候需要注意为其设置默认chain,以兼容输入内容不满足任意一项时的情况
RounterChain图示:

文档链StuffDocumentsChain
StuffDocumentsChain是一种文档处理链,他的核心作用是将多个文档内容合并
("填充"或"塞入")到单个提示词(promt)中,然后传递给语言模型(LLM)处理
使用场景
:由于所有文档被完整拼接,LLM能同时看到全部内容,所以适合需要全局理解的任务,如总结,问答,对比分析等。但注意,仅适合处理少量/中等长度文档的场景。
基于LCEL构建的Chains的类型
前面讲解的都是Legacy Chains,下面看最新的基于LCEL构建的Chains
- create_sql_query_chain
- create_stuff_documents_chain
- create_openai_fn_runnable
- create_structured_output_runnable
- load_query_constructor_runnable
- create_history_aware_retriever
- create_retrieval_chain
create_sql_query_chain
create_sql_query_chain,SQL查询链,时创建生成SQL查询的链,用于将自然语言
转换成数据库的SQL查询
例:
python
from langchain_community.utilities import SQLDatabase
# 连接 MySQL 数据库
db_user = "root"
db_password = "abc123" #根据自己的密码填写
db_host = "127.0.0.1"
db_port = "3306"
db_name = "employees"
# mysql+pymysql://用户名:密码@ip地址:端口号/数据库名
db = SQLDatabase.from_uri(f"mysql+pymysql://{db_user}:{db_password}@{db_host}:
{db_port}/{db_name}")
print("哪种数据库:", db.dialect)
print("获取数据表:", db.get_usable_table_names())
# 执行查询
res = db.run("SELECT count(*) FROM employees;")
print("查询结果:", res)
create_stuff_documents_chain
create_stuff_documents_chain用于将多个文档内容
合并成单个长文本
的链式工具,并一次性传递给LLM处理(而不是分多次处理)
适合场景:
- 保持上下文完整,适合需要全局理解所有文档内容的任务(如总结、问答)
- 适合处理少量/中等长度文档的场景
例:多文档摘要
python
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.documents import Document
# 定义提示词模板
prompt = PromptTemplate.from_template(
"如下文档{docs}中说,蓝莓是什么颜色的?"
)
# 创建链
chain = create_stuff_documents_chain(chat_model,prompt,document_variable_name ="docs")
# 文档输入
docs = [
Document(
page_content="苹果,学名Malus pumila Mill.,别称西洋苹果、柰,属于蔷薇科苹果属的植物。苹果是全球最广泛种植和销售的水果之一,具有悠久的栽培历史和广泛的分布范围。苹果的原始种群主要起源于中亚的天山山脉附近,尤其是现代哈萨克斯坦的阿拉木图地区,提供了所有现代苹果品种的基因库。苹果通过早期的贸易路线,如丝绸之路,从中亚向外扩散到全球各地。"
),
Document(
page_content="香蕉是黄色的水果,主要产自热带地区。"
),
Document(
page_content="蓝莓是蓝色的浆果,含有抗氧化物质。"
)
]
# 执行摘要
chain.invoke({"docs":docs})
'蓝莓是蓝色的浆果。'