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接口。比如我们常用的PromptTemplateLLMChainStructuredOutputParser 等等。

管道符("|")在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你搞懂了吗的更多相关文章

  1. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...

  2. 彻底搞懂Javascript的“==”

    本文转载自:@manxisuo的<通过一张简单的图,让你彻底地.永久地搞懂JS的==运算>. 大家知道,==是JavaScript中比较复杂的一个运算符.它的运算规则奇怪,容让人犯错,从而 ...

  3. 完全搞懂傅里叶变换和小波(2)——三个中值定理<转载>

    书接上文,本文章是该系列的第二篇,按照总纲中给出的框架,本节介绍三个中值定理,包括它们的证明及几何意义.这三个中值定理是高等数学中非常基础的部分,如果读者对于高数的内容已经非常了解,大可跳过此部分.当 ...

  4. 完全搞懂傅里叶变换和小波(1)——总纲<转载>

    无论是学习信号处理,还是做图像.音视频处理方面的研究,你永远避不开的一个内容,就是傅里叶变换和小波.但是这两个东西其实并不容易弄懂,或者说其实是非常抽象和晦涩的! 完全搞懂傅里叶变换和小波,你至少需要 ...

  5. 不想再被鄙视?那就看进来! 一文搞懂Python2字符编码

    程序员都自视清高,觉得自己是创造者,经常鄙视不太懂技术的产品或者QA.可悲的是,程序员之间也相互鄙视,程序员的鄙视链流传甚广,作为一个Python程序员,自然最关心的是下面这幅图啦 我们项目组一值使用 ...

  6. 来一轮带注释的demo,彻底搞懂javascript中的replace函数

    javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...

  7. java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

  8. for语句,你真正搞懂了吗?

    今天看书时,无意间看到了这个知识点,啥知识点?也许在各位大神看来,那是再简单不过的东西了. 说来惭愧.原来直到今天我才真正搞懂for语句. for语句的结构如下所示: for(语句A;语句B;语句C) ...

  9. 每个java初学者都应该搞懂的问题

    对于这个系列里的问题,每个学JAVA的人都应该搞懂.当然,如果只是学JAVA玩玩就无所谓了.如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列.内容均来自于CSDN的经典老贴. ...

  10. 一天搞懂深度学习-训练深度神经网络(DNN)的要点

    前言 这是<一天搞懂深度学习>的第二部分 一.选择合适的损失函数 典型的损失函数有平方误差损失函数和交叉熵损失函数. 交叉熵损失函数: 选择不同的损失函数会有不同的训练效果 二.mini- ...

随机推荐

  1. 7.28考试总结(NOIP模拟26)[神炎皇·降雷皇·幻魔皇]

    或许只需一滴露水,便能守护这绽放的花朵. 前言 疯狂挂分,本来T2是想用树状数组优化一下的不知道为啥后来看了一下就少看了一层循环, 然后就想,我都 n 的复杂度了,足以搞过第一问了,还优化啥呀.... ...

  2. ES5的继承语法

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  3. CSP-S2019 江西 题解

    为什么有 \(5\) 道题? [CSP-S2019 江西] 和积和 简单化一下式子: \[(n + 1) \times \sum A_i \times B_i - (\sum A_i) \times ...

  4. http请求方式GET,POST工具类RestTemplate

    http请求方式GET,POST工具类RestTemplate import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serial ...

  5. 浅拷贝、深拷贝与序列化【初级Java必需理解的概念】

    浅拷贝 首先创建两个类,方便理解浅拷贝 @Data class Student implements Cloneable{ //年龄和名字是基本属性 private int age; private ...

  6. 漏洞复现之CVE-2012-1823(PHP-CGI远程代码执行)

    关于CGI知识点 `CGI模式下的参数: -c 指定php.ini文件的位置 -n 不要加载php.ini文件 -d 指定配置项 -b 启动fastcgi进程 -s 显示文件源码 -T 执行指定次该文 ...

  7. Exception:kylin构建cube, Cannot modify mapReduce.queue.name at runtime

    Failed to open new session: java.lang.IllegalArgumentException: Cannot modify tez.queue.name at runt ...

  8. C++类与对象详解

    什么是类和对象 类和对象的概念 类是对象的抽象,对象是对客观事物的抽象. 用通俗的话来说: 类是类别的意思,是数据类型. 对象是类别下的具体事物. 也就是说: 类是数据类型,对象是变量. 比如: 水果 ...

  9. Apache Kylin(二)在EMR上搭建Kylin

    EMR上搭建kylin 1. 启动EMR集群 根据官网说明: http://kylin.apache.org/docs21/install/kylin_aws_emr.html 启动EMR时,若是 h ...

  10. EF Core并发控制

    EF Core并发控制 并发控制概念 并发控制:避免多个用户同时操作资源造成的并发冲突问题. 最好的解决方案:非数据库解决方案 数据库层面的两种策略:悲观.乐观 悲观锁 悲观并发控制一般采用行锁 ,表 ...