返回文章

LangGraph 概述与入门

12 分钟阅读

如果你用过 LangChain 框架,那么大概率也听说过 LangGraph——甚至很多文章说 LangGraph 比 LangChain 更重要,更应该优先学习。那么 LangGraph 到底是什么?它和 LangChain 是什么关系?什么时候该用它?

LangGraph 是什么?

从字面上看,“Graph” 是”图、流程图”的意思。但 LangGraph 并不是画图工具,而是一个图编排框架,它把 AI 工作流建模为一张状态图(StateGraph)。这张图由三个核心概念构成:

  • 节点(Node):负责执行具体的动作,可以理解为一个个操作函数;
  • 边(Edge):控制逻辑的流转方向,把各个节点串联起来;
  • 状态(State):在节点之间传递和更新的数据。

了解了这三个要素,LangGraph 的定位就清晰了:LangChain 的关键词是”链”,它把模型、提示词、工具等基础能力串成一条链,封装成一个 Agent;而这个 Agent 就可以作为 LangGraph 图里的一个节点。通过边把多个 Agent 节点连接起来,就构成了一个更大的工作流——这就是 LangGraph 的核心作用。

需要注意的是,LangGraph 并不强制依赖 LangChain,它可以独立运行。只不过在实际项目中,图中的节点经常需要调用 LangChain 的模型、Prompt 和工具,所以两者通常是搭配使用的。

为什么需要 LangGraph?

了解了 LangGraph 是什么之后,一个自然的问题是:有了 LangChain 为什么还要 LangGraph?

根本原因在于,实际项目中的工作流很少是”一条直线”走到底的。

直线流程 vs 非直线流程

什么样的流程是直线?比如一个简单的 RAG 项目:

flowchart LR
    A[用户提问] --> B[向量检索]
    B --> C[LLM 生成答案]

这种线性流程,LangChain 的 Chain 足以胜任。

但更多项目包含以下三类”非直线”元素:

  • 条件分支 —— 下一步是继续搜索、重写草稿、调整结构,还是直接提交?
  • 循环回退 —— 如果结果不够好,能不能回到前面的步骤再跑一轮?
  • 状态驱动决策 —— 下一步怎么走,不靠硬编码,而是根据当前草稿、检索结果、审核结论等状态来决定。

举个例子,开发一个代码助手,它的流程大致是:

flowchart TD
    A[用户提问] --> B[分析需求]
    B --> C[生成代码]
    C --> D[运行测试]
    D --> E{测试通过?}
    E -->|否| F[分析失败原因]
    F --> G[修复代码]
    G --> D
    E -->|是| H[返回结果]

这是一个带循环条件分支的流程,显然一条直线无法表达。当然,用 LangChain + 手写胶水代码也能实现,但代价不小:

  • 状态管理 —— 测试结果、错误信息、重试次数……都得自己维护
  • 中断与恢复 —— 想在修复后停下来让人审一下?自己写持久化和恢复逻辑
  • 多 Agent 通信 —— 多个 Agent 协作时,消息传递全靠自己拼
  • 调试困难 —— 流程一复杂,日志和断点基本失控
  • 维护成本 —— if/while 越堆越多,代码最终变成”面条式”逻辑

LangGraph 的解法

LangGraph 把流程拆解为三个要素:

要素作用类比
Node(节点)执行具体逻辑一个函数
Edge(边)控制流转方向if/else 判断
State(状态)在节点间传递数据全局共享上下文

代码里就是定义几个节点函数,它们共享同一个 State:

class CodeState(TypedDict):
    question: str
    code: str
    test_result: str
    attempts: int

def generate(state: CodeState) -> dict:
    # 根据 question 生成代码
    return {"code": generated_code}

def run_test(state: CodeState) -> dict:
    # 运行测试,返回结果
    return {"test_result": result}

def fix_code(state: CodeState) -> dict:
    # 根据 test_result 修复代码
    return {"code": fixed_code}

State 在各个节点间自动传递,不需要手写 while 循环来重试,也不需要手写 if/else 来分流——把这些逻辑声明在边上,框架自动调度。

对比手写胶水代码,LangGraph 带来的好处是:

  • 声明式定义流程 —— 节点只管业务逻辑,边的条件写在配置里,一目了然
  • 内置状态管理 —— 状态自动传递和更新,不用自己维护上下文
  • 原生支持循环与分支 —— 图结构天然可以表达复杂拓扑
  • 可检查与可恢复 —— 每一步都可以持久化、中断、回放,调试和容错都更方便

Hello World 示例

说了这么多理论,来看一个具体的例子。我们不接入 LLM,纯粹用 LangGraph 把一个字符串拼成 "hello, world",借此了解它的完整使用流程。

安装

# 需要安装 langgraph,我这里使用 uv 安装
uv add langgraph

代码

from typing import TypedDict
from langgraph.graph import StateGraph, START, END


class MyState(TypedDict):
    """整个工作流共享的状态"""
    val: str


def add_hello(state: MyState):
    return {"val": state["val"] + "hello"}


def add_comma(state: MyState):
    return {"val": state["val"] + ", "}


def add_world(state: MyState):
    return {"val": state["val"] + "world"}


state_graph = StateGraph(MyState)

state_graph.add_node(add_hello)
state_graph.add_node(add_comma)
state_graph.add_node(add_world)

state_graph.add_edge(START, "add_hello")
state_graph.add_edge("add_hello", "add_comma")
state_graph.add_edge("add_comma", "add_world")
state_graph.add_edge("add_world", END)

app = state_graph.compile()

result = app.invoke({"val": ""})
print(result)  # {'val': 'hello, world'}

逐段拆解

1. 定义 State

class MyState(TypedDict):
    val: str

TypedDict 是 Python 自带类型,用来声明 State 的字段。这里的 val 就是整个流程中不断累积的字符串。每个节点都会收到这个状态,也可以选择更新它。

2. 定义 Node(节点)

def add_hello(state: MyState):
    return {"val": state["val"] + "hello"}

节点就是一个纯函数:接收当前 State,返回要更新的字段。返回值会和当前 State 合并(类似 dict.update),所以只返回你想改的字段即可。

三个节点做的事情完全一样——在 val 后面追加一段文本。

3. 构建 Graph

state_graph = StateGraph(MyState)

先创建一个 StateGraph 实例,传入 State 类型。然后:

  • add_node() 注册节点,节点名默认等于函数名;
  • add_edge() 定义边的走向,START 是入口,END 是终点。

整个图的结构一目了然:

flowchart LR
    START --> add_hello --> add_comma --> add_world --> END

4. 编译与执行

app = state_graph.compile()
result = app.invoke({"val": ""})

compile() 会把图定义编译成可执行对象,invoke() 传入初始状态并触发执行。框架会按照边的定义依次调度节点,状态自动流动:

初始:    {"val": ""}
→ add_hello:  {"val": "hello"}
→ add_comma:  {"val": "hello, "}
→ add_world:  {"val": "hello, world"}

小结

这个例子虽然简单,但它完整展示了 LangGraph 的四个核心步骤:

步骤你的代码说明
1. 定义 Stateclass MyState(TypedDict)声明工作流共享什么数据
2. 定义 Nodedef add_hello(state) → 返回 dict每个函数是一个处理单元
3. 连接 Edgeadd_edge(START, ...) / add_edge(..., END)声明节点间的执行顺序
4. 编译 & 调用compile()invoke()让框架按图调度执行

对比前面说的”手写胶水代码”——如果不用 LangGraph,你大概会写:

val = ""
val += "hello"
val += ", "
val += "world"

这个简单的拼接看不出图的优势。但只要把节点换成 LLM 调用、数据库查询、API 请求,把直线边换成条件边和循环边,LangGraph 的威力就会显现出来。

图结构可视化

上面一直在聊”图”,其实这张图是可以直接”看”到的。LangGraph 提供了三种可视化方式:

方式适用场景稳定性
ASCII 文本图终端快速查看本地,稳定
Mermaid 代码复制到编辑器二次编辑本地,稳定
PNG 图片导出为文档配图依赖渲染服务,国内可能不稳定

以上面的 Hello World 为例,注意需要先安装 grandalf 依赖:

pip install grandalf

方式 1:打印 ASCII 图

print(app.get_graph().print_ascii())

终端会直接输出:

+-----------+
| __start__ |
+-----------+
      *
      *
      *
+-----------+
| add_hello |
+-----------+
      *
      *
      *
+-----------+
| add_comma |
+-----------+
      *
      *
      *
+-----------+
| add_world |
+-----------+
      *
      *
      *
 +---------+
 | __end__ |
 +---------+

最清晰直观,适合开发调试时扫一眼。

方式 2:生成 Mermaid 代码

print(app.get_graph().draw_mermaid())

输出 Mermaid 格式代码:

---
config:
  flowchart:
    curve: linear
---
graph TD;
	__start__([__start__]):::first
	add_hello(add_hello)
	add_comma(add_comma)
	add_world(add_world)
	__end__([__end__]):::last
	__start__ --> add_hello;
	add_hello --> add_comma;
	add_comma --> add_world;
	add_world --> __end__;
	classDef default fill:#f2f0ff,line-height:1.2
	classDef first fill-opacity:0
	classDef last fill:#bfb6fc

其实我的博客中是可以渲染 Mermaid 图的,上面章节中就有三处渲染的是 Mermaid 图。为了观看代码我去掉了格式渲染,你可以把这段代码粘贴到 Mermaid Live EditorProcessOn 中查看和二次编辑。

方式 3:生成 PNG 图片

import uuid

png_bytes = app.get_graph().draw_mermaid_png()
output_path = "langgraph_" + str(uuid.uuid4())[:8] + ".png"
with open(output_path, "wb") as f:
    f.write(png_bytes)
print(f"图片已生成:{output_path}")

此方法底层调用在线渲染服务,国内网络可能不稳定,仅供导出文档配图时使用。生成效果如下:

langgraph 生成结构图

总结

本文从三个问题出发——LangGraph 是什么、为什么需要它、怎么用——建立对 LangGraph 的基本认知。以上都只是最基础的概念入门,LangGraph 涉及的东西还是很多的,后面会持续更新相关文章。

最后想说,LangGraph 的出现并不意味着 LangChain 要被替代。Chain 处理简单线性流程时仍然是最趁手的工具;而当应用需要复杂拓扑时,LangGraph 提供了更自然、更可维护的表达方式。两者各司其职,组合使用才是生产级 AI 应用的正确姿势。

评论