使用 interrupt() 和 Command 暂停与恢复 LangGraph Agent
LangGraph的interrupt()和Command功能如何利用检查点与线程来暂停智能体,以便人类进行审核、修改或输入操作,之后再安全地恢复其运行。
能够转账、删除资源或部署到生产环境的智能体不应总是被允许完成它开始的任务。有时,该流程必须刻意停止,等待数小时等待人工干预,然后再从之前停下的地方继续执行。本文将解释 LangGraph 如何通过 interrupt() 和 Command 实现这一点,为何需要依赖检查点与线程,以及在将人工干预型智能体投入生产环境之前还需要做出哪些决策。
底层构成要素
HITL 由三层结构组成。首先是对图的执行:状态进入一个节点,然后通过边确定下一个节点。
STATE
↓
NODE
↓
EDGE
↓
NEXT NODE
其次是状态管理:各个节点返回更新信息,还原器将这些信息合并为新的状态。
NODE
↓
STATE UPDATE
↓
REDUCER
↓
NEW STATE
第三是持久化:检查点工具会将状态保存为按线程分类的检查点文件。
STATE
↓
CHECKPOINT
↓
CHECKPOINTER
↓
THREAD
↓
PERSISTENCE
这些功能共同使得智能体能够运行、记忆信息,并在重启后继续存活。但目前还缺少一种判断其是否应继续运行的机制。
何时不应让智能体直接继续运行
用户对支付助手说:“将50,000卢比转至这个账户。”智能体会准备该交易,从技术层面来看并没有任何因素阻止其执行。
User
↓
Agent
↓
Prepare Transfer
↓
Transfer ₹50,000
↓
Execute
对于如此重要的操作,应当先由人工审核提案,从而根据人的决策来决定流程走向。
User
↓
Agent
↓
Prepare Transfer
↓
┌─────────────────────┐
│ HUMAN APPROVAL │
└──────────┬──────────┘
│
┌───┴───┐
↓ ↓
Approve Reject
│ │
↓ ↓
Execute Cancel
现在该流程必须主动停止:不是因为出现错误或崩溃,而是因为此处需要人工干预。
“人机协同”意味着什么
HITL将人的决策融入自动化流程中。它不会直接从决策跳到行动,
Agent
↓
Decision
↓
Action
而是让智能体提出建议后暂停,等待人工审核。
Agent
↓
Proposed Action
↓
PAUSE
↓
Human Review
↓
Decision
↓
Continue
审核者可以批准、拒绝该提案,编辑提案内容,补充缺失数据,修正相关组件,或在不同选项中做出选择。这适用于任何会产生实际后果的步骤。
Financial transactions
Sending emails
Deleting resources
Production deployments
Database modifications
Legal workflows
Publishing content
Access approvals
Sensitive tool calls
暂停图表真正需要什么
以一个在最后一个节点之前包含审核步骤的链式结构为例。
Node A
↓
Node B
↓
Human Approval
↓
Node C
如果审核者在六小时后才回复,阻塞流程是错误的做法:这会浪费一个处理资源,并且在首次重启时就会失败。
sleep(21600)
实际上应该做的是保存当前状态并暂停,
Node B
↓
SAVE STATE
↓
PAUSE EXECUTION
↓
之后在另一进程中等合适时机恢复该状态,再从下一个节点继续处理。
Application can stop──────── 6 HOURS LATER ────────Human responds
↓
RESTORE STATE
↓
CONTINUE EXECUTION
↓
Node C
持久化功能已经解决了其中一半问题。检查点、检查点管理器、线程以及thread_id正是长时间暂停所必需的。
Checkpoint
Checkpointer
Thread
thread_id
Persistence
interrupt()如何停止执行
interrupt()会暂停图表并向外部请求一个值。你需要在节点内部调用它,同时传入可JSON序列化的数据,用于指定审核者应看到的内容。尽管原始标签显示为其他语言,但实际上这段代码是Python编写的。
from langgraph.types import interrupt
def approval_node(state):
decision = interrupt({
"question": "Do you approve this transaction?",
"amount": state["amount"]
})
return {
"decision": decision
}
当执行到达该调用时,
interrupt(...)
节点不会执行到返回语句。程序运行会被暂停,而传入的数据会返回给调用方进行显示。
approval_node
│
↓
interrupt()
│
↓
┌─────────────────┐
│ EXECUTION PAUSED│
└─────────────────┘
有两个要点需要注意。interrupt()要求使用带有检查指针编译的图表,因为暂停状态必须存储在某个地方。而在恢复运行时,LangGraph会从顶部重新执行被中断的节点,此时interrupt()会返回用户输入的值,因此调用之前的代码会被执行两次。请避免在节点的该部分引入副作用,并在LangGraph文档中确认你所使用版本的行为表现。
中断时保存了什么
请看这个图表。
START
↓
Prepare Transfer
↓
Human Approval
↓
Execute Transfer
↓
END
准备节点会更新状态,同时会写入一个检查点,随后批准节点会调用interrupt()函数。
START
↓
Prepare Transfer
↓
State Updated
↓
Checkpoint
↓
Approval Node
↓
interrupt()
当前状态会被捕获并存储起来。
Current State
│
↓
Checkpoint
│
↓
Persistent Storage
此时线程已持有传输详情、待处理状态以及停止的原因。
THREAD: transaction-123
Checkpoint
────────────────────
amount ₹50,000
recipient Account X
status pending
────────────────────
Execution:
PAUSED
Reason:
Human approval required
这些就是之后继续执行所需的一切。如果没有持久化存储,进程在中断后就会直接消失。
Agent
↓
interrupt
↓
process disappears
↓
???
当审核人员在第二天标记“已批准”时,没人能知道具体批准了什么、图表在哪个节点停止,以及下一个该处理哪个节点。有了检查点,就可以从线程中加载状态并在恢复执行前将其还原。
INTERRUPT
│
↓
CHECKPOINT
│
↓
THREAD
│
↓
PERSISTENT STORAGE
...time passes...
HUMAN INPUT
│
↓
THREAD
│
↓
LOAD CHECKPOINT
│
↓
RESTORE STATE
│
↓
RESUME
通过命令继续执行
该应用程序将中断内容以问题的形式呈现
Approve transfer of ₹50,000?
并配有两个按钮,
[ APPROVE ] [ REJECT ]
由审核者选择其中一个。
APPROVE
答案会通过Command传回,从而告知图结构接下来的操作。若要继续执行,请使用Command(resume=...)调用图结构,而非常规输入方式。
from langgraph.types import Command
graph.invoke(
Command(resume="approved"),
config=config
)
config必须与暂停时的运行线程相同,
thread_id = transaction-123
因为线程ID是LangGraph识别你所指的哪个暂停执行任务的依据。
生命周期逐步解析
一次运行始于常规调用,
graph.invoke()
从配置中读取对应的线程信息,
thread_id = transaction-123
进入准备节点,
START
↓
Prepare Transfer
并将状态更新为传输后的结果。
amount = ₹50,000
recipient = Account X
status = pending
审批节点会调用interrupt()函数,
Approval Node
↓
interrupt()
从而导致当前执行被暂停。
EXECUTION PAUSED
最新的检查点会安全地保存在对应线程中。
Thread
↓
Checkpoint
↓
Persistent Storage
有人看到了该请求
Approve ₹50,000 transfer?
并给出了回复。
YES
应用程序会向同一个线程发送恢复执行的指令。
Command(resume="approved")
至该线程处。
transaction-123
LangGraph会恢复执行流程,将决策权交还给审批节点,然后继续执行到结束。
Restore Execution
↓
Human Decision = approved
↓
Continue Graph
↓
Execute Transfer
↓
END
以下所有模式都是这种生命周期的不同变体。
非简单的肯定或否定决策
审核人的回复不一定是二元的。
YES / NO
审批节点会执行该提案。
Agent Proposal
↓
Human
↓
APPROVE
↓
Execute
拒绝操作则会取消该提案。
Agent Proposal
↓
Human
↓
REJECT
↓
Cancel
在流程继续之前,可对其进行修改,例如调整金额等参数。
Agent:
Send ₹50,000
↓
Human:
Change to ₹40,000
↓
Graph continues with
updated information
提供缺失的信息会使得中断变成一个待解决的问题。
Agent
↓
Missing Account Number
↓
INTERRUPT
↓
Human provides account
↓
RESUME
选择功能允许代理提供多种选项,由人来做最终决定。
Agent generates:
Option A
Option B
Option C
↓
Human chooses B
↓
Graph continues
HITL是一种将人工输入注入正在运行的图结构中的通用方法,而不仅仅是一个审批对话框。
命令作为数据流与控制流
命令还可以用于更新状态并选择下一个处理目标。
Resume Execution
Update State
Choose Next Destination
单个命令即可设置状态并将执行任务发送到特定节点;在LangGraph中,这种形式通常由节点返回。
Command(
update={
"status": "approved"
},
goto="execute"
)
这把两个相关问题结合在了一起,
DATA FLOW
+
CONTROL FLOW
因为路由功能过去仅属于边。
Edges control where we go.
现在一个对象就可以同时承载状态变更与下一步操作。
Command can participate in
state updates + control decisions.
在继续之前修正状态
假设状态保存着这个数值,
amount = 50000
然后审核者通过修正来批准它。
Approve, but change amount to 40000.
流程从初始值开始,经过审核后变为更新后的值,之后才继续执行(第二段代码被合并到同一行中)。
BEFORE
amount = 50000 ↓Human Review ↓UPDATEamount = 40000 ↓RESUME
问题从“我可以继续吗?”变成了“先检查,如有必要则修复,然后再继续。”
控制敏感工具的调用
HITL在处理高风险工具时最为实用。
search_web()
read_database()
send_email()
delete_resource()
transfer_money()
网络搜索可以自动运行,
search_web()
↓
Execute Automatically
而删除资源则需等待批准。
delete_resource()
↓
INTERRUPT
↓
Human Approval
每次工具调用都会经过敏感度检查:安全的调用会继续执行,而敏感的调用则会暂停以等待批准或取消。
Agent
│
↓
Tool Call
│
↓
Sensitive Tool?
/ \
NO YES
│ │
↓ ↓
Execute INTERRUPT
│ │
│ ↓
│ Human
│ / \
│ Approve Reject
│ │ │
└─────────┘ ↓
Cancel
如需了解基于存储系统的完整实现方式,请参阅LangGraph中的审批控制智能体。
将审核视为有意的控制边界
人工审核并非为了解决智能体不够智能的问题,而是人为设定的边界。低风险任务会自动执行,
LOW RISK
Search
Read
Summarize
↓
Automatic
而高风险任务则需要由人工处理。
HIGH RISKSend
Delete
Purchase
Transfer
Deploy
↓
Human Approval
其目的是确定在何处适用自主决策,而非处处削减自主性。
处理拒绝与沉默状态
对于拒绝情况,需要专门的设计方案。
Agent
↓
Prepare Action
↓
Interrupt
↓
Human
↓
REJECT
将处理结果记录在状态中,
status = "rejected"
然后通过条件边根据该结果进行路由。
Decision
/ \
approved rejected
│ │
↓ ↓
Execute Cancel
│ │
↓ ↓
END END
所有先前的概念都会参与其中,而人工输入只是用于影响执行过程的额外信息。
State
Reducers
Conditional Routing
Checkpoints
Threads
Commands
审核者也可能永远不回复。
Interrupt
↓
Waiting for approval
↓
1 hour
↓
1 day
↓
7 days
被暂停的线程可能会无限期等待,因此应用程序需要相应的策略。
Expiration
Timeout
Cancellation
Reminder
Escalation
常见的一种方法是设置时间限制的等待,通常由调度器通过超时值来恢复那些已停滞的线程。
Approval Requested
│
↓
Wait up to 24 hours
│
┌───┴────┐
↓ ↓
Response Timeout
│ │
↓ ↓
Resume Cancel
LangGraph提供基础功能,业务规则由用户自行定义。
多个关卡与流式处理
部署代理可能会在计划提交前以及上线前再次请求批准。
START
↓
Analyze Code
↓
Generate Deployment Plan
↓
Human Approval #1
↓
Deploy to Staging
↓
Run Tests
↓
Human Approval #2
↓
Deploy Production
↓
END
每个关卡都会暂停并恢复同一个线程,从而留下检查点与中断的历史记录。
Thread
│
├── CP1
│
├── CP2
│
├── INTERRUPT
│
├── RESUME
│
├── CP3
│
├── INTERRUPT
│
├── RESUME
│
└── END
这是一种可供人和代理共享的持久化工作流。到目前为止,所有示例都使用了阻塞式调用,
graph.invoke(...)
这类调用只有在运行结束或被暂停时才会返回。
START → A → B → C → END
↓
RESULT
交互式应用需要实时显示处理进度,直至审批请求完成。
START
↓
Planner Update ─────────→ UI
↓
Tool Call ──────────────→ UI
↓
Tool Result ────────────→ UI
↓
Approval Required ──────→ UI
流式处理能够传递这些事件,从而使用户界面能够展示已完成的步骤以及待处理的决策。
┌──────────────────────────────────┐
│ Deployment Agent │
├──────────────────────────────────┤
│ │
│ ✓ Analyzed repository │
│ │
│ ✓ Generated deployment plan │
│ │
│ ✓ Staging tests passed │
│ │
│ ⚠ Production deployment ready │
│ │
│ Human approval required │
│ │
│ [ APPROVE ] [ REJECT ] │
│ │
└──────────────────────────────────┘
在后台,事件会被传递到前端,而决策结果则通过Command(resume=...)返回。
LangGraph
│
↓
Streaming
│
↓
Frontend
│
↓
Human Decision
│
↓
Command(resume=...)
│
↓
LangGraph
完整的执行模型
在一个流程图中:用户在一个线程上触发该流程,状态在节点和还原器之间流转,会保存检查点,路由机制选择下一步操作,而敏感操作则会触发中断,由人工进行处理。
USER
│
↓
graph.invoke()
│
↓
THREAD
│
↓
CHECKPOINTER
│
↓
STATE
│
↓
NODE
│
↓
STATE UPDATE
│
↓
REDUCER
│
↓
NEW STATE
│
↓
CHECKPOINT
│
↓
EDGE / ROUTING
│
↓
SENSITIVE ACTION?
/ \
NO YES
│ │
↓ ↓
NEXT NODE interrupt()
│
↓
PAUSED
│
↓
HUMAN
/ | \
/ | \
Approve Edit Reject
│ │ │
└────────┼────────┘
↓
Command(resume)
│
↓
SAME thread_id
│
↓
RESTORE CHECKPOINT
│
↓
CONTINUE GRAPH
│
↓
END
从整体流程来看,首先会有一个请求到达。
User Input
随后执行便开始起来。
START
节点读取状态并返回更新结果,还原器将它们合并,检查点在特定线程下保存这些数据,而边则决定下一步该执行什么操作。该图结构可能分支、循环或调用各种工具。
BRANCH
LOOP
CALL TOOLS
当需要人工干预时,中断会暂停当前运行过程。
interrupt()
操作人员会进行响应,
APPROVE
REJECT
EDIT
PROVIDE INPUT
恢复命令会从之前保存的线程继续执行,
Command(resume=...)
随后整个图结构便完成运行。
END
大多数项目都是从一次模型调用开始的,
LLM
↓
Response
最终形成一个整合了大语言模型节点、工具节点、还原器、检查点、路由机制、中断处理以及各种命令的图结构。
USER
│
↓
START
│
↓
STATE
│
↓
NODE
│
┌──────┴──────┐
│ │
LLM TOOL
│ │
└──────┬──────┘
↓
STATE UPDATE
│
↓
REDUCER
│
↓
NEW STATE
│
↓
CHECKPOINT
│
↓
THREAD
│
↓
ROUTING
/ | \
/ | \
NODE LOOP INTERRUPT
│
↓
HUMAN
/ | \
Approve Edit Reject
\ | /
\ | /
↓
COMMAND
│
↓
RESUME
│
↓
NEXT NODE
│
↓
END
生产环境检查清单
保持节点功能专注
小型且单一功能的节点更易于测试、重试以及安全地中断,
Plan Node
Search Node
Validate Node
Execute Node
与那些承担所有功能的单一节点不同。
do_everything_agent()
精心设计状态与还原器
仅保留节点所需的元素,因为所有内容都会被保存为检查点并可能展示给审核人员。对于会持续积累数据的字段,需谨慎选择还原器。
messages
events
results
observations
确保副作用的可重试性
不可逆的操作需要特别处理。
Send Email
Charge Card
Delete Resource
Deploy
Transfer Money
重试和恢复时绝不能重复执行这些操作。由于被中断的节点在恢复时会重新启动,因此应将不可逆的调用放在审批点之后或独立的节点中,并在 API 支持的情况下使用可重试性键。
采用持久化存储
内存中的检查点工具适合学习使用;生产环境则需要符合可靠性要求的持久化存储方案。
明确界定边界与限制
需为每个工作流明确指定这些界限,
What can AI do automatically?
What requires approval?
What can humans modify?
When does approval expire?
并为循环设定终止条件。
Maximum attempts
Timeouts
Validation
Failure paths
Escalation
何时使用HITL
在特定情况下,人工审核能带来显著效果。
Cost of wrong action is high
Action is irreversible
Policy requires approval
Agent confidence is insufficient
Sensitive information is involved
Human judgment adds value
如果到处都使用人工审核,就会削弱自动化优势,还会让审核人员养成不阅读就直接点击的习惯。
Agent
↓
Ask Human
↓
Agent
↓
Ask Human
↓
Agent
↓
Ask Human
更好的设计应为智能体提供用于规划和推理的独立区域,并在采取行动前设置一个边界。
AUTONOMOUS ZONE
┌───────────────────────┐
│ │
│ Plan → Search → Think │
│ │
└───────────┬───────────┘
│
↓
CONTROL BOUNDARY
│
↓
HUMAN
│
↓
APPROVED ACTION
各层次之间的关联
图执行涉及节点、边以及路由机制;
Graph
Nodes
Edges
START
END
Loops
Conditional Routing
状态管理涉及更新操作和还原函数;
State
State Updates
Reducers
Messages
State Evolution
持久化功能涉及检查点、线程以及恢复机制;
Checkpoints
Checkpointer
Threads
thread_id
State History
Recovery
而HITL则负责处理中断、指令以及审核模式。
interrupt()
Command
Pause
Resume
Approve
Reject
Edit
Human Control
这些层次叠加在一起就构成了一个完整的处理流程。
GRAPH
↓
STATE
↓
NODE
↓
UPDATE
↓
REDUCER
↓
CHECKPOINT
↓
THREAD
↓
ROUTING
↓
INTERRUPT
↓
HUMAN
↓
COMMAND
↓
RESUME
↓
END
核心要点
相关术语可能看起来相当复杂,
StateGraph
MessagesState
Reducers
Checkpointers
Threads
Commands
Interrupts
但每个组件都只为实现单一目标的一部分服务:即让智能体能够感知环境、采取行动、更新状态、做出决策、保存进度,在适当时停止运行,向人类请求帮助后安全恢复。
Remember what it knows
↓
Perform some work
↓
Update what it knows
↓
Decide what to do next
↓
Persist its progress
↓
Pause when necessary
↓
Ask a human
↓
Resume safely
↓
Continue until complete
- 状态是智能体所知晓的信息,节点代表其执行的行为,边表示其移动的方向,而 reducer 则定义状态如何变化。
- 检查点用于记录智能体的当前位置;线程则标识某个历史记录属于哪条线程。
interrupt()用于标记停止并请求帮助的位置;Command则规定如何在同一线程上继续执行。- 被中断的节点在恢复时会重新启动,因此需要在确认点之后避免出现不可逆的副作用。
- 应在真正的控制边界处设置审核机制,并明确超时与升级规则。