LCEL(Lang Chain Expression Language) 介绍:LangChain 的开发提效技巧

LCEL 介绍

LCEL(Lang Chain Expression Language)是将一些有趣的 Python 概念抽象成一种格式,使得可以构建 LangChain 组件链的 “极简主义” 代码层。

LCEL 具有以下强大的支持:

  • 超快速开发链。
  • 高级特性,如流式处理、异步、并行执行等。
  • 与 LangSmith 和 LangServe 等工具集成。

在本章节中,我们将介绍 LCEL 是什么,它是如何工作的,以及 LCEL 链、管道(pipe)和可运行项(Runnable)的基本要点。

LCEL 语法样例
  • 为了理解 LCEL 语法,让我们首先使用传统的 LangChain 语法构建一个简单的链。
# 导入所需的模块和类
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.chains import LLMChain

# 创建聊天提示模板,指定要获取关于的主题
prompt = ChatPromptTemplate.from_template(
    "给我一个关于{topic}的一句话介绍"
)

# 创建ChatOpenAI模型实例
model = ChatOpenAI(temperature=0)

# 创建输出解析器实例
output_parser = StrOutputParser()

# 创建LLMChain链,将聊天提示、模型和输出解析器组合在一起
chain = LLMChain(
    prompt=prompt,
    llm=model,
    output_parser=output_parser
)

# 运行链,并指定主题为"大语言模型"
out = chain.run(topic="大语言模型")
print(out)
# -> 大语言模型是一种基于深度学习的人工智能技术,能够自动学习和生成自然语言文本,具有广泛的应用领域,如机器翻译、文本生成和对话系统等

这个链的目标是使用 ChatOpenAI 模型生成一个简短的关于指定主题的介绍。我们通过设置温度参数为 0,确保模型生成的输出更加确定性,使得结果更加精确和可控。

  • 而通过 LCEL 语法,我们使用管道操作符(|)而不是 LLMChain 来创建我们的链。
# 使用 LangChain Expression Language(LCEL)创建链
lcel_chain = prompt | model | output_parser

# 运行链,并通过字典传递主题为"大语言模型"
out = lcel_chain.invoke({"topic": "大语言模型"})
print(out)
# -> 大语言模型是一种基于深度学习的人工智能技术,能够自动学习和生成自然语言文本,具有广泛的应用领域,如机器翻译、文本生成和对话系统等

这里的语法并不典型于Python,但只使用了原生Python。| 操作符简单地将左侧的输出传递给右侧的函数。

管道运算符的工作原理

为了理解 LCEL 和管道运算符的工作原理,我们创建自己的管道兼容函数。

当 Python 解释器在两个对象之间看到 | 运算符(如 a | b)时,它会尝试将对象 a 传递给对象 b__or__ 方法。这意味着这些模式是等价的:

# 对象方法
chain = a.__or__(b)
chain("一些输入")

# 管道方法
chain = a | b
chain("一些输入")

考虑到这一点,我们可以构建一个 Runnable 类,它接受一个函数并将其转换为可以使用管道运算符 | 与其他函数链接的函数。

class Runnable:
    def __init__(self, func):
        self.func = func

    def __or__(self, other):
        def chained_func(*args, **kwargs):
            # 其他函数使用这个函数的结果
            return other(self.func(*args, **kwargs))
        return Runnable(chained_func)

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

让我们实现这个,取值 3,加上 5(得到 8),然后乘以 2,最后期望得到 16。

def add_five(x):
    return x + 5

def multiply_by_two(x):
    return x * 2

# 使用 Runnable 包装这些函数
add_five = Runnable(add_five)
multiply_by_two = Runnable(multiply_by_two)

# 使用对象方法运行它们
chain = add_five.__or__(multiply_by_two)
print(chain(3))  # (3 + 5) * 2 = 16
# -> 16

直接使用 __or__ 我们会得到正确答案,让我们尝试使用管道操作符 | 将它们链接在一起:

# 将可运行的函数链接在一起
chain = add_five | multiply_by_two

# 调用链
print(chain(3))  # (3 + 5) * 2 = 16
# -> 16

无论使用哪种方法,我们都会得到相同的响应,这就是 LCEL 在链接组件时使用的管道逻辑。

RunnableLambda 是一个 LangChain 抽象,它允许我们将 Python 函数转换为与管道兼容的函数,类似于我们在之前介绍的 Runnable 类。
让我们尝试一下我们之前的 add_fivemultiply_by_two 函数。

from langchain_core.runnables import RunnableLambda

# 使用 RunnableLambda 包装这些函数
add_five = RunnableLambda(add_five)
multiply_by_two = RunnableLambda(multiply_by_two)

与之前的 Runnable 抽象类似,我们可以使用 | 运算符将 RunnableLambda 抽象连接在一起:

# 将可运行的函数链接在一起
chain = add_five | multiply_by_two

与我们的 Runnable 抽象不同,我们不能通过直接调用它来运行 RunnableLambda 链,而是必须调用 chain.invoke

# 调用链
print(chain.invoke(3))
# -> 16

可以看到使用 RunnableLambda 获得了和 Runnable 类似的结果。

LCEL 介绍小结

以上内容概述了 LangChain 表达语言(LCEL)的基础知识,通过 LCEL 我们可以轻松地构建链式结构。

LCEL 的优劣势多种多样。喜欢 LCEL 的人通常注重其极简的代码风格,以及对流式、并行操作和异步的支持,同时也看好 LCEL 与 LangChain 在组件链式连接方面的良好集成。

然而,有些人对 LCEL 持有不太喜欢的态度。这些人通常指出 LCEL 是对已经非常抽象的库再加一层抽象,语法令人困扰,违背了 Python 之禅,并且需要花费较多的时间来学习新的(或不太常见的)语法。

这两种观点都是有道理的,因为 LCEL 是一种极为不同的方法。然而,由于 LCEL 具有快速开发的特性,目前在 LangChain 开源社区中被广泛使用。对 LCEL 原理的简单了解将有助于读者在今后使用各种 LangChain 代码时更加得心应手。

你可能感兴趣的:(langchain,语言模型,人工智能,深度学习)