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 内置了 MemorySaverSqliteSaver 等检查点机制:

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) 恢复执行。整个过程状态不丢失。

全面对比

维度LangChainLangGraph
执行模型线性链有向图
状态管理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论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。