Skip to main content
要审查、编辑并批准代理或工作流中的工具调用,请使用中断功能暂停图的执行并等待人工输入。中断功能利用 LangGraph 的持久化层保存图状态,从而无限期暂停图的执行,直到您恢复运行。 image

使用 interrupt 暂停

动态中断(也称为动态断点)根据图的当前状态触发。您可以通过在适当位置调用 interrupt 函数 来设置动态中断。图将暂停以允许人工干预,然后根据人工输入继续执行。这适用于审批、编辑或收集额外上下文等任务。
自 v1.0 起,interrupt 是暂停图的推荐方式。NodeInterrupt 已弃用,并将在 v2.0 中移除。
要在图中使用 interrupt,您需要:
  1. 指定一个检查点器,以便在每一步后保存图的状态。
  2. 在适当位置调用 interrupt()。请参阅常见模式部分获取示例。
  3. 使用线程 ID 运行图,直到触发 interrupt
  4. 使用 invoke/stream 恢复执行(参见Command 原语)。
  1. interrupt(...)human_node 处暂停执行,向人工展示给定的有效载荷。
  2. 可向 interrupt 函数传递任何 JSON 可序列化的值。此处为包含待修订文本的字典。
  3. 恢复后,interrupt(...) 的返回值即为人工提供的输入,用于更新状态。
  4. 需要检查点器来持久化图状态。在生产环境中,应使用持久化存储(如数据库支持)。
  5. 图以某些初始状态被调用。
  6. 当图触发中断时,会返回包含有效载荷和元数据的 Interrupt 对象。
  7. 图通过 Command(resume=...) 恢复,注入人工输入并继续执行。
  1. interrupt(...)human_node 处暂停执行,向人工展示给定的有效载荷。
  2. 可向 interrupt 函数传递任何 JSON 可序列化的值。此处为包含待修订文本的字典。
  3. 恢复后,interrupt(...) 的返回值即为人工提供的输入,用于更新状态。
  4. 需要检查点器来持久化图状态。在生产环境中,应使用持久化存储(如数据库支持)。
  5. 图以某些初始状态被调用。
  6. 当图触发中断时,会返回包含有效载荷和元数据的 Interrupt 对象。
  7. 图通过 Command(resume=...) 恢复,注入人工输入并继续执行。
从开发者体验角度看,中断类似于 Python 的 input() 函数,但它们不会自动从中断点恢复执行。相反,它们会重新运行使用中断的整个节点。因此,中断通常最好放置在节点开头或专用节点中。

使用 Command 原语恢复

interrupt 恢复不同于 Python 的 input() 函数,后者会从调用 input() 函数的确切位置恢复执行。
当在图内使用 interrupt 函数时,执行会在该点暂停并等待用户输入。 要恢复执行,请使用 Command 原语,可通过 invokestream 方法提供。图将从最初调用 interrupt(...) 的节点开头恢复执行。此时,interrupt 函数将返回 Command(resume=value) 中提供的值,而不再次暂停。从节点开头到 interrupt 的所有代码都将重新执行。

一次调用恢复多个中断

当具有中断条件的节点并行运行时,任务队列中可能出现多个中断。 例如,以下图有两个需要人工输入的节点并行运行: two nodes run in parallel that require human input 一旦您的图被中断并停滞,您可以使用 Command.resume 一次性恢复所有中断,传入一个将中断 ID 映射到恢复值的字典。

常见模式

您可以使用 interruptCommand 实现四种典型设计模式:
  • 批准或拒绝:在关键步骤(如 API 调用)前暂停图,以审查并批准操作。如果操作被拒绝,您可以阻止图执行该步骤,并可能采取替代操作。此模式通常涉及根据人工输入路由图。
  • 编辑图状态:暂停图以审查并编辑图状态。这对于纠正错误或使用附加信息更新状态非常有用。此模式通常涉及使用人工输入更新状态。
  • 审查工具调用:暂停图以在工具执行前审查并编辑 LLM 请求的工具调用。
  • 验证人工输入:暂停图以在进行下一步之前验证人工输入。
下面展示了可以使用 interruptCommand 实现的不同设计模式。

批准或拒绝

Depending on the human’s approval or rejection, the graph can proceed with the action or take an alternative path 在关键步骤(如 API 调用)前暂停图,以审查并批准操作。如果操作被拒绝,您可以阻止图执行该步骤,并可能采取替代操作。

审查和编辑状态

A human can review and edit the state of the graph. This is useful for correcting mistakes or updating the state with additional information

审查工具调用

A human can review and edit the output from the LLM before proceeding. This is particularly critical in applications where the tool calls requested by the LLM may be sensitive or require human oversight. 要为工具添加人工批准步骤:
  1. 在工具中使用 interrupt() 暂停执行。
  2. 使用 Command 根据人工输入继续执行。
  1. interrupt 函数 在特定节点处暂停代理图。在此情况下,我们在工具函数开头调用 interrupt(),这会在执行工具的节点处暂停图。interrupt() 内的信息(如工具调用)可呈现给人类,图可根据用户输入(工具调用批准、编辑或反馈)恢复。
  2. InMemorySaver 用于在工具调用循环的每一步存储代理状态。这启用了短期记忆人在回路功能。在此示例中,我们使用 InMemorySaver 将代理状态存储在内存中。在生产应用中,代理状态将存储在数据库中。
  3. 使用 checkpointer 初始化代理。
使用 stream() 方法运行代理,传入 config 对象以指定线程 ID。这允许代理在未来的调用中恢复相同的对话。
您应该看到代理运行直到达到 interrupt() 调用,此时它会暂停并等待人工输入。
使用 Command 恢复代理以根据人工输入继续执行。
  1. interrupt 函数Command 对象结合使用,以人工提供的值恢复图。
  2. @interrupt 函数与 @Command 对象结合使用,以便用人类提供的值恢复图的执行。 :::

为任意工具添加中断

你可以创建一个包装器,为任意工具添加中断功能。下面的示例提供了一个与 Agent Inbox UIAgent Chat UI 兼容的参考实现。
为任意工具添加人在回路功能的包装器
  1. 此包装器创建一个新工具,在执行被包装的工具之前调用 interrupt()
  2. interrupt() 使用 Agent Inbox UI 期望的特殊输入和输出格式: - 将 [HumanInterrupt] 对象列表发送给 AgentInbox,向最终用户渲染中断信息 - AgentInbox 提供的恢复值是一个列表(即 Command(resume=[...])
你可以使用此包装器为任何工具添加 interrupt(),而无需在工具内部添加它:
  1. add_human_in_the_loop 包装器用于为工具添加 interrupt()。这允许代理在继续执行工具调用之前暂停并等待人类输入。
你应该会看到代理运行直到它到达 interrupt() 调用, 此时它会暂停并等待人类输入。
使用 Command 恢复代理以根据人类输入继续执行。

验证人类输入

如果你需要在图本身内验证人类提供的输入(而不是在客户端),可以通过在一个节点内使用多个中断调用来实现。

使用中断进行调试

要调试和测试图,请使用静态中断(也称为静态断点)逐步执行图,一次一个节点,或在特定节点暂停图的执行。静态中断在定义的点触发,要么在节点执行之前,要么在节点执行之后。你可以在编译时或运行时通过指定 interrupt_beforeinterrupt_after 来设置静态中断。
不建议将静态中断用于人在回路工作流。请改用动态中断
  1. 断点在 compile 时设置。
  2. interrupt_before 指定应在执行节点之前暂停执行的节点。
  3. interrupt_after 指定应在执行节点之后暂停执行的节点。
  4. 必须启用检查点才能使用断点。
  5. 图运行直到命中第一个断点。
  6. 通过传入 None 作为输入来恢复图。这将运行图直到命中下一个断点。

在 LangGraph Studio 中使用静态中断

你可以使用 LangGraph Studio 来调试你的图。你可以在 UI 中设置静态断点,然后运行图。你也可以使用 UI 在执行的任何时刻检查图的状态。 image 使用 langgraph dev 本地部署的应用程序,LangGraph Studio 是免费的。

注意事项

使用人在回路时,有一些注意事项需要牢记。

与有副作用的代码一起使用

将具有副作用的代码(例如 API 调用)放在 interrupt 之后或放在单独的节点中,以避免重复执行,因为这些代码在节点每次恢复时都会重新触发。

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

当作为函数调用子图时,父图将从调用子图的节点开头恢复执行,该节点是触发 interrupt 的地方。同样,子图将从调用 interrupt() 函数的节点开头恢复执行。
假设我们有一个包含 3 个节点的父图:父图: node_1node_2 (子图调用) → node_3而子图有 3 个节点,其中第二个节点包含一个 interrupt子图: sub_node_1sub_node_2 (interrupt) → sub_node_3当恢复图时,执行将按以下顺序进行:
  1. 跳过父图中的 node_1(已执行,图状态已保存在快照中)。
  2. 从头开始重新执行父图中的 node_2
  3. 跳过子图中的 sub_node_1(已执行,图状态已保存在快照中)。
  4. 从头开始重新执行子图中的 sub_node_2
  5. 继续执行 sub_node_3 及后续节点。
下面是一个简化的示例代码,你可以用它来理解子图如何与中断一起工作。 它计算每个节点被进入的次数并打印计数。
这将打印出

在单个节点中使用多个中断

单个节点中使用多个中断对于像验证人类输入这样的模式很有帮助。但是,如果不小心处理,在同一个节点中使用多个中断可能会导致意外行为。 当一个节点包含多个中断调用时,LangGraph 会为执行该节点的任务保留一个恢复值列表。每当执行恢复时,它都会从节点的开头开始。对于遇到的每个中断,LangGraph 会检查任务的恢复列表中是否存在匹配的值。匹配是严格基于索引的,因此节点内中断调用的顺序至关重要。 为避免问题,请避免在执行之间动态更改节点的结构。这包括添加、删除或重新排序中断调用,因为此类更改可能导致索引不匹配。这些问题通常源于非常规模式,例如通过 Command(resume=..., update=SOME_STATE_MUTATION) 突变状态或依赖全局变量来动态修改节点结构。