Skip to main content
Open on GitHub

LangChain 表达式语言 (LCEL)

Prerequisites

LangChain Expression Language (LCEL) 采用了一种声明式的方法,从现有的Runnables构建新的Runnables。

这意味着你描述的是应该发生什么,而不是如何发生,从而允许LangChain优化链的运行时执行。

我们通常将使用LCEL创建的Runnable称为“链”。重要的是要记住,“链”是Runnable,并且它实现了完整的Runnable接口

note
  • LCEL 速查表展示了涉及 Runnable 接口和 LCEL 表达式的常见模式。
  • 请参阅以下操作指南列表,这些指南涵盖了使用LCEL的常见任务。
  • 内置的Runnables列表可以在LangChain Core API 参考中找到。这些Runnables中的许多在使用LCEL在LangChain中组合自定义“链”时非常有用。

LCEL的优势

LangChain 通过多种方式优化了使用 LCEL 构建的链的运行时执行:

  • 优化的并行执行:使用RunnableParallel并行运行Runnables,或使用Runnable Batch API通过给定链并行运行多个输入。并行执行可以显著减少延迟,因为处理可以并行进行而不是顺序进行。
  • 保证的异步支持:任何使用LCEL构建的链都可以通过Runnable Async API异步运行。这在服务器环境中运行链时非常有用,因为您可能希望同时处理大量请求。
  • 简化流式处理:LCEL链可以进行流式处理,允许在链执行时逐步输出。LangChain可以优化输出的流式处理,以最小化首次输出时间(从聊天模型llm输出的第一块内容所经过的时间)。

其他好处包括:

  • 无缝的LangSmith追踪 随着你的链变得越来越复杂,了解每一步到底发生了什么变得越来越重要。 使用LCEL,所有步骤都会自动记录到LangSmith,以实现最大的可观察性和可调试性。
  • 标准API:因为所有的链都是使用Runnable接口构建的,所以它们可以像任何其他Runnable一样使用。
  • 可使用LangServe部署: 使用LCEL构建的链可以部署用于生产环境。

我应该使用LCEL吗?

LCEL 是一种编排解决方案——它允许 LangChain 以优化的方式处理链的运行时执行。

虽然我们见过用户在生产环境中运行包含数百个步骤的链,但我们通常建议使用LCEL来处理更简单的编排任务。当应用程序需要复杂的状态管理、分支、循环或多个代理时,我们建议用户利用LangGraph

在LangGraph中,用户定义指定应用程序流程的图。这使得用户在需要LCEL时,可以继续在单个节点中使用LCEL,同时更容易定义更易读和可维护的复杂编排逻辑。

以下是一些指导原则:

  • 如果您正在进行一次LLM调用,您不需要LCEL;而是直接调用底层的聊天模型
  • 如果你有一个简单的链(例如,prompt + llm + parser,简单的检索设置等),并且你正在利用LCEL的优势,那么LCEL是一个合理的选择。
  • 如果您正在构建一个复杂的链(例如,带有分支、循环、多个代理等),请使用LangGraph。请记住,您始终可以在LangGraph的各个节点中使用LCEL。

组合原语

LCEL 链是通过将现有的 Runnables 组合在一起构建的。两个主要的组合原语是 RunnableSequenceRunnableParallel

许多其他组合原语(例如,RunnableAssign)可以被视为这两种原语的变体。

note

你可以在LangChain Core API 参考中找到所有组合原语的列表。

可运行序列

RunnableSequence 是一个组合原语,允许你按顺序“链式”连接多个可运行对象,其中一个可运行对象的输出作为下一个可运行对象的输入。

from langchain_core.runnables import RunnableSequence
chain = RunnableSequence([runnable1, runnable2])
API Reference:RunnableSequence

使用一些输入调用chain

final_output = chain.invoke(some_input)

对应以下内容:

output1 = runnable1.invoke(some_input)
final_output = runnable2.invoke(output1)
note

runnable1runnable2 是用于链接任何 Runnable 的占位符。

可运行并行

RunnableParallel 是一个组合原语,允许您并发运行多个可运行对象,并为每个对象提供相同的输入。

from langchain_core.runnables import RunnableParallel
chain = RunnableParallel({
"key1": runnable1,
"key2": runnable2,
})
API Reference:RunnableParallel

使用一些输入调用chain

final_output = chain.invoke(some_input)

将生成一个final_output字典,其键与输入字典相同,但值被替换为相应可运行对象的输出。

{
"key1": runnable1.invoke(some_input),
"key2": runnable2.invoke(some_input),
}

回想一下,runnables 是并行执行的,因此虽然结果与上面显示的字典推导相同,但执行时间要快得多。

note

RunnableParallel支持同步和异步执行(就像所有的Runnables一样)。

  • 对于同步执行,RunnableParallel 使用 ThreadPoolExecutor 来并发运行可运行对象。
  • 对于异步执行,RunnableParallel 使用 asyncio.gather 来并发运行可运行对象。

组合语法

RunnableSequenceRunnableParallel 的使用非常普遍,因此我们为它们创建了一种简写语法。这有助于使代码更具可读性和简洁性。

The | 操作符

我们已经重载|操作符,以便从两个Runnables创建一个RunnableSequence

chain = runnable1 | runnable2

等同于:

chain = RunnableSequence([runnable1, runnable2])

The .pipe 方法`

如果你对操作符重载有道德上的顾虑,你可以使用.pipe方法代替。这等同于|操作符。

chain = runnable1.pipe(runnable2)

强制转换

LCEL 应用自动类型强制转换,使组合链更容易。

如果你不理解类型强制转换,你可以直接使用RunnableSequenceRunnableParallel类。

这将使代码更加冗长,但也会使其更加明确。

字典到RunnableParallel

在LCEL表达式中,字典会自动转换为RunnableParallel

例如,以下代码:

mapping = {
"key1": runnable1,
"key2": runnable2,
}

chain = mapping | runnable3

它会自动转换为以下内容:

chain = RunnableSequence([RunnableParallel(mapping), runnable3])
caution

你必须小心,因为mapping字典不是一个RunnableParallel对象,它只是一个字典。这意味着以下代码将引发AttributeError

mapping.invoke(some_input)

函数到RunnableLambda

在LCEL表达式中,函数会自动转换为RunnableLambda

def some_func(x):
return x

chain = some_func | runnable1

它会自动转换为以下内容:

chain = RunnableSequence([RunnableLambda(some_func), runnable1])
caution

你必须小心,因为lambda函数不是一个RunnableLambda对象,它只是一个函数。这意味着以下代码将引发一个AttributeError

lambda x: x + 1.invoke(some_input)

遗留链

LCEL 旨在提供一致的行为和定制性,超越传统的子类链,如 LLMChainConversationalRetrievalChain。许多这些传统链隐藏了重要的细节,如提示,随着更多可行的模型的出现,定制变得越来越重要。

如果您目前正在使用这些旧版链之一,请参阅此指南以获取迁移指导

有关如何使用LCEL执行特定任务的指南,请查看相关操作指南


这个页面有帮助吗?