人机交互:interrupt 中断与 Command 恢复
完全自动化虽好,但在「发邮件、转账、删除数据」这类高风险操作前,我们希望 AI 先停下来,等人确认或修改后再继续。
本篇讲持久化落地的第二大能力------人机交互(human-in-the-loop,人在回路中) ,核心是 interrupt(中断) 和 Command(指令恢复)。
目录
- 为什么需要人机交互
- 核心机制:interrupt 中断
- 用 Command 恢复执行
- 黄金法则(重要)
- 四种典型应用模式
- 本篇小结
1. 为什么需要人机交互
LLM 不是 100% 可靠,可能误判、误操作。在关键节点引入人工把关,能大幅降低风险:
- 发邮件前先给人看一眼内容;
- 转账、下单前确认金额和对象;
- 工具参数不确定时让人补全或修正。
人机交互就是让图能在指定位置暂停 ,把控制权交给人,处理完再恢复。
2. 核心机制:interrupt 中断
在节点(或工具)里调用 interrupt(要展示给人的信息),图会在这里立即暂停,并把传入的信息抛给外部等待人工处理:
python
from langgraph.types import interrupt, Command
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, START, END
from typing_extensions import TypedDict
class State(TypedDict):
messages: list
def human_feedback(state: State):
# interrupt:暂停,并把问题抛给人
answer = interrupt("你想继续吗?")
# 恢复后,answer 就是人传回的值
return {"messages": [f"用户的回答是:{answer}"]}
builder = StateGraph(State)
builder.add_node("human_feedback", human_feedback)
builder.add_edge(START, "human_feedback")
builder.add_edge("human_feedback", END)
# 必须配置 checkpointer,中断状态才能保存
graph = builder.compile(checkpointer=InMemorySaver())
第一次 invoke 会在 interrupt 处暂停,返回中断信息:
python
config = {"configurable": {"thread_id": "1"}}
result = graph.invoke({"messages": []}, config)
print(result) # __interrupt__: (Interrupt(value='你想继续吗?', ...))
此时图的状态已被保存,程序可以退出,人可以过很久再回来。
3. 用 Command 恢复执行
人处理完后,用 Command(resume=人的输入) 恢复,interrupt() 的返回值就是这个输入:
python
from langgraph.types import Command
for event in graph.stream(Command(resume="继续"), config=config):
print(event)
也可以用 invoke(Command(resume=...), config)。恢复后图从中断点继续往下走。
4. 黄金法则(重要)
interrupt 有一条必须牢记的行为:
当图恢复执行时,整个节点(或工具)会从头重新执行,
interrupt()这一次直接返回人传入的值,不会再次暂停。
为什么这么设计:暂停期间程序可能已经退出,恢复时 LangGraph 是重跑整个节点,而不是从暂停的那一行「接着跑」。
由此带来的关键坑点:
- interrupt 之前的副作用会被重复执行!
比如节点里在 interrupt 之前先调了一次外部 API、写了一次数据库,恢复时节点重跑,这个 API / 写库会再执行一遍。
正确做法------把 interrupt 放在节点最前面,让有副作用的操作都在 interrupt 之后:
python
def bad_node(state):
send_email() # ❌ 恢复时会再发一次邮件!
confirm = interrupt("确认发送?")
...
def good_node(state):
confirm = interrupt("确认发送?") # ✅ 先暂停
if confirm == "yes":
send_email() # 副作用在中断之后,只执行一次
记忆点:interrupt 尽量放节点开头;中断前不要写有副作用的代码。
5. 四种典型应用模式
5.1 模式一:批准 / 拒绝(Approval)
执行高风险操作前暂停,人选择同意或拒绝:
python
def sensitive_tool_node(state):
decision = interrupt({"action": "删除文件", "file": "data.csv"})
if decision == "approve":
return {"messages": ["已删除"]}
return {"messages": ["已取消"]}
5.2 模式二:编辑状态 / 参数(Edit State)
人在恢复时直接修改要执行的内容或参数:
python
def email_node(state):
draft = "您好,感谢购买..."
edited = interrupt({"draft": draft}) # 人可以改邮件正文
# edited 里带回修改后的内容
send_email(edited)
return {"messages": ["邮件已发送"]}
5.3 模式三:补充输入(Review Inputs)
信息不全时暂停,等人补全后再继续(如补一个地址、选一个选项)。
5.4 模式四:多代理 / 多环节协同中的人工评审
在「评估器-优化器」里,把质量评估这一环交给人:生成内容后 interrupt 暂停,人给评分和修改意见,再带着反馈重新生成。
这正好对应前面工作流模式里说的「人机交互式评估」。
6. 本篇小结
- 人机交互让图在关键步骤暂停、等人处理后恢复,用于高风险操作把关。
- interrupt(信息) 让图暂停并抛出信息;必须配 checkpointer 才能保存中断状态。
- 用 Command(resume=人的输入) 恢复,
interrupt()的返回值即人的输入。 - 黄金法则 :恢复时整个节点从头重跑,interrupt 直接返回值;所以 interrupt 前的副作用会重复执行------把 interrupt 放节点最前面,副作用放后面。
- 四种模式:批准/拒绝、编辑状态/参数、补充输入、人工评审(评估器)。
下一篇讲持久化落地的第三大能力------时间旅行(Time Travel):回到任意历史检查点,修改输入或状态后重新执行,是调试复杂 Agent 的利器。