如何使用子图¶
子图 允许你构建由多个组件组成的复杂系统,而这些组件本身也是图。使用子图的一个常见用例是构建 多智能体系统。
添加子图时的主要问题是父图和子图如何进行通信,即它们在图执行期间如何在彼此之间传递 状态。有两种情况:
- 父图和子图 共享模式键。在这种情况下,你可以 添加一个包含已编译子图的节点
- 父图和子图具有 不同的模式。在这种情况下,你必须 添加一个调用子图的节点函数:当父图和子图具有不同的状态模式,并且你需要在调用子图之前或之后转换状态时,这很有用
下面我们将展示如何针对每种情况添加子图。
安装设置¶
首先,让我们安装所需的软件包。
为 LangGraph 开发设置 LangSmith
注册 LangSmith 以快速发现问题并提升你的 LangGraph 项目的性能。LangSmith 允许你使用跟踪数据来调试、测试和监控使用 LangGraph 构建的大语言模型应用程序 — 点击 此处 了解更多关于如何开始使用的信息。
添加带有已编译子图的节点¶
一种常见的情况是,父图和子图通过共享状态键(通道)进行通信。例如,在多智能体系统中,智能体通常通过共享的消息键进行通信。
如果你的子图与父图共享状态键,你可以按照以下步骤将其添加到你的图中:
- 定义子图工作流(在下面的示例中为
subgraph_builder
)并对其进行编译 - 在定义父图工作流时,将已编译的子图传递给
.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()
"bar"
)。如果你想查看子图的输出,可以在流式传输时指定 subgraphs=True
。有关从子图进行流式传输的更多信息,请参阅此 操作指南。
((), {'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()
((), {'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'}})