跳到正文

LangGraph 术语表

LangGraph 的核心是将智能体工作流建模为图。您可以使用三个关键组件来定义智能体的行为

  1. State:一个共享数据结构,表示您应用程序的当前快照。它可以是任何 Python 类型,但通常是 TypedDict 或 Pydantic BaseModel

  2. 节点:Python 函数,用于编码智能体的逻辑。它们接收当前 State 作为输入,执行一些计算或副作用,并返回更新后的 State

  3. :Python 函数,根据当前 State 确定接下来执行哪个 Node。它们可以是条件分支或固定转换。

通过组合 NodesEdges,您可以创建复杂的、循环的工作流,这些工作流会随时间推移演变 State。然而,真正的强大之处在于 LangGraph 管理 State 的方式。需要强调的是:NodesEdges 不过是 Python 函数——它们可以包含一个 LLM 或仅仅是普通的 Python 代码。

简而言之:节点完成工作,边指示下一步要做什么

LangGraph 底层的图算法使用消息传递来定义通用程序。当一个节点完成其操作时,它会沿着一条或多条边将消息发送到其他节点。这些接收节点随后执行其函数,将结果消息传递给下一组节点,这个过程持续进行。受到 Google Pregel 系统的启发,程序以离散的“超级步”进行。

一个超级步可以被认为是图节点的一次迭代。并行运行的节点属于同一个超级步,而顺序运行的节点则属于不同的超级步。图执行开始时,所有节点都处于 inactive 状态。当一个节点在其任何入边(或“通道”)上接收到新消息(状态)时,它就变为 active。活跃节点随后运行其函数并返回更新。在每个超级步结束时,没有入站消息的节点会通过将自身标记为 inactive 来投票 halt(停止)。当所有节点都处于 inactive 状态且没有消息正在传输时,图执行终止。

StateGraph

StateGraph 类是主要使用的图类。它通过用户定义的 State 对象进行参数化。

编译您的图

构建您的图时,首先定义状态,然后添加节点,最后进行编译。图的编译究竟是什么?为何需要编译?

编译是一个相当简单的步骤。它对图的结构进行了一些基本检查(例如,没有孤立节点)。您也可以在此处指定运行时参数,例如检查点(checkpointers)断点(breakpoints)。只需调用 .compile 方法即可编译您的图。

graph = graph_builder.compile(...)

在使用您的图之前,您必须对其进行编译。

状态

定义图时首先要做的是定义图的 StateState图的模式(schema)以及指定如何应用状态更新的reducer 函数组成。State 的模式将成为图中所有 NodesEdges 的输入模式,可以是 TypedDictPydantic 模型。所有 Nodes 都将发出对 State 的更新,然后使用指定的 reducer 函数应用这些更新。

模式

指定图模式的主要文档化方法是使用 TypedDict。但是,我们也支持使用 Pydantic BaseModel 作为图状态,以添加默认值和额外的数据验证。

默认情况下,图具有相同的输入和输出模式。如果您想更改这一点,也可以直接指定显式的输入和输出模式。当您有很多键,其中一些明确用于输入,另一些用于输出时,这非常有用。请参阅此处的 notebook,了解如何使用。

多种模式

通常,所有图节点都使用单一模式进行通信。这意味着它们将读写相同的状态通道。但是,在某些情况下,我们希望对此有更多控制。

  • 内部节点可以传递图的输入/输出中不需要的信息。
  • 我们可能还希望为图使用不同的输入/输出模式。例如,输出可能仅包含一个相关的输出键。

可以使节点写入图内部的私有状态通道,用于内部节点通信。我们可以简单地定义一个私有模式 PrivateState。有关更多详情,请参阅此 notebook

还可以为图定义显式的输入和输出模式。在这些情况下,我们定义一个包含与图操作相关的所有键的“内部”模式。但是,我们还定义了作为“内部”模式子集的 inputoutput 模式,以约束图的输入和输出。有关更多详情,请参阅此 notebook

我们来看一个例子

class InputState(TypedDict):
    user_input: str

class OutputState(TypedDict):
    graph_output: str

class OverallState(TypedDict):
    foo: str
    user_input: str
    graph_output: str

class PrivateState(TypedDict):
    bar: str

def node_1(state: InputState) -> OverallState:
    # Write to OverallState
    return {"foo": state["user_input"] + " name"}

def node_2(state: OverallState) -> PrivateState:
    # Read from OverallState, write to PrivateState
    return {"bar": state["foo"] + " is"}

def node_3(state: PrivateState) -> OutputState:
    # Read from PrivateState, write to OutputState
    return {"graph_output": state["bar"] + " Lance"}

builder = StateGraph(OverallState,input=InputState,output=OutputState)
builder.add_node("node_1", node_1)
builder.add_node("node_2", node_2)
builder.add_node("node_3", node_3)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
builder.add_edge("node_2", "node_3")
builder.add_edge("node_3", END)

graph = builder.compile()
graph.invoke({"user_input":"My"})
{'graph_output': 'My name is Lance'}

这里有两个微妙而重要的点需要注意

  1. 我们将 state: InputState 作为输入模式传递给 node_1。但是,我们写入 foo,这是 OverallState 中的一个通道。如何写入输入模式中未包含的状态通道?这是因为节点可以写入图状态中的任何状态通道。图状态是初始化时定义的状态通道的并集,其中包括 OverallState 以及过滤器 InputStateOutputState

  2. 我们使用 StateGraph(OverallState,input=InputState,output=OutputState) 初始化图。那么,如何在 node_2 中写入 PrivateState 呢?如果未在 StateGraph 初始化中传递此模式,图如何访问它?我们可以这样做,因为节点也可以声明额外的状态通道,只要状态模式定义存在即可。在本例中,PrivateState 模式已定义,因此我们可以将 bar 作为图中的新状态通道添加并写入其中。

Reducer

Reducer 是理解如何将节点更新应用到 State 的关键。State 中的每个键都有自己独立的 reducer 函数。如果未显式指定 reducer 函数,则假定对该键的所有更新都应覆盖它。有几种不同类型的 reducer,从默认类型的 reducer 开始。

默认 Reducer

这两个示例展示了如何使用默认 reducer

示例 A

from typing_extensions import TypedDict

class State(TypedDict):
    foo: int
    bar: list[str]

在此示例中,未为任何键指定 reducer 函数。假设图的输入是 {"foo": 1, "bar": ["hi"]}。然后假设第一个 Node 返回 {"foo": 2}。这被视为对状态的更新。请注意,Node 不需要返回整个 State 模式——只需返回更新即可。应用此更新后,State 将变为 {"foo": 2, "bar": ["hi"]}。如果第二个节点返回 {"bar": ["bye"]},则 State 将变为 {"foo": 2, "bar": ["bye"]}

示例 B

from typing import Annotated
from typing_extensions import TypedDict
from operator import add

class State(TypedDict):
    foo: int
    bar: Annotated[list[str], add]

在此示例中,我们使用 Annotated 类型为第二个键 (bar) 指定了一个 reducer 函数 (operator.add)。请注意,第一个键保持不变。假设图的输入是 {"foo": 1, "bar": ["hi"]}。然后假设第一个 Node 返回 {"foo": 2}。这被视为对状态的更新。请注意,Node 不需要返回整个 State 模式——只需返回更新即可。应用此更新后,State 将变为 {"foo": 2, "bar": ["hi"]}。如果第二个节点返回 {"bar": ["bye"]},则 State 将变为 {"foo": 2, "bar": ["hi", "bye"]}。请注意此处 bar 键是通过将两个列表相加来更新的。

在图状态中使用消息

为何使用消息?

大多数现代 LLM 提供商都有一个聊天模型接口,接受消息列表作为输入。特别是 LangChain 的 ChatModel 接受 Message 对象列表作为输入。这些消息有多种形式,例如 HumanMessage(用户输入)或 AIMessage(LLM 响应)。要了解更多关于消息对象的信息,请参阅概念指南。

在您的图中使用消息

在许多情况下,将先前的对话历史记录存储为图状态中的消息列表会很有帮助。为此,我们可以在图状态中添加一个键(通道),用于存储 Message 对象列表,并使用 reducer 函数对其进行标注(参见下方示例中的 messages 键)。reducer 函数对于告知图如何使用每次状态更新(例如,当节点发送更新时)来更新状态中的 Message 对象列表至关重要。如果您未指定 reducer,则每次状态更新都会用最近提供的值覆盖消息列表。如果您只想将消息简单地附加到现有列表中,可以使用 operator.add 作为 reducer。

然而,您可能还想手动更新图状态中的消息(例如,人工介入)。如果您使用 operator.add,您发送给图的手动状态更新将附加到现有消息列表,而不是更新现有消息。为了避免这种情况,您需要一个能够跟踪消息 ID 并在更新时覆盖现有消息的 reducer。为此,您可以使用预置的 add_messages 函数。对于全新消息,它只会附加到现有列表,但它也能正确处理现有消息的更新。

序列化

除了跟踪消息 ID 外,add_messages 函数还会尝试将消息反序列化为 LangChain Message 对象,无论何时在 messages 通道上接收到状态更新。有关 LangChain 序列化/反序列化的更多信息,请参阅此处。这允许以以下格式发送图输入/状态更新

# this is supported
{"messages": [HumanMessage(content="message")]}

# and this is also supported
{"messages": [{"type": "human", "content": "message"}]}

由于使用 add_messages 时,状态更新总是被反序列化为 LangChain Messages,因此您应该使用点表示法访问消息属性,例如 state["messages"][-1].content。下面是一个使用 add_messages 作为其 reducer 函数的图的示例。

API 参考:AnyMessage | add_messages

from langchain_core.messages import AnyMessage
from langgraph.graph.message import add_messages
from typing import Annotated
from typing_extensions import TypedDict

class GraphState(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

MessagesState

由于在状态中包含消息列表非常常见,因此存在一个预置状态 MessagesState,可以轻松使用消息。MessagesState 定义了一个名为 messages 的键,它是一个 AnyMessage 对象列表,并使用 add_messages reducer。通常,除了消息之外还需要跟踪更多状态,因此我们看到人们子类化此状态并添加更多字段,例如

from langgraph.graph import MessagesState

class State(MessagesState):
    documents: list[str]

节点

在 LangGraph 中,节点通常是 Python 函数(同步或异步),其中第一个位置参数是状态,而(可选的)第二个位置参数是“配置”,包含可选的可配置参数(例如 thread_id)。

NetworkX 类似,您可以使用 add_node 方法将这些节点添加到图中

API 参考:RunnableConfig | StateGraph

from langchain_core.runnables import RunnableConfig
from langgraph.graph import StateGraph

builder = StateGraph(dict)


def my_node(state: dict, config: RunnableConfig):
    print("In node: ", config["configurable"]["user_id"])
    return {"results": f"Hello, {state['input']}!"}


# The second argument is optional
def my_other_node(state: dict):
    return state


builder.add_node("my_node", my_node)
builder.add_node("other_node", my_other_node)
...

在幕后,函数被转换为 RunnableLambda,这为您的函数添加了批处理和异步支持,以及原生的跟踪和调试功能。

如果您在向图添加节点时未指定名称,则会为其赋予与函数名称等效的默认名称。

builder.add_node(my_node)
# You can then create edges to/from this node by referencing it as `"my_node"`

START 节点

START 节点是一个特殊节点,表示将用户输入发送到图的节点。引用此节点的主要目的是确定应首先调用哪些节点。

API 参考:START

from langgraph.graph import START

graph.add_edge(START, "node_a")

END 节点

END 节点是一个特殊节点,代表终止节点。当您想表示哪些边在完成后没有后续操作时,会引用此节点。

from langgraph.graph import END

graph.add_edge("node_a", END)

边定义了逻辑如何路由以及图如何决定停止。这是智能体工作以及不同节点之间如何通信的重要组成部分。边有几种主要类型

  • 普通边:直接从一个节点到下一个节点。
  • 条件边:调用函数来确定接下来要去哪个或哪些节点。
  • 入口点:当用户输入到达时,首先调用哪个节点。
  • 条件入口点:调用函数来确定当用户输入到达时,首先调用哪个或哪些节点。

一个节点可以有多个出站边。如果一个节点有多个出站边,则所有这些目标节点将在下一个超级步中并行执行。

普通边

如果您总是想从节点 A 到节点 B,可以直接使用 add_edge 方法。

graph.add_edge("node_a", "node_b")

条件边

如果您想可选地路由到一条或多条边(或可选地终止),可以使用 add_conditional_edges 方法。此方法接受节点名称和在该节点执行后调用的“路由函数”

graph.add_conditional_edges("node_a", routing_function)

与节点类似,routing_function 接受图的当前 state 并返回值。

默认情况下,routing_function 的返回值用作下一个接收状态的节点(或节点列表)的名称。所有这些节点将在下一个超级步中并行运行。

您可以选择提供一个字典,将 routing_function 的输出映射到下一个节点的名称。

graph.add_conditional_edges("node_a", routing_function, {True: "node_b", False: "node_c"})

提示

如果您想在单个函数中结合状态更新和路由,请使用 Command 而非条件边。

入口点

入口点是图启动时运行的第一个或多个节点。您可以使用虚拟 add_edge 方法从 START 节点到第一个要执行的节点来指定图的入口位置。

API 参考:START

from langgraph.graph import START

graph.add_edge(START, "node_a")

条件入口点

条件入口点允许您根据自定义逻辑从不同的节点开始。您可以使用虚拟 add_conditional_edges 方法从 START 节点来实现此功能。

API 参考:START

from langgraph.graph import START

graph.add_conditional_edges(START, routing_function)

您可以选择提供一个字典,将 routing_function 的输出映射到下一个节点的名称。

graph.add_conditional_edges(START, routing_function, {True: "node_b", False: "node_c"})

Send

默认情况下,NodesEdges 是预先定义的,并在同一共享状态上运行。然而,在某些情况下,确切的边可能无法预知,并且/或者您可能希望同时存在不同版本的 State。一个常见的例子是map-reduce 设计模式。在这种设计模式中,第一个节点可能会生成一个对象列表,您可能希望将某些其他节点应用于所有这些对象。对象的数量可能无法预知(这意味着边的数量可能未知),并且下游 Node 的输入 State 应该不同(每个生成的对象对应一个)。

为了支持这种设计模式,LangGraph 支持从条件边返回 Send 对象。Send 接受两个参数:第一个是节点名称,第二个是要传递给该节点的状态。

def continue_to_jokes(state: OverallState):
    return [Send("generate_joke", {"subject": s}) for s in state['subjects']]

graph.add_conditional_edges("node_a", continue_to_jokes)

Command

将控制流(边)和状态更新(节点)结合起来可能会很有用。例如,您可能希望在同一个节点中既执行状态更新又决定下一步前往哪个节点。LangGraph 提供了一种方法,即从节点函数返回一个 Command 对象。

def my_node(state: State) -> Command[Literal["my_other_node"]]:
    return Command(
        # state update
        update={"foo": "bar"},
        # control flow
        goto="my_other_node"
    )

使用 Command,您还可以实现动态控制流行为(与条件边相同)

def my_node(state: State) -> Command[Literal["my_other_node"]]:
    if state["foo"] == "bar":
        return Command(update={"foo": "baz"}, goto="my_other_node")

重要提示

在节点函数中返回 Command 时,必须添加返回类型注解,其中包含节点将路由到的节点名称列表,例如 Command[Literal["my_other_node"]]。这对于图的渲染是必需的,并告知 LangGraph my_node 可以导航到 my_other_node

请查看这篇操作指南,了解如何使用 Command 的端到端示例。

何时应使用 Command 而非条件边?

当您需要同时更新图状态路由到不同的节点时,请使用 Command。例如,在实现多智能体交接时,需要路由到不同的智能体并将一些信息传递给该智能体。

使用条件边在节点之间有条件地进行路由,而无需更新状态。

如果您正在使用子图,您可能希望从子图中的一个节点导航到另一个子图(即父图中的不同节点)。为此,您可以在 Command 中指定 graph=Command.PARENT

def my_node(state: State) -> Command[Literal["other_subgraph"]]:
    return Command(
        update={"foo": "bar"},
        goto="other_subgraph",  # where `other_subgraph` is a node in the parent graph
        graph=Command.PARENT
    )

注意

graph 设置为 Command.PARENT 将导航到最近的父图。

使用 Command.PARENT 进行状态更新

当您从子图节点向父图节点发送更新时,对于父图和子图状态模式共享的键,您必须在父图状态中为您正在更新的键定义一个reducer。请参见此示例

这在实现多智能体交接时特别有用。

在工具中使用

一个常见的用例是从工具内部更新图状态。例如,在客户支持应用程序中,您可能希望在对话开始时根据客户的账号或 ID 查找客户信息。要从工具更新图状态,您可以从工具返回 Command(update={"my_custom_key": "foo", "messages": [...]})

@tool
def lookup_user_info(tool_call_id: Annotated[str, InjectedToolCallId], config: RunnableConfig):
    """Use this to look up user information to better assist them with their questions."""
    user_info = get_user_info(config.get("configurable", {}).get("user_id"))
    return Command(
        update={
            # update the state keys
            "user_info": user_info,
            # update the message history
            "messages": [ToolMessage("Successfully looked up user information", tool_call_id=tool_call_id)]
        }
    )

重要提示

从工具返回 Command 时,您必须Command.update 中包含 messages(或用于消息历史记录的任何状态键),并且 messages 中的消息列表必须包含 ToolMessage。这对于生成有效的消息历史记录是必需的(LLM 提供商要求带有工具调用的 AI 消息后跟工具结果消息)。

如果您使用通过 Command 更新状态的工具,我们建议使用预置的 ToolNode,它会自动处理返回 Command 对象的工具,并将它们传播到图状态。如果您正在编写调用工具的自定义节点,则需要手动将工具返回的 Command 对象作为节点的更新进行传播。

人工介入

Command 是人工介入工作流的重要组成部分:当使用 interrupt() 收集用户输入时,然后使用 Command 通过 Command(resume="User input") 提供输入并恢复执行。有关更多信息,请查看此概念指南

持久化

LangGraph 使用检查点(checkpointers)为智能体的状态提供内置持久化。检查点在每个超级步保存图状态的快照,允许随时恢复。这使得人工介入交互、记忆管理和容错等功能成为可能。您甚至可以在图执行后使用适当的 getupdate 方法直接操作图的状态。有关更多详细信息,请参阅持久化概念指南

会话

LangGraph 中的会话代表图和用户之间的独立会话或对话。使用检查点时,单个对话中的回合(甚至是单个图执行中的步骤)都由唯一的会话 ID 组织。

存储

LangGraph 通过 BaseStore 接口提供内置的文档存储功能。与按会话 ID 保存状态的检查点不同,存储使用自定义命名空间来组织数据。这实现了跨会话持久化,允许智能体保持长期记忆、从过去的交互中学习并随时间积累知识。常见的用例包括存储用户配置文件、构建知识库和管理所有会话的全局偏好设置。

图迁移

即使在使用检查点跟踪状态时,LangGraph 也能轻松处理图定义(节点、边和状态)的迁移。

  • 对于图末尾的会话(即未中断的),您可以更改整个图的拓扑结构(即所有节点和边,进行删除、添加、重命名等操作)
  • 对于当前被中断的会话,我们支持除节点重命名/删除之外的所有拓扑更改(因为该会话可能正要进入一个不再存在的节点)——如果这造成了阻碍,请联系我们,我们可以优先处理解决方案。
  • 对于修改状态,我们对添加和删除键提供了完整的向前和向后兼容性
  • 已重命名的状态键将在现有会话中丢失其保存的状态
  • 其类型发生不兼容更改的状态键目前可能会在包含更改前状态的会话中引发问题——如果这造成了阻碍,请联系我们,我们可以优先处理解决方案。

配置

创建图时,您还可以标记图的某些部分是可配置的。这通常用于方便地在不同模型或系统提示之间切换。这允许您创建单个“认知架构”(即图),但拥有其多个不同的实例。

创建图时,您可以选择指定一个 config_schema

class ConfigSchema(TypedDict):
    llm: str

graph = StateGraph(State, config_schema=ConfigSchema)

然后可以使用 configurable 配置字段将此配置传递给图。

config = {"configurable": {"llm": "anthropic"}}

graph.invoke(inputs, config=config)

然后可以在节点内部访问和使用此配置

def node_a(state, config):
    llm_type = config.get("configurable", {}).get("llm", "openai")
    llm = get_llm(llm_type)
    ...

有关配置的完整细分,请参阅本指南

递归限制

递归限制设置了图在单次执行期间可以执行的最多超级步骤次数。达到此限制后,LangGraph 将引发 GraphRecursionError。默认情况下,此值设置为 25 步。递归限制可以在运行时设置在任何图上,并通过 config 字典传递给 .invoke/.stream。重要的是,recursion_limit 是一个独立的 config 键,不应像所有其他用户自定义配置那样传递到 configurable 键中。请参见下面的示例

graph.invoke(inputs, config={"recursion_limit": 5, "configurable":{"llm": "anthropic"}})

阅读本操作指南以了解有关递归限制工作原理的更多信息。

interrupt

使用interrupt函数在特定点**暂停**图以收集用户输入。interrupt函数会向客户端展示中断信息,允许开发人员在恢复执行之前收集用户输入、验证图状态或做出决策。

API 参考: interrupt

from langgraph.types import interrupt

def human_approval_node(state: State):
    ...
    answer = interrupt(
        # This value will be sent to the client.
        # It can be any JSON serializable value.
        {"question": "is it ok to continue?"},
    )
    ...

通过将一个 Command 对象传递给图来恢复图的执行,其中 resume 键的值设置为 interrupt 函数返回的值。

人机协作概念指南中阅读更多关于 interrupt 如何用于**人机协作**工作流的信息。

断点

断点可在特定点暂停图的执行,并允许逐步单步执行。断点由 LangGraph 的**持久层**提供支持,持久层会在图的每个步骤后保存状态。断点也可用于启用**人机协作**工作流,但我们建议为此目的使用interrupt函数

断点概念指南中阅读更多关于断点的信息。

子图

子图是一个,它在另一个图中用作节点。这不过是应用于 LangGraph 的古老封装概念。使用子图的一些原因包括

  • 构建多智能体系统

  • 当您想在多个图中重用一组节点时(这些节点可能共享一些状态),您可以在一个子图中定义它们一次,然后在多个父图中使用它们

  • 当您希望不同的团队独立地处理图的不同部分时,可以将每个部分定义为一个子图,只要子图接口(输入和输出模式)得到遵守,就可以构建父图而无需了解子图的任何细节

有两种方法可以将子图添加到父图

  • 添加一个带有编译子图的节点:这在父图和子图共享状态键且您无需在进入或退出时转换状态时很有用
builder.add_node("subgraph", subgraph_builder.compile())
  • 添加一个调用子图的函数的节点:这在父图和子图具有不同的状态模式且您需要在调用子图之前或之后转换状态时很有用
subgraph = subgraph_builder.compile()

def call_subgraph(state: State):
    return subgraph.invoke({"subgraph_key": state["parent_key"]})

builder.add_node("subgraph", call_subgraph)

让我们分别看一些示例。

作为编译图

创建子图节点的最简单方法是直接使用编译子图。这样做时,**重要**的是父图和子图的状态模式共享至少一个可用于通信的键。如果您的图和子图不共享任何键,则应改为编写一个函数调用子图

注意

如果您向子图节点传递额外的键(即,除了共享键之外),它们将被子图节点忽略。类似地,如果您从子图返回额外的键,它们将被父图忽略。

API 参考: StateGraph

from langgraph.graph import StateGraph
from typing import TypedDict

class State(TypedDict):
    foo: str

class SubgraphState(TypedDict):
    foo: str  # note that this key is shared with the parent graph state
    bar: str

# Define subgraph
def subgraph_node(state: SubgraphState):
    # note that this subgraph node can communicate with the parent graph via the shared "foo" key
    return {"foo": state["foo"] + "bar"}

subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node(subgraph_node)
...
subgraph = subgraph_builder.compile()

# Define parent graph
builder = StateGraph(State)
builder.add_node("subgraph", subgraph)
...
graph = builder.compile()

作为函数

您可能希望定义一个具有完全不同模式的子图。在这种情况下,您可以创建一个调用子图的节点函数。此函数需要先将输入(父)状态转换为子图状态,然后再调用子图;并在从节点返回状态更新之前将结果转换回父状态。

class State(TypedDict):
    foo: str

class SubgraphState(TypedDict):
    # note that none of these keys are shared with the parent graph state
    bar: str
    baz: str

# Define subgraph
def subgraph_node(state: SubgraphState):
    return {"bar": state["bar"] + "baz"}

subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node(subgraph_node)
...
subgraph = subgraph_builder.compile()

# Define parent graph
def node(state: State):
    # transform the state to the subgraph state
    response = subgraph.invoke({"bar": state["foo"]})
    # transform response back to the parent state
    return {"foo": response["bar"]}

builder = StateGraph(State)
# note that we are using `node` function instead of a compiled subgraph
builder.add_node(node)
...
graph = builder.compile()

可视化

能够可视化图通常很好,尤其是在它们变得更复杂时。LangGraph 提供了几种内置方法来可视化图。有关更多信息,请参阅本操作指南

流式处理

LangGraph 内建了对流式处理的一流支持,包括在执行过程中从图节点流式传输更新、从 LLM 调用流式传输 token 等。有关更多信息,请参阅本概念指南

评论