Skip to main content
中断允许您在特定位置暂停图执行,并在继续之前等待外部输入。这使您能够实现需要外部输入才能继续的人机交互模式。当触发中断时,LangGraph 使用其 持久化 层保存图状态,并无限期等待您恢复执行。 中断通过在您的图节点中的任何位置调用 interrupt() 函数来工作。该函数接受任何 JSON 可序列化的值,这些值会展示给调用者。当您准备好继续时,您通过重新调用图使用 Command 来恢复执行,然后它成为节点内部 interrupt() 调用的返回值。 与静态断点(在特定节点之前或之后暂停)不同,中断是 动态的:它们可以放置在您的代码中的任何位置,并且可以根据您的应用程序逻辑进行条件性设置。 :::python
  • 检查点保持您的位置:检查点会写入确切的图状态,以便您稍后可以恢复,即使在错误状态下也是如此。
  • thread_id 是您的指针:设置 config={"configurable": {"thread_id": ...}} 以告诉检查点要加载哪个状态。
  • 中断有效载荷通过 stream.interrupts 展示:在使用 事件流 (graph.stream_events(..., version="v3")) 时,您传递给 interrupt() 的值会出现在 stream.interrupts 上,并且 stream.interrupted 是 True 当运行因输入而暂停时。 ::: :::js
  • 检查点保持您的位置:检查点会写入确切的图状态,以便您稍后可以恢复,即使在错误状态下也是如此。
  • thread_id 是您的指针:使用 { configurable: { thread_id: ... } } 作为 invoke 方法的选项来告诉检查点要加载哪个状态。
  • 中断有效载荷作为 __interrupt__ 展示:您传递给 interrupt() 的值会以 __interrupt__ 字段返回给调用者,以便您知道图正在等待什么。 :::
您选择的 thread_id 实际上是您的持久化游标。重复使用它将恢复相同的检查点;使用新值将启动一个具有空状态的新线程。

使用 interrupt 暂停

@[interrupt] 函数暂停图执行并返回值给调用者。当你在节点中调用 @[interrupt] 时,LangGraph 会保存当前图状态并等待你使用输入来恢复执行。 要使用 @[interrupt],你需要:
  1. 一个 检查点器 来持久化图状态(生产环境中使用持久检查点器)
  2. 配置中一个 线程 ID,以便运行时知道从哪个状态恢复
  3. 在你想暂停的位置调用 interrupt()(payload 必须是 JSON-可序列化的)
:::python
::: :::js
::: 当你调用 @[interrupt] 时,会发生以下情况:
  1. 图执行在 @[interrupt] 调用的确切位置被挂起
  2. 状态使用检查点器保存,以便稍后可以恢复执行,在生产环境中,这应该是持久检查点器(例如,由数据库支持) :::python
  3. 值通过 stream.interrupts 返回给调用者,当使用 事件流 (graph.stream_events(..., version="v3")) 或在 __interrupt__ 下使用默认 invoke() API;可以是任何 JSON-可序列化的值(字符串、对象、数组等) ::: :::js
  4. 值通过 __interrupt__ 返回给调用者;可以是任何 JSON-可序列化的值(字符串、对象、数组等) :::
  5. 图无限期等待,直到你使用响应恢复执行
  6. 响应在你恢复时传递回节点,成为 interrupt() 调用的返回值

恢复中断

中断暂停执行后,你通过再次调用图并使用包含恢复值的 Command 来恢复图。恢复值被传递回 interrupt 调用,允许节点使用外部输入继续执行。 :::python 推荐的方式是 事件流 — 它通过 stream.interrupts 和 stream.interrupted 表面中断,并通过 stream.output 暴露最终状态。

View example trace

为此示例打开一个公开的 LangSmith 运行。
默认的 graph.invoke(...) API 仍然有效,并通过 result["__interrupt__"] 表面中断。当你不需要流式投影时使用它;否则优先使用 graph.stream_events(..., version="v3")。
关于恢复的关键点:
  • 你在恢复时必须使用与中断发生时相同的线程 ID
  • 传递给 Command(resume=...) 的值成为 @[interrupt] 调用的返回值
  • 节点在恢复时从 @[interrupt] 被调用处的节点开头重新启动,因此 @[interrupt] 之前的任何代码都会再次运行
  • 你可以将任何 JSON 可序列化值作为恢复值,但当你指定一个类型的 response_schema 时,需要经过验证
Command(resume=...) 是作为输入传递给 invoke()/stream()/stream_events() 的唯一 Command 模式。其他 Command 参数(update, goto, graph)是为 从节点函数返回 而设计的。不要将 Command(update=...) 作为输入来继续多轮对话——请传递一个普通的输入字典。
::: :::js
关于恢复的关键点:
  • 你在恢复时必须使用与中断发生时相同的线程 ID
  • 传递给 new Command({ resume: ... }) 的值成为 @[interrupt] 调用的返回值
  • 节点在恢复时从 @[interrupt] 被调用处的节点开头重新启动,因此 @[interrupt] 之前的任何代码都会再次运行
  • 你可以将任何 JSON 可序列化值作为恢复值,但当你指定一个 Zod responseSchema 时,需要经过验证
new Command({ resume: ... }) 是作为输入传递给 invoke()/stream()/stream_events() 的唯一 Command 模式。其他 Command 参数(update, goto, graph)是为 从节点函数返回 而设计的。不要将 new Command({ update: ... }) 作为输入来继续多轮对话——请传递一个普通的输入对象。
:::

定义中断响应模式

:::python @[interrupt] 的 response_schema 参数描述了你在恢复图时期望的输入。客户端可以使用该模式来渲染带类型的输入表单。
中断响应模式需要 langgraph>=1.2.12。
传递一个 Pydantic 模型类、来自 typing_extensions 的 TypedDict,或一个数据类来验证恢复值。LangGraph 将类型转换为 JSON Schema 并将其包含在 Interrupt.response_schema 中,与中断的 value 负载分开。该模式可在返回给调用者的中断和检查点任务中断中获取。 此示例使用 LangGraph 中断测试中的批准模式:
使用带类型的模式,interrupt() 返回验证后的对象:Pydantic 模型实例、TypedDict 的字典或数据类实例。Pydantic 的验证和强制规则适用。无效输入引发 pydantic.ValidationError;您可以使用相同的线程 ID 重试使用更正的 Command(resume=...)。 您还可以将 JSON Schema 字典作为 response_schema 传递。LangGraph 原样暴露该字典,但不根据它验证恢复值。当您省略 response_schema 时,其值为 None,恢复值在未经模式验证的情况下通过。 ::: :::js @[interrupt] 的 responseSchema 选项描述了在您恢复图时预期的输入。客户端可以使用该模式来渲染带类型的输入表单。
中断响应模式需要 @langchain/langgraph 版本 1.4.16 或更高版本。
传递一个 Zod 模式来验证恢复值。LangGraph 将其转换为 JSON Schema 并将其包含在 Interrupt.response_schema 中,与中断的 value 负载分开。该模式可在返回给调用者的中断和检查点任务中断中获取。 此示例使用 LangGraph 中断测试中的批准模式:
使用 Zod 模式,interrupt() 返回解析后的值。在此示例中,结果是 { approved: true, note: "" },包括默认的 note。无效输入引发 ZodError;您可以使用相同的线程 ID 重试使用更正的 new Command({ resume: ... })。 您还可以将 JSON Schema 对象作为 responseSchema 传递。LangGraph 原样暴露该对象,但不根据它验证恢复值。当您省略 responseSchema 时,中断没有 response_schema 字段,恢复值在未经模式验证的情况下通过。 ::: Studio 将中断响应模式渲染为带类型输入字段,而不是 JSON 编辑器。此 Studio 组件预览使用示例数据,包含批准、备注和数值金额字段。 带有批准切换、备注字段和数值金额设置为 125.50 的 Studio 中断表单

常见模式

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

带有人工参与(HITL)的中断流

在构建带有人工参与工作流的中断智能体时,您可以使用 事件流 来同时消费消息块和状态快照,同时处理中断。 使用 graph.stream_events(..., version="v3") 返回的类型投影在循环中,直到运行完成:
  • 通过 stream.messages 以 Token 为单位流式输出 AI 响应
  • 通过 stream.values 观察每一步的状态快照
  • 通过 stream.interrupted 检测中断,并从 stream.interrupts 读取其有效载荷
  • 通过再次调用 stream_events 并传入 Command(resume=...) 来恢复执行,重复直到 stream.interrupted 为假
:::python

View example trace

为此示例打开一个公开的 LangSmith 运行。
  • stream.messages: 对话模型输出为内容块;迭代每个 message.text 获取 Token 差异。对于嵌套子图,从 stream.subgraphs[*].messages 读取消息块。
  • stream.values: 每步后的完整状态快照
  • stream.interrupted / stream.interrupts: 每次运行后,检查图是否暂停;从 stream.interrupts 读取有效载荷
  • Command(resume=...): 传递为下一个 stream_events 输入以继续;循环直到运行完成且未中断 :::
:::js

View example trace

为此示例打开一个公开的 LangSmith 运行。
  • stream.messages: 对话模型输出为内容块;迭代 message.text 获取 Token 差异。对于嵌套子图,从 stream.subgraphs[*].messages 读取消息块。
  • stream.values: 每步后的完整状态快照
  • stream.interrupted / stream.interrupts: 每次运行后,检查图是否暂停;从 stream.interrupts 读取有效载荷
  • Command(resume=...): 传递为下一个 streamEvents 输入以继续;循环直到运行完成且未中断 :::

处理多个中断

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

View example trace

为此示例打开一个公开的 LangSmith 运行。
::: :::js
:::

批准或拒绝

中断最常见的用途之一是在关键操作前暂停并请求批准。例如,您可能需要请求人类批准 API 调用、数据库更改或任何其他重要决策。 :::python
::: :::js
::: :::python 恢复图时,传递 True 批准或 False 拒绝:
::: :::js 恢复图时,传递 true 批准或 false 拒绝:
:::
:::python

View example trace

为此示例打开一个公开的 LangSmith 运行。
::::::js
:::

审阅和编辑状态

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

View example trace

为此示例打开一个公开的 LangSmith 运行。
::::::js
:::

工具中的中断

你也可以直接在工具函数内部放置中断。这会使工具在每次被调用时暂停以等待审批,并允许在执行之前对工具调用进行人工审查和编辑。 首先,定义一个使用 @[interrupt] 的工具: :::python
::: :::js
::: 这种方法在你希望审批逻辑与工具本身共存时很有用,使其可以在图的不同部分重复使用。大型语言模型可以自然地调用该工具,而中断将在工具被调用时暂停执行,允许你审批、编辑或取消操作。
:::python
::::::js
:::

验证人工输入

有时你需要验证来自人类的输入,如果值无效则重新提示。推荐的方法是 interrupt() 每个节点调用一次,从节点返回时将错误消息存储在状态中,并使用 条件边 循环回节点,直到提供有效值。
避免在单个节点内使用 while True + interrupt() 循环。 由于节点在每次恢复时从头开始重新运行(参见 中断规则),调用 interrupt() 多次的循环会导致每次恢复重播所有之前的迭代:第一次恢复重播 1 次迭代,第二次重播 2 次,以此类推。结果是循环体内任何代码的指数级重新执行。
:::python 正确的模式:
  1. 将重新提示的问题存储在状态中(例如 pending_question)。
  2. 在节点中,调用 interrupt() 恰好一次,传递状态中的当前问题。
  3. 如果答案无效,返回更新的 pending_question,以便下次调用重新提示。
  4. 使用 add_conditional_edges 将流程路由回节点,直到收集到有效值。
每个简历(resume)恰好调用一次 get_age_node,执行一次 interrupt() 调用,然后退出。当答案无效时,条件边会回环,下一个中断会使用更新后的问题重新提示。每个简历中没有任何代码会被执行超过一次。 ::: :::js 正确的模式:
  1. 将重新提示的问题存储在状态中(例如 pendingQuestion)。
  2. 在节点中,恰好调用一次 interrupt(),并将状态中的当前问题传递给它。
  3. 如果答案无效,返回更新的 pendingQuestion,以便下一个调用重新提示。
  4. 使用 addConditionalEdges 将流程路由回节点,直到收集到有效值。

View example trace

为此示例打开一个公开的 LangSmith 运行。
每个简历(resume)恰好调用一次 getAgeNode,执行一次 interrupt() 调用,然后退出。当答案无效时,条件边会回环,下一个中断会使用更新后的问题重新提示。 :::
:::python
::::::js
:::

中断(interrupts)的规则

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

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

@[interrupt] 暂停执行的方式是在调用点抛出一个特殊异常。如果你将 @[interrupt] 调用包裹在 try/except 块中,你会捕获这个异常,中断将不会传递回图。
  • ✅ 将 @[interrupt] 调用与易出错的代码分开
  • ✅ 在 try/except 块中使用特定的异常类型
  • 🔴 不要用裸的 try/except 块包裹 @[interrupt] 调用
::: :::js

不要用 try/catch 包裹 interrupt 调用

@[interrupt] 在调用点暂停执行的方式是通过抛出一个特殊异常。如果你用 try/catch 块包裹 @[interrupt] 调用,你会捕获这个异常,中断将不会被传递回图。
  • ✅ 将 @[interrupt] 调用与易出错的代码分离
  • ✅ 如有必要,条件性地捕获错误
  • 🔴 不要用裸的 try/catch 块包裹 @[interrupt] 调用
:::

不要重排节点内的 interrupt 调用

在单个节点中使用多个中断是很常见的,但如果处理不当,可能会导致意外行为。 当节点包含多个中断调用时,LangGraph 会为执行节点的任务维护一个特定的恢复值列表。每次执行恢复时,它都会从节点的开头开始。对于遇到的中断,LangGraph 会检查任务恢复列表中是否存在匹配的值。匹配是严格基于索引的,因此节点内中断调用的顺序很重要。
  • ✅ 在节点执行过程中保持 @[interrupt] 调用一致
:::python
::: :::js
:::
  • 🔴 不要在节点内条件性地跳过 @[interrupt] 调用
  • 🔴 不要用跨执行不确定的逻辑循环 @[interrupt] 调用,包括 while True 验证循环。使用条件边代替(参见 验证人类输入)
:::python
::: :::js
:::

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

根据使用的检查点器,复杂值可能不可序列化(例如,你不能序列化一个函数)。为了使你的图适应任何部署,最佳实践是只使用可以合理序列化的值。
  • ✅ 将简单、JSON 可序列化类型传递给 @[interrupt]
  • ✅ 传递具有简单值的字典/对象
:::python
::: :::js
  • 🔴 不要将函数、类实例或其他复杂对象传递给 @[interrupt]
:::python
::: :::js
:::

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

因为中断是通过重新运行它们被调用的节点来工作的,所以在 @[interrupt] 之前调用的副作用应该(理想情况下)是幂等的。从上下文来看,幂等性意味着相同的操作可以多次应用,而结果不会超出初始执行后的变化。 例如,你可能在节点内部有一个更新记录的 API 调用。如果 @[interrupt] 在该调用之后被调用,当节点恢复时,它将被多次重新运行,可能会覆盖初始更新或创建重复的记录。
  • ✅ 在 @[interrupt] 之前使用幂等操作
  • ✅ 将副作用放在 @[interrupt] 调用之后
  • ✅ 尽可能将副作用分离到单独的节点中
:::python
::: :::js
:::
  • 🔴 在 @[interrupt] 之前不要执行非幂等操作
  • 🔴 在创建新记录之前不要检查它们是否存在
:::python
::: :::js
:::

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

当在节点内调用子图时,父图将从子图被调用并触发 @[interrupt] 的节点的开头恢复执行。同样,子图也将从 @[interrupt] 被调用节点的开头恢复。 :::python
::: :::js
:::

使用中断进行调试

:::python 要调试和测试图,您可以使用静态断点来逐个节点地单步执行图。静态断点在节点执行之前或之后定义的点上被触发。您可以通过在编译图时指定 interrupt_before 和 interrupt_after 来设置这些断点。 ::: :::js 要调试和测试图,您可以使用静态断点来逐个节点地单步执行图。静态断点在节点执行之前或之后定义的点上被触发。您可以通过在编译图时指定 interruptBefore 和 interruptAfter 来设置这些断点。 :::
建议不要在人类参与的工作流程中使用静态断点。请使用 @[interrupt] 函数代替。
:::python
  1. 断点是在 compile 期间设置的。
  2. interrupt_before 指定了在节点执行前应暂停的节点。
  3. interrupt_after 指定了在节点执行后应暂停的节点。
  4. 需要一个检查点器才能启用断点。
  5. 图表运行直到遇到第一个断点。
  6. 通过传递 None 作为输入来恢复图表。这将运行图表直到遇到下一个断点。 ::: :::js
  1. 断点是在 compile 期间设置的。
  2. interruptBefore 指定了在节点执行前应暂停的节点。
  3. interruptAfter 指定了在节点执行后应暂停的节点。
  4. 需要一个检查点器才能启用断点。
  5. 图表运行直到遇到第一个断点。
  6. 通过传递 null 作为输入来恢复图表。这将运行图表直到遇到下一个断点。 :::
要调试您的中断,请使用 LangSmith。

使用 LangSmith Studio

您可以使用 LangSmith Studio 在运行图之前通过 UI 在您的图中设置静态中断。您还可以使用 UI 在执行过程中的任何一点检查图状态。 image
官方原文:src/oss/langgraph/interrupts.mdx · 非官方翻译,译文由 AI 生成,以英文原文为准。
Last modified on November 3, 2026