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. iOS开发环境theos开发环境搭建与介绍

    iOS开发环境theos开发环境搭建与介绍 标签(空格分隔): 越狱开发-第一篇 1. 环境准备 一台Mac,本人的机器是MacBook Air (13-inch, Mid 2013),系统是10.1 ...

  2. LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架]

    LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架] 训练后的模型会用于推理或者部署.推理即使用 ...

  3. fabric compose文件解读(peer篇)

    peer是fabric中的基础单元,主要负责背书,验证交易合法性,保存区块链数据,查询数据.peer与orderer配合完成区块链的全部功能,orderer可以比作是管理员,peer属于是干货的员工, ...

  4. pandas基础--数据结构:索引对象

    pandas含有是数据分析工作变得更快更简单的高级数据结构和操作工具,是基于numpy构建的. 本章节的代码引入pandas约定为:import pandas as pd,另外import numpy ...

  5. kettle从入门到精通 第六十一课 ETL之kettle 任务调度器,轻松使用xxl-job调用kettle中的job和trans

    1.大家都知道kettle设计的job流程文件有个缺点:只能设置简单的定时任务,无法设置复杂的如支持cron表达式的job. 今天给大家分享一个使用xxl-job调度carte的流程文件的示例.整个调 ...

  6. Grafana 开源了一款 eBPF 采集器 Beyla

    eBPF 的发展如火如荼,在可观测性领域大放异彩,Grafana 近期也发布了一款 eBPF 采集器,可以采集服务的 RED 指标,本文做一个尝鲜介绍,让读者有个大概了解. eBPF 基础介绍可以参考 ...

  7. Idea SpringBoot 子模块 加载不到该子模块根目录config下面的配置文件

    Idea SpringBoot 子模块 加载不到该子模块根目录config下面的配置文件 import org.mybatis.spring.annotation.MapperScan; import ...

  8. .Net Core5.0中Autofac依赖注入整合多层,项目中可直接用

    一.配置Autofac替换内置DI 1.安装Nuget包:Autofac,Autofac.Extensions.DependencyInjection 2.Program.cs中CreateHostB ...

  9. 3D捕鱼大富翁源码分析

    ​ 今天接受了一个捕鱼的源码,技术栈采用: 客户端:Unity 服务端:Java 数据库:mysql 缓存:redis 先来几张成品图 ​编辑​编辑 ​编辑​编辑 ​编辑 在代码中看到有腾讯推广渠道, ...

  10. Linux下命令行开启关闭触摸板

    Linux下命令行开启关闭触摸板 从设备列表中找到触摸板的设备id,调用xinput可以控制设备的开启关闭. 示例代码如下: #!/bin/bash device=`xinput list | gre ...