Skip to main content
本指南演示了 LangGraph 的 Graph API 的基础知识。它将介绍 状态,以及组合常见的图结构,如 序列分支循环。它还涵盖了 LangGraph 的控制功能,包括用于 map-reduce 工作流的 Send API 以及用于将状态更新与节点间的“跳转”相结合的 Command API

设置

安装 langgraph
设置 LangSmith 以获得更好的调试体验注册 LangSmith 以快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许您使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用——有关如何入门的更多信息,请阅读 文档

定义和更新状态

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

定义状态

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

更新状态

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

使用归约器处理状态更新

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

MessagesValue

在实践中,更新消息列表还有其他注意事项:
  • 我们可能希望更新状态中的现有消息。
  • 我们可能希望接受 消息格式 的简写,例如 OpenAI 格式
LangGraph 包含内置的 MessagesValue,可处理这些注意事项:
这是涉及 聊天模型 的应用程序的通用状态表示。LangGraph 包含预构建的 MessagesValue 以供方便使用,因此我们可以拥有:

定义输入和输出模式

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

在节点之间传递私有状态

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

替代状态定义

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

通道 API

通道 API 提供对状态管理的低级控制。LangGraph 提供几种内置通道类型: 使用对象简写: 当您传递具有 reducerdefault 的对象时,它会创建 BinaryOperatorAggregate 通道。传递 null 会创建 LastValue 通道:
直接使用通道类: 为了获得更大的控制权,您可以直接实例化通道类:

Annotation.Root

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

带 Zod v3 的对象

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

带 Zod v4 的对象

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

比较表

添加运行时配置

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

添加重试策略

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

创建步骤序列

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

创建分支

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

并行运行图节点

在此示例中,我们从 Node A 扇出到 B 和 C,然后扇入到 D。使用我们的状态,我们指定归约器 add 操作。这将组合或累积特定键的值,而不是简单地覆盖现有值。对于列表,这意味着将新列表与现有列表连接。有关使用归约器更新状态的更多详细信息,请参阅上面的 状态归约器 部分。
使用归约器,您可以看到每个节点中添加的值都被累积了。
在上述示例中,节点 "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 agent,其中节点 "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 生成图表。