相信第一次看到这个名词的时候,你一定也和我一样的疑问:什么是LCEL?LCEL有什么用?接下来我将通过官方文档结合自己的一些代码样例带你简单了解一下LCEL。
什么是LCEL
首先来看LangChain中文文档中对LCEL的基本介绍: LangChain表达式语言(LCEL)是一种声明式方法,可以轻松地将链组合在一起。 LCEL从第一天起就被设计成支持将原型投入生产,无需更改代码,从最简单的 “prompt + LLM” 链到最复杂的链(我们已经看到人们在生产中成功运行了具有数百个步骤的LCEL链)。
首先在介绍中我们可以知道,LCEL是一种声明式方法,它可以将各种各样的链组合在一起,简单到“Prompt+LLM”链,复杂到具有数百个步骤的LCEL链,都可以通过LCEL实现组合。可能你会想问:所谓的声明式方法到底是怎样声明的?LCEL又是如何将各种各样的链组合在一起的呢?带着疑问,接下来我将通过代码和理论相结合的方式,让你对此有个基本的认识。
如何使用LCEL
首先,我们先来看下面的代码:
import os
from langchain.chains.llm import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
template = "什么是{thing}?"
prompt = PromptTemplate.from_template(template)
llm = ChatOpenAI(model=os.environ.get("LLM_MODELEND"), temperature=0.5, max_tokens=512)
chain = LLMChain(
prompt=prompt,
llm=llm
)
invoke = chain.invoke({"thing": "人工智能"})
print(invoke)
很简单,这就是一个调用最基础LLM链向大模型提问什么是人工智能的一段代码,那如果我们使用了LCEL,这段代码又会是怎样的呢?请看下面的代码:
import os
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
template = "什么是{thing}?"
prompt = PromptTemplate.from_template(template)
llm = ChatOpenAI(model=os.environ.get("LLM_MODELEND"), temperature=0.5, max_tokens=512)
llm_chain = prompt | llm
invoke = llm_chain.invoke({"thing": "人工智能"})
print(invoke)
可以看到,两段代码实际上的区别是很小的,为数不多的不同之处可能就在于第二段代码在构建链的时候,使用了更为简洁的LCEL表达式来构建LLM链,以及在import的时候可以少导入一个LLMChain的模块(doge)。那实际运行过程中会有什么不同呢?
不使用LCEL代码的运行结果:
使用LCEL后代码的运行结果:
可以看到,两段代码的输出结果的内容基本上是相同的(因为LLM是生成式大模型,即使我们的提问是相同的,但是两次它给我们的输出,也就是描述,也可能是不相同的),区别之处在于返回的结果中字段的key不一样,以及不使用LCEL时,代码在运行时会出现警告(Warning),而在使用LCEL后警告就消失了。接下来让我们看一下具体Warning的内容:
LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed
in 1.0. Use :meth:`~RunnableSequence, e.g., `prompt | llm`` instead.
chain = LLMChain(
借助翻译,将其翻译为我们大家都能够理解的内容:
翻译之后这段警告提示的信息就很明确了,说LLMChain这个类实际上在LangChain的0.1.17版本中就已经被弃用了(因为我目前环境中使用的是LangChain的0.3.x版本),而且将会在1.0(正式发布的版本)中删除,我们可以用runnable序列代替,举的例子正是我们在使用LCEL版本代码中用到的格式,可以看出,langchain实际上是鼓励我们现在使用LCEL的,知道了这个之后,使用LCEL之后在运行时就不会出现警告的原因就很明显了吧。
再来分析LCEL组成链的格式,前面已经说过了,LCEL是一种声明式的方法(可以类比Vue中声明式和组合式的区别),具体的声明格式正如我们前面代码中所使用的一样:
prompt | llm
是通过类似于Unix系统中管道(Pipe)的形式,通过在组件之间使用“|”来进行连接声明的,我们在这里仅仅使用了最简单的链,实际上正如开篇提到的,可以有很多的小组件使用“|”进行连接,不过在使用过程中,还需要特别注意这些组件的组合顺序,因为这实际上像一条流水线,需要顺序执行,如果顺序不同,就会造成无法运行甚至更严重的后果。
最后,不知道在前面你有没有注意到,我实际上是把不需要导入LLMChian链这一部分进行了加粗的,因为我们在使用LCEL的代码中并没有指明我们使用的是LLMChain,但是我们仍然可以得到正确的结果(如果再为LCEL加上输出解析组件,实际上就与我们前面讲到MODEL I/O的流程是一样的了)。这就说明使用了LCEL之后,langchain并不关心我们具体使用的是哪种链,只要是符合langchain规范的runnable序列,就可以通过LCEL来进行构建并达到与原本代码基本相同(甚至更好)的结果。这就为我们以后的开发提供了相当大的便利,试想,如果我们在开发过程中一直关注我们要使用什么链,导入什么包,我想对于开发人员来说,这是十分痛苦的。
总结
简单总结一下,LCEL实际上就是声明链的一种方式,也可以理解为是一种生成链的特殊语法,为我们提供了更便捷的链的生成以及使用方式。此外,langchain实际上在目前已经算是比较前沿的技术了,但是我们现在在学习过程中仍然会遇到很多方法或者类被弃用的警告,这说明langcahin的发展实际上是很快的,需要我们不断去学习,相信未来langchain也会越来越好,为我们使用AI带来更多的便利。