【大模型LangChain开发-3】LCEL打造高效、灵活的大模型应用开发流程

245 阅读7分钟

1. LCEL 能为你做什么?为什么它现在才变得重要?

在构建 AI 应用的过程中,你可能已经发现:随着逻辑复杂度上升,LangChain 的代码会变得越来越冗长、难维护。组件嵌套、数据流转不清晰,调试也愈发困难。这正是 LCEL(LangChain Expression Language) 诞生的背景。

它是一种声明式语言,让你用更简洁、直观的方式组织 LangChain 组件,构建高可读性、高复用性的处理链,尤其适合应对日益复杂的 LLM 应用开发需求。

简单来说:LangChain 越复杂,越需要 LCEL。

LangChain 命令式 vs LCEL

LangChain命令式:

from langchain_core.prompts.chat import (
    ChatPromptTemplate,
    SystemMessagePromptTemplate,
    HumanMessagePromptTemplate
)
from langchain_community.chat_models import ChatOllama

# 系统提示词模版
template = "你是一个翻译专家,你擅长将 {input_language} 翻译成 {output_language}"
system_message_prompt = SystemMessagePromptTemplate.from_template(template)

# 用户提示词模版
human_template = "{text}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

# 构建对话的完整提示词模版
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 格式化输入,生成完整对话的消息
message = chat_prompt.format_messages(input_language="English", output_language="中文", text="I like swimming")

# 使用llama3模型,执行对话并生成内容
llm = ChatOllama(model="llama3.2")
response = llm.invoke(message)
print(response.content)  

使用LCEL:

from langchain_core.prompts.chat import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate
from langchain_community.chat_models import ChatOllama
from langchain_core.output_parsers import StrOutputParser

# 创建提示模板
system_template = "你是一个翻译专家,你擅长将 {input_language} 翻译成 {output_language}"
system_message_prompt = SystemMessagePromptTemplate.from_template(system_template)
human_message_prompt = HumanMessagePromptTemplate.from_template("{text}")
chat_prompt = ChatPromptTemplate.from_messages([system_message_prompt, human_message_prompt])

# 创建模型和输出解析器
llm = ChatOllama(model="llama3.2")
output_parser = StrOutputParser()

# 创建LCEL链
chain = chat_prompt | llm | output_parser

# 使用链
result = chain.invoke({
    "input_language": "English",
    "output_language": "中文",
    "text": "I like swimming"
})
print(result)

LCEL使用简单的|操作符连接各个组件,将不同的组件链接在一起,将一个组件的输出作为下一个组件的输入。 在这个链条中,用户输入被传递给提示模板,然后提示模板的输出被传递给模型,然后模型的输出被传递给输出解析器。

image.png

为什么现在需要LCEL?

  1. 代码简洁性:使用|运算符连接组件,消除了大量中间变量和嵌套调用
  2. 可维护性:数据流向清晰明了,使代码更易理解和修改
  3. 组件复用:可以轻松提取、重组和重用链的各个部分
  4. 一致性接口:统一的调用方式(invokestream等),无需担心不同组件的接口差异

2. LCEL 的设计哲学与编程范式

在深入LCEL细节之前,我们需要理解其背后的核心设计理念,这将帮助你以正确的思维模式来设计和开发LCEL应用。

一切皆 Runnable:统一抽象的力量

LCEL的基础是Runnable接口,所有组件(从提示模板到语言模型再到输出解析器)都实现了这个接口,具备相同的基本能力:

invoke()      # 同步调用
ainvoke()     # 异步调用
batch()       # 批量处理
abatch()      # 异步批量处理
stream()      # 流式处理
astream()     # 异步流式处理

这种统一抽象极大简化了组件间的集成,因为每个组件都以相同的方式"说话"。

声明式 vs 命令式:控制流转为数据流

  • LCEL的核心是将命令式编程(关注"如何做")转变为声明式编程(关注"做什么")。
  • 这种转变让我们能够专注于定义数据流向,而不是手动管理每一步的执行细节。

3. LCEL 的核心能力与特性

现在让我们深入了解LCEL提供的实际能力,并通过具体示例看看如何利用这些特性构建高效的AI应用。

可组合性与可重用性

LCEL的核心优势在于其极强的组合能力,让你可以像搭积木一样构建复杂流程:

# 基本组件
prompt = ChatPromptTemplate.from_template("写一篇关于{topic}的总结,50字以内")
llm = ChatOllama(model="llama3.2")
output_parser = StrOutputParser()
# 组合成链
content_chain = prompt | llm | output_parser

# 构建复杂链
advanced_chain = (
    RunnablePassthrough.assign( content=lambda x: content_chain.invoke({"topic": x["topic"]}))
    | ChatPromptTemplate.from_template("将这段内容的关键字提取出来:{content}")
    | llm
    | output_parser
)

# 显式调用链
result = advanced_chain.invoke({"topic": "人工智能"})
print(result)

这种设计让你可以构建可重用的链组件

并发任务

# 并行执行多个检索器
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableParallel
from langchain_community.chat_models import ChatOllama

# 基础组件
prompt = ChatPromptTemplate.from_template("给我讲一个关于 {topic} 的笑话")
llm = ChatOllama(model="llama3.2")

# 绑定变量后再组装
chain_1 = prompt.partial(topic="程序员") | llm
chain_2 = prompt.partial(topic="人工智能") | llm

# 并发执行
parallel_jokes = RunnableParallel(
    joke1 = chain_1,
    joke2 = chain_2,
)

# 调用
results = parallel_jokes.invoke({})

# 输出结果
print("程序员笑话:", results["joke1"].content)
print("AI 笑话:", results["joke2"].content)

流式输出

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("给我讲一个关于 {topic} 的笑话")
llm = ChatOllama(model="llama3.2")
chain = prompt | llm

# 流式输出
for chunk in chain.stream({"topic": "冰淇淋"}):
    print(chunk.content, end="", flush=True)

异步调用

import asyncio
from langchain_core.prompts import ChatPromptTemplate

# 定义 prompt 和模型
prompt = ChatPromptTemplate.from_template("给我讲一个关于 {topic} 的笑话")
llm = ChatOllama(model="llama3.2")
chain = prompt | llm

await chain.ainvoke({"topic": "雪花"})

错误处理与回退机制

在LLM应用程序中,可能会出现许多故障点,无论是LLM API的问题、模型输出不佳、其他集成的问题等等。回退可以帮助您优雅地处理和隔离这些问题。

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnableLambda
from langchain_ollama import ChatOllama

# 定义提示模板和解析器
prompt = PromptTemplate.from_template("请将以下文本翻译为英文:{text}")
llm = ChatOllama(model="llama3.2")
parser = StrOutputParser()
chain = prompt | llm | parser

# 定义备用链,返回默认响应
fallback_chain = RunnableLambda(lambda x: "无法翻译输入文本。")
# 创建带有回退机制的链
chain_with_fallback = chain.with_fallbacks([fallback_chain])
# 执行链
result = chain_with_fallback.invoke({"text": "你好"})
print(result)

这段代码正常情况输出:Hello,如果我故意让模型调用出错,比如修改为

chain_with_fallback.invoke({"text66": "你好"})

输出结果就会是: 无法翻译输入文本。

4. LCEL 应用场景示例

4.1 多轮对话代理:融合记忆机制与工具调用能力

在实际应用中,我们常常希望构建一个不仅能自然对话、还能调用外部功能(如天气查询、数据库检索等)且具备上下文记忆的智能体。通过 LCEL(LangChain Expression Language)构建这样的 多链组合 Agent,可以轻松实现:

  • 语言模型(LLM) :作为核心推理引擎,负责自然语言理解和响应生成。
  • 工具(Tools) :将外部函数封装成可调用的能力组件,供模型根据上下文自动使用。
  • 记忆(Memory) :保留多轮对话历史,实现跨轮次的上下文理解与指代解析。
  • 推理过程(Memory) :可以选择展示推理过程

下面的示例中,我们构建了一个具备 “记忆 + 工具调用 + 对话” 三种能力的对话代理。用户可以先提问天气,再在后续轮次中基于上下文继续提问,比如“那我该穿什么?”——代理会通过记忆理解,结合第一个问题的回答来答复第2个问题。

from langchain.agents import initialize_agent, Tool, AgentType
from langchain.memory import ConversationBufferMemory
from langchain_ollama import ChatOllama


def fake_weather(city: str) -> str:
    """获取指定城市的天气情况"""
    return f"{city} 今天多云转晴,最高气温 22℃,最低气温 10℃,北风微风,空气良。"


# 定义工具, Agent 根据描述调用
weather_tool = Tool(
    name="天气查询",
    func=fake_weather,
    description="获取指定城市的天气情况,输入应该是城市名称,例如:北京"
)

tools = [weather_tool]

# 初始化模型
llm = ChatOllama(model="llama3.2", temperature=0)

# 创建记忆,便于多轮对话理解上下文。
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# 创建代理,具备工具调用能力的智能体(Agent)
agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.CONVERSATIONAL_REACT_DESCRIPTION, #对话型 Agent 类型,结合工具调用和语言模型推理,适合对话交互。
    memory=memory,
    verbose=True # 可选,会打印 Agent 推理过程。
)

# 测试对话
user_inputs = [
    "帮我查一下北京的天气",
    "那明天我去那边穿什么合适?"
]

for i, user_input in enumerate(user_inputs, 1):
    print(f"\n[轮次 {i}]")
    print(f"用户: {user_input}")

    try:
        response = agent.run(user_input)
        print(f"助手: {response}")
    except Exception as e:
        print(f"出现错误: {e}")

输出

image.png

5. 总结与下一步建议

LCEL 的价值总结

  1. 简化复杂性:通过声明式语法处理复杂的AI组件交互
  2. 提高可维护性:清晰展示数据流,使代码更易理解
  3. 增强复用性:轻松组合和重用链的各个部分
  4. 生态系统支持:与监控、调试和部署工具无缝集成

延伸学习路径

下一步我们继续带大家快速掌握:

  1. LangGraph:基于LCEL构建更复杂的状态管理和循环控制流
  2. LangServe:将LCEL链部署为可扩展的API服务
  3. LangSmith:深入跟踪和调试链的执行情况
  4. AutoGraph:自动化工作流构建和优化