Agent 框架 09:LangChain——生态全景、LCEL 管道与架构反思

提及大语言模型与智能体开发,LangChain 是绝对无法绕开的名字。它在 GitHub 上斩获了近十万颗星标,几乎成了 LLM 应用开发的代名词。许多开发者编写的第一行大模型调用代码,就是通过 LangChain 开始的。

然而,在工业界与工程社区中,LangChain 同样是被吐槽、被重构乃至被团队“去 LangChain 化”最多的框架之一:“过度抽象”、“文档混乱”、“API 频繁破坏性断代”、“排查一个空指针要翻阅 30 层调用栈”。

作为技术选型调研,我们既不能盲目神化它,也不能因噎废食全盘否定。本文旨在穿透其纷繁复杂的类库外表,厘清 LangChain 的核心演进逻辑(LCEL)、它不可替代的生态资产、以及在严肃生产环境中应如何克制地使用它。


架构演进:从 v0.1 繁重继承到 v0.2+ LCEL 管道

LangChain 经历过一次极其彻底的思想重构:

1. v0.1 旧式写法(已废弃)

在早期版本中,每个功能都被包裹成一个专有类,参数靠黑盒字典隐式流转:

1
2
3
4
5
6
7
8
9
10
# 旧版繁重写法 (不推荐)
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.llms import OpenAI

chain = LLMChain(
llm=OpenAI(),
prompt=PromptTemplate.from_template("将以下文本翻译为英文:{text}"),
)
res = chain.run(text="你好世界")

2. v0.2+ 现代写法:LCEL 管道

现代 LangChain 放弃了深层类继承,转而采用类似 Unix 管道的 | 操作符与统一的 Runnable 协议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 现代 LCEL 写法 (标准推荐)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini")
prompt = ChatPromptTemplate.from_messages([
("system", "你是一名资深翻译官,风格自然地道。"),
("human", "翻译成英文:{text}"),
])

# 纯声明式管道:Prompt -> LLM -> Parser
chain = prompt | llm | StrOutputParser()

# 统一支持 .invoke(), .stream(), .batch(), .ainvoke()
result = chain.invoke({"text": "纸上得来终觉浅,绝知此事要躬行。"})
print("翻译结果:", result)

LCEL 核心基语:函数式管道组合

LCEL(LangChain Expression Language)的设计核心是:所有组件均实现 Runnable 协议。无论同步、异步、批处理还是流式,接口完全统一。

1. 动态分支与并行流水线(RunnableParallel)

利用管道符号,可以极简地表达并行分支计算:

1
2
3
4
5
6
7
8
9
10
11
12
13
from langchain_core.runnables import RunnableLambda, RunnableParallel

step_upper = RunnableLambda(lambda s: s.upper())
step_length = RunnableLambda(lambda s: len(s))

# 并行执行两个独立处理逻辑
parallel_chain = RunnableParallel(
uppercase=step_upper,
char_count=step_length,
)

print(parallel_chain.invoke("LangChain"))
# 输出: {'uppercase': 'LANGCHAIN', 'char_count': 9}

LangChain 真正强大的基本盘:工业级 RAG 生态

如果说在单体 Agent 决策逻辑上 LangChain 屡受争议,那么在 RAG(检索增强生成)与非结构化数据处理 领域,LangChain 积累的生态资产是无与伦比的:从 PDF/Word/Notion/GitHub 等数百种 DocumentLoader,到各类文本分块算法,再到几乎所有向量数据库的连接器。

标准的 RAG 端到端构建只需十几行优雅的 LCEL 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 1. 加载本地知识文档
docs = TextLoader("knowledge_base.txt", encoding="utf-8").load()

# 2. 递归语义分块
splitter = RecursiveCharacterTextSplitter(chunk_size=400, chunk_overlap=50)
chunks = splitter.split_documents(docs)

# 3. 灌入向量数据库构建检索器
vectorstore = Chroma.from_documents(chunks, OpenAIEmbeddings(model="text-embedding-3-small"))
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 4. 构建声明式 RAG 管道
prompt_template = ChatPromptTemplate.from_template("""
仅基于以下已知信息回答问题。如果信息不足,请明确说明无法回答:
【参考背景】:
{context}

【用户问题】:
{question}
""")

rag_pipeline = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt_template
| ChatOpenAI(model="gpt-4o-mini", temperature=0)
| StrOutputParser()
)

# 执行问答
answer = rag_pipeline.invoke("公司差旅报销的标准是什么?")
print("RAG 检索回答:\n", answer)

Agent 工具调用与执行器

在传统 LangChain 中,通过 create_tool_calling_agent 与 AgentExecutor 组合实现 ReAct 循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI

@tool
def calculate_salary_tax(monthly_income: float) -> str:
"""计算个人所得税预估金额"""
taxable = max(0.0, monthly_income - 5000)
tax = taxable * 0.1 # 简化计算
return f"当月应缴个税预估为: {tax:.2f} 元"

tools = [calculate_salary_tax]
llm = ChatOpenAI(model="gpt-4o-mini")

prompt = ChatPromptTemplate.from_messages([
("system", "你是一名专业财务助理,具备财务工具调用能力。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"), # 预留给中间思考和工具结果的槽位
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

res = executor.invoke({"input": "我本月税前工资是 18000 元,帮我算一下个税是多少?"})
print("最终输出:", res["output"])

生产级可观测性:LangSmith 赋能

LangChain 团队最成功的商业化产品是其配套的可观测性平台 LangSmith。只需配置环境变量,全链路每个 Token 的消耗、Latency 延迟以及 Prompt 渲染细节都会被自动捕获:

1
2
3
export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="lsv2_pt_your_api_key"
export LANGCHAIN_PROJECT="enterprise-prod"

为什么很多团队想放弃 LangChain?架构痛点与反思

在经历了从兴奋到疲惫之后,工业界对 LangChain 的批评主要集中在以下三个方面:

1. 过度抽象与类膨胀(Over-abstraction)

在 Python 官方 SDK 中,调用一次聊天只需要:

1
client.chat.completions.create(model="gpt-4o", messages=[{"role": "user", "content": "hi"}])

而在早期 LangChain 中,开发者需要面对 LLM、BaseLanguageModel、BaseChatModel、ChatPromptTemplate、SystemMessagePromptTemplate、HumanMessagePromptTemplate、OutputFixingParser 等上百个类。当业务变复杂时,这种抽象层不仅没有减少代码量,反而成了一层厚厚的迷雾。

2. 破坏性重构太频繁(Breaking Changes)

从早期的顶级命名空间导入,到后来的拆包(langchain-core、langchain-community、langchain-openai),再到类方法的重命名,2023 年编写的 LangChain 代码在 2024、2025 年的版本中几乎无法直接运行,带来了沉重的升级与维护包袱。

3. 错误排查极度困难

一旦某处配置出错,控制台会打印出穿透了 20~30 个内部框架文件的巨大 Traceback,深陷在装饰器、RunnableGenerator 和回调管理器中,定位真正出问题的函数签名极为痛苦。


理性选型准则:如何正确使用 LangChain 生态?

经过整个社区的反复博弈,目前业内形成了一种共识性的最佳实践准则:

  • 取其精华:善用 langchain-community 和 langchain-text-splitters,避免重复手写海量格式的文件解析器与分块逻辑。
  • 去其糟粕:严禁在复杂的生产核心逻辑中使用 AgentExecutor。涉及多步骤跳转、循环重试、持久化断点的智能体,必须迁移至以图为核心的 LangGraph。

关联导航