如何结合控制流和状态更新与 Command¶
将控制流(边)和状态更新(节点)结合起来会很有用。例如,您可能希望在**同一个节点**中既执行状态更新,又决定接下来要转到哪个节点。LangGraph 提供了一种方法来实现这一点,即从节点函数返回一个 Command
对象。
const myNode = (state: typeof StateAnnotation.State) => {
return new Command({
// state update
update: {
foo: "bar",
},
// control flow
goto: "myOtherNode",
});
};
如果您正在使用子图,您可能希望从子图中的某个节点导航到另一个子图(即父图中的不同节点)。为此,您可以在 Command 中指定 graph: Command.PARENT
。
const myNode = (state: typeof StateAnnotation.State) => {
return new Command({
update: { foo: "bar" },
goto: "other_subgraph", // where `other_subgraph` is a node in the parent graph
graph: Command.PARENT,
});
};
本指南展示了如何使用 Command
在 LangGraph 应用中添加动态控制流。
设置¶
首先,让我们安装所需的包
设置 LangSmith 以进行 LangGraph 开发
注册 LangSmith,以便快速发现问题并提高您的 LangGraph 项目的性能。LangSmith 允许您使用跟踪数据来调试、测试和监控您使用 LangGraph 构建的 LLM 应用 — 在此处了解更多入门信息。
让我们创建一个包含 3 个节点(A、B 和 C)的简单图。我们将首先执行节点 A,然后根据节点 A 的输出决定接下来是转到节点 B 还是节点 C。
定义图¶
import { Annotation, Command } from "@langchain/langgraph";
// Define graph state
const StateAnnotation = Annotation.Root({
foo: Annotation<string>,
});
// Define the nodes
const nodeA = async (_state: typeof StateAnnotation.State) => {
console.log("Called A");
// this is a replacement for a real conditional edge function
const goto = Math.random() > .5 ? "nodeB" : "nodeC";
// note how Command allows you to BOTH update the graph state AND route to the next node
return new Command({
// this is the state update
update: {
foo: "a",
},
// this is a replacement for an edge
goto,
});
};
// Nodes B and C are unchanged
const nodeB = async (state: typeof StateAnnotation.State) => {
console.log("Called B");
return {
foo: state.foo + "|b",
};
}
const nodeC = async (state: typeof StateAnnotation.State) => {
console.log("Called C");
return {
foo: state.foo + "|c",
};
}
现在我们可以使用上述节点创建 StateGraph
。请注意,该图没有用于路由的条件边!这是因为控制流是在 nodeA
内部使用 Command
定义的。
import { StateGraph } from "@langchain/langgraph";
// NOTE: there are no edges between nodes A, B and C!
const graph = new StateGraph(StateAnnotation)
.addNode("nodeA", nodeA, {
ends: ["nodeB", "nodeC"],
})
.addNode("nodeB", nodeB)
.addNode("nodeC", nodeC)
.addEdge("__start__", "nodeA")
.compile();
重要提示
您可能已经注意到,我们在使用 Command
的节点中添加了一个额外的参数 ends
字段。这对于图的编译和验证是必需的,并且告诉 LangGraph nodeA
可以导航到 nodeB
和 nodeC
。
import * as tslab from "tslab";
const drawableGraph = await graph.getGraphAsync();
const image = await drawableGraph.drawMermaidPng();
const arrayBuffer = await image.arrayBuffer();
await tslab.display.png(new Uint8Array(arrayBuffer));
如果我们多次运行该图,会看到它根据节点 A 中的随机选择而采取不同的路径(A -> B 或 A -> C)。
导航到父图中的节点¶
现在让我们演示如何从子图内部导航到父图中的另一个节点。我们将通过将上述示例中的 node_a
更改为一个单节点图来实现,然后将其作为子图添加到我们的父图中。
// Define the nodes
const nodeASubgraph = async (_state: typeof StateAnnotation.State) => {
console.log("Called A");
// this is a replacement for a real conditional edge function
const goto = Math.random() > .5 ? "nodeB" : "nodeC";
// note how Command allows you to BOTH update the graph state AND route to the next node
return new Command({
update: {
foo: "a",
},
goto,
// this tells LangGraph to navigate to node_b or node_c in the parent graph
// NOTE: this will navigate to the closest parent graph relative to the subgraph
graph: Command.PARENT,
});
};
const subgraph = new StateGraph(StateAnnotation)
.addNode("nodeA", nodeASubgraph)
.addEdge("__start__", "nodeA")
.compile();
const parentGraph= new StateGraph(StateAnnotation)
.addNode("subgraph", subgraph, { ends: ["nodeB", "nodeC"] })
.addNode("nodeB", nodeB)
.addNode("nodeC", nodeC)
.addEdge("__start__", "subgraph")
.compile();
await parentGraph.invoke({ foo: "" });