> ## Documentation Index
> Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 中断

中断允许您在特定位置暂停图的执行，并在继续之前等待外部输入。这实现了人机回环模式，其中您需要外部输入才能继续。当中断被触发时，LangGraph 使用其 [持久化](/oss/javascript/langgraph/persistence) 层保存图状态，并无限期等待直到您恢复执行。

中断通过在图的任意节点中调用 `interrupt()` 函数来工作。该函数接受任何可 JSON 序列化的值并将其呈现给调用者。当您准备好继续时，您可以通过使用 `Command` 重新调用图来恢复执行，然后它将成为节点内部 `interrupt()` 调用的返回值。

与在特定节点之前或之后暂停的静态断点不同，中断是**动态**的：它们可以放置在代码的任何位置，并且可以根据您的应用程序逻辑进行条件判断。

* **检查点保持您的位置：** 检查点器写入确切的图状态，以便您可以在稍后恢复，即使在错误状态下也是如此。
* **`thread_id` 是指针：** 将 `{ configurable: { thread_id: ... } }` 作为 `invoke` 方法的选项，以告诉检查点器加载哪个状态。
* **中断负载作为 `__interrupt__` 呈现：** 您传递给 `interrupt()` 的值返回到调用者的 `__interrupt__` 字段中，这样您就知道图在等待什么。

您选择的 `thread_id` 实际上是您的持久化游标。重用它会恢复相同的检查点；使用新值会启动一个具有空状态的全新线程。

## 使用 `interrupt` 暂停

[`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 函数暂停图的执行并向调用者返回值。当您在节点内调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 时，LangGraph 保存当前图状态并等待您使用输入恢复执行。

要使用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt)，您需要：

1. 用于持久化图状态的 **检查点器**（在生产环境中使用耐久的检查点器）
2. 配置中的 **线程 ID**，以便运行时知道从哪个状态恢复
3. 在您想要暂停的位置调用 `interrupt()`（负载必须是可 JSON 序列化的）

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { interrupt } from "@langchain/langgraph";

async function approvalNode(state: State) {
    // Pause and ask for approval
    const approved = interrupt("Do you approve this action?");

    // Command({ resume: ... }) provides the value returned into this variable
    return { approved };
}
```

当您调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 时，会发生以下情况：

1. **图执行被挂起** 在调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的确切位置
2. **状态被保存** 使用检查点器，以便以后可以恢复执行，在生产环境中，这应该是持久化检查点器（例如，由数据库支持）
3. **返回值** 返回给调用者，位于 `__interrupt__` 下；它可以是任何可 JSON 序列化的值（字符串、对象、数组等）
4. **图无限期等待** 直到您使用响应恢复执行
5. **响应被传回** 当您恢复时进入节点，成为 `interrupt()` 调用的返回值

## 恢复中断

在中断暂停执行后，您通过使用包含恢复值的 `Command` 再次调用图来恢复图。恢复值被传回给 `interrupt` 调用，允许节点使用外部输入继续执行。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { Command } from "@langchain/langgraph";

// Initial run - hits the interrupt and pauses
// thread_id is the durable pointer back to the saved checkpoint
const config = { configurable: { thread_id: "thread-1" } };
const result = await graph.invoke({ input: "data" }, config);

// Check what was interrupted
// __interrupt__ mirrors every payload you passed to interrupt()
console.log(result.__interrupt__);
// [{ value: 'Do you approve this action?', ... }]

// Resume with the human's response
// Command({ resume }) returns that value from interrupt() in the node
await graph.invoke(new Command({ resume: true }), config);
```

**关于恢复的关键点：**

* 恢复时必须使用与中断发生时相同的 **线程 ID**
* 传递给 `new Command({ resume: ... })` 的值成为 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用的返回值
* 恢复时，节点从调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的节点的开头重新开始，因此 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 之前的任何代码都会再次运行
* 您可以传递任何可 JSON 序列化的值作为恢复值

<Warning>
  `new Command({ resume: ... })` 是 **唯一** 设计为 `invoke()`/`stream()` 输入的 `Command` 模式。其他 `Command` 参数（`update`, `goto`, `graph`）旨在用于 [从节点函数返回](/oss/javascript/langgraph/graph-api#command)。不要将 `new Command({ update: ... })` 作为输入来继续多轮对话——请传递普通输入对象。
</Warning>

## 常见模式

中断解锁的关键功能是暂停执行并等待外部输入的能力。这对于各种用例很有用，包括：

* <Icon icon="circle-check" /> [审批工作流](#approve-or-reject)：在执行关键操作（API 调用、数据库更改、金融交易）之前暂停
* <Icon icon="link" /> [处理多个中断](#handling-multiple-interrupts)：在单次调用中恢复多个中断时，将中断 ID 与恢复值配对
* <Icon icon="pencil" /> [审查和编辑](#review-and-edit-state)：让人类在继续之前审查和修改 LLM 输出或工具调用
* <Icon icon="tool" /> [中断工具调用](#interrupts-in-tools)：在执行工具调用之前暂停，以便在执行前审查和编辑工具调用
* <Icon icon="shield-check" /> [验证人类输入](#validating-human-input)：在进行下一步之前暂停以验证人类输入

### 使用人机回环 (HITL) 中断进行流式传输

在构建具有人机回环工作流的交互式代理时，您可以同时流式传输消息块和节点更新，以便在处理中断的同时提供实时反馈。

使用多种流模式（`"messages"` 和 `"updates"`）以及 `subgraphs=True`（如果存在子图）来：

* 实时流式传输生成的 AI 响应

* 检测图何时遇到中断

* 无缝处理用户输入并恢复执行

* **`Command(resume=...)`**：使用用户提供的数据恢复图执行

### 处理多个中断

当并行分支同时中断时（例如，扇出到多个每个都调用 `interrupt()` 的节点），您可能需要在单次调用中恢复多个中断。
当使用单次调用恢复多个中断时，将每个中断 ID 映射到其恢复值。
这确保每个响应在运行时都与正确的中断配对。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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() {
  // Step 1: invoke - both parallel nodes hit interrupt() and pause
  const interruptedResult = await graph.invoke({ vals: [] }, config);
  console.log(interruptedResult);
  /*
  {
    vals: [],
    __interrupt__: [
      { id: '...', value: 'question_a' },
      { id: '...', value: 'question_b' }
    ]
  }
  */

  // Step 2: resume all pending interrupts at once
  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 调用、数据库更改或任何其他重要决定。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { interrupt, Command } from "@langchain/langgraph";

const approvalNode: typeof State.Node = (state) => {
  // Pause execution; payload surfaces in result.__interrupt__
  const isApproved = interrupt({
    question: "Do you want to proceed?",
    details: state.actionDetails
  });

  // Route based on the response
  if (isApproved) {
    return new Command({ goto: "proceed" }); // Runs after the resume payload is provided
  } else {
    return new Command({ goto: "cancel" });
  }
}
```

当您恢复图时，传递 `true` 表示批准或 `false` 表示拒绝：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
// To approve
await graph.invoke(new Command({ resume: true }), config);

// To reject
await graph.invoke(new Command({ resume: false }), config);
```

<Accordion title="完整示例">
  ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  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) => {
      // Expose details so the caller can render them in a UI
      const decision = interrupt({
        question: "Approve this action?",
        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);

  // Use a more durable checkpointer in production
  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: ... } }]

  // Resume with the decision; true routes to proceed, false to cancel
  const resumed = await graph.invoke(new Command({ resume: true }), config);
  console.log(resumed.status); // -> "approved"
  ```
</Accordion>

### 审查和编辑状态

有时您希望让人类在继续之前审查和编辑图的一部分状态。这对于纠正 LLM、添加缺失信息或进行调整很有用。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { interrupt } from "@langchain/langgraph";

const reviewNode: typeof State.Node = (state) => {
  // Pause and show the current content for review (surfaces in result.__interrupt__)
  const editedContent = interrupt({
    instruction: "Review and edit this content",
    content: state.generatedText
  });

  // Update the state with the edited version
  return { generatedText: editedContent };
}
```

恢复时，提供编辑后的内容：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await graph.invoke(
  new Command({ resume: "The edited and improved text" }), // Value becomes the return from interrupt()
  config
);
```

<Accordion title="完整示例">
  ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  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) => {
      // Ask a reviewer to edit the generated content
      const updated = interrupt({
        instruction: "Review and edit this content",
        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: ... } }]

  // Resume with the edited text from the reviewer
  const finalState = await graph.invoke(
    new Command({ resume: "Improved draft after review" }),
    config,
  );
  console.log(finalState.generatedText); // -> "Improved draft after review"
  ```
</Accordion>

### 工具中的中断

您还可以直接将中断放在工具函数内部。这使得工具本身在被调用时暂停以获取批准，并允许在执行前对人类审查和编辑工具调用。

首先，定义一个使用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的工具：

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { tool } from "@langchain/core/tools";
import { interrupt } from "@langchain/langgraph";
import * as z from "zod";

const sendEmailTool = tool(
  async ({ to, subject, body }) => {
    // Pause before sending; payload surfaces in result.__interrupt__
    const response = interrupt({
      action: "send_email",
      to,
      subject,
      body,
      message: "Approve sending this email?",
    });

    if (response?.action === "approve") {
      // Resume value can override inputs before executing
      const finalTo = response.to ?? to;
      const finalSubject = response.subject ?? subject;
      const finalBody = response.body ?? body;
      return `Email sent to ${finalTo} with subject '${finalSubject}'`;
    }
    return "Email cancelled by user";
  },
  {
    name: "send_email",
    description: "Send an email to a recipient",
    schema: z.object({
      to: z.string(),
      subject: z.string(),
      body: z.string(),
    }),
  },
);
```

这种方法在您希望批准逻辑存在于工具本身中时非常有用，使其可以在图的不同部分重复使用。LLM 可以自然地调用工具，并且每当调用工具时，中断将暂停执行，允许您批准、编辑或取消操作。

<Accordion title="完整示例">
  ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  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 }) => {
      // Pause before sending; payload surfaces in result.__interrupt__
      const response = interrupt({
        action: "send_email",
        to,
        subject,
        body,
        message: "Approve sending this email?",
      });

      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 `Email sent to ${finalTo}`;
      }
      return "Email cancelled by user";
    },
    {
      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 may decide to call the tool; interrupt pauses before sending
    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: "Send an email to alice@example.com about the meeting" },
      ],
    },
    config,
  );
  console.log(initial.__interrupt__); // -> [{ value: { action: 'send_email', ... } }]

  // Resume with approval and optionally edited arguments
  const resumed = await graph.invoke(
    new Command({
      resume: { action: "approve", subject: "Updated subject" },
    }),
    config,
  );
  console.log(resumed.messages.at(-1)); // -> Tool result returned by send_email
  ```
</Accordion>

### 验证人类输入

有时您需要验证来自人类的输入，如果无效则再次询问。您可以使用循环中的多个 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用来完成此操作。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { interrupt } from "@langchain/langgraph";

const getAgeNode: typeof State.Node = (state) => {
  let prompt = "What is your age?";

  while (true) {
    const answer = interrupt(prompt); // payload surfaces in result.__interrupt__

    // Validate the input
    if (typeof answer === "number" && answer > 0) {
      // Valid input - continue
      return { age: answer };
    } else {
      // Invalid input - ask again with a more specific prompt
      prompt = `'${answer}' is not a valid age. Please enter a positive number.`;
    }
  }
}
```

每次您使用无效输入恢复图时，它将再次使用更清晰的消息询问。一旦提供有效输入，节点完成，图继续。

<Accordion title="完整示例">
  ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  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 = "What is your age?";

      while (true) {
        const answer = interrupt(prompt); // payload surfaces in result.__interrupt__

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

        prompt = `'${answer}' is not a valid age. Please enter a positive number.`;
      }
    })
    .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: "What is your age?", ... }]

  // Provide invalid data; the node re-prompts
  const retry = await graph.invoke(new Command({ resume: "thirty" }), config);
  console.log(retry.__interrupt__); // -> [{ value: "'thirty' is not a valid age...", ... }]

  // Provide valid data; loop exits and state updates
  const final = await graph.invoke(new Command({ resume: 30 }), config);
  console.log(final.age); // -> 30
  ```
</Accordion>

## 中断规则

当您在节点内调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 时，LangGraph 通过引发异常来挂起执行，该异常向运行时发出暂停信号。此异常沿调用栈传播并被运行时捕获，运行时通知图保存当前状态并等待外部输入。

当执行恢复时（在您提供所需输入后），运行时从头开始重新启动整个节点——它不会从调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的确切行恢复。这意味着 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 之前运行的任何代码都将再次执行。因此，在使用中断时要遵循一些重要规则，以确保它们按预期行为。

### 不要在 try/catch 中包装 `interrupt` 调用

[`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 通过在调用点抛出特殊异常来暂停执行的方式。如果您在 try/catch 块中包装 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用，您将捕获此异常，中断将不会传回给图。

* ✅ 将 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用与易错代码分离
* ✅ 如有需要，有条件地捕获错误

<CodeGroup>
  ```typescript Separating logic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: interrupting first, then handling error conditions separately
    const name = interrupt("What's your name?");
    try {
      await fetchData(); // This can fail
    } catch (err) {
      console.error(error);
    }
    return state;
  }
  ```

  ```typescript Conditionally handling errors theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: re-throwing the exception will
    // allow the interrupt to be passed back to
    // the graph
    try {
      const name = interrupt("What's your name?");
      await fetchData(); // This can fail
    } catch (err) {
      if (error instanceof NetworkError) {
        console.error(error);
      }
      throw error;
    }
    return state;
  }
  ```
</CodeGroup>

* 🔴 不要在裸 try/catch 块中包装 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
async function nodeA(state: State) {
    // ❌ Bad: wrapping interrupt in bare try/catch will catch the interrupt exception
    try {
        const name = interrupt("What's your name?");
    } catch (err) {
        console.error(error);
    }
    return state;
}
```

### 不要在节点内重新排序 `interrupt` 调用

在一个节点中使用多个中断很常见，但如果处理不当可能会导致意外行为。

当节点包含多个中断调用时，LangGraph 会维护一个针对执行该节点的任务的恢复值列表。每当执行恢复时，它从节点的开头开始。对于遇到的每个中断，LangGraph 检查任务的恢复列表中是否存在匹配的值。匹配是**严格基于索引的**，因此节点内中断调用的顺序很重要。

* ✅ 在节点执行之间保持 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用一致

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
async function nodeA(state: State) {
    // ✅ Good: interrupt calls happen in the same order every time
    const name = interrupt("What's your name?");
    const age = interrupt("What's your age?");
    const city = interrupt("What's your city?");

    return {
        name,
        age,
        city
    };
}
```

* 🔴 不要在节点内有条件地跳过 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用
* 🔴 不要使用跨执行非确定性的逻辑循环 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用

<CodeGroup>
  ```typescript Skipping interrupts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ❌ Bad: conditionally skipping interrupts changes the order
    const name = interrupt("What's your name?");

    // On first run, this might skip the interrupt
    // On resume, it might not skip it - causing index mismatch
    if (state.needsAge) {
      const age = interrupt("What's your age?");
    }

    const city = interrupt("What's your city?");

    return { name, city };
  }
  ```

  ```typescript Looping interrupts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ❌ Bad: looping based on non-deterministic data
    // The number of interrupts changes between executions
    const results = [];
    for (const item of state.dynamicList || []) {  # List might change between runs
      const result = interrupt(`Approve ${item}?`);
      results.push(result);
    }

    return { results };
  }
  ```
</CodeGroup>

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

根据使用的检查点器不同，复杂值可能无法序列化（例如，您不能序列化函数）。为了使您的图适应任何部署，最佳实践是仅使用可以合理序列化的值。

* ✅ 向 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 传递简单、可 JSON 序列化的类型
* ✅ 传递具有简单值的字典/对象

<CodeGroup>
  ```typescript Simple values theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: passing simple types that are serializable
    const name = interrupt("What's your name?");
    const count = interrupt(42);
    const approved = interrupt(true);

    return { name, count, approved };
  }
  ```

  ```typescript Structured data theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: passing objects with simple values
    const response = interrupt({
      question: "Enter user details",
      fields: ["name", "email", "age"],
      currentValues: state.user || {}
    });

    return { user: response };
  }
  ```
</CodeGroup>

* 🔴 不要向 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 传递函数、类实例或其他复杂对象

<CodeGroup>
  ```typescript Functions theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  function validateInput(value: string): boolean {
      return value.length > 0;
  }

  const nodeA: GraphNode<typeof State> = async (state) => {
    // ❌ Bad: passing a function to interrupt
    // The function cannot be serialized
    const response = interrupt({
      question: "What's your name?",
      validator: validateInput  # This will fail
    });
    return { name: response };
  }
  ```

  ```typescript Class instances theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  class DataProcessor {
      constructor(private config: any) {}
  }

  const nodeA: GraphNode<typeof State> = async (state) => {
    const processor = new DataProcessor({ mode: "strict" });

    // ❌ Bad: passing a class instance to interrupt
    // The instance cannot be serialized
    const response = interrupt({
      question: "Enter data to process",
      processor: processor  # This will fail
    });
    return { result: response };
  }
  ```
</CodeGroup>

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

因为中断通过重新运行调用它们的节点来工作，所以在 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 之前调用的副作用应该（理想情况下）是幂等的。为了上下文，幂等性意味着同一操作可以应用多次，而不会改变初始执行之外的结果。

作为一个例子，您可能有一个在节点内更新记录的 API 调用。如果在调用之后调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt)，当节点恢复时它将多次重新运行，可能会覆盖初始更新或创建重复记录。

* ✅ 在 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 之前使用幂等操作
* ✅ 将副作用放在 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 调用之后
* ✅ 尽可能将副作用分离到单独的节点中

<CodeGroup>
  ```typescript Idempotent operations theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: using upsert operation which is idempotent
    # Running this multiple times will have the same result
    await db.upsertUser({
      userId: state.userId,
      status: "pending_approval"
    });

    const approved = interrupt("Approve this change?");

    return { approved };
  }
  ```

  ```typescript Side effects after interrupt theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    // ✅ Good: placing side effect after the interrupt
    # This ensures it only runs once after approval is received
    const approved = interrupt("Approve this change?");

    if (approved) {
      await db.createAuditLog({
        userId: state.userId,
        action: "approved"
      });
    }

    return { approved };
  }
  ```

  ```typescript Separating into different nodes theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const approvalNode: GraphNode<typeof State> = async (state) => {
    // ✅ Good: only handling the interrupt in this node
    const approved = interrupt("Approve this change?");

    return { approved };
  }

  const notificationNode: GraphNode<typeof State> = async (state) => {
    // ✅ Good: side effect happens in a separate node
    # This runs after approval, so it only executes once
    if (state.approved) {
      await sendNotification({
        userId: state.userId,
        status: "approved",
      });
    }

    return state;
  }
  ```
</CodeGroup>

* 🔴 不要在 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 之前执行非幂等操作
* 🔴 不要在不检查是否存在的情况下创建新记录

<CodeGroup>
  ```typescript Creating records theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    # ❌ Bad: creating a new record before interrupt
    # This will create duplicate records on each resume
    const auditId = await db.createAuditLog({
      userId: state.userId,
      action: "pending_approval",
      timestamp: new Date()
    });

    const approved = interrupt("Approve this change?");

    return { approved, auditId };
  }
  ```

  ```typescript Appending to arrays theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const nodeA: GraphNode<typeof State> = async (state) => {
    # ❌ Bad: appending to an array before interrupt
    # This will add duplicate entries on each resume
    await db.appendToHistory(state.userId, "approval_requested");

    const approved = interrupt("Approve this change?");

    return { approved };
  }
  ```
</CodeGroup>

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

当在节点内调用子图时，父图将从调用子图和触发 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的 **节点开头** 恢复执行。同样，**子图** 也将从调用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 的节点开头恢复。

```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
async function nodeInParentGraph(state: State) {
    someCode(); // <-- This will re-execute when resumed
    // Invoke a subgraph as a function.
    // The subgraph contains an `interrupt` call.
    const subgraphResult = await subgraph.invoke(someInput);
    // ...
}

async function nodeInSubgraph(state: State) {
    someOtherCode(); // <-- This will also re-execute when resumed
    const result = interrupt("What's your name?");
    // ...
}
```

## 使用中继调试

要调试和测试图，您可以使用静态中断作为断点，一次一个节点地逐步执行图执行。静态中断在节点执行之前或之后的定义点触发。您可以通过在编译图时指定 `interruptBefore` 和 `interruptAfter` 来设置这些。

<Note>
  静态中断 **不** 推荐用于人机回环工作流。请使用 [`interrupt`](https://reference.langchain.com/javascript/langchain-langgraph/index/interrupt) 函数。
</Note>

<Tabs>
  <Tab title="编译时">
    ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    const graph = builder.compile({
        interruptBefore: ["node_a"],  # [!code highlight]
        interruptAfter: ["node_b", "node_c"],  # [!code highlight]
        checkpointer,
    });

    # Pass a thread ID to the graph
    const config = {
        configurable: {
            thread_id: "some_thread"
        }
    };

    # Run the graph until the breakpoint
    await graph.invoke(inputs, config);# [!code highlight]

    await graph.invoke(null, config);  # [!code highlight]
    ```

    1. 断点在 `compile` 期间设置。
    2. `interruptBefore` 指定应在节点执行之前暂停执行的节点。
    3. `interruptAfter` 指定应在节点执行之后暂停执行的节点。
    4. 需要检查点器来启用断点。
    5. 图运行直到击中第一个断点。
    6. 通过传入 `null` 作为输入来恢复图。这将运行图直到击中下一个断点。
  </Tab>

  <Tab title="运行时">
    ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    # Run the graph until the breakpoint
    graph.invoke(inputs, {
        interruptBefore: ["node_a"],  # [!code highlight]
        interruptAfter: ["node_b", "node_c"],  # [!code highlight]
        configurable: {
            thread_id: "some_thread"
        }
    });

    # Resume the graph
    await graph.invoke(null, config);  # [!code highlight]
    ```

    1. `graph.invoke` 使用 `interruptBefore` 和 `interruptAfter` 参数调用。这是运行时配置，可以为每次调用更改。
    2. `interruptBefore` 指定应在节点执行之前暂停执行的节点。
    3. `interruptAfter` 指定应在节点执行之后暂停执行的节点。
    4. 图运行直到击中第一个断点。
    5. 通过传入 `null` 作为输入来恢复图。这将运行图直到击中下一个断点。
  </Tab>
</Tabs>

<Tip>
  要调试您的中断，请使用 [LangSmith](/langsmith/home)。
</Tip>

### 使用 LangSmith Studio

您可以在运行图之前在 UI 中使用 [LangSmith Studio](/langsmith/studio) 在图中设置静态中断。您还可以使用 UI 检查执行过程中任何点的图状态。

<img src="https://mintcdn.com/hhh-8c10bf0c/nuzu1mnzaCcJfRiZ/oss/images/static-interrupt.png?fit=max&auto=format&n=nuzu1mnzaCcJfRiZ&q=85&s=a43c50a7cd2684086334264ce4c7a822" alt="image" width="1252" height="1040" data-path="oss/images/static-interrupt.png" />

***

<div className="source-links">
  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/i18n\zh-CN\oss\langgraph\interrupts.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>

  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to Claude, VSCode, and more via MCP for real-time answers.
  </Callout>
</div>
