如何使用子图¶
子图允许您构建包含多个组件的复杂系统,这些组件本身是图。使用子图的一个常见用例是构建多代理系统。
在添加子图时的主要问题是父图和子图如何通信,即它们在执行过程中如何传递状态。这里有两种情况:
- 父图和子图**共享模式键**。在这种情况下,您可以添加一个带有编译后的子图的节点
- 父图和子图具有**不同的模式**。在这种情况下,您必须添加一个调用子图的节点函数:这在父图和子图具有不同的状态模式并且需要在调用子图之前或之后转换状态时很有用
下面我们将展示每种情况下的子图添加方法。
设置环境¶
首先,让我们安装所需的包
为LangGraph开发设置LangSmith
注册LangSmith可以快速发现并解决您的LangGraph项目中的问题,并提高性能。使用LangSmith,您可以利用跟踪数据来调试、测试和监控使用LangGraph构建的LLM应用程序——更多关于如何开始的信息,请参阅这里。
添加包含编译子图的节点¶
一种常见的情况是父图和子图通过共享状态键(通道)进行通信。例如,在多代理系统中,代理通常通过共享消息键进行通信。
如果你的子图与父图共享状态键,你可以按照以下步骤将其添加到你的图中:
- 定义子图工作流(如下例中的
subgraph_builder
),并对其进行编译。 - 在定义父图工作流时,将编译后的子图传递给
.add_node
方法。
让我们来看一个简单的例子。
API Reference: START | StateGraph
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系统中,搜索代理可能只需要跟踪查询和检索到的文档。
如果您的应用程序也有这种情况,则需要定义一个**调用子图的节点函数**。此函数需要在调用子图之前将输入(父图)状态转换为子图状态,并在返回节点的状态更新之前将结果转换回父图状态。
以下我们展示了如何修改我们的原始示例以从节点内部调用子图。
Warning
你**不能**在同一节点内调用多个子图。
# 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'}})