中断允许你在特定节点暂停图的执行,并在继续之前等待外部输入。这实现了需要外部输入才能继续的人工介入(human-in-the-loop)模式。当中断触发时,LangGraph 会利用其持久化层保存图状态,然后无限期等待,直到你恢复执行。 中断通过在图节点的任意位置调用 interrupt() 函数来工作。该函数接受任何可 JSON 序列化的值,该值会暴露给调用者。当你准备好继续时,通过使用 Command 重新调用图来恢复执行,该 Command 会成为节点内部 interrupt() 调用的返回值。 与静态断点(在特定节点前后暂停)不同,中断是动态的:它们可以放在代码的任何位置,并且可以根据你的应用逻辑设置条件。
  • 检查点会保留你的位置: 检查点记录器会写入精确的图状态,以便你稍后恢复,即使处于错误状态也是如此。
  • thread_id 是你的指针:invoke 方法的选项中设置 { configurable: { thread_id: ... } } 来告诉检查点记录器加载哪个状态。
  • 中断的有效负载通过 __interrupt__ 暴露: 你传递给 interrupt() 的值会在 __interrupt__ 字段中返回给调用者,这样你就知道图在等待什么。
你选择的 thread_id 实际上就是你持久化的游标。重用该 ID 会恢复同一个检查点;使用新值则会启动一个全新的线程,状态为空。

使用 interrupt 暂停

interrupt 函数会暂停图的执行并将一个值返回给调用者。当你在节点内调用 interrupt 时,LangGraph 会保存当前的图状态并等待你使用输入来恢复执行。 要使用 interrupt,你需要:
  1. 一个检查点记录器来持久化图状态(生产环境中应使用持久化的检查点记录器)
  2. 在配置中提供一个线程 ID,以便运行时知道从哪个状态恢复
  3. 在你希望暂停的位置调用 interrupt()(有效负载必须是 JSON 可序列化的)
import { interrupt } from "@langchain/langgraph";

async function approvalNode(state: State) {
    // 暂停并请求批准
    const approved = interrupt("你批准这个操作吗?");

    // Command({ resume: ... }) 提供的值会返回到这个变量中
    return { approved };
}
当你调用 interrupt 时,会发生以下情况:
  1. 图的执行在调用 interrupt 的准确位置被挂起
  2. 使用检查点记录器保存状态,以便稍后恢复执行。在生产环境中,这应当是一个持久化的检查点记录器(例如由数据库支持)
  3. 值被返回给调用者,出现在 __interrupt__ 下;它可以是任何 JSON 可序列化的值(字符串、对象、数组等)
  4. 图无限期等待,直到你使用响应恢复执行
  5. 当你恢复时,响应会传回节点,成为 interrupt() 调用的返回值

恢复中断

中断暂停执行后,你可以通过使用包含恢复值的 Command 再次调用图来恢复它。这个恢复值会被传递回 interrupt 调用,使得节点能够使用外部输入继续执行。
import { Command } from "@langchain/langgraph";

// 初次运行 - 触发中断并暂停
// thread_id 是指回已保存检查点的持久化指针
const config = { configurable: { thread_id: "thread-1" } };
const result = await graph.invoke({ input: "data" }, config);

// 检查被中断的内容
// __interrupt__ 镜像了你传递给 interrupt() 的每个有效负载
console.log(result.__interrupt__);
// [{ value: '你批准这个操作吗?', ... }]

// 使用人类的响应恢复
// Command({ resume }) 会在节点中将该值从 interrupt() 返回
await graph.invoke(new Command({ resume: true }), config);
关于恢复的关键点:
  • 恢复时,你必须使用与触发中断时相同的线程 ID
  • 传递给 new Command({ resume: ... }) 的值会成为 interrupt 调用的返回值
  • 恢复时,节点会从调用 interrupt 的节点开头重新开始执行,因此 interrupt 之前的任何代码都会再次运行
  • 你可以将任何 JSON 可序列化的值作为恢复值传递
new Command({ resume: ... })唯一设计用作 invoke()/stream()/stream_events() 输入的 Command 模式。其他 Command 参数(updategotograph)是设计用于从节点函数返回的。不要将 new Command({ update: ... }) 作为输入传递给多轮对话——请传递一个普通输入对象。

常见模式

中断所解锁的核心能力是能够暂停执行并等待外部输入。这对于多种用例都很有用,包括:
  • 审批工作流:在执行关键操作(API 调用、数据库更改、金融交易)前暂停
  • 处理多个中断:在单次调用中恢复多个中断时,将中断 ID 与恢复值配对
  • 审查与编辑:在继续之前,让人类审查并修改 LLM 输出或工具调用
  • 在工具中中断:在执行工具调用前暂停,以便在执行前审查并编辑工具调用
  • 验证人类输入:在进入下一步前暂停,以验证人类输入

使用人工介入(HITL)中断进行流式处理

在构建具有人工介入工作流的交互式智能体时,你可以使用事件流在循环中并发消费消息块和状态快照,同时处理中断。 在循环中使用 graph.stream_events(..., version="v3") 返回的类型化投影,直到运行完成:
  • 通过 stream.messages 逐令牌地流式传输 AI 响应
  • 通过 stream.values 观察每一步的状态快照
  • 通过 stream.interrupted 检测中断,并从 stream.interrupts 读取其有效负载
  • 通过再次调用 stream_events 并传入 Command(resume=...) 来恢复执行,并重复此过程直到 stream.interrupted 为 false
import { Command } from "@langchain/langgraph";

let streamInput: Record<string, unknown> | Command = initialInput;

while (true) {
  const stream = await graph.streamEvents(streamInput, {
    ...config,
    version: "v3",
  });

  // Stream LLM message chunks (including any in subgraphs) as they arrive.
  for await (const message of stream.messages) {
    for await (const token of message.text) {
      displayStreamingContent(token);
    }
  }

  // After the run finishes (or pauses), check for interrupts and resume.
  if (!stream.interrupted) {
    const finalState = await stream.output;
    break;
  }

  const interruptInfo = stream.interrupts[0].value;
  const userResponse = await getUserInput(interruptInfo);
  streamInput = new Command({ resume: userResponse });
}
  • stream.messages: 以内容块形式输出的聊天模型输出;迭代 message.text 以获取令牌差异。对于嵌套子图,从 stream.subgraphs[*].messages 读取消息块。
  • stream.values: 每一步之后完整的状态快照
  • stream.interrupted / stream.interrupts: 每次运行后,检查图是否暂停;从 stream.interrupts 读取有效负载
  • Command(resume=...): 作为下一个 streamEvents 输入传递以恢复执行;循环直到运行完成且不再中断

处理多个中断

当并行分支同时中断时(例如,扇出到多个节点,每个节点都调用 interrupt()),你可能需要在一次调用中恢复多个中断。 在单次调用中恢复多个中断时,将每个中断 ID 映射到其恢复值。 这可以确保每个响应在运行时与正确的中断配对。
import {
  Annotation,
  Command,
  END,
  INTERRUPT,
  MemorySaver,
  START,
  StateGraph,
  interrupt,
  isInterrupted,
} from "@langchain/langgraph";

const State = Annotation.Root({
  vals: Annotation<string[]>({
    reducer: (left, right) =>
      left.concat(Array.isArray(right) ? right : [right]),
    default: () => [],
  }),
});

function nodeA(_state: typeof State.State) {
  const answer = interrupt("question_a") as string;
  return { vals: [`a:${answer}`] };
}

function nodeB(_state: typeof State.State) {
  const answer = interrupt("question_b") as string;
  return { vals: [`b:${answer}`] };
}

const graph = new StateGraph(State)
  .addNode("a", nodeA)
  .addNode("b", nodeB)
  .addEdge(START, "a")
  .addEdge(START, "b")
  .addEdge("a", END)
  .addEdge("b", END)
  .compile({ checkpointer: new MemorySaver() });

const config = { configurable: { thread_id: "1" } };

async function main() {
  // 步骤 1: 调用 - 两个并行节点都触发 interrupt() 并暂停
  const interruptedResult = await graph.invoke({ vals: [] }, config);
  console.log(interruptedResult);
  /*
  {
    vals: [],
    __interrupt__: [
      { id: '...', value: 'question_a' },
      { id: '...', value: 'question_b' }
    ]
  }
  */

  // 步骤 2: 一次性恢复所有挂起的中断
  const resumeMap: Record<string, string> = {};
  if (isInterrupted(interruptedResult)) {
    for (const i of interruptedResult[INTERRUPT]) {
      if (i.id != null) {
        resumeMap[i.id] = `answer for ${i.value}`;
      }
    }
  }
  const result = await graph.invoke(new Command({ resume: resumeMap }), config);

  console.log("Final state:", result);
  //> Final state: { vals: ['a:answer for question_a', 'b:answer for question_b'] }
}

main().catch(console.error);

批准或拒绝

中断最常见的用途之一是在关键操作前暂停并请求批准。例如,你可能希望让人类批准 API 调用、数据库更改或任何其他重要决策。
import { interrupt, Command } from "@langchain/langgraph";

const approvalNode: typeof State.Node = (state) => {
  // 暂停执行;有效负载会暴露在 result.__interrupt__ 中
  const isApproved = interrupt({
    question: "是否继续?",
    details: state.actionDetails
  });

  // 根据响应进行路由
  if (isApproved) {
    return new Command({ goto: "proceed" }); // 在提供恢复有效负载后执行
  } else {
    return new Command({ goto: "cancel" });
  }
}
恢复图时,传递 true 表示批准,传递 false 表示拒绝:
// 批准
await graph.invoke(new Command({ resume: true }), config);

// 拒绝
await graph.invoke(new Command({ resume: false }), config);
import {
  Command,
  MemorySaver,
  START,
  END,
  StateGraph,
  StateSchema,
  interrupt,
} from "@langchain/langgraph";
import * as z from "zod";

const State = new StateSchema({
  actionDetails: z.string(),
  status: z.enum(["pending", "approved", "rejected"]).nullable(),
});

const graphBuilder = new StateGraph(State)
  .addNode("approval", async (state) => {
    // 暴露详情,以便调用者可以在 UI 中渲染
    const decision = interrupt({
      question: "批准此操作?",
      details: state.actionDetails,
    });
    return new Command({ goto: decision ? "proceed" : "cancel" });
  }, { ends: ['proceed', 'cancel'] })
  .addNode("proceed", () => ({ status: "approved" }))
  .addNode("cancel", () => ({ status: "rejected" }))
  .addEdge(START, "approval")
  .addEdge("proceed", END)
  .addEdge("cancel", END);

// 生产环境请使用更持久的检查点记录器
const checkpointer = new MemorySaver();
const graph = graphBuilder.compile({ checkpointer });

const config = { configurable: { thread_id: "approval-123" } };
const initial = await graph.invoke(
  { actionDetails: "Transfer $500", status: "pending" },
  config,
);
console.log(initial.__interrupt__);
// [{ value: { question: ..., details: ... } }]

// 使用决策恢复;true 路由到 proceed,false 路由到 cancel
const resumed = await graph.invoke(new Command({ resume: true }), config);
console.log(resumed.status); // -> "approved"

审查和编辑状态

有时,你希望让人类在继续之前审查并编辑图状态的某一部分。这对于纠正 LLM 输出、添加缺失信息或进行调整非常有用。
import { interrupt } from "@langchain/langgraph";

const reviewNode: typeof State.Node = (state) => {
  // 暂停并显示当前内容以供审查(有效负载会暴露在 result.__interrupt__ 中)
  const editedContent = interrupt({
    instruction: "审查并编辑此内容",
    content: state.generatedText
  });

  // 用编辑后的版本更新状态
  return { generatedText: editedContent };
}
恢复时,提供编辑后的内容:
await graph.invoke(
  new Command({ resume: "已编辑和改进的文本" }), // 该值将成为 interrupt() 的返回值
  config
);
import {
  Command,
  MemorySaver,
  START,
  END,
  StateGraph,
  StateSchema,
  interrupt,
} from "@langchain/langgraph";
import * as z from "zod";

const State = new StateSchema({
  generatedText: z.string(),
});

const builder = new StateGraph(State)
  .addNode("review", async (state) => {
    // 要求审查者编辑生成的内容
    const updated = interrupt({
      instruction: "审查并编辑此内容",
      content: state.generatedText,
    });
    return { generatedText: updated };
  })
  .addEdge(START, "review")
  .addEdge("review", END);

const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });

const config = { configurable: { thread_id: "review-42" } };
const initial = await graph.invoke({ generatedText: "Initial draft" }, config);
console.log(initial.__interrupt__);
// [{ value: { instruction: ..., content: ... } }]

// 使用审查者编辑后的文本恢复
const finalState = await graph.invoke(
  new Command({ resume: "改进后的草稿(已审查)" }),
  config,
);
console.log(finalState.generatedText); // -> "改进后的草稿(已审查)"

工具中的中断

你也可以直接在工具函数内部放置中断。这使得工具在被调用时自身会暂停以等待批准,并允许在工具调用执行之前进行人工审查和编辑。 首先,定义一个使用 interrupt 的工具:
import { tool } from "@langchain/core/tools";
import { interrupt } from "@langchain/langgraph";
import * as z from "zod";

const sendEmailTool = tool(
  async ({ to, subject, body }) => {
    // 发送前暂停;有效负载会暴露在 result.__interrupt__ 中
    const response = interrupt({
      action: "send_email",
      to,
      subject,
      body,
      message: "批准发送这封邮件吗?",
    });

    if (response?.action === "approve") {
      // 恢复值可以在执行前覆盖输入
      const finalTo = response.to ?? to;
      const finalSubject = response.subject ?? subject;
      const finalBody = response.body ?? body;
      return `邮件已发送至 ${finalTo},主题为 '${finalSubject}'`;
    }
    return "用户取消了邮件";
  },
  {
    name: "send_email",
    description: "Send an email to a recipient",
    schema: z.object({
      to: z.string(),
      subject: z.string(),
      body: z.string(),
    }),
  },
);
当你希望审批逻辑与工具本身共存,使其在图的不同部分可重用时,这种方法非常有用。LLM 可以自然地调用该工具,并且每当工具被调用时,中断都会暂停执行,允许你批准、编辑或取消该操作。
import { tool } from "@langchain/core/tools";
import { ChatAnthropic } from "@langchain/anthropic";
import {
  Command,
  MemorySaver,
  START,
  END,
  StateGraph,
  StateSchema,
  MessagesValue,
  GraphNode,
  interrupt,
} from "@langchain/langgraph";
import * as z from "zod";

const sendEmailTool = tool(
  async ({ to, subject, body }) => {
    // 发送前暂停;有效负载会暴露在 result.__interrupt__ 中
    const response = interrupt({
      action: "send_email",
      to,
      subject,
      body,
      message: "批准发送这封邮件吗?",
    });

    if (response?.action === "approve") {
      const finalTo = response.to ?? to;
      const finalSubject = response.subject ?? subject;
      const finalBody = response.body ?? body;
      console.log("[sendEmailTool]", finalTo, finalSubject, finalBody);
      return `邮件已发送至 ${finalTo}`;
    }
    return "用户取消了邮件";
  },
  {
    name: "send_email",
    description: "Send an email to a recipient",
    schema: z.object({
      to: z.string(),
      subject: z.string(),
      body: z.string(),
    }),
  },
);

const model = new ChatAnthropic({ model: "claude-sonnet-4-6" }).bindTools([sendEmailTool]);

const State = new StateSchema({
  messages: MessagesValue,
});

const agent: typeof State.Node = async (state) => {
  // LLM 可能决定调用工具;中断会在发送前暂停
  const response = await model.invoke(state.messages);
  return { messages: [response] };
};

const graphBuilder = new StateGraph(State)
  .addNode("agent", agent)
  .addEdge(START, "agent")
  .addEdge("agent", END);

const checkpointer = new MemorySaver();
const graph = graphBuilder.compile({ checkpointer });

const config = { configurable: { thread_id: "email-workflow" } };
const initial = await graph.invoke(
  {
    messages: [
      { role: "user", content: "发邮件给 alice@example.com,讨论会议事宜" },
    ],
  },
  config,
);
console.log(initial.__interrupt__); // -> [{ value: { action: 'send_email', ... } }]

// 恢复时批准并可选择编辑参数
const resumed = await graph.invoke(
  new Command({
    resume: { action: "approve", subject: "已更新的主题" },
  }),
  config,
);
console.log(resumed.messages.at(-1)); // -> send_email 返回的工具结果

验证人类输入

有时你需要验证人类的输入,如果无效则再次询问。你可以通过在一个循环中使用多个 interrupt 调用来实现这一点。
import { interrupt } from "@langchain/langgraph";

const getAgeNode: typeof State.Node = (state) => {
  let prompt = "你的年龄是多少?";

  while (true) {
    const answer = interrupt(prompt); // 有效负载会暴露在 result.__interrupt__ 中

    // 验证输入
    if (typeof answer === "number" && answer > 0) {
      // 有效输入 - 继续
      return { age: answer };
    } else {
      // 无效输入 - 用更具体的提示再次询问
      prompt = `'${answer}' 不是一个有效的年龄。请输入一个正数。`;
    }
  }
}
每次你用无效输入恢复图时,它都会用更清晰的消息再次询问。一旦提供了有效输入,节点就完成,图继续执行。
import {
  Command,
  MemorySaver,
  START,
  END,
  StateGraph,
  StateSchema,
  interrupt,
} from "@langchain/langgraph";
import * as z from "zod";

const State = new StateSchema({
  age: z.number().nullable(),
});

const builder = new StateGraph(State)
  .addNode("collectAge", (state) => {
    let prompt = "你的年龄是多少?";

    while (true) {
      const answer = interrupt(prompt); // 有效负载会暴露在 result.__interrupt__ 中

      if (typeof answer === "number" && answer > 0) {
        return { age: answer };
      }

      prompt = `'${answer}' 不是一个有效的年龄。请输入一个正数。`;
    }
  })
  .addEdge(START, "collectAge")
  .addEdge("collectAge", END);

const checkpointer = new MemorySaver();
const graph = builder.compile({ checkpointer });

const config = { configurable: { thread_id: "form-1" } };
const first = await graph.invoke({ age: null }, config);
console.log(first.__interrupt__); // -> [{ value: "你的年龄是多少?", ... }]

// 提供无效数据;节点会重新提示
const retry = await graph.invoke(new Command({ resume: "thirty" }), config);
console.log(retry.__interrupt__); // -> [{ value: "'thirty' 不是一个有效的年龄...", ... }]

// 提供有效数据;循环退出,状态更新
const final = await graph.invoke(new Command({ resume: 30 }), config);
console.log(final.age); // -> 30

中断的规则

当你在节点内调用 interrupt 时,LangGraph 会通过抛出一个特殊异常来挂起执行,该异常通知运行时暂停。该异常会沿着调用堆栈向上传播,并被运行时捕获,运行时通知图保存当前状态并等待外部输入。 当执行恢复时(在你提供所需的输入之后),运行时从头重新开始整个节点——它不会从调用 interrupt 的确切行恢复。这意味着在 interrupt 之前运行的任何代码都将再次执行。因此,在使用中断时,需要遵循一些重要的规则,以确保它们按预期工作。

不要将 interrupt 调用包裹在 try/catch 中

interrupt 通过在调用点抛出一个特殊异常来暂停执行。如果你将 interrupt 调用包裹在 try/catch 块中,你会捕获这个异常,中断将不会传回给图。
  • ✅ 将 interrupt 调用与容易出错的代码分开
  • ✅ 如有需要,有条件地捕获错误
const nodeA: GraphNode<typeof State> = async (state) => {
  // ✅ 好的做法:先中断,然后单独处理错误情况
  const name = interrupt("你叫什么名字?");
  try {
    await fetchData(); // 这可能会失败
  } catch (err) {
    console.error(error);
  }
  return state;
}
  • 🔴 不要将 interrupt 调用包裹在裸 try/catch 块中
async function nodeA(state: State) {
    // ❌ 糟糕的做法:在裸 try/catch 中包裹中断会捕获中断异常
    try {
        const name = interrupt("你叫什么名字?");
    } catch (err) {
        console.error(error);
    }
    return state;
}

不要重新排序节点内的 interrupt 调用

在单个节点内使用多个中断是很常见的,但如果不小心处理,可能会导致意外行为。 当一个节点包含多个中断调用时,LangGraph 会维护一个特定于执行该节点的任务的恢复值列表。每当执行恢复时,它都会从节点的开头开始。对于遇到的每个中断,LangGraph 都会检查任务的恢复列表中是否存在匹配的值。匹配是严格基于索引的,因此节点内中断调用的顺序很重要。
  • ✅ 保持 interrupt 调用在节点执行之间一致
async function nodeA(state: State) {
    // ✅ 好的做法:中断调用每次都以相同的顺序发生
    const name = interrupt("你叫什么名字?");
    const age = interrupt("你的年龄是多少?");
    const city = interrupt("你住在哪个城市?");

    return {
        name,
        age,
        city
    };
}
  • 🔴 不要有条件地跳过节点内的 interrupt 调用
  • 🔴 不要使用在不同执行之间不是确定性的逻辑来循环 interrupt 调用
const nodeA: GraphNode<typeof State> = async (state) => {
  // ❌ 糟糕的做法:有条件地跳过中断会改变顺序
  const name = interrupt("你叫什么名字?");

  // 在第一次运行时,这可能会跳过中断
  // 在恢复时,它可能不会跳过——导致索引不匹配
  if (state.needsAge) {
    const age = interrupt("你的年龄是多少?");
  }

  const city = interrupt("你住在哪个城市?");

  return { name, city };
}

不要在 interrupt 调用中返回复杂的值

根据所使用的检查点记录器,复杂的值可能无法序列化(例如,你无法序列化一个函数)。为了使你的图能够适应任何部署,最佳实践是仅使用可以合理序列化的值。
  • ✅ 向 interrupt 传递简单的、可 JSON 序列化的类型
  • ✅ 传递包含简单值的字典/对象
const nodeA: GraphNode<typeof State> = async (state) => {
  // ✅ 好的做法:传递可序列化的简单类型
  const name = interrupt("你叫什么名字?");
  const count = interrupt(42);
  const approved = interrupt(true);

  return { name, count, approved };
}
  • 🔴 不要向 interrupt 传递函数、类实例或其他复杂对象
function validateInput(value: string): boolean {
    return value.length > 0;
}

const nodeA: GraphNode<typeof State> = async (state) => {
  // ❌ 糟糕的做法:向 interrupt 传递函数
  // 函数无法被序列化
  const response = interrupt({
    question: "你叫什么名字?",
    validator: validateInput  // 这会失败
  });
  return { name: response };
}

interrupt 之前调用的副作用必须是幂等的

由于中断会重新运行调用它们的节点,因此在 interrupt 之前调用的副作用(理想情况下)应该是幂等的。作为背景,幂等性意味着同一个操作可以被多次应用,而不会改变初始执行之外的结果。 例如,你可能会在节点内部有一个更新记录的 API 调用。如果在该调用之后又调用了 interrupt,那么当节点恢复时,该调用会被多次重新运行,可能会覆盖初始更新或创建重复记录。
  • ✅ 在 interrupt 之前使用幂等操作
  • ✅ 将副作用放在 interrupt 调用之后
  • ✅ 尽可能将副作用分隔到单独的节点中
const nodeA: GraphNode<typeof State> = async (state) => {
  // ✅ 好的做法:使用幂等的 upsert 操作
  // 多次运行此操作将得到相同的结果
  await db.upsertUser({
    userId: state.userId,
    status: "pending_approval"
  });

  const approved = interrupt("批准此更改吗?");

  return { approved };
}
  • 🔴 不要在 interrupt 之前执行非幂等的操作
  • 🔴 不要在未检查记录是否存在的情况下创建新记录
const nodeA: GraphNode<typeof State> = async (state) => {
  // ❌ 糟糕的做法:在中断之前创建新记录
  // 每次恢复都会创建重复记录
  const auditId = await db.createAuditLog({
    userId: state.userId,
    action: "pending_approval",
    timestamp: new Date()
  });

  const approved = interrupt("批准此更改吗?");

  return { approved, auditId };
}

与作为函数调用的子图一起使用

当在节点内调用子图时,父图将从调用子图并且触发 interrupt 的节点的开头恢复执行。同样,子图也将从调用 interrupt 的节点开头恢复。
async function nodeInParentGraph(state: State) {
    someCode(); // <-- 恢复时将重新执行这部分
    // 以函数形式调用子图。
    // 该子图包含一个 `interrupt` 调用。
    const subgraphResult = await subgraph.invoke(someInput);
    // ...
}

async function nodeInSubgraph(state: State) {
    someOtherCode(); // <-- 恢复时这也将重新执行
    const result = interrupt("你叫什么名字?");
    // ...
}

使用中断进行调试

要调试和测试图,你可以使用静态中断作为断点,一次一个节点地单步通过图执行。静态中断是在节点执行之前或之后的已定义点触发的。你可以在编译图时通过指定 interruptBeforeinterruptAfter 来设置它们。
静态中断推荐用于人工介入工作流。请改用 interrupt 函数。
const graph = builder.compile({
    interruptBefore: ["node_a"],
    interruptAfter: ["node_b", "node_c"],
    checkpointer,
});

// 向图传递一个线程 ID
const config = {
    configurable: {
        thread_id: "some_thread"
    }
};

// 运行图直到断点
await graph.invoke(inputs, config);# [!code highlight]

await graph.invoke(null, config);  # [!code highlight]
  1. 断点在 compile 时设置。
  2. interruptBefore 指定在节点执行之前应暂停的节点。
  3. interruptAfter 指定在节点执行之后应暂停的节点。
  4. 需要使用检查点记录器来启用断点。
  5. 图运行直到遇到第一个断点。
  6. 通过传入 null 作为输入来恢复图。这将运行图直到遇到下一个断点。
要调试你的中断,请使用 LangSmith

使用 LangSmith Studio

你可以使用 LangSmith Studio 在 UI 中为你的图设置静态中断,然后再运行图。你也可以使用 UI 在执行中的任意点检查图状态。 image