如何转换子图的输入和输出¶
你的子图状态有可能完全独立于父图状态,即两者之间没有重叠的通道(键)。例如,你可能有一个 supervisor 智能体,它需要借助多个 ReAct 智能体来生成报告。ReAct 智能体子图可能会跟踪消息列表,而 supervisor 只需要用户输入和最终报告在其状态中,并且不需要跟踪消息。
在这种情况下,你需要在调用子图之前转换其输入,然后在返回之前转换其输出。本指南将展示如何做到这一点。
设置¶
首先,让我们安装所需的软件包
设置 LangSmith 用于 LangGraph 开发
注册 LangSmith 以快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许你使用跟踪数据来调试、测试和监控使用 LangGraph 构建的 LLM 应用程序 — 在此处阅读更多关于如何开始的信息。
定义图和子图¶
让我们定义 3 个图:- 一个父图 - 一个将被父图调用的子图 - 一个将被子图调用的孙子图
定义孙子图¶
import { StateGraph, START, Annotation } from "@langchain/langgraph";
const GrandChildAnnotation = Annotation.Root({
myGrandchildKey: Annotation<string>,
})
const grandchild1 = (state: typeof GrandChildAnnotation.State) => {
// NOTE: child or parent keys will not be accessible here
return {
myGrandchildKey: state.myGrandchildKey + ", how are you"
}
}
const grandchild = new StateGraph(GrandChildAnnotation)
.addNode("grandchild1", grandchild1)
.addEdge(START, "grandchild1")
const grandchildGraph = grandchild.compile();
定义子图¶
import { StateGraph, START, Annotation } from "@langchain/langgraph";
const ChildAnnotation = Annotation.Root({
myChildKey: Annotation<string>,
});
const callGrandchildGraph = async (state: typeof ChildAnnotation.State) => {
// NOTE: parent or grandchild keys won't be accessible here
// we're transforming the state from the child state channels (`myChildKey`)
// to the grandchild state channels (`myGrandchildKey`)
const grandchildGraphInput = { myGrandchildKey: state.myChildKey };
// we're transforming the state from the grandchild state channels (`myGrandchildKey`)
// back to the child state channels (`myChildKey`)
const grandchildGraphOutput = await grandchildGraph.invoke(grandchildGraphInput);
return {
myChildKey: grandchildGraphOutput.myGrandchildKey + " today?"
};
};
const child = new StateGraph(ChildAnnotation)
// NOTE: we're passing a function here instead of just compiled graph (`childGraph`)
.addNode("child1", callGrandchildGraph)
.addEdge(START, "child1");
const childGraph = child.compile();
注意
我们将 grandchildGraph
调用包装在一个单独的函数 (callGrandchildGraph
) 中,该函数在调用孙子图之前转换输入状态,然后将孙子图的输出转换回子图状态。如果你直接将 grandchildGraph
传递给 .addNode
而不进行转换,LangGraph 将会引发错误,因为子图和孙子图状态之间没有共享的状态通道(键)。
请注意,子图和孙子图有它们自己的、独立的状态,这些状态不与父图共享。
定义父图¶
import { StateGraph, START, END, Annotation } from "@langchain/langgraph";
const ParentAnnotation = Annotation.Root({
myKey: Annotation<string>,
});
const parent1 = (state: typeof ParentAnnotation.State) => {
// NOTE: child or grandchild keys won't be accessible here
return { myKey: "hi " + state.myKey };
};
const parent2 = (state: typeof ParentAnnotation.State) => {
return { myKey: state.myKey + " bye!" };
};
const callChildGraph = async (state: typeof ParentAnnotation.State) => {
// we're transforming the state from the parent state channels (`myKey`)
// to the child state channels (`myChildKey`)
const childGraphInput = { myChildKey: state.myKey };
// we're transforming the state from the child state channels (`myChildKey`)
// back to the parent state channels (`myKey`)
const childGraphOutput = await childGraph.invoke(childGraphInput);
return { myKey: childGraphOutput.myChildKey };
};
const parent = new StateGraph(ParentAnnotation)
.addNode("parent1", parent1)
// NOTE: we're passing a function here instead of just a compiled graph (`childGraph`)
.addNode("child", callChildGraph)
.addNode("parent2", parent2)
.addEdge(START, "parent1")
.addEdge("parent1", "child")
.addEdge("child", "parent2")
.addEdge("parent2", END);
const parentGraph = parent.compile();
注意
我们将 childGraph
调用包装在一个单独的函数 (callChildGraph
) 中,该函数在调用子图之前转换输入状态,然后将子图的输出转换回父图状态。如果你直接将 childGraph
传递给 .addNode
而不进行转换,LangGraph 将会引发错误,因为父图和子图状态之间没有共享的状态通道(键)。
让我们运行父图,并确保它正确地调用了子图和孙子图。
完美!父图正确地调用了子图和孙子图(我们知道这一点是因为“,你好吗”和“今天?”被添加到了我们原始的 “myKey” 状态值中)。