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

推荐订阅源

T
Threat Research - Cisco Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
V
Vulnerabilities – Threatpost
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
S
Secure Thoughts
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
云风的 BLOG
云风的 BLOG
The Last Watchdog
The Last Watchdog
L
LINUX DO - 热门话题
酷 壳 – CoolShell
酷 壳 – CoolShell
WordPress大学
WordPress大学
AWS News Blog
AWS News Blog
美团技术团队
G
Google Developers Blog
宝玉的分享
宝玉的分享
www.infosecurity-magazine.com
www.infosecurity-magazine.com
C
CXSECURITY Database RSS Feed - CXSecurity.com
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
InfoQ
小众软件
小众软件
Google DeepMind News
Google DeepMind News
P
Privacy & Cybersecurity Law Blog
Stack Overflow Blog
Stack Overflow Blog
Webroot Blog
Webroot Blog
D
DataBreaches.Net
IT之家
IT之家
PCI Perspectives
PCI Perspectives
人人都是产品经理
人人都是产品经理
Hacker News: Ask HN
Hacker News: Ask HN
L
LangChain Blog
SecWiki News
SecWiki News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cisco Blogs
T
Threatpost
P
Proofpoint News Feed
Y
Y Combinator Blog
Cloudbric
Cloudbric
T
Tor Project blog
量子位
博客园_首页
B
Blog
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
D
Darknet – Hacking Tools, Hacker News & Cyber 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();