前言
我们以我们的国产模型deepseek为例,进行langchain的练习
一:环境配置
(1)安装包
pip install -U langchain langchain-core langchain-deepseek
(2)配置环境变量
环境变量DEEPSEEK_API_KEY 虽然可以不用配置,但是暴露在代码中还是不太好



进行填写环境变量

(3)这里调用的是远程模型,不是本地模型
二:链的理解与使用
(1)定义模型
python
from langchain_deepseek import ChatDeepSeek
model = ChatDeepSeek(
model="deepseek-chat",
temperature=0
)
1.from langchain_deepseek import ChatDeepSeek:表示从langchain_deepseek 这个包里面调用ChatDeepSeek这个类
2.model="deepseek-chat"表示你要选择哪个模型
(2)定义消息
python
from langchain_core.messages import HumanMessage, SystemMessage
messages = [
SystemMessage(content="请帮我把英文翻译成中文"),
HumanMessage(content="hi")
]
SystemMessage(系统命令,给模型限制):它通常作为消息序列里的第一条消息,用来规定 AI 后面整体的行为。
HumanMessage:对模型所说的话
(3)调用模型
python
result = model.invoke(messages)
1.invoke(...) 就是执行这个组件的方法
2.你把 messages 传进去,本质上就是拿这些消息去问模型。
(4)结果
result:返回类型其实是一个 AIMessage。
也就是说:
你发出去的是 HumanMessage / SystemMessage
模型回给你的是 AIMessage
这就形成了完整闭环:
人类消息 → 模型处理 → AI 消息
所以:下面的代码就是一个简单的可调用的
python
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import HumanMessage, SystemMessage
# 1. 定义模型
model = ChatDeepSeek(
model="deepseek-chat",
temperature=0
)
# 2. 定义消息
messages = [
SystemMessage(content="请帮我把英文翻译成中文"),
HumanMessage(content="hi")
]
# 3. 调用模型
result = model.invoke(messages)
# 4. 输出结果
print(result.content)
(2)链式结构体现
对于前面的代码,本质上就是:
输入消息 → 模型处理 → 返回结果
输出解释器
模型返回给你的 result,其实不是你最想直接拿来用的终态数据。它里面除了正文,还有别的信息。
但很多时候你根本不关心这么多。
你可能只想要:
一段纯文本
一个 JSON
一个固定结构的数据
怎么办?
这时候就需要一个专门的"收尾处理者":输出解释器

最终代码:
python
from langchain_deepseek import ChatDeepSeek
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
# 1. 定义模型
model = ChatDeepSeek(
model="deepseek-chat",
temperature=0
)
# 2. 定义消息
messages = [
SystemMessage(content="请把用户输入的英文翻译成中文"),
HumanMessage(content="My name is Xiaoming.")
]
# 3. 调用模型
result = model.invoke(messages)
# 定义输出解释器
parser = StrOutputParser()
# 4. 输出结果
#print(result.content)
# print(result)
#将结果给放进输出解释器
final_result = parser.invoke(result)
# 输出最终结果
print(final_result)
结果:

前面的普通调用还不算链,因为只是手动调用模型。
LangChain 先引入输出解析器,让模型组件和解析器组件都出现。
模型负责生成结果,解析器负责把结果整理成指定格式。
但此时仍然是手动逐个 invoke,所以还不是真正的链式调用。
真正的链怎么定义?
前面我们已经有两个组件了:
1.model:模型组件
2.parser:输出解析器组件
之前我们还是手动的在调:
python
result = model.invoke(messages)
final_result = parser.invoke(result)
使用链的调用:
python
chain = model | parser
这里的意思是调用完model的结果后面然后给parser
LangChain 的链式结构,本质是把多个组件按顺序串联起来。
定义链时可以使用管道符 |,例如 chain = model | parser。
它表示先执行 model,再把输出交给 parser。
执行时不再分别调用每个组件,而是直接调用 chain.invoke(messages)。
链会按照从前到后的顺序自动完成整条处理流程。
三:返回值和 invoke()
首先我们要解决俩问题
为什么模型和解析器都能用 invoke()?
模型返回的 result 到底是什么,里面有哪些信息?
1.为什么都能 invoke()?
在 LangChain 里,很多东西都被抽象成"组件",既然都是组件,就尽量用统一方式去执行。
python
model.invoke(...)
parser.invoke(...)
LangChain 不是一堆零散 API,而是一套"统一接口 + 可拼装组件"的设计
2.result 到底是什么?
result 的类型不是普通字符串,而是 AIMessage。
为什么不是字符串?
因为模型返回的,不只是"回答正文"。
如果直接只给你一段字符串 ,那太简陋了。
真实调用里,往往还会带很多附加信息,比如:
回答内容
本次请求消耗了多少 token
模型版本
请求 id
其他响应原数据
所以框架把它包装成一个对象,而不是直接给你个 "你好"
3.AIMessage 可以怎么理解?
前面已经见过两种消息:
HumanMessage:人发给模型的
SystemMessage:系统给模型定规则的
那现在模型回给你的,就叫:
AIMessage
4.AIMessage 里最重要的字段:content
它表示:
AI 返回的正文内容。
比如翻译例子里,你真正想看的就是:
python
result.content
这就是"你好"或者"我的名字是小明"这种最终文本。
为什么前面又要引入解析器?
因为如果你每次都自己去:
result.content
那说明你还是在手动扒对象。
后面引入 StrOutputParser(),本质上就是:
让我别再手动管 AIMessage 这些包装,
我只想拿最后的干净字符串。
所以解析器其实是在做整理。
python
messages
↓
model.invoke(messages)
↓
得到 AIMessage
├── content -> 真正正文
├── response_metadata -> 响应本身的信息
└── usage / token 等信息 -> 资源消耗信息
↓
parser.invoke(result)
↓
拿到最终想要的纯字符串/标准格式
LangChain 快速上手的核心,不是单纯调用一次大模型,而是理解它的链式开发思想。
最开始可以按照普通流程来调用模型:先定义模型,再定义消息,然后通过 invoke 调用模型并拿到结果。
模型返回的不是普通字符串,而是 AIMessage 对象,其中 content 表示真正的回答内容。
如果只想得到干净的字符串结果,可以引入 StrOutputParser 作为输出解析器。
当模型组件和解析器组件都定义好之后,就可以用管道符把它们串起来,例如 chain = model | parser。
这样就把原本手动逐步调用的流程,变成了一个自动串联的链式执行流程。
最终只需要执行 chain.invoke(messages),链就会自动按照从前到后的顺序完成整条处理过程。
四:Runnable与LCEL
1.什么是Runnable?
它是LangChain 中"可运行组件"的统一抽象接口。
也就是说,只要一个对象实现了 Runnable 这套标准接口,那么它就可以被 LangChain 当作"一个可以执行的组件"来使用。
比如:
1.模型可以是 Runnable
2.输出解析器可以是 Runnable
所以你才会看到:
为什么调用模型用 invoke(),调用解析器也用 invoke(),调用链还是 invoke()。
因为它们在 LangChain 看来,本质上都是"可运行对象"。
2.Runnable有什么意义?
只要实现了 Runnable ,组件就拥有一致的调用方式,Runnable 的本质,是给 LangChain 的组件定义了一套统一执行规范。
3.为什么 Runnable 很重要?
3.1. 原生接入大模型时的问题
如果你直接使用各家模型自己的 SDK 或 API,那么你会遇到一个现实问题:
模型切换很麻烦。
因为不同模型平台的:
1,请求参数格式不同
2.返回结果结构不同
3.调用方式不同
比如你接 OpenAI,要适配 OpenAI 的接口;
你接 Gemini,要重新适配 Gemini;
你接 DeepSeek,又要再适配一套。
这就像什么?
就像你买了 3 种不同品牌的充电器,每个接口都不一样。
你每换一个手机,就得换一整套线。
3.2.LangChain 是怎么解决的?
LangChain 的思路是:
把不同模型都包装成符合 Runnable 标准的对象。
这样一来,你调用它们时,方式就统一了。
python
result = model.invoke("你好")
你换模型时,很多时候只需要:
改模型定义
不改后续调用逻辑
也就是说,变的是模型实例,不变的是使用方式 。
这就是抽象层的价值。
它把底层差异藏起来,把上层调用统一起来。
4,什么是 LCEL?
LCEL全称:LangChain Expression Language
可以理解成:
LangChain 用来"把多个 Runnable 组织起来"的表达方式
也就是说,Runnable 解决的是:
"组件怎么统一执行"
而 LCEL 解决的是:
"多个组件怎么按顺序连起来执行"
所以 LCEL 更像一种 编排方案。
5.链(Chain)到底是什么?
通过链式方式构造出来的 chain,本质上并不是一个"特殊黑盒",它其实也是一个 Runnable 对象。
也就是:可运行序列。
模型是 Runnable
解析器是 Runnable
链是由多个 Runnable 组合出来的新 Runnable
所以链本身当然也能继续 invoke()。
这就像搭积木:
单块积木能用
多块拼起来还是一个新的整体
这个整体还能继续被拿来使用
这就是 LCEL 的核心思想。
6.LCEL 为什么能实现链式调用?
前一个 Runnable 的输出,会自动作为后一个 Runnable 的输入。
python
chain = model | parser
执行时发生的事情,可以理解成:
1.model.invoke(input) 先执行
2.得到模型输出结果
3.这个输出结果自动传给 parser
4.parser 再继续处理
最终得到整个链的输出
原来你可能得自己写:
python
result1 = model.invoke(input)
result2 = parser.invoke(result1)
现在 LCEL 让你可以直接写成:
python
chain = model | parser
result = chain.invoke(input)
这就是它作为"编排方案"的价值:
既解决了顺序组织问题,也解决了执行衔接问题。
7.为什么链的顺序不能乱?
因为前一个组件的输出类型,必须能被后一个组件接收。
8.定义链的三种方式
方式 1:管道符 |
这是最常用、最推荐的方式。
python
chain = model | parser
优点是直观,一眼就能看出执行顺序。
方式 2:显式构造 RunnableSequence
python
from langchain_core.runnables import RunnableSequence
chain = RunnableSequence(first=model, last=parser)
这种写法更偏底层,适合你想明确知道链本质是什么的时候。
方式 3:使用 pipe()
php
chain = model.pipe(parser)
这和 | 本质上也是同一种思想,只是换了写法。
在 LangChain 中,Runnable 是最核心的底层抽象之一。它为各种组件定义了一套统一的标准接口,因此无论是大模型、输出解析器,还是由多个组件组合而成的链,都可以通过一致的方式调用,例如 invoke()。
这种统一抽象的好处在于,它屏蔽了不同模型和组件之间的底层差异,使开发者能够以一致的方式使用它们,从而降低模型切换和系统扩展的成本。
在 Runnable 的基础上,LangChain 又提供了 LCEL(LangChain Expression Language)来完成组件编排。LCEL 可以把多个 Runnable 按顺序连接起来,形成一个新的 Runnable 对象,通常表现为 RunnableSequence。链式调用的本质,就是前一个组件的输出自动作为后一个组件的输入,从而实现从前到后的流水线执行。
因此可以说:Runnable 解决的是"组件如何统一执行",而 LCEL 解决的是"多个组件如何组合执行"。这也是 LangChain 能够高效组织模型、解析器和其他能力组件的关键所在。