跳到内容

如何使用子图

先决条件

本指南假设您熟悉以下内容

子图 允许您构建具有多个组件的复杂系统,这些组件本身就是图。使用子图的一个常见用例是构建 多代理系统

添加子图时的主要问题是父图和子图如何通信,即它们在图执行期间如何在彼此之间传递状态。有两种情况

下面我们展示了如何为每种场景添加子图。

Screenshot 2024-07-11 at 1.01.28 PM.png

设置

首先,让我们安装所需的软件包

%%capture --no-stderr
%pip install -U langgraph

设置 LangSmith 以进行 LangGraph 开发

注册 LangSmith 以快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许您使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用程序——阅读更多关于如何开始使用的信息 这里

添加带有已编译子图的节点

一种常见的情况是父图和子图通过共享状态键(通道)进行通信。例如,在 多代理 系统中,代理通常通过共享的 消息 键进行通信。

如果您的子图与父图共享状态键,您可以按照以下步骤将其添加到您的图中

  1. 定义子图工作流程(以下示例中的 subgraph_builder)并编译它
  2. 在定义父图工作流程时,将编译后的子图传递给 .add_node 方法

让我们看一个简单的例子。

from langgraph.graph import START, StateGraph
from typing import TypedDict


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


def subgraph_node_1(state: SubgraphState):
    return {"bar": "bar"}


def subgraph_node_2(state: SubgraphState):
    # note that this node is using a state key ('bar') that is only available in the subgraph
    # and is sending update on the shared state key ('foo')
    return {"foo": state["foo"] + state["bar"]}


subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node(subgraph_node_1)
subgraph_builder.add_node(subgraph_node_2)
subgraph_builder.add_edge(START, "subgraph_node_1")
subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")
subgraph = subgraph_builder.compile()


# Define parent graph
class ParentState(TypedDict):
    foo: str


def node_1(state: ParentState):
    return {"foo": "hi! " + state["foo"]}


builder = StateGraph(ParentState)
builder.add_node("node_1", node_1)
# note that we're adding the compiled subgraph as a node to the parent graph
builder.add_node("node_2", subgraph)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
graph = builder.compile()

API 参考: START | StateGraph

for chunk in graph.stream({"foo": "foo"}):
    print(chunk)
{'node_1': {'foo': 'hi! foo'}}
{'node_2': {'foo': 'hi! foobar'}}
您可以看到来自父图的最终输出包括子图调用的结果(即字符串 "bar")。如果您想查看来自子图的输出,您可以在流式处理时指定 subgraphs=True。有关来自子图的流式处理的更多信息,请参阅此 操作指南

for chunk in graph.stream({"foo": "foo"}, subgraphs=True):
    print(chunk)
((), {'node_1': {'foo': 'hi! foo'}})
(('node_2:e58e5673-a661-ebb0-70d4-e298a7fc28b7',), {'subgraph_node_1': {'bar': 'bar'}})
(('node_2:e58e5673-a661-ebb0-70d4-e298a7fc28b7',), {'subgraph_node_2': {'foo': 'hi! foobar'}})
((), {'node_2': {'foo': 'hi! foobar'}})

添加调用子图的节点函数

对于更复杂的系统,您可能希望定义与父图具有完全不同模式(没有共享键)的子图。例如,在多代理 RAG 系统中,搜索代理可能只需要跟踪查询和检索到的文档。

如果您的应用程序是这种情况,您需要定义一个调用子图的节点函数。此函数需要在调用子图之前将输入(父)状态转换为子图状态,并在从节点返回状态更新之前将结果转换回父状态。

下面我们展示了如何修改我们的原始示例以从节点内部调用子图。

警告

不能在同一个节点内调用多个子图。

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


def subgraph_node_1(state: SubgraphState):
    return {"baz": "baz"}


def subgraph_node_2(state: SubgraphState):
    return {"bar": state["bar"] + state["baz"]}


subgraph_builder = StateGraph(SubgraphState)
subgraph_builder.add_node(subgraph_node_1)
subgraph_builder.add_node(subgraph_node_2)
subgraph_builder.add_edge(START, "subgraph_node_1")
subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")
subgraph = subgraph_builder.compile()


# Define parent graph
class ParentState(TypedDict):
    foo: str


def node_1(state: ParentState):
    return {"foo": "hi! " + state["foo"]}


def node_2(state: ParentState):
    # 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(ParentState)
builder.add_node("node_1", node_1)
# note that instead of using the compiled subgraph we are using `node_2` function that is calling the subgraph
builder.add_node("node_2", node_2)
builder.add_edge(START, "node_1")
builder.add_edge("node_1", "node_2")
graph = builder.compile()

for chunk in graph.stream({"foo": "foo"}, subgraphs=True):
    print(chunk)
((), {'node_1': {'foo': 'hi! foo'}})
(('node_2:c47d7ea3-7798-87c4-adf4-2543a91d6891',), {'subgraph_node_1': {'baz': 'baz'}})
(('node_2:c47d7ea3-7798-87c4-adf4-2543a91d6891',), {'subgraph_node_2': {'bar': 'hi! foobaz'}})
((), {'node_2': {'foo': 'hi! foobaz'}})

评论