[LangGrpah] 静态断点
基本介绍
一句话定义:静态断点,是在编译图时就确定好的中断点,用于保证某个节点在执行前或执行后一定会暂停。
静态断点的两种形式
LangGraph 提供了两种静态断点配置方式:
-
interrupt_before:在指定节点 开始执行之前 暂停。
interruptBefore: ["node_name"] -
interrupt_after:在指定节点 执行完成之后 暂停。
interruptAfter: ["node_name"]
静态断点定义在哪里
静态断点不是写在节点函数里的。它定义在:
- 图的结构上
- 节点与节点之间的执行关系中
- 通常配置在
graph.compile()阶段
换句话说:静态断点是图的一部分,不是代码逻辑的一部分。
例如:
const graph = new StateGraph(State)
.addNode("nodeA", nodeA)
.addNode("nodeB", nodeB)
.addEdge(START, "nodeA")
.addEdge("nodeA", "nodeB")
.addEdge("nodeB", END);
const compiledGraph = graph.compile({
interruptBefore: ["nodeB"], // 在 nodeB 执行前暂停
});
静态断点的控制粒度
静态断点的控制粒度是:节点级
这意味着,一个节点:
-
要么完全不执行(interrupt_before)
-
要么完整执行完(interrupt_after)
-
不能在节点函数的某一行中断,或者在节点内部写条件判断决定是否中断
如果你有这样的需求:“执行到一半,根据 state 决定要不要停”
那它就不属于静态断点的范畴了。
静态断点与动态断点核心对比
如下表所示:
| 对比维度 | 静态断点 | 动态断点 |
|---|---|---|
| 何时确定 | 编译图时 | 运行时 |
| 定义位置 | 图结构上 | 节点函数内部 |
| 是否依赖条件 | 否 | 是 |
| 是否依赖 state | 否 | 是 |
| 控制粒度 | 节点级 | 语句级 |
| 常见用途 | 流程控制 | 决策 |
快速入门
假设我们在做一个 Agent 流程,逻辑是:
- 解析用户输入
- 整理出要调用外部系统的参数
- 调用外部工具(下单 / 发邮件 / 写数据库)
在真实业务中,第 3 步往往是有风险的:
- 真的要下单吗?
- 参数对不对?
- 要不要人工确认?
静态断点非常适合卡在这里。
整体流程结构:
START
↓
parseInput // 解析用户意图
↓
prepareAction // 生成“即将执行的操作”
↓
executeAction // 真正产生副作用
↓
END
我们要做的事:在 executeAction 执行之前,强制暂停流程。
import { z } from "zod/v4";
import readline from "readline-sync";
import { StateGraph, START, END, MemorySaver } from "@langchain/langgraph";
const StateSchema = z.object({
action: z.string().optional().describe("要执行的行为"),
to: z.email().optional().describe("要对哪一个对象执行这个行为"),
content: z.string().optional().describe("该行为对应的具体内容"),
});
type State = z.infer<typeof StateSchema>;
// 1. 解析用户意图节点
async function parseInput(): Promise<State> {
return {
action: "send_email", // 意图:发邮件
to: "test@example.com", // 收件人
content: "Hello, this is a test email", // 邮件内容
};
}
// 2. 准备即将执行的操作
async function prepareAction(state: State): Promise<State> {
console.log("\n【准备执行的操作】");
console.log({
action: state.action,
to: state.to,
content: state.content,
});
return state;
}
// 3. 执行具体的操作
async function executeAction(state: State): Promise<State> {
console.log("\n🚨 正在执行真实操作!");
console.log(`邮件已发送给 ${state.to}`);
return state;
}
const checkpointer = new MemorySaver();
// 4. 编排图
const graph = new StateGraph(StateSchema)
.addNode("parseInput", parseInput)
.addNode("prepareAction", prepareAction)
.addNode("executeAction", executeAction)
.addEdge(START, "parseInput")
.addEdge("parseInput", "prepareAction")
.addEdge("prepareAction", "executeAction")
.addEdge("executeAction", END)
.compile({
checkpointer,
interruptBefore: ["executeAction"], // 在具体执行动作之前,需要中断
});
async function main() {
const config = {
configurable: {
thread_id: "static-interrupt-demo",
},
};
// 执行整个图
console.log("\n=== 第一次执行:跑到静态断点 ===");
await graph.invoke({}, config);
// 第一次执行的时候,跑到静态断点,就会从图里面出来,回到主逻辑
console.log("\n流程已在 executeAction 前暂停");
const input = readline.question("是否确认执行真实操作?(y / n): ");
if (input.toLowerCase() !== "y") {
console.log("\n❌ 操作已取消,流程结束");
return;
}
// 说明用户输入的是 y 或者 Y
console.log("\n✅ 已确认,继续执行流程");
// 恢复图的执行,会从中断的节点开始执行
// 1. 第一个参数传递null:因为这一次是从中断点开始执行,null表示使用checkpointer保存的状态继续
// 2. 第二个参数config
await graph.invoke(null, config);
}
main();

浙公网安备 33010602011771号