惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
Webroot Blog
Webroot Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Threat Research - Cisco Blogs
V2EX - 技术
V2EX - 技术
L
LINUX DO - 热门话题
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
S
Schneier on Security
I
InfoQ
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
The GitHub Blog
The GitHub Blog
S
Security @ Cisco Blogs
O
OpenAI News
W
WeLiveSecurity
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Simon Willison's Weblog
Simon Willison's Weblog
人人都是产品经理
人人都是产品经理
Cloudbric
Cloudbric
The Last Watchdog
The Last Watchdog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
NISL@THU
NISL@THU
T
Tailwind CSS Blog
V
Visual Studio Blog
PCI Perspectives
PCI Perspectives
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Jina AI
Jina AI
D
DataBreaches.Net
B
Blog RSS Feed
N
News and Events Feed by Topic
N
News and Events Feed by Topic
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
腾讯CDC
Latest news
Latest news
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
WordPress大学
WordPress大学
V
V2EX
aimingoo的专栏
aimingoo的专栏
博客园 - 司徒正美
Apple Machine Learning Research
Apple Machine Learning Research
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The Register - Security
The Register - Security
Help Net Security
Help Net Security

博客园 - Zhentiw

[GenAI] Migration Plan: Flat API → Layered Architecture [Skill] Frontend Design Skill Claude Code Hooks: Complete Guide [GenAI] Pre-retieval overview [GenAI] About Indexing [GenAI] Indexing overview [Vibe Coding] 降低大模型幻觉 - 重试机制 [Vibe coding] 降低大模型幻觉 - JSON 安全输出提示词 [Node.js] WebSocket基础知识 [LangGraph] 应用结构 [LangGrpah] Unit testing [LangGraph] Functional API [LangGraph] 中断注意事项 [LangGraph] 中断相关细节 [LangGrpah] 非阻塞式中断 [LangGraph] 阻塞式中断 [LangGraph] 语义搜索 [LangGraph] 长期记忆 [LangGraph] 管理短期记忆 [LangGraph] 自定义checkpointer [LangGraph] 短期记忆 [LangGraph] 时间旅行 [LangGraph] checkpoint常用API [LangGraph] 元数据标记 [LangGraph] 流 [Vitest] mockClear, mockReset, mockRestore [LangGraph] 将子图添加为节点
[LangGrpah] 静态断点
Zhentiw · 2026-03-17 · via 博客园 - Zhentiw

基本介绍

一句话定义:静态断点,是在编译图时就确定好的中断点,用于保证某个节点在执行前或执行后一定会暂停。

静态断点的两种形式

LangGraph 提供了两种静态断点配置方式:

  1. interrupt_before:在指定节点 开始执行之前 暂停。

    interruptBefore: ["node_name"]
    
  2. 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 流程,逻辑是:

  1. 解析用户输入
  2. 整理出要调用外部系统的参数
  3. 调用外部工具(下单 / 发邮件 / 写数据库)

在真实业务中,第 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();