一、前言:从 "路由" 转向 "表达式"
在传统编程范式中,我们往往通过命令式结构(如 if/else、for、try/except)明确编排程序的执行路径,就像手动设计一张流程图。
LangChain 通过 LCEL(LangChain 表达式语言)将这类流程控制抽象为"可组合的执行表达式 "。每个模块都被封装为 Runnable,多个模块之间通过链式语法自然组合,系统根据数据流自动推导执行顺序。
这不仅仅是语法层面的变化,更代表着一种编程思维的演进:从"命令式控制"转向"表达式组合",从"显式逻辑"转向"模块化构建"。
二、核心概念:Runnable 是什么?
Runnable 是 LangChain 架构中的核心抽象单元,是 LCEL 表达式语言的最小执行模块和构建基石。
任何可执行的单元都是 Runnable
核心基类
            
            
              python
              
              
            
          
          class Runnable(ABC, Generic[Input, Output]):
    """A unit of work that can be invoked, batched, streamed, transformed and composed.
    ...
    """
    
    @abstractmethod
    def invoke(
        self,
        input: Input,
        config: Optional[RunnableConfig] = None,
        **kwargs: Any,
    ) -> Output:
        """Transform a single input into an output.
        ...
        """
        .invoke(input) 是 LangChain 中 Runnable 接口的核心同步调用方法,用于将输入传入模块,执行并返回结果。适用于单次执行、测试链路或组合多个模块后的最终触发。
特点:
- 同步执行:立即返回处理结果
 - 通用入口:适用于任何 Runnable 对象(LLM、链、工具等)
 - 可组合:支持与 
.with_retry()、.transform()等方法链式使用 
LangChain 将你所知道的:
- LLM
 - PromptTemplate
 - ChatHistory
 - Tool
 - AgentExecutor
 
通通都抽象为 Runnable,不同类型只是指定了不同的输入/输出数据类型。
其他方法
.stream()将普通的执行过程变成流式执行,允许逐步产出结果,而不是等待全部完成后一次性返回。- 在调用大型语言模型(LLM)或其它需要长时间运行的任务时,输出结果往往是增量产生的。使用 
.stream(),你可以实时接收部分结果,实现更流畅的交互体验。 
- 在调用大型语言模型(LLM)或其它需要长时间运行的任务时,输出结果往往是增量产生的。使用 
 .batch()支持一次性处理一批输入,返回一批对应输出,提高整体吞吐量。.transform()为输出添加一个转换器(函数),对结果进行进一步加工或格式化。.with_fallbacks()设置备用执行路径,当主任务失败时自动切换执行备用任务。.with_retry()为当前任务添加自动重试功能,遇到异常自动按重试策略重试。
| 方法 | 作用说明 | 典型应用场景 | 
|---|---|---|
.stream() | 
实时流式输出,边生成边返回 | 聊天机器人、文本生成 | 
.batch() | 
批量处理输入,提高吞吐量 | 批量分类、批量生成 | 
.transform() | 
对输出结果进行后续转换处理 | 格式化、过滤、标注 | 
.with_fallbacks() | 
多路径备选执行,容错降级 | 多模型容灾、工具降级 | 
.with_retry() | 
失败自动重试,保证稳定性 | 网络异常、API 失败重试 | 
三、LCEL 组合式表达式:从几个 Runnable 到一条链
LCEL 是 "LangChain Expression Language" 的缩写,目标是用最少的输入以最光明的方式构造最处理的连接流程。
示例:
            
            
              python
              
              
            
          
          from langchain_core.runnables import RunnableMap, RunnableLambda
chain = RunnableMap({"name": lambda _: "LangChain"}) \
    | RunnableLambda(lambda d: f"Hello, {d['name']}!")
print(chain.invoke({}))
# Hello, LangChain!
        相当于:
- 先生成一个字典 
{"name": "LangChain"} - 传递给下一个 
lambda,输出 "Hello, LangChain" 
后续可以这样扩展:
            
            
              python
              
              
            
          
          | some_chat_model \
| some_output_parser \
| final_formatter
        四、实现解析:执行链条与数据流通道
LangChain 的 Runnable 原理根基于一套"输入 - 运行 - 输出"的基本通道模型:
通过 invoke() 启动
        
            
            
              python
              
              
            
          
          def invoke(self, input: Input) -> Output:
    return self._call_with_config(input, config)
        配合 Configurable 和 RunnableBinding
它们会为 chain 中的每个 Runnable 自动分配唯一的 ID,并附加配置(config),以便于后续的日志记录、异常追踪以及 fallback 分支的排查与切换。
支持 RunnableSequence 系列化
- 用 
|连接 - 内部编排 
_invoke,_batch,_stream 
总结核心意图
LangChain 在执行层的设计并不仅仅是为了"串起来能用",而是希望通过 Runnable + LCEL 表达式系统,构建一个可观测、可控制、易扩展、可组合、可替换的 AI 应用执行基座。
下图是典型执行链条的简化视图,展示了 LangChain 中各个模块的基本作用和周边能力增强点:
            
            
              css
              
              
            
          
          [Prompt] -> [LLM] -> [Parser] -> [PostProcess]
   \         |         |            |
  Config  Retry     OutputMap    LangSmith Trace
        - 每个模块之间通过 Runnable 接口无缝衔接
 - 任意节点都可以挂接 Retry / Config / Debug / Tracing 能力
 - 整条链条既是执行流程,也是组合表达式(LCEL)
 
这种结构让 LangChain 能像搭积木一样构建复杂任务,又能像监控系统一样追踪每个模块的执行情况。
五、扩展方案:如何实现自定义 Runnable
所有 Runnable 只需要继承 Runnable,重写 _invoke:
示例:实现一个简单 Tool
            
            
              python
              
              
            
          
          from langchain_core.runnables import Runnable
class AddOne(Runnable[int, int]):
    def invoke(self, input: int, config=None) -> int:
        return input + 1
        AddOne继承了Runnable[int, int],表示这个类输入是int,输出也是int。
这个简单的
AddOne类展示了 LangChain 架构的核心理念:将所有可执行逻辑抽象为统一接口Runnable,使其天然具备组合性、控制性和可观测性。通过继承
Runnable,即便是最简单的 Python 函数,也能被纳入 LCEL 表达式系统,与 LLM、Prompt、Parser 等模块无缝集成,构建出结构清晰、逻辑强大、易于维护的 AI 应用流程。
与其他 Runnable 组合
            
            
              python
              
              
            
          
          from langchain_core.runnables import RunnableLambda
add = AddOne()
square = RunnableLambda(lambda x: x * x)
workflow = add | square
workflow.invoke(2)  # 输出:9
        六、相关扩展功能
功能说明和代码示例
with_retry(RetryConfig):自动重试机制,增强鲁棒性- 缓解 LLM 调用时的 timeout、RateLimitError 或网络故障问题。
 - 就像给函数加上 
try...except...retry,但更优雅、自动、声明式。 
            
            
              python
              
              
            
          
          from langchain_core.runnables.retry import RetryHandler
retryable = llm.with_retry(RetryHandler(max_attempts=3))
response = retryable.invoke("你好")
        with_fallbacks([B, C]):备用路径,失败时自动切换- 当主要模块(如 LLM A)不可用时,自动尝试备用方案(如 B 和 C)。
 - 像 
try A except → try B → try C的自动化策略,非常适合生产场景。 
            
            
              python
              
              
            
          
          robust_chain = llm.with_fallbacks([openai_llm, local_llm])
        with_config(tags=[...], run_name=...):执行元信息,便于可观测性- 为每次执行添加追踪信息,方便在 LangSmith 等调试平台中记录来源与上下文。
 - 相当于"打标签 + 命名",你在 LangSmith 中可以看到漂亮的执行轨迹。
 
            
            
              python
              
              
            
          
          configured = chain.with_config(tags=["query", "qa"], run_name="qa_main_chain")
        RunnableParallel:并行执行多个任务,提高吞吐率- 一次性运行多个 
Runnable,并收集所有结果,适合处理多个输入源或分任务处理。 - 输出是一个字典:
{"qa": ..., "summary": ...},并行执行效率更高。 
- 一次性运行多个 
 
            
            
              python
              
              
            
          
          from langchain_core.runnables import RunnableParallel
parallel_chain = RunnableParallel({
    "qa": qa_chain,
    "summary": summary_chain,
})
result = parallel_chain.invoke({"input": "今天的新闻..."})
        RunnableBranch:条件分支逻辑,像if-else一样灵活- 根据输入内容的特征动态选择执行路径。
 - 把控制流 
if/elif/else模块化、声明式地挂接到链路中。 
            
            
              python
              
              
            
          
          from langchain_core.runnables import RunnableBranch
branch = RunnableBranch(
    (lambda x: "help" in x.lower(), help_chain),
    (lambda x: "buy" in x.lower(), shopping_chain),
    default_chain
)
        扩展功能与 .stream() / .batch() 联合使用
LangChain 中的 Runnable 拥有统一的三种执行入口:
.invoke():同步执行单次输入.batch():同步执行一批输入.stream():逐步返回输出(生成式任务常用)
下面我们按扩展能力逐一说明如何结合使用:
✅ 1. with_retry() + .batch() / .stream()
- 
作用:为一批请求或流式数据执行增加自动重试机制,增强稳健性。
 - 
示例:
 
            
            
              python
              
              
            
          
          retry_llm = llm.with_retry()
results = retry_llm.batch(["你好", "请翻译", "帮我写一封邮件"])
        
            
            
              python
              
              
            
          
          for chunk in llm.with_retry().stream("请写一段诗"):
    print(chunk, end="")
        - 提示:
RetryHandler会针对每个失败样本重试,不会重跑整个 batch。- 如果用于 
.stream(),只会在流开始失败时重试,流中间错误不会回滚。 
 
✅ 2. with_fallbacks() + .batch() / .stream()
- 
作用:当主模块失败时,自动切换到备用模块处理批量请求或流式任务。
 - 
示例:
 
            
            
              python
              
              
            
          
          robust = llm.with_fallbacks([backup_llm])
results = robust.batch(["你是谁", "请总结这段文字"])
        
            
            
              python
              
              
            
          
          for chunk in robust.stream("用 fallback 模式生成一段回答"):
    print(chunk, end="")
        - 提示:
- fallback 是按整个输入粒度判断是否失败,不是逐个 token fallback
 - 不适合频繁局部失败但整体还有效的情况(建议配合 
RetryHandler) 
 
✅ 3. with_config() + .batch() / .stream()
- 
作用:批处理或流式调用时,添加执行标识,用于日志标注、链路追踪(LangSmith 等)。
 - 
示例:
 
            
            
              python
              
              
            
          
          configured = chain.with_config(tags=["批处理任务"], run_name="qa_batch")
results = configured.batch(list_of_questions)
        
            
            
              python
              
              
            
          
          for token in chain.with_config(run_name="stream_chat").stream("你好啊"):
    print(token, end="")
        - 提示:
- 在调试多链条并行执行时非常有用
 tags和run_name可配合 LangSmith 实现全链路调用树分析
 
✅ 4. RunnableParallel + .batch() / .stream()
- 
作用:并行执行多个子链,支持同时流式或批量处理多个子任务。
 - 
示例:
 
            
            
              python
              
              
            
          
          parallel = RunnableParallel({
    "translate": translator,
    "summary": summarizer
})
results = parallel.batch([{"text": "A"}, {"text": "B"}])
        输出结果结构:
            
            
              python
              
              
            
          
          [
  {"translate": "...", "summary": "..."},
  {"translate": "...", "summary": "..."}
]
        目前 stream + 并行使用受限,不建议用于多路 stream 合并,需自定义包装
✅ 5. RunnableBranch + .batch() / .stream()
- 
作用:批量输入中可根据条件路由到不同模块,或对每条输入动态选择处理逻辑。
 - 
示例:
 
            
            
              python
              
              
            
          
          branch = RunnableBranch(
    (lambda x: "翻译" in x, translator),
    (lambda x: "摘要" in x, summarizer),
    default_chain
)
results = branch.batch(["请翻译", "请摘要", "默认处理"])
        每条输入匹配一个路径,独立处理
七、总结思维
Runnable 和 LCEL 不是一套 API,而是一套 执行逻辑架构模型。
它为 LLM 应用提供了一套通用型执行单元,并能够被分层、并行、切换、监控和应急处理,是 LangChain 搭建模块化调度器的基础。
接下来我们将手工实现一个完整的 Runnable 模块,并构建属于自己的 LangChain 流式链条。