[LangGraph] 将子图添加为节点
核心概念
子图直接“嵌入”到父图内,成为父图的一部分,与父图共享 state 的 key。
示意图:
父图节点 ===> { 子图内部的很多节点 } ===> 父图下一个节点
看起来像父图的一个 node,但内部却是一个完整的 graph。
也正因为如此:
-
子图与父图使用同一个 state(共享部分或全部 key)
-
子图内部的 node 可以读写父图 state
子图不再是一个“黑盒”,而是父图的一部分
适用场景:
- 多团队协作,把一段流程拆成一个子图
- 想复用一段常用流程(比如全局日志链路、消息管道)
- 子图需要访问父图的多个 state 字段,不止输入/输出
快速上手
场景
客户注册需要执行复杂的 KYC(身份核验)流程:
子图负责:
- OCR 身份证识别
- 人脸对比
- 风险评分
- 写入结果到父图的 shared state
父图负责:
- 收集用户信息
- 调用 KYC 子图验证
- 根据验证结果决定是否注册成功
示意图
flowchart TD
%% ---------- 父图 ----------
subgraph ParentGraph[父图:用户注册流程]
P_START([START])
P1[父节点:kycFlow<br/>(子图作为复合节点)]
P2[父节点:finish<br/>根据 kycPassed 生成注册结果]
P_END([END])
P_START --> P1 --> P2 --> P_END
end
%% ---------- 子图 ----------
subgraph Subgraph[子图:KYC 验证流程(共享父图 State)]
S_START([START])
S1[子节点:OCR 校验<br/>ocrPassed]
S2[子节点:人脸比对<br/>faceMatch]
S3[子节点:KYC 汇总<br/>kycPassed]
S_END([END])
S_START --> S1 --> S2 --> S3 --> S_END
end
%% ---------- 连接关系 ----------
%% 说明:父图的 kycFlow 节点 == 整个子图执行
P1 -. 调用并共享 state .-> S_START
S_END -. 返回更新后的 state .-> P2
index.ts
import { mainGraph } from "./mainGraph.ts";
async function main() {
// 模拟数据
const input = {
userName: "张三",
idCardImage: "path/idcard.jpg",
selfImage: "path/selfie.jpg",
};
const result = await mainGraph.invoke(input);
console.log("\n=== 最终结果 ===");
console.log(result);
}
main();
mainGraph.ts
// 父图
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
import { kycSubgraph } from "./kycSubgraph.ts";
// 父图的状态
const MainState = z.object({
// 用户注册的基本信息
userName: z.string().describe("用户名"),
idCardImage: z.string().describe("用户身份证图片"),
selfImage: z.string().describe("用户相关的图片"),
// 各种验证
ocrPassed: z.boolean().optional().describe("ocr验证是否通过"),
faceMatch: z.boolean().optional().describe("人脸匹配是否通过"),
kycPassed: z.boolean().optional().describe("kyc验证是否通过"),
// 最终注册的结果
registerResult: z.string().optional().describe("最终注册的结果"),
});
type TMainState = z.infer<typeof MainState>;
function finish(state: TMainState) {
if (state.kycPassed) {
return {
registerResult: "注册成功",
};
}
return {
registerResult: "注册失败,KYC验证没有通过",
};
}
export const mainGraph = new StateGraph(MainState)
.addNode("kycFlow", kycSubgraph)
.addNode("finish", finish)
// 边
.addEdge(START, "kycFlow")
.addEdge("kycFlow", "finish")
.addEdge("finish", END)
.compile();
kycSubgraph.ts
// 做身份验证的子图
import { StateGraph, START, END } from "@langchain/langgraph";
import { z } from "zod/v4";
const KycState = z.object({
// 用户注册的基本信息
userName: z.string().describe("用户名"),
idCardImage: z.string().describe("用户身份证图片"),
selfImage: z.string().describe("用户相关的图片"),
// 各种验证
ocrPassed: z.boolean().optional().describe("ocr验证是否通过"),
faceMatch: z.boolean().optional().describe("人脸匹配是否通过"),
kycPassed: z.boolean().optional().describe("kyc验证是否通过"),
});
type TKycState = z.infer<typeof KycState>;
// 检查的节点:1. OCR验证 2. 人脸比对
async function ocrCheck() {
const randomPass = Math.random() > 0.5; // 90% 通过
console.log("🔍 [子图] OCR 检查:", randomPass);
return { ocrPassed: randomPass };
}
async function faceMatchCheck() {
const randomPass = Math.random() > 0.5; // 80% 通过
console.log("🔍 [子图] 人脸比对:", randomPass);
return { faceMatch: randomPass };
}
// 汇总节点
async function summary(state: TKycState) {
// 这两项验证都通过了,那就是通过了
const passed = state.ocrPassed && state.faceMatch;
return {
kycPassed: passed,
};
}
export const kycSubgraph = new StateGraph(KycState)
.addNode("ocrCheck", ocrCheck)
.addNode("faceMatchCheck", faceMatchCheck)
.addNode("summary", summary)
// 边
.addEdge(START, "ocrCheck")
.addEdge("ocrCheck", "faceMatchCheck")
.addEdge("faceMatchCheck", "summary")
.addEdge("summary", END)
.compile();

浙公网安备 33010602011771号