Skip to main content
本指南演示了 LangGraph 图 API 的基础知识。它将引导你了解状态,以及如何组合常见的图结构,如序列分支循环。它还涵盖了 LangGraph 的控制功能,包括用于 Map-Reduce 工作流的 [Send API](#map-reduce 和 send api) 以及用于将状态更新与跨节点的“跳转”相结合的 [Command API](#使用 command 结合控制流和状态更新)。

设置

安装 langgraph
设置 LangSmith 以获得更好的调试体验注册 LangSmith 以快速发现问题并提升 LangGraph 项目的性能。LangSmith 允许你使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用——在文档中了解更多入门信息。

定义和更新状态

这里我们展示如何在 LangGraph 中定义和更新状态。我们将演示:
  1. 如何使用状态来定义图的模式
  2. 如何使用归约器来控制状态更新的处理方式。

定义状态

LangGraph 中的状态使用 StateSchema 类定义。这提供了一个统一的 API,它接受用于各个字段的标准模式(如 Zod),以及像 ReducedValueMessagesValueUntrackedValue 这样的特殊值类型。 默认情况下,图将具有相同的输入和输出模式,状态决定了该模式。有关如何定义不同的输入和输出模式,请参阅定义输入和输出模式 让我们考虑一个使用消息的简单示例。这代表了许多 LLM 应用中一种通用的状态表述。更多详情请参阅我们的概念页面
此状态跟踪一个消息对象列表,以及一个额外的整数字段。

更新状态

让我们构建一个包含单个节点的示例图。我们的节点只是一个 TypeScript 函数,它读取图的状态并对其进行更新。此函数的第一个参数始终是状态:
此节点只是向我们的消息列表追加一条消息(归约器处理连接操作),并填充一个额外字段。
节点应直接返回对状态的更新,而不是修改状态。
接下来,让我们定义一个包含此节点的简单图。我们使用 StateGraph 来定义一个在此状态上操作的图。然后我们使用 addNode 来填充我们的图。
LangGraph 提供了内置的可视化工具来可视化你的图。让我们检查一下我们的图。有关可视化的详细信息,请参阅可视化你的图
在这种情况下,我们的图只执行一个节点。让我们进行一个简单的调用:
请注意:
  • 我们通过更新状态的单个键来启动调用。
  • 我们在调用结果中接收到整个状态。
为了方便,我们经常通过日志记录来检查消息对象的内容:

使用归约器处理状态更新

状态中的每个键都可以有自己的独立归约器函数,该函数控制来自节点的更新如何被应用。如果没有明确指定归约器函数,则假定对该键的所有更新都应覆盖它。 在我们之前的示例中,我们使用了 MessagesValue,它已经有一个内置的归约器。对于自定义字段,你可以使用 ReducedValue 来定义更新的应用方式。 在之前的示例中,我们的节点通过向状态中的 "messages" 键追加一条消息来更新它。MessagesValue 归约器会自动处理这一点:
我们的节点可以简单地返回新消息(归约器处理连接操作):

MessagesValue

实际上,更新消息列表时还有额外的考虑因素:
  • 我们可能希望更新状态中的现有消息。
  • 我们可能希望接受消息格式的简写,例如 OpenAI 格式
LangGraph 包含内置的 MessagesValue,它处理了这些考虑因素:
这是涉及聊天模型的应用程序中状态的一种通用表示。LangGraph 包含预构建的 MessagesValue 以方便使用,这样我们就可以:

定义输入和输出模式

默认情况下,StateGraph 使用单一模式操作,所有节点都期望使用该模式进行通信。但是,也可以为图定义不同的输入和输出模式。 当指定了不同的模式时,内部模式仍将用于节点之间的通信。输入模式确保提供的输入符合预期的结构,而输出模式根据定义的输出模式过滤内部数据,仅返回相关信息。 下面,我们将看到如何定义不同的输入和输出模式。
请注意,invoke 的输出仅包含输出模式。

在节点之间传递私有状态

在某些情况下,你可能希望节点交换对中间逻辑至关重要但不需要成为图主要模式一部分的信息。这些私有数据与图的整体输入/输出无关,只应在特定节点之间共享。 下面,我们将创建一个由三个节点(node_1、node_2 和 node_3)组成的示例顺序图,其中私有数据在前两个步骤(node_1 和 node_2)之间传递,而第三个步骤(node_3)只能访问公共的整体状态。

替代的状态定义方法

虽然 StateSchema 是定义状态的推荐方法,但 LangGraph 支持其他几种方法。本节涵盖所有可用选项。

Channels API

Channels API 提供了对状态管理的底层控制。LangGraph 提供了几种内置的通道类型: 使用对象简写: 当你传递一个带有 reducerdefault 的对象时,它会创建一个 BinaryOperatorAggregate 通道。传递 null 会创建一个 LastValue 通道:
直接使用通道类: 为了更多控制,你可以直接实例化通道类:

Annotation.Root

Annotation.Root 提供了一种声明式的方式来定义带有归约器的状态。它类似于 StateSchema,但使用不同的语法:

使用 Zod v3 的 Zod 对象

使用 Zod v3 时,你可以使用普通的 z.object() 模式定义状态。LangGraph 扩展了 Zod v3,添加了 .langgraph 插件,该插件提供了 .reducer().metadata() 方法:

使用 Zod v4 的 Zod 对象

Zod v4 使用基于注册表的方法。使用 LangGraph 注册表将元数据附加到模式字段:

比较表

添加运行时配置

有时你希望在调用图时能够配置它。例如,你可能希望能够在运行时指定使用哪个 LLM 或系统提示,而不将这些参数污染图状态 要添加运行时配置:
  1. 为你的配置指定一个模式
  2. 将配置添加到节点或条件边的函数签名中
  3. 将配置传递给图。
请参阅下面的简单示例:
下面我们演示一个实际示例,我们在运行时配置使用哪个 LLM。我们将同时使用 OpenAI 和 Anthropic 模型。
下面我们演示一个实际示例,我们在运行时配置两个参数:LLM 和系统消息。

添加重试策略

在许多用例中,你可能希望节点具有自定义的重试策略,例如,如果你正在调用 API、查询数据库或调用 LLM 等。LangGraph 允许你为节点添加重试策略。 要配置重试策略,请将 retryPolicy 参数传递给 addNoderetryPolicy 参数接受一个 RetryPolicy 对象。下面我们使用默认参数实例化一个 RetryPolicy 对象并将其与节点关联:
默认情况下,重试策略会在除以下异常之外的任何异常上重试:
  • TypeError
  • SyntaxError
  • ReferenceError
考虑一个我们从 SQL 数据库读取的示例。下面我们向节点传递两个不同的重试策略:

在节点内访问执行信息

你可以通过 runtime.executionInfo 访问执行标识和重试信息。这提供了线程、运行和检查点标识符以及重试状态,而无需直接从 config 读取。

访问线程和运行 ID

使用 executionInfo 在节点内访问线程 ID、运行 ID 和其他标识字段:

根据重试状态调整行为

当节点具有重试策略时,使用 executionInfo 检查当前尝试次数,并在第一次尝试失败后切换到回退方案:
即使没有重试策略,executionInfo 也可在 Runtime 对象上使用——nodeAttempt 默认为 1nodeFirstAttemptTime 设置为节点开始执行的时间。

在节点内访问服务器信息

当你的图在 LangGraph Server 上运行时,你可以通过 runtime.serverInfo 访问服务器特定的元数据。
当图未在 LangGraph Server 上运行时,serverInfonull
需要 deepagents>=1.9.0(或 @langchain/langgraph>=1.2.8)才能使用 runtime.executionInforuntime.serverInfo

创建步骤序列

前提条件 本指南假设你熟悉上面关于状态的部分。
这里我们演示如何构建一个简单的步骤序列。我们将展示:
  1. 如何构建一个顺序图
  2. 用于构建类似图的内置简写。
要添加一系列节点,我们使用图的 .addNode.addEdge 方法:
LangGraph 使得为你的应用程序添加底层持久化层变得容易。 这允许在节点执行之间对状态进行检查点保存,因此你的 LangGraph 节点控制:它们还决定了执行步骤如何被流式传输,以及你的应用程序如何使用 Studio 进行可视化和调试。让我们演示一个端到端的示例。我们将创建一个包含三个步骤的序列:
  1. 在状态的一个键中填充一个值
  2. 更新相同的值
  3. 填充一个不同的值
让我们首先定义我们的状态。这控制着图的模式,也可以指定如何应用更新。有关更多详细信息,请参阅使用归约器处理状态更新在我们的例子中,我们将只跟踪两个值:
我们的节点只是读取图的状态并对其进行更新的 TypeScript 函数。此函数的第一个参数始终是状态:
请注意,当向状态发出更新时,每个节点只需指定它希望更新的键的值。默认情况下,这将覆盖相应键的值。你也可以使用归约器来控制更新的处理方式——例如,你可以将连续的更新追加到一个键中。有关更多详细信息,请参阅使用归约器处理状态更新
最后,我们定义图。我们使用 StateGraph 来定义一个在此状态上操作的图。然后我们将使用 addNodeaddEdge 来填充我们的图并定义其控制流。
指定自定义名称 你可以使用 .addNode 为节点指定自定义名称:
请注意:
  • .addEdge 接受节点的名称,对于函数,默认为 node.name
  • 我们必须指定图的入口点。为此,我们添加一条带有 START 节点的边。
  • 当没有更多节点可执行时,图停止。
接下来我们编译我们的图。这会对图的结构进行一些基本检查(例如,识别孤立节点)。如果我们通过检查点保存器为应用程序添加持久化,它也会在这里传入。LangGraph 提供了内置的可视化工具来可视化你的图。让我们检查一下我们的序列。有关可视化的详细信息,请参阅可视化你的图
让我们进行一个简单的调用:
请注意:
  • 我们通过为单个状态键提供值来启动调用。我们必须始终至少为一个键提供值。
  • 我们传入的值被第一个节点覆盖。
  • 第二个节点更新了该值。
  • 第三个节点填充了一个不同的值。

创建分支

节点的并行执行对于加速整体图操作至关重要。LangGraph 提供了对节点并行执行的原生支持,这可以显著提高基于图的工作流的性能。这种并行化是通过扇出和扇入机制实现的,利用标准边和 conditional_edges。以下是一些示例,展示如何添加创建适合你的分支数据流。

并行运行图节点

在这个示例中,我们从 Node A 扇出到 B 和 C,然后扇入到 D。对于我们的状态,我们指定了归约器添加操作。这将组合或累积状态中特定键的值,而不是简单地覆盖现有值。对于列表,这意味着将新列表与现有列表连接。有关使用归约器更新状态的更多详细信息,请参阅上面的状态归约器部分。
使用归约器,你可以看到在每个节点中添加的值被累积了。
在上面的示例中,节点 "b""c" 在同一个超级步骤中并发执行。因为它们在同一个步骤中,所以节点 "d""b""c" 都完成后执行。重要的是,并行超级步骤的更新可能不会按一致的顺序排列。如果你需要并行超级步骤的更新具有一致的、预定的顺序,你应该将输出写入状态中的一个单独字段,并附带一个用于排序的值。
LangGraph 在超级步骤内执行节点,这意味着虽然并行分支是并行执行的,但整个超级步骤是事务性的。如果其中任何一个分支引发异常,则不会有任何更新应用到状态(整个超级步骤出错)。重要的是,当使用检查点保存器时,超级步骤内成功节点的结果会被保存,并且在恢复时不会重复。如果你有容易出错的(可能想处理不稳定的 API 调用),LangGraph 提供了两种解决方法:
  1. 你可以在节点内编写常规的 Python 代码来捕获和处理异常。
  2. 你可以设置一个 retry_policy 来指示图重试引发某些类型异常的节点。只有失败的分支会被重试,因此你不必担心执行冗余工作。
这些结合在一起,让你可以执行并行执行并完全控制异常处理。
设置最大并发数 你可以在调用图时通过在配置中设置 max_concurrency 来控制最大并发任务数。

条件分支

如果你的扇出需要在运行时根据状态变化,你可以使用 addConditionalEdges 来使用图状态选择一个或多个路径。参见下面的示例,其中节点 a 生成一个状态更新,该更新决定了后续节点。
你的条件边可以路由到多个目标节点。例如:

Map-Reduce 和 send API

LangGraph 使用 Send API 支持 Map-Reduce 和其他高级分支模式。以下是使用它的一个示例:

创建和控制循环

在创建带有循环的图时,我们需要一种终止执行的机制。最常见的是通过添加一个条件边来实现,一旦达到某个终止条件,该边就路由到 END 节点。 你也可以在调用或流式传输图时设置图的递归限制。递归限制设置了图在引发错误之前允许执行的超级步骤数量。阅读更多关于递归限制概念的信息。 让我们考虑一个带有循环的简单图,以更好地理解这些机制是如何工作的。
要返回状态的最后一个值而不是收到递归限制错误,请参阅下一节
创建循环时,你可以包含一个指定终止条件的条件边:
要控制递归限制,请在配置中指定 "recursionLimit"。这将引发一个 GraphRecursionError,你可以捕获并处理它:
让我们定义一个带有简单循环的图。请注意,我们使用条件边来实现终止条件。
这种架构类似于 ReAct 代理,其中节点 "a" 是一个工具调用模型,节点 "b" 代表工具。 在我们的 route 条件边中,我们指定当状态中的 "aggregate" 列表超过阈值长度后应结束。 调用图,我们看到在达到终止条件之前,我们在节点 "a""b" 之间交替执行。

强制递归限制

在某些应用程序中,我们可能无法保证会达到给定的终止条件。在这些情况下,我们可以设置图的递归限制。这将在给定数量的超级步骤后引发一个 GraphRecursionError。然后我们可以捕获并处理此异常:

使用 Command 结合控制流和状态更新

将控制流(边)和状态更新(节点)结合起来会很有用。例如,你可能希望在同一个节点中既执行状态更新又决定接下来去哪个节点。LangGraph 提供了一种通过从节点函数返回 Command 对象来实现此目的的方法:
我们在下面展示一个端到端的示例。让我们创建一个包含 3 个节点的简单图:A、B 和 C。我们将首先执行节点 A,然后根据节点 A 的输出决定接下来是去节点 B 还是节点 C。
我们现在可以使用上面的节点创建 StateGraph。请注意,该图没有用于路由的条件边!这是因为控制流是在 nodeA 内部使用 Command 定义的。
你可能已经注意到我们使用了 ends 来指定 nodeA 可以导航到哪些节点。这对于图渲染是必要的,并告诉 LangGraph nodeA 可以导航到 nodeBnodeC
如果我们多次运行该图,我们会看到它根据节点 A 中的随机选择采取不同的路径(A -> B 或 A -> C)。

导航到父图中的节点

如果你正在使用子图,你可能希望从子图内的节点导航到不同的子图(即父图中的不同节点)。为此,你可以在 Command 中指定 graph=Command.PARENT
让我们使用上面的示例来演示这一点。我们将通过将上面示例中的 nodeA 更改为一个单节点图来实现,该图将作为子图添加到我们的父图中。
使用 Command.PARENT 进行状态更新 当你从子图节点向父图节点发送更新时,对于父图和子图状态模式共享的键,你必须为父图状态中你正在更新的键定义一个归约器。请参阅下面的示例。

在工具内使用

一个常见的用例是从工具内部更新图状态。例如,在客户支持应用程序中,你可能希望在对话开始时根据客户的账号或 ID 查找客户信息。要从工具更新图状态,你可以从工具返回 Command(update={"my_custom_key": "foo", "messages": [...]})
当你从工具返回 Command 时,你必须Command.update 中包含 messages(或用于消息历史的任何状态键),并且 messages 中的消息列表必须包含一个 ToolMessage。这对于生成的消息历史记录有效是必要的(LLM 提供商要求带有工具调用的 AI 消息后必须跟有工具结果消息)。
如果你正在使用通过 Command 更新状态的工具,我们建议使用预构建的 ToolNode,它自动处理返回 Command 对象的工具并将其传播到图状态。如果你正在编写一个调用工具的自定义节点,你需要手动将工具返回的 Command 对象作为节点的更新进行传播。

可视化你的图

这里我们演示如何可视化你创建的图。 你可以可视化任何任意的 Graph,包括 StateGraph 让我们创建一个简单的示例图来演示可视化。

Mermaid

我们也可以将图类转换为 Mermaid 语法。

PNG

如果需要,我们可以将图渲染为 .png。这使用 Mermaid.ink API 生成图表。