LangChain的LCEL和Runnable你搞懂了吗
LangChain的LCEL估计行业内的朋友都听过,但是LCEL里的RunnablePassthrough、RunnableParallel、RunnableBranch、RunnableLambda又是什么意思?什么场景下用?
1、LCEL的定义和原理
LangChain的核心是Chain,即对多个组件的一系列调用。
LCEL是LangChain 定义的表达式语言,是一种更加高效简洁的调用一系列组件的方式。
LCEL使用方式就是:以一堆管道符("|")串联所有实现了Runnable接口的组件。
比如这样:
prompt_tpl = ChatPromptTemplate.from_messages(
[
("system", "{parser_instructions}"),
("human", "列出{cityName}的{viewPointNum}个著名景点。"),
]
)
output_parser = CommaSeparatedListOutputParser()
parser_instructions = output_parser.get_format_instructions()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain = prompt_tpl | model | output_parser
response = chain.invoke(
{"cityName": "南京", "viewPointNum": 3, "parser_instructions": parser_instructions}
)
所以LangChain为了让组件能以LCEL的方式快速简洁的被调用,计划将所有组件都实现Runnable接口。比如我们常用的PromptTemplate 、LLMChain 、StructuredOutputParser 等等。
管道符("|")在Python里就类似or运算(或运算),比如A|B,就是A.or(B)。
那对应到LangChain的Runnable接口里,这个or运算是怎么实现的呢?一起看到源码:

LangChain通过or将所有的Runnable串联起来,在通过invoke去一个个执行,上一个组件的输出,作为下一个组件的输入。

LangChain这风格怎么有点像神经网络呀,不得不说,这个世界到处都是相似的草台班子。嗨!
总结起来讲就是:LangChain的每个组件都实现了Runnable,通过LCEL方式,将多个组件串联到一起,最后一个个执行每个组件的invoke方法。上一个组件的输出是下一个组件的输入。

2、Runnable的含义和应用场景
2.1、RunnablePassthrough
定义
RunnablePassthrough 主要用在链中传递数据。RunnablePassthrough一般用在链的第一个位置,用于接收用户的输入。如果处在中间位置,则用于接收上一步的输出。
应用场景
比如,依旧使用上面的例子,接受用户输入的城市,如果输入城市是南京,则替换成北京,其余不变。代码如下。此处的{}和RunnablePassthrough.assign()是同一个语义。
chain = (
{
"cityName": lambda x: '北京' if x["cityName"] == '南京' else x["cityName"],
"viewPointNum": lambda x: x["viewPointNum"],
"parser_instructions": lambda x: x["parser_instructions"],
}
| prompt_tpl
| model
| output_parser
)
2.2、RunnableParallel
定义
RunnableParallel看名字里的Parallel就猜到一二,用于并行执行多个组件。通过RunnableParallel,可以实现部分组件或所有组件并发执行的需求。
应用场景
比如,同时要执行两个任务,一个列出城市著名景点,一个列出城市著名书籍。
prompt_tpl_1 = ChatPromptTemplate.from_messages(
[
("system", "{parser_instructions}"),
("human", "列出{cityName}的{viewPointNum}个著名景点。"),
]
)
prompt_tpl_2 = ChatPromptTemplate.from_messages(
[
("system", "{parser_instructions}"),
("human", "列出关于{cityName}历史的{viewPointNum}个著名书籍。"),
]
)
output_parser = CommaSeparatedListOutputParser()
parser_instructions = output_parser.get_format_instructions()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain_1 = prompt_tpl_1 | model | output_parser
chain_2 = prompt_tpl_2 | model | output_parser
chain_parallel = RunnableParallel(view_point=chain_1, book=chain_2)
response = chain_parallel.invoke(
{"cityName": "南京", "viewPointNum": 3, "parser_instructions": parser_instructions}
)
2.3、RunnableBranch
定义
RunnableBranch主要用于多分支子链的场景,为链的调用提供了路由功能,这个有点类似于LangChain的路由链。我们可以创建多个子链,然后根据条件选择执行某一个子链。
应用场景
比如,有多个回答问题的链,先根据问题找到分类,然后在使用具体的链回答问题。
model = ChatOpenAI(model="gpt-3.5-turbo")
output_parser = StrOutputParser()
# 准备2条目的链:一条物理链,一条数学链
# 1. 物理链
physics_template = """
你是一位物理学家,擅长回答物理相关的问题,当你不知道问题的答案时,你就回答不知道。
具体问题如下:
{input}
"""
physics_chain = PromptTemplate.from_template(physics_template) | model | output_parser
# 2. 数学链
math_template = """
你是一个数学家,擅长回答数学相关的问题,当你不知道问题的答案时,你就回答不知道。
具体问题如下:
{input}
"""
math_chain = PromptTemplate.from_template(math_template) | model | output_parser
# 4. 其他链
other_template = """
你是一个AI助手,你会回答一下问题。
具体问题如下:
{input}
"""
other_chain = PromptTemplate.from_template(other_template) | model | output_parser
classify_prompt_template = """
请你对以下问题进行分类,将问题分类为"数学"、"物理"、"其它",不需要返回多个分类,返回一个即可。
具体问题如下:
{input}
分类结果:
"""
classify_chain = PromptTemplate.from_template(classify_prompt_template) | model | output_parser
answer_chain = RunnableBranch(
(lambda x: "数学" in x["topic"], math_chain),
(lambda x: "物理" in x["topic"], physics_chain),
other_chain
)
final_chain = {"topic": classify_chain, "input": itemgetter("input")} | RunnableLambda(print_info) | answer_chain
# final_chain.invoke({"input":"地球的半径是多少?"})
final_chain.invoke({"input":"对y=x求导的结果是多少?"})
2.4、RunnableLambda
定义
要说牛批还得是RunnableLambda,它可以将Python 函数转换为 Runnable对象。这种转换使得任何函数都可以被看作 LCEL 链的一部分,我们把自己需要的功能通过自定义函数 + RunnableLambda的方式包装一下,集成到 LCEL 链中,这样算是可以跟任何外部系统打通了。
应用场景
比如,在执行过程中,想在中间插入一段自定义功能(如 打印日志 等),可以通过自定义函数 + RunnableLambda的方式实现。
def print_info(info: str):
print(f"info: {info}")
return info
prompt_tpl_1 = ChatPromptTemplate.from_messages(
[
("system", "{parser_instructions}"),
("human", "列出{cityName}的{viewPointNum}个著名景点。"),
]
)
output_parser = CommaSeparatedListOutputParser()
parser_instructions = output_parser.get_format_instructions()
model = ChatOpenAI(model="gpt-3.5-turbo")
chain_1 = prompt_tpl_1 | model | RunnableLambda(print_info) | output_parser
response = chain_1.invoke(
{"cityName": "南京", "viewPointNum": 3, "parser_instructions": parser_instructions}
)
3、总结
本篇主要聊了LangChain的LCEL表达式,以及LangChain链的原理,以及常用的几个Runnable的定义和应用场景,希望对你有帮助。
近期我准备推出一个关于《助力开发者加持AI技术》的专栏,感兴趣的小伙伴可以加微信交流。
本篇完结!欢迎 关注、加微信(yclxiao)交流、二维码如下!!!
原文链接:https://mp.weixin.qq.com/s/l-EPH0hsmzQousPz8-MXcQ

LangChain的LCEL和Runnable你搞懂了吗的更多相关文章
- 轻松搞懂Java中的自旋锁
前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...
- 彻底搞懂Javascript的“==”
本文转载自:@manxisuo的<通过一张简单的图,让你彻底地.永久地搞懂JS的==运算>. 大家知道,==是JavaScript中比较复杂的一个运算符.它的运算规则奇怪,容让人犯错,从而 ...
- 完全搞懂傅里叶变换和小波(2)——三个中值定理<转载>
书接上文,本文章是该系列的第二篇,按照总纲中给出的框架,本节介绍三个中值定理,包括它们的证明及几何意义.这三个中值定理是高等数学中非常基础的部分,如果读者对于高数的内容已经非常了解,大可跳过此部分.当 ...
- 完全搞懂傅里叶变换和小波(1)——总纲<转载>
无论是学习信号处理,还是做图像.音视频处理方面的研究,你永远避不开的一个内容,就是傅里叶变换和小波.但是这两个东西其实并不容易弄懂,或者说其实是非常抽象和晦涩的! 完全搞懂傅里叶变换和小波,你至少需要 ...
- 不想再被鄙视?那就看进来! 一文搞懂Python2字符编码
程序员都自视清高,觉得自己是创造者,经常鄙视不太懂技术的产品或者QA.可悲的是,程序员之间也相互鄙视,程序员的鄙视链流传甚广,作为一个Python程序员,自然最关心的是下面这幅图啦 我们项目组一值使用 ...
- 来一轮带注释的demo,彻底搞懂javascript中的replace函数
javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...
- java线程间通信:一个小Demo完全搞懂
版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...
- for语句,你真正搞懂了吗?
今天看书时,无意间看到了这个知识点,啥知识点?也许在各位大神看来,那是再简单不过的东西了. 说来惭愧.原来直到今天我才真正搞懂for语句. for语句的结构如下所示: for(语句A;语句B;语句C) ...
- 每个java初学者都应该搞懂的问题
对于这个系列里的问题,每个学JAVA的人都应该搞懂.当然,如果只是学JAVA玩玩就无所谓了.如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列.内容均来自于CSDN的经典老贴. ...
- 一天搞懂深度学习-训练深度神经网络(DNN)的要点
前言 这是<一天搞懂深度学习>的第二部分 一.选择合适的损失函数 典型的损失函数有平方误差损失函数和交叉熵损失函数. 交叉熵损失函数: 选择不同的损失函数会有不同的训练效果 二.mini- ...
随机推荐
- Qt开发技术:Q3D图表开发笔记(四):Q3DSurface三维曲面图颜色样式详解、Demo以及代码详解
前言 qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的. 其中就包括华丽绚烂的三维图表,数据量不大的时候是可 ...
- Vue第三方库与插件实战手册
title: Vue第三方库与插件实战手册 date: 2024/6/8 updated: 2024/6/8 excerpt: 这篇文章介绍了如何在Vue框架中实现数据的高效验证与处理,以及如何集成E ...
- Prime Solutions
Prime Solutions 以下是一段中学时代的惨痛回忆-每当学到排列组合的单元时,最痛苦的不是分析题目,也不是带错公式或计算错误,而是所谓的「苦工题」,以下这题是个例子:给定正整数N与S,求出方 ...
- .htaccess伪静态规则
Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` .htaccess伪静态规则 日期:2017-12-4 阿 ...
- 云服务器通过内网穿透的方式ssh访问内网服务器
云服务器通过内网穿透的方式ssh访问内网服务器 背景 买了一台云服务器,了解到可以通过外部服务器连接到公司内部服务器. 为了加快办公的效率,配置了一下. 以Ubuntu为例. 原文(有删改):http ...
- shell 根据 指定列 进行 去除 重复行
根据指定列进行去除重复行 这里的重复是指如果两行的某一列数据相同,则认为是重复数据. 例如:第1行与第2行数据,其中的第2列(以- 作为分隔符)明显是重复的. 100069 - ARM Compile ...
- k8s网络原理之flannel
首先当你创建一个k8s集群后一般会存在三种IP分别是,Pod IP,Node IP,Cluster IP 其中一个Cluster IP之下包含多个Node IP,而一个Node IP之下又包含多个Po ...
- win10 搭建 npm 环境
前言 最近,根据CSDN和博客园等文章的帮助下,搭建了一个npm的环境,现在将搭建过程记录下来,留作参考. 搭建过程 下载nodejs,我是使用的zip包安装的,安装包官网地址https://node ...
- virtualbox ubuntu拓展存储空间
1. 关闭虚拟机,右键点击virtualbox图标,选择打开文件位置,记录下路径: 2. 找到需要拓容的ubuntu虚拟机的.vdi文件,记录下路径: 3. windows命令行转到virtualbo ...
- ComfyUI进阶篇:ComfyUI核心节点(一)
ComfyUI进阶篇:ComfyUI核心节点(一) 前言: 学习ComfyUI是一场持久战.当你掌握了ComfyUI的安装和运行之后,会发现大量五花八门的节点.面对各种各样的工作流和复杂的节点种类,可 ...