[LangGraph] 从节点中调用子图
所谓子图,顾名思义,就是在一张图里面还嵌套了另外一张图。
子图常见的应用场景:
- 构建多智能体系统
- 在多个图中复用一组节点
- 分布式开发
- 当希望不同团队独立开发图的不同部分时,可以将每个部分定义为一个子图。
- 只要子图的接口(输入输出 schema)保持一致,父图就可以在无需了解子图内部细节的情况下进行构建。
在使用子图的时候,有两种使用方式:
- 从节点中调用子图:将子图作为一个函数来调用
- 将子图添加为节点
核心概念
在这种方式下:
- 父图把输入传给子图;
- 子图返回结果回给父图
这种模式下,子图看起来就像父图的一个“函数”,类似于平时我们写代码时调用一个函数:
const result = subProcess(input);
父流程不关心子流程内部的 node、状态、步骤。
父图与子图之间数据通过输入/输出 schema 明确传递,子图内部使用的 state,不直接暴露给父图。
示意图:
父图 NodeA → 子图(…) → 父图继续执行
快速上手
场景
父图负责处理“用户下单流程”。当用户下单时,父图会调用“风控子图”去判断订单是否有风险。
- 子图内部有多步,例如:IP 检测 + 金额检测。
- 父图调用子图 → 获取风控结果 → 决定是否继续下单或拒绝。
示意图
flowchart TD
%% --- 父图部分 ---
A[父图:下单流程<br/>Receive Order] --> B[节点:checkRisk<br/>调用子图执行风控]
%% --- 调用子图 ---
B --> subgraph_call[调用子图 → 风控子流程]
%% --- 子图内部 ---
subgraph Subgraph[子图:风控子流程]
S1[Node1: 检查 IP 风险]
S2[Node2: 检查金额风险]
S3[Node3: 汇总并生成 riskLevel]
S1 --> S2 --> S3
end
subgraph_call --> S1
S3 --> C[父图继续:根据 riskLevel 判断结果<br/>生成 finalMessage]
rishsubgraph.ts
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
// 子图的状态
const RiskState = z.object({
orderId: z.string().describe("订单ID"),
ip: z.string().describe("下订单的IP"),
amount: z.number().describe("订单的总金额"),
ipRisk: z.boolean().optional().describe("IP是否存在风险"),
amountRisk: z.boolean().optional().describe("金额是否存在风险"),
riskLevel: z.enum(["low", "mid", "high"]).optional().describe("风险的等级"),
});
export type TRiskState = z.infer<typeof RiskState>;
// 两个子图节点:1. 检查IP 2. 检查金额
async function checkIpRisk(state: TRiskState) {
const riskyIps = ["10.10.10.10", "123.123.123.123"]; // 这一组是有风险的IP
const isRisk = riskyIps.includes(state.ip);
return {
ipRisk: isRisk,
};
}
async function checkAmountRisk(state: TRiskState) {
return {
amountRisk: state.amount > 5000, // 假设订单总额大于5000就存在风险
};
}
// 汇总节点
async function summarizeRisk(state: TRiskState) {
const { ipRisk, amountRisk } = state;
if (ipRisk || amountRisk) {
return {
riskLevel: "high",
};
}
return {
riskLevel: "low",
};
}
// 创建子图
export const riskSubgraph = new StateGraph(RiskState)
.addNode("checkIpRisk", checkIpRisk)
.addNode("checkAmountRisk", checkAmountRisk)
.addNode("summarizeRisk", summarizeRisk)
// 边
.addEdge(START, "checkIpRisk")
.addEdge("checkIpRisk", "checkAmountRisk")
.addEdge("checkAmountRisk", "summarizeRisk")
.addEdge("summarizeRisk", END)
.compile();
maingraph.ts
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
import { riskSubgraph } from "./risksubgraph.ts";
// 父图的状态
const OrderState = z.object({
orderId: z.string().describe("订单的ID"),
ip: z.string().describe("下订单的IP"),
amount: z.number().describe("订单的金额"),
riskLevel: z.enum(["low", "mid", "high"]).optional().describe("风险等级"),
finalMessage: z.string().optional().describe("最终生成的消息"),
});
type TOrderState = z.infer<typeof OrderState>;
// 该节点用于检验订单是否存在风险
async function checkRisk(state: TOrderState) {
console.log("父图开始检验订单是否存在风险");
// 直接调用子图
const result = await riskSubgraph.invoke({
orderId: state.orderId,
ip: state.ip,
amount: state.amount,
});
console.log(`子图返回的结果为:`, result);
return {
riskLevel: result.riskLevel,
};
}
// 根据风险的结果生成最终的信息
async function finish(state: TOrderState) {
if (state.riskLevel === "high") {
return {
finalMessage: "订单被拒绝:风控风险过高。",
};
}
return {
finalMessage: "订单审核通过,允许下单。",
};
}
export const mainGraph = new StateGraph(OrderState)
.addNode("checkRisk", checkRisk)
.addNode("finish", finish)
// 边
.addEdge(START, "checkRisk")
.addEdge("checkRisk", "finish")
.addEdge("finish", END)
.compile();
index.ts
import { mainGraph } from "./maingraph.ts";
async function main() {
const input = {
orderId: "A10101",
ip: "10.1.10.10",
amount: 6000,
};
const result = await mainGraph.invoke(input);
console.log("\n=====最终结果=======");
console.log(result);
}
main();

浙公网安备 33010602011771号