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

推荐订阅源

P
Palo Alto Networks Blog
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
GbyAI
GbyAI
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
量子位
T
The Blog of Author Tim Ferriss
Y
Y Combinator Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
CERT Recently Published Vulnerability Notes
Recent Announcements
Recent Announcements
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
P
Privacy International News Feed
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 叶小钗
L
Lohrmann on Cybersecurity
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
Hugging Face - Blog
Hugging Face - Blog
P
Proofpoint News Feed
NISL@THU
NISL@THU
博客园 - Franky
C
Cybersecurity and Infrastructure Security Agency CISA
The Register - Security
The Register - Security
M
MIT News - Artificial intelligence
Know Your Adversary
Know Your Adversary
A
Arctic Wolf
F
Full Disclosure
T
Threat Research - Cisco Blogs
P
Privacy & Cybersecurity Law Blog
The Hacker News
The Hacker News
博客园 - 【当耐特】
D
Docker
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Jina AI
Jina AI
Help Net Security
Help Net Security
V
Visual Studio Blog
小众软件
小众软件
B
Blog
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
Forbes - Security
Forbes - Security
N
Netflix TechBlog - Medium
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
C
Cisco Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic

博客园 - 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] 静态断点 [LangGrpah] 非阻塞式中断 [LangGraph] 语义搜索 [LangGraph] 长期记忆 [LangGraph] 管理短期记忆 [LangGraph] 自定义checkpointer [LangGraph] 短期记忆 [LangGraph] 时间旅行 [LangGraph] checkpoint常用API [LangGraph] 元数据标记 [LangGraph] 流 [Vitest] mockClear, mockReset, mockRestore [LangGraph] 将子图添加为节点
[LangGraph] 阻塞式中断
Zhentiw · 2026-03-13 · via 博客园 - Zhentiw

中断指的就是允许图在执行的过程中暂停,等待外部输入。这种机制提供了一种“人类参与”的交互模式,某些场景下某些决策非常重要,需要人类来做出最终抉择,这个时候就可以使用中断。

场景案例

下面我们先来看一下,如果不使用中断,是否能够实现下面的场景:

需求:让大模型帮我们书写邮件,人类给出反馈

  • approve:邮件内容没问题,直接发送邮件
  • 其它内容:作为邮件内容的修改建议
import { StateGraph, START, END } from "@langchain/langgraph";
import { ChatOpenAI } from "@langchain/openai";
import readlineSync from "readline-sync";
import { z } from "zod/v4";
import "dotenv/config";

// 图的状态
const Schema = z.object({
  subject: z.string().default("").describe("邮件主题"),
  message: z.string().default("").describe("邮件内容"),
  feedback: z.string().default("").describe("反馈"),
});

// 根据Schema生成的ts类型
type TState = z.infer<typeof Schema>;

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
});

// 节点:1. 写邮件的节点  2. 用户审查节点  3. 发送邮件
async function writeEmail(state: TState) {
  console.log("AI: 正在生成/修改邮件内容...");

  // 提示词
  const lines: string[] = [
    "你是一个专业的中文邮件撰写助手。",
    "请用自然、礼貌、简洁的中文撰写邮件正文。",
    "只输出邮件正文内容本身,不要输出额外的解释说明。",
  ];

  if (!state.message || !state.feedback) {
    // 说明是初次生成邮件内容,当前只有主题
    lines.push(`邮件主题:${state.subject}`);
    lines.push("请根据以上主题撰写第一版邮件正文。");
  } else if (state.feedback !== "approve") {
    // 说明有修改意见,需要根据上一版的正文 + 用户的反馈意见来进行修改
    lines.push("下面是上一版邮件正文:");
    lines.push(state.message);
    lines.push("下面是用户给出的修改意见:");
    lines.push(state.feedback);
    lines.push(
      "请严格根据修改意见,在保留合理内容的前提下,重写一封新的邮件正文,只输出修改后的完整邮件内容。",
    );
  }

  const pt = lines.join("\n");

  const result = await model.invoke(pt);

  const content =
    typeof result.content === "string"
      ? result.content
      : JSON.stringify(result.content);

  return {
    message: content,
  };
}

async function humanReview(state: TState) {
  // 先将模型生成的邮件内容显示出来
  console.log("\n===== 当前 AI 生成的邮件内容 =====\n");
  console.log(state.message);
  console.log("\n系统: 等待人类审核...\n");

  const input = readlineSync.question(
    "是否发送?请输入 'approve' 表示发送,或输入你的修改意见:",
  );

  console.log(`\n\n用户的反馈为:${input}`);

  return {
    feedback: input,
  };
}

function sendEmail(state: TState) {
  console.log("\n===== 模拟发送邮件 =====");
  const to = "demo@example.com";
  console.log(`收件人: ${to}`);
  console.log(`主题: ${state.subject}`);
  console.log("正文:\n");
  console.log(state.message);
  console.log("\n[模拟] 邮件已发送!");
  return {};
}

// 构建图
const graph = new StateGraph(Schema)
  .addNode("writeEmail", writeEmail)
  .addNode("humanReview", humanReview)
  .addNode("sendEmail", sendEmail)
  .addEdge(START, "writeEmail")
  .addEdge("writeEmail", "humanReview")
  .addConditionalEdges("humanReview", (state: TState) => {
    if (state.feedback === "approve") return "sendEmail";
    return "writeEmail";
  })
  .addEdge("sendEmail", END)
  .compile();

async function main() {
  const subject = readlineSync.question("请输入邮件的主题:");

  console.log(
    "\n===== 开始:大模型根据主题生成邮件,并支持多次人工修改 =====\n",
  );

  const stream = await graph.stream({ subject });

  for await (const _e of stream) {
  }

  console.log("\n===== 流程结束 =====");
}

main();

存在的问题

目前来看,我们的代码好像也实现了中断的需求,并且没有用到langgraph里面的interrupt.

🙋那是不是官方提供的interrupt是多此一举呢?

上面的案例,在实现中断,等待用户输入的时候:

const input = readlineSync.question(
  "是否发送?请输入 'approve' 表示发送,或输入你的修改意见:"
);

这是一个同步阻塞式的操作。

这也就意味着程序执行到这里的时候,后面的步骤被卡死,等待用户的输入。只要用户不输入,无法进入到后面的流程。

企业审批系统不能这么做,因为:

  • 审批者不可能立刻在线
  • 后端不可能阻塞线程等待审批
  • 流程不能暂停并等待下一次调用
  • 多个审批人不可能同时处理
  • 审批A没回消息,整个系统卡死

总结起来就一句:阻塞式的逻辑会让后端挂起线程,企业级系统绝对不会这么做。


-EOF-