LangChain 的痛点:为什么需要 LangGraph
LangChain 最初设计目标是链式调用 LLM——prompt → model → output parser 串成一条链。这在简单的 Q&A 和 RAG 场景下足够用,但当你要构建真正的 Agent时,问题就来了:
| 痛点 | 具体表现 |
|---|---|
| 链是线性的 | Agent 需要循环、条件分支、回退,Chain 只能直线走 |
| 状态管理粗糙 | Memory 机制简陋,复杂状态需手动管理,多步推理容易丢上下文 |
| 没有检查点 | 长流程中途失败,无法从断点恢复,只能从头来 |
| Human-in-the-loop 困难 | 链执行中无法暂停等待人类输入 |
| AgentExecutor 黑盒 | 封装太重,调试困难,自定义控制流代价高 |
LangChain 的 AgentExecutor 本质上是一个 while 循环 + tool 调用,你无法精确控制每一步的行为。当你需要"如果工具返回 X 则走分支 A,否则走分支 B"这种逻辑时,AgentExecutor 就力不从心了。
LangGraph 的核心思路:把 Agent 当图来建模
LangGraph 的设计哲学很简单:Agent 的执行流程是一张有向图。
- 节点(Node):每个节点是一个函数,接收状态,返回更新后的状态
- 边(Edge):连接节点的有向边,可以是固定的或条件性的
- 状态(State):一个共享的 TypedDict 或 dataclass,在节点间流转
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
messages: Annotated[list, add_messages]
tool_results: list
def call_model(state: AgentState):
response = llm.invoke(state["messages"])
return {"messages": [response]}
def should_use_tool(state: AgentState) -> str:
last_msg = state["messages"][-1]
if last_msg.tool_calls:
return "tools"
return END
# 构建图
graph = StateGraph(AgentState)
graph.add_node("agent", call_model)
graph.add_node("tools", tool_node)
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_use_tool)
graph.add_edge("tools", "agent") # 工具执行完回到 agent
app = graph.compile()
这段代码实现了一个标准的 ReAct Agent,但控制流完全透明。你能看到每一步在做什么,为什么走这个分支。
状态管理:从黑盒到白盒
LangChain 的 Memory 是附加在 Chain 上的侧线组件,状态和执行逻辑耦合不清。LangGraph 把状态提升为一等公民:
class ResearchState(TypedDict):
messages: Annotated[list, add_messages]
search_results: list
draft: str
revision_count: int
approved: bool
def research_node(state: ResearchState):
# 读取 state["search_results"],写入新结果
results = search_tool(state["messages"][-1].content)
return {"search_results": results}
def draft_node(state: ResearchState):
draft = llm.invoke(f"基于以下资料写草稿: {state['search_results']}")
return {"draft": draft.content}
def review_node(state: ResearchState):
if state["revision_count"] >= 3:
return {"approved": True}
# 审核逻辑
return {"revision_count": state["revision_count"] + 1}
关键区别:状态是显式的、类型化的、可追踪的。每个节点的输入输出都清晰可见,调试时你能精确看到状态在每一步的变化。
条件边:真正的控制流
LangGraph 的条件边是实现复杂 Agent 行为的核心:
def route_after_review(state: ResearchState) -> str:
if state["approved"]:
return "publish"
elif state["revision_count"] >= 3:
return "escalate" # 超过3次修改,升级到人工
else:
return "draft" # 回去重写
graph.add_conditional_edges(
"review",
route_after_review,
{
"publish": "publish_node",
"escalate": "human_review",
"draft": "draft_node"
}
)
这在 LangChain 的 Chain 里几乎不可能优雅实现。你不得不写一堆 if-else 嵌套在 callback 里,或者拆成多个 Chain 用 if-else 在外部拼接。
检查点(Checkpoint):容错与恢复
LangGraph 内置了 MemorySaver 和 SqliteSaver 等检查点机制:
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("agent.db")
app = graph.compile(checkpointer=checkpointer)
# 每次执行自动保存状态
config = {"configurable": {"thread_id": "thread-1"}}
result = app.invoke(initial_state, config)
# 从断点恢复
result = app.invoke(None, config) # 继续上次的状态
实际意义:一个多步 Agent 跑了 10 步,第 11 步失败。不用从头开始,直接从第 10 步的检查点恢复。这对生产环境的 Agent 系统至关重要。
Human-in-the-loop:暂停与审批
LangGraph 的检查点机制让 Human-in-the-loop 变得自然:
from langgraph.types import interrupt
def human_approval_node(state: ResearchState):
# 暂停执行,等待人类输入
feedback = interrupt({
"draft": state["draft"],
"prompt": "请审核草稿,输入修改意见或输入 APPROVE 通过"
})
if feedback == "APPROVE":
return {"approved": True}
return {
"messages": [HumanMessage(content=feedback)],
"revision_count": state["revision_count"] + 1
}
执行到这个节点时,Agent 会暂停,把状态保存到检查点。人类审核后,通过 Command(resume=feedback) 恢复执行。整个过程状态不丢失。
全面对比
| 维度 | LangChain | LangGraph |
|---|---|---|
| 执行模型 | 线性链 | 有向图 |
| 状态管理 | Memory 侧线组件 | 显式 State 对象,一等公民 |
| 条件分支 | 需要外部 if-else 拼接 | 原生条件边 |
| 循环 | AgentExecutor 黑盒循环 | 显式图边,完全可控 |
| 检查点 | 无内置 | MemorySaver/SqliteSaver |
| Human-in-the-loop | 困难 | interrupt() 原生支持 |
| 调试 | 回调链,难追踪 | 图可视化,状态透明 |
| 学习曲线 | 较低 | 中等 |
| 适用场景 | 简单 RAG、单步链 | 复杂 Agent、多步工作流 |
| 社区生态 | 最大 | 快速增长,背靠 LangChain 生态 |
迁移建议
不需要迁移的情况:
- 你在用 LangChain 做 RAG,且 RAG 逻辑简单(retrieve → generate)
- 你的应用是单轮 Q&A,不需要复杂控制流
- 你重度依赖 LangChain 的某个特定集成(如某个向量库的 Loader)
应该迁移的情况:
- 你在用
AgentExecutor且遇到了控制流瓶颈 - 你需要多 Agent 协作
- 你需要 Human-in-the-loop
- 你的 Agent 流程需要容错和恢复
- 你需要精确控制每一步的行为
渐进式迁移:LangGraph 和 LangChain 不是互斥的。你可以在 LangGraph 的节点里使用 LangChain 的组件(LLM、Tool、Retriever)。建议从最复杂的 Agent 流程开始,用 LangGraph 重写,简单的 RAG 链保持不变。
# LangGraph 节点内使用 LangChain 组件
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
llm = ChatOpenAI(model="gpt-4o")
@tool
def search(query: str) -> str:
"""搜索网络"""
return tavily.search(query)
def agent_node(state):
# LangChain 的工具绑定 + LangGraph 的状态管理
llm_with_tools = llm.bind_tools([search])
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
结论
LangChain 仍然是 LLM 应用生态的基石——它的抽象(Prompt、LLM、Tool、Retriever)被广泛复用。但当你要构建真正的 Agent(有状态、有分支、有循环、需要人类介入),LangGraph 是更合适的框架。
一句话总结:LangChain 是积木,LangGraph 是图纸。用积木搭建,用图纸设计结构。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
