LCEL & Runnable

What is LCEL & Runnable?

  • Runnable is a standard interface (protocol) that represents anything that can be “run” — it takes an input and produces an output. It is the basic building block of LangChain.
    是一个标准接口(协议),代表任何“可运行”的东西——它接收输入并产生输出。它是 LangChain 的基础构建块
  • LCEL (LangChain Expression Language) is a declarative way to compose chains using the pipe operator |. It allows you to string together multiple Runnables into a processing pipeline
     是一种声明式语言,通过管道操作符 | 来组合链。它允许你将多个 Runnable 串联成一个处理流水线

Core Content

Runnable Interface

Runnable is an abstract interface that defines five core methods.

MethodTypePurpose
invoke(input)同步单次调用,阻塞式返回完整结果
ainvoke(input)异步单次异步调用,非阻塞
stream(input)同步流式输出,逐 token 生成结果
batch([inputs])同步批量处理多个输入
abatch([inputs])异步异步批量处理

LCEL (LangChain Expression Language)

LCEL is the syntax/rules for composing Runnables. The core syntax is:

chain = component1 | component2 | component3
result = chain.invoke(input)

Data flows left to right: output of component1 → input of component2 → output of component2 → input of component3

 Key Runnable Primitives

PrimitivePurposeExample Use
RunnablePassthroughPass data through unchanged or with transformationsAdd default values, transform inputs
RunnableParallelRun multiple Runnables in parallelFetch data from multiple sources simultaneously
RunnableBranchConditional routing based on inputIf/else logic in chains
RunnableLambdaWrap any Python function as a RunnableCustom logic in the pipeline
RunnableSequenceSequential execution (this is what | creates)Standard linear pipeline

Code Implementation

Basic LCEL Chain

# ============================================================
# 导入必要的模块
# Import necessary modules
# ============================================================
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

# ============================================================
# 1. 创建各个组件(每个都实现了 Runnable 接口)
# 1. Create individual components (all implement Runnable interface)
# ============================================================

# 提示模板:定义与 LLM 对话的格式
# Prompt template: defines the format for talking to the LLM
prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant that translates {source_lang} to {target_lang}."),
    ("human", "{text}")
])

# LLM 模型:实际的语言模型
# LLM model: the actual language model
model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# 输出解析器:将 LLM 的字符串输出原样返回(或可解析为其他格式)
# Output parser: returns the LLM's string output as-is (or can parse to other formats)
parser = StrOutputParser()

# ============================================================
# 2. 用 LCEL 的管道操作符 | 组合成链
# 2. Compose into a chain using LCEL pipe operator |
# ============================================================
# 数据流:prompt -> model -> parser
# 每个组件的输出自动成为下一个组件的输入
# Data flow: prompt -> model -> parser
# Each component's output automatically becomes the next component's input
translation_chain = prompt | model | parser

# ============================================================
# 3. 调用链(invoke 是 Runnable 接口的标准方法)
# 3. Invoke the chain (invoke is the standard method of Runnable interface)
# ============================================================
result = translation_chain.invoke({
    "source_lang": "English",
    "target_lang": "Chinese",
    "text": "Hello, how are you today?"
})

print(result)
# 输出: 你好,今天你好吗?

Parallel Execution

# ============================================================
# RunnableParallel: 并行执行多个任务
# RunnableParallel: Execute multiple tasks in parallel
# ============================================================
from langchain_core.runnables import RunnableParallel

# ============================================================
# 定义两个独立的提示模板
# Define two independent prompt templates
# ============================================================
prompt_summary = ChatPromptTemplate.from_messages([
    ("system", "You are a summarizer. Summarize the following text in one sentence."),
    ("human", "{text}")
])

prompt_keywords = ChatPromptTemplate.from_messages([
    ("system", "You are a keyword extractor. Extract 3 keywords from the following text."),
    ("human", "{text}")
])

# ============================================================
# 创建两个独立的链
# Create two independent chains
# ============================================================
summary_chain = prompt_summary | model | parser
keywords_chain = prompt_keywords | model | parser

# ============================================================
# RunnableParallel 并行执行两个链
# RunnableParallel executes both chains in parallel
# ============================================================
# 注意:两个链共享同一个输入 {"text": ...}
# Note: Both chains share the same input {"text": ...}
parallel_chain = RunnableParallel({
    "summary": summary_chain,
    "keywords": keywords_chain
})

# ============================================================
# 调用并行链 - 两个任务同时执行
# Invoke the parallel chain - both tasks execute simultaneously
# ============================================================
result = parallel_chain.invoke({
    "text": "LangChain is a framework for developing applications powered by language models. "
            "It provides tools for building chains, agents, and retrieval systems."
})

print(f"Summary: {result['summary']}")
print(f"Keywords: {result['keywords']}")

RunnablePassthrough

# ============================================================
# RunnablePassthrough: 传递数据或进行转换
# RunnablePassthrough: Pass data through or transform it
# ============================================================
from langchain_core.runnables import RunnablePassthrough

# ============================================================
# 场景:在输入进入 LLM 之前,对输入进行预处理
# Scenario: Preprocess the input before it goes into the LLM
# ============================================================

# 方式1:RunnablePassthrough 原样传递数据
# Method 1: RunnablePassthrough passes data through unchanged
# 这里用 RunnablePassthrough() 占位,表示"把输入原样传给下一步"
# Here RunnablePassthrough() is a placeholder meaning "pass input to next step as-is"
passthrough_chain = (
    RunnablePassthrough()  # 输入原样传递 / Pass input through
    | prompt               # 然后进入 prompt / Then into prompt
    | model                # 然后进入 model / Then into model
    | parser               # 最后解析 / Finally parse
)

# ============================================================
# 方式2:用字典 + lambda 函数进行数据转换
# Method 2: Use dict + lambda functions for data transformation
# ============================================================
# RunnablePassthrough.assign() 可以在传递数据的同时添加/修改字段
# RunnablePassthrough.assign() can add/modify fields while passing data
from langchain_core.runnables import RunnablePassthrough

# 假设我们想:如果输入文本超过100字符,就截断
# Suppose we want to: truncate input text if it exceeds 100 characters
preprocessing_chain = (
    {
        # 对 "text" 字段应用转换:如果超过100字符则截断
        # Apply transformation to "text" field: truncate if > 100 chars
        "text": lambda x: x["text"][:100] + "..." if len(x["text"]) > 100 else x["text"],
        # "source_lang" 和 "target_lang" 原样传递
        # "source_lang" and "target_lang" pass through unchanged
        "source_lang": lambda x: x["source_lang"],
        "target_lang": lambda x: x["target_lang"],
    }
    | prompt
    | model
    | parser
)

RunnableBranch 

# ============================================================
# RunnableBranch: 基于条件选择不同的执行路径
# RunnableBranch: Choose different execution paths based on conditions
# ============================================================
from langchain_core.runnables import RunnableBranch
from langchain_core.prompts import ChatPromptTemplate

# ============================================================
# 定义不同场景的提示模板
# Define prompt templates for different scenarios
# ============================================================
prompt_short = ChatPromptTemplate.from_messages([
    ("system", "You are a concise assistant. Answer briefly in under 20 words."),
    ("human", "{text}")
])

prompt_long = ChatPromptTemplate.from_messages([
    ("system", "You are a detailed assistant. Provide comprehensive answers."),
    ("human", "{text}")
])

# ============================================================
# 创建对应的链
# Create corresponding chains
# ============================================================
short_chain = prompt_short | model | parser
long_chain = prompt_long | model | parser

# ============================================================
# RunnableBranch: (条件, 链) 对 的列表
# RunnableBranch: list of (condition, chain) pairs
# ============================================================
# 如果文本长度 < 50 字符,用 short_chain,否则用 long_chain
# If text length < 50 chars, use short_chain, otherwise use long_chain
branch_chain = RunnableBranch(
    (lambda x: len(x["text"]) < 50, short_chain),  # 条件1:短文本 / Condition 1: short text
    long_chain  # 默认分支 / Default branch
)

# ============================================================
# 测试:短文本走 short_chain,长文本走 long_chain
# Test: short text goes to short_chain, long text goes to long_chain
# ============================================================
short_result = branch_chain.invoke({"text": "What is AI?"})
long_result = branch_chain.invoke({"text": "Explain the complete history of artificial intelligence from 1950 to today."})

Key Takeaways

要点 (Key Point)ENCN
Runnable 是统一接口Runnable is the standard interface that all LangChain components implementRunnable 是所有 LangChain 组件实现的标准接口
LCEL 是组合语法LCEL is the declarative syntax for composing Runnables using |LCEL 是用 | 组合 Runnable 的声明式语法
数据从左到右流动Data flows left to right — each component’s output is the next component’s input数据从左到右流动 — 每个组件的输出是下一个组件的输入
自动获得四种执行模式LCEL chains automatically support invoke, ainvoke, stream, and batchLCEL 链自动支持 invoke、ainvoke、stream 和 batch
RunnableParallel 实现并行Use RunnableParallel to execute multiple tasks concurrently用 RunnableParallel 并发执行多个任务
RunnableBranch 实现条件路由Use RunnableBranch for conditional logic in chains用 RunnableBranch 实现链中的条件逻辑
从原型到生产无需改代码LCEL was designed to take prototypes to production with no code changesLCEL 从设计上支持原型直接上生产,无需改代码