首页 > 其他分享 >LangChain的LCEL和Runnable你搞懂了吗

LangChain的LCEL和Runnable你搞懂了吗

时间:2024-08-01 20:28:47浏览次数:15  
标签:Runnable chain parser LangChain LCEL output 搞懂 model instructions

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

标签:Runnable,chain,parser,LangChain,LCEL,output,搞懂,model,instructions
From: https://www.cnblogs.com/mangod/p/18337425

相关文章

  • Java并发(十六)一文搞懂Java 线程池原理
    简介什么是线程池线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。为什么要用线程池如果并发请求数量很多,但每个线程执行的时间很短,就会出现频繁的创建和销毁线程。如此一来,会大大降低系统的效率,可能频繁创建和销毁线程的时间......
  • 使用 LCEL 构建简单的LLM应用程序
    摘要:本文是对自己学习基于LangChain学习LLM开发的记录,根据LangChain官网使用LCEL构建了一个简单的LLM应用程序。1.获取各个LLM模型的api。比如OPENAI、讯飞星火大模型等。本文使用讯飞星火大模型进行开发。使用OPENAI的api显示超额,但是明明有18美元的余额,如果读者知道的话请解......
  • 一篇文章搞懂docker日志的查看(转)
    命令docker查看容器日志使用的命令是:dockerlogs下面是dockerlogs的命令格式$dockerlogs[OPTIONS]CONTAINEROptions:--details显示更多的信息-f,--follow跟踪实时日志--sincestring显示自某个timestamp之后的日志,或相对时间,......
  • 记录--终于搞懂了!原来vue3中template使用ref无需.value是因为这个
    ......
  • 什么是Cookie与Session?(一文搞懂)
    在Web开发中,用户状态管理是一个重要的概念。由于HTTP协议的无状态性,每次请求都是独立的,这意味着服务器无法识别连续的请求是否来自同一用户。为了克服这一限制,Cookie和Session应运而生。本文将详细讲解Cookie和Session的概念、工作原理、优缺点以及它们的使用场景,并通过示例帮......
  • 一文彻底搞懂浏览器事件机制、事件委托、事件冒泡、事件循环、Event Loop、react事件
    一、事件是什么?事件模型?事件是用户操作网页时发生的交互动作,比如click/move,事件除了用户触发的动作外,还可以是文档加载,窗口滚动和大小调整。事件被封装成一个event对象,包含了该事件发生时的所有相关信息(event的属性)以及可以对事件进行的操作(event的方法)。事件是用......
  • 【经典问题】精析:一文搞懂 动态规划解决 0-1背包问题
    背包问题核心元素:数量为n的物品,容量为size的背包,给出每个物品的重量weight和价值val,求背包能装的物品最大总价值。求解思路的本质:从小体量(少物品,少容量)的问题开始,不断求出局部最优解,然后以此为基础放大问题(求得较多物品,较大容量下最优解)重复上述过程,直到求出题目要求的最优......
  • 一文搞懂系列——PEM文件解析流程
    背景前几周,协助同事解决了SM2软签名的需求,其流程可参考终于解决了!!!基于GmSSL的SM2签名算法及思路分享。但是在解决这个问题的过程中,让我想起了一些不好的回忆:曾经在大众项目中,也接触过椭圆曲线算法签名。其中因为平台下发的公钥格式,由于双方理解不一致,导致最终调试很久,并......
  • 【Git-驯化】一文搞懂git中代码回测reset详细使用方法
    【Git-驯化】一文搞懂git中代码回测reset详细使用方法 本次修炼方法请往下查看......
  • 使用 LCEL 链接 langchain 响应
    我已经开始与Langchain合作来感受它,很多视频似乎已经过时了。经过一些研究,我了解到LCEL正在被使用,因为其他方法似乎已被弃用。在我的代码中,我尝试使用一个链的输出作为另一个链的输入,但它似乎不起作用。defmain():prompt1=ChatPromptTemplate.from_messages([......