StateGraph
定义
StateGraph 是 LangGraph 里用于“定义图”的 builder,而不是“执行图”的 runtime。
它的职责是把复杂 Agent 需要的几个核心要素先声明出来:State、Node、Edge、条件分支、状态通道以及 reducer。开发者先用它把图结构画出来,再通过 compile() 把定义对象编译成真正可执行的 CompiledStateGraph。
这一区分很重要:
StateGraph负责收集和组织图定义。[CompiledStateGraph](/wiki/it/ai/entity/CompiledStateGraph)负责实际运行图。invoke、stream、ainvoke这类执行能力,是编译之后才具备的,不应误认为 StateGraph 本身就能直接跑流程。
如果把这两者混为一谈,就会把“图的定义对象”和“图的执行对象”混成一个概念,进而误解 LangGraph 的工程化分层。
在本文语境中的位置
在本文讨论里,LangGraph 被概括为 State + Node + Edge + Runtime。
其中,StateGraph 对应的是把前三者组织起来的“图构建器”角色:
- 用 State 描述当前任务有哪些业务字段。
- 用 Node 描述每一步要做什么。
- 用 Edge 和条件边描述下一步怎么走。
- 最后再交给编译后的运行时去执行。
这背后的工程化意图,是把原本藏在 Prompt 里的流程控制、分支路由和状态更新,抽出来变成显式的程序结构。也就是说,复杂 Agent 不再是“模型自己决定怎么走”,而是“开发者先把路画出来,模型只在被允许决策的地方决策”。
因此,StateGraph 的意义不是让 Agent 更花哨,而是让复杂流程更可控、更可审计、更容易恢复和排查。
初始化阶段会准备什么
创建 StateGraph 时,内部会先准备一组核心容器,用来承接后续定义信息:
nodes:保存已经注册的节点。edges:保存固定边,也就是确定性的流转关系。branches:保存条件分支信息,用于按状态决定去向。channels:保存状态通道,用来管理状态字段及其更新流。
除了这些容器,初始化阶段还会解析 State schema。
这一步不是装饰性的元数据登记,而是整个图构建期的基础。因为后续节点能改哪些字段、多个节点结果如何合并、并行或多次更新时按什么规则处理,都依赖这里对 schema 的解析结果。
State schema 在构建期的作用
在 StateGraph 中,State schema 的作用不是只做“字段说明”,而是直接参与图的构建语义。
它至少决定三件事:
- 哪些字段属于可被节点更新的状态。
- 节点返回的 Partial State 应该写回到哪些字段。
- 当多个更新同时作用到同一字段时,结果应该如何合并。
这也是为什么节点通常不返回整份 State,而只返回自己改动的部分。图运行时会把这些局部更新交给对应的 reducer 合并,而不是让节点彼此覆盖整份状态。
在复杂 Agent 中,这一点尤其关键,因为常见场景里会出现:
- 多个节点同时产出结果。
- 列表字段需要追加而不是覆盖。
- 消息字段需要合并而不是丢失旧内容。
- 同一业务字段在不同步骤被逐步补全。
如果没有 schema 与 reducer 的配合,状态更新就容易互相覆盖,导致流程不可预测。也正因为如此,StateGraph 在构建阶段就要把这些合并规则准备好,而不是等到运行时临时猜测。
add_node:注册节点的语义
add_node 用于把一个步骤注册进图里。
从语义上看,节点本质上就是“读入当前 State,执行一段逻辑,返回 Partial State”的处理单元。节点可以是普通函数,也可以是 Runnable。
LangGraph 并不要求节点内部必须是某一种能力,它关心的是输入输出契约,而不是实现手段。一个节点内部可以封装:
- LLM 调用。
- RAG 检索。
- 工具调用。
- 数据库访问。
- 搜索或业务接口请求。
- 人工审批。
- 普通 Python 业务逻辑。
换句话说,StateGraph 不会因为节点里装的是模型、数据库还是人工步骤而改变其定义方式。对图来说,它们都只是一个“会读取 State 并产出局部更新”的节点。
这种抽象使得复杂系统能拆成“一个节点只做一件事”的结构。例如在智能客服场景里,意图识别节点只负责分类,RAG 节点只负责查资料,工具节点只负责调接口,人工节点只负责审批,质检节点只负责拦截风险答案。StateGraph 负责把这些步骤编排成图,而不是把它们糊成一个大 Prompt。
add_edge:固定边
add_edge 表达的是确定性的流程关系。
它的典型语义是:A 执行完之后,下一步就去 B。
这种边适合那些业务上已经确定、无需模型或路由函数再判断的步骤,比如:
- 意图识别后固定进入某个检索步骤。
- 查询完成后固定进入答案生成步骤。
- 生成完成后固定进入质检步骤。
固定边的价值在于把“确定流程”从 Prompt 中剥离出来,变成显式结构。这样开发者能明确知道图在哪一步、下一步会去哪里,而不是让模型自由跳转。
add_conditional_edges:条件边、分支与循环
add_conditional_edges 用于表达“执行完当前节点后,下一步去哪里,要根据状态来判断”的情况。
它和 add_edge 的关键区别不是 API 形式,而是流程语义:
add_edge表达确定流转。add_conditional_edges表达按状态决定去向的分支、路由或循环。
典型形式包括:
- A 执行完后,根据状态去 B。
- A 执行完后,根据状态去 C。
- A 执行完后,根据状态直接结束到
END。 - A 执行完后,如果条件未满足,再回到前面的某个节点形成循环。
这使得图不仅能表示线性流程,也能表示复杂 Agent 常见的分支决策、重试、回退和循环控制。
从工程角度看,这一步的意义是把“业务流程中的判断逻辑”从自然语言指令里拿出来,落成真正的程序结构。流程因此不再只存在于模型的隐式推理中,而是体现在可检查、可追踪的图定义中。
compile:从定义对象变成可执行图
compile() 是 StateGraph 的关键分界点。
在 compile() 之前,开发者手里拿到的仍然只是图的 builder;在 compile() 之后,得到的才是可执行的 CompiledStateGraph。
compile() 的核心职责包括:
- 做结构校验。
- 检查是否存在孤立节点。
- 检查入口和出口设置是否合理。
- 检查条件分支指向的目标是否存在。
- 把收集到的节点、边、分支、通道与 schema 信息整理成可执行图对象。
这里的结构校验非常重要,因为复杂 Agent 的问题很多并不是“模型答错了”,而是图本身定义得不完整,例如:
- 某个节点被注册了,但没有任何路径能到达它。
- 分支函数可能返回一个不存在的目标节点。
- 图没有合理入口,导致无法启动。
- 图没有合理出口,导致流程无法结束。
只有在这些结构问题被确认后,图才适合交给运行时执行。