从零认识 LangGraph:一个 Hello World 搞懂图编排

搞 AI 应用开发的人,迟早会碰到一个问题:怎么让 LLM 不只是"问答一轮就完事",而是能多步协作、记住上下文、有条件分支地完成复杂任务?

LangGraph 就是为这个场景而生的。它不关心你怎么调模型、怎么写 prompt,只管一件事——编排:把多个处理步骤串成一张图,数据沿着图流动,最终输出结果。

这篇博客从一个最简单的 Hello World 出发,把 LangGraph 的核心概念拆开讲清楚。读完你能回答三个问题:LangGraph 是什么?它解决什么问题?那十几行代码到底在干嘛?


一、LangGraph 是什么

一句话:LangGraph 是 LangChain 生态里的图编排框架,用来构建有状态、多步骤的 AI Agent 工作流。

它的定位很"底层"。不抽象 prompt,不预设模型调用方式,只提供图(Graph)的构建和执行能力。你可以把它想象成一个"流程图引擎"——你定义好节点和连线,它负责按顺序执行、管理中间状态。

和 LangChain 的关系:

产品 定位 一句话说清
LangChain Agent 框架 提供模型调用、工具集成、Agent 循环等上层抽象
LangGraph 编排运行时 提供图结构、状态管理、持久化、人机协作等底层能力
LangSmith 可观测平台 链路追踪、评估、部署,贯穿开发到生产

三者可以搭配用,也可以各用各的。LangGraph 不强制依赖 LangChain,你可以用任何模型 SDK 作为节点实现。

为什么需要"图编排"

直接调 LLM 是线性的:发一条消息,收一条回复。但真实场景远比这复杂:

  • 客服 Agent 需要先判断意图,再走不同分支(退款 / 查询 / 转人工)
  • 数据分析 Agent 需要先生成 SQL,执行后发现结果为空,得重试
  • 审批流需要中间插入人工审核节点

这些场景用简单的顺序调用很难优雅处理。LangGraph 用来描述这种流程——节点是处理步骤,边是流转条件,状态在节点之间传递和累积。


二、核心概念拆解

下面结合代码逐个讲。先贴完整代码,12 行,一个完整的 LangGraph 应用:

from langgraph.graph import StateGraph, MessagesState, START, END

def mock_llm(state: MessagesState):
    return {"messages": [{"role": "ai", "content": "hello world"}]}

graph = StateGraph(MessagesState)
graph.add_node(mock_llm)
graph.add_edge(START, "mock_llm")
graph.add_edge("mock_llm", END)
graph = graph.compile()

graph.invoke({"messages": [{"role": "user", "content": "hi!"}]})

2.1 StateGraph — 状态图

graph = StateGraph(MessagesState)

StateGraph 是 LangGraph 最核心的类。创建图的时候,需要告诉它"状态长什么样"——这里传入的 MessagesState 就是状态的定义。

说白了,StateGraph 就是一张空白画布,MessagesState 规定了画布上流转的数据格式。

2.2 MessagesState — 消息状态

from langgraph.graph import MessagesState

MessagesState 是 LangGraph 内置的状态类型,结构极简:

class MessagesState(TypedDict):
    messages: list  # 消息列表

列表里每个元素是一条消息,包含两个字段:

字段 含义 常见值
role 谁说的 "user" / "ai" / "system"
content 说了什么 文本内容

这跟 OpenAI Chat API 的消息格式是一脉相承的。如果你用过 OpenAI 的 messages 参数,这里毫无障碍。

当然,你也可以自定义状态结构,不止是消息列表。比如加一个 step_count 计数器、一个 current_intent 分类结果等。MessagesState 只是最简单的起点。

2.3 节点(Node)— 执行单元

def mock_llm(state: MessagesState):
    return {"messages": [{"role": "ai", "content": "hello world"}]}

节点就是一个普通 Python 函数。 没有基类、没有装饰器、没有魔法。

规矩很简单:

  • 入参:接收当前完整状态(字典)
  • 返回值:返回一个字典,LangGraph 会自动把它合并到当前状态中

这里的 mock_llm 做的事:读取当前状态(虽然没用到),返回一条新的 AI 消息。LangGraph 会把这条消息追加到 messages 列表末尾。

🔴 重点: 节点函数名叫什么,这个节点就叫什么。def mock_llm(...) 注册后,节点名就是 "mock_llm"。后面 add_edge 里引用的也是这个名字。

为什么叫 mock_llm?因为它没有真的调任何模型,只是固定返回 "hello world"。这是故意的——先跑通流程,再接真实模型。

2.4 边(Edge)— 连接节点

graph.add_edge(START, "mock_llm")
graph.add_edge("mock_llm", END)

边定义了数据往哪流。STARTEND 是 LangGraph 内置的特殊节点,分别代表图的入口和出口。

画出来就是:

START → mock_llm → END

最朴素的线性结构。实际项目中可以有分支(条件边)、循环(回到某个节点)、并行(多个节点同时执行),但 Hello World 阶段先不展开。

2.5 compile() — 编译

graph = graph.compile()

定义好节点和边之后,调 compile() 把图编译成可执行对象。这一步会做结构校验——节点有没有注册、边有没有断开、循环有没有死锁风险。

类比一下:写完代码要编译才能跑,compile() 就是这个"编译"动作。

2.6 invoke() — 执行

graph.invoke({"messages": [{"role": "user", "content": "hi!"}]})

传入初始状态,触发整个图的执行。完整流程:

  1. 初始状态:messages = [{"role": "user", "content": "hi!"}]
  2. 流入 mock_llm 节点
  3. 节点返回:{"messages": [{"role": "ai", "content": "hello world"}]}
  4. LangGraph 自动合并,最终 messages 里有两条消息
  5. 到达 END,执行结束

最终状态长这样:

{
    "messages": [
        {"role": "user", "content": "hi!"},
        {"role": "ai", "content": "hello world"}
    ]
}

就这么简单。一条用户消息进去,一条 AI 消息出来,中间经历了一次图编排。


三、图的执行模型:数据怎么流动的

理解了单个概念,再串起来看数据流。

初始状态 {"messages": [{"role": "user", "content": "hi!"}]}
        │
        ▼
      START(入口,不做任何处理)
        │
        ▼
    ┌─────────────┐
    │  mock_llm   │ ← 读取状态,返回新消息
    └─────────────┘
        │
        ▼
       END(出口,结束执行)
        │
        ▼
最终状态 {"messages": [用户消息, AI消息]}

核心机制是状态累积。每个节点不是"替换"状态,而是往状态里追加数据。messages 列表从 1 条变成 2 条,这个列表就是整个图的"记忆"。

如果后面接上真正的 LLM 节点,它就能读到完整的对话历史,实现多轮对话。这就是"有状态"的意义。


四、LangGraph 能做什么(超越 Hello World)

Hello World 只展示了最基础的线性流程。LangGraph 真正的威力在于:

持久化执行 — 图执行到一半挂了,可以从断点恢复,不用从头跑。对长周期任务(比如需要几小时的数据处理流水线)很有用。

人机协作(Human-in-the-loop) — 图执行到某个节点时可以暂停,等人工审核、修改状态后再继续。审批流、敏感操作拦截都靠这个。

短期 + 长期记忆 — 短期记忆就是当前图的状态(messages 列表);长期记忆可以跨会话持久化,让 Agent 记住"上次用户说过什么"。

条件分支 — 根据状态决定走哪条边。比如"如果用户想退款,走退款节点;如果想查询,走查询节点"。

可视化调试 — 配合 LangSmith,可以看到每次执行的完整链路、状态变化、耗时分布。

这些能力在 Hello World 里一个都看不到,但它们是 LangGraph 存在的理由。


五、安装与环境

pip install -U langgraph

本文以 Python 3.10+ 为例。LangGraph 支持 Python 3.9 及以上,但 3.10 以上体验更好。

如果你想用 LangSmith 做链路追踪,还需要设置环境变量:

export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=你的API密钥

LangSmith 有免费额度,注册即可用。开发阶段强烈建议开着——调试 Agent 行为时,可视化链路比 print 大法好用太多。


六、避坑与常见问题

节点函数返回值格式不对

节点必须返回字典,且 key 要跟状态结构匹配。返回列表、字符串、或者 key 写错(比如 message 而不是 messages),都会报错或静默丢数据。

验证方式:在节点函数末尾加 print(result) 看返回值。

add_nodeadd_edge 搞混

add_node 注册节点,add_edge 定义连接。两者都要调,少一个都会出问题。顺序无所谓——可以先注册节点再连边,也可以反过来。

忘记调 compile()

定义好图之后不 compile(),直接调 invoke() 会报 Graph 对象没有 invoke 方法。这是新手最常踩的坑。

节点名引用错误

add_edge(START, "mock_llm") 里的字符串必须跟函数名完全一致。写成 "MockLLM""mock_llm_node" 都会报找不到节点。


七、总结:你真正需要记住的事

  1. LangGraph 是图编排框架,专注 Agent 工作流的编排,不碰模型调用本身。
  2. 状态是核心 — 所有节点共享一份状态,节点通过读取和追加状态来协作。
  3. 节点就是普通函数 — 入参是状态,返回值是状态的增量,没有魔法。
  4. 边定义流转STARTEND 是内置的起止点,中间是你自己定义的节点。
  5. 先跑通再迭代 — Hello World 不需要真实 LLM,用 mock 数据把流程跑通,再逐步替换。

验证清单

  • 能独立写出 helloworld.py 并成功运行
  • 能解释 StateGraphMessagesState、节点、边各自的作用
  • 知道 compile()invoke() 分别做什么
  • 理解状态是如何在节点之间流动和累积的
  • 能画出这个 Hello World 的图结构

参考资源


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐