第 6 章 · 人工审批、中断与恢复
本章实现一个不会发送任何真实邮件的审批流。先展示草稿,再显式批准或拒绝,最后记录结果;模型不能替人决定是否批准。
工作机制与设计选择
interrupt() 暂停图并保存待恢复状态,需要 checkpointer 与 thread_id。恢复使用相同线程的 Command(resume=...)。被中断的节点从头重新执行,不是从 Python 源码下一行继续;因此中断前的副作用可能重复。把外部写操作放在审批之后,并用业务幂等键保护。
示例
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class State(TypedDict):
draft: str
status: str
def review(state: State):
approved = interrupt({"draft": state["draft"], "question": "Approve this draft?"})
return {"status": "approved" if approved is True else "rejected"}
builder = StateGraph(State)
builder.add_node("review", review)
builder.add_edge(START, "review")
builder.add_edge("review", END)
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "draft-001"}}
paused = graph.invoke({"draft": "Welcome to the course!", "status": "pending"}, config)
print(paused["__interrupt__"][0].value)
answer = input("Type APPROVE to approve; anything else rejects: ")
result = graph.invoke(Command(resume=answer == "APPROVE"), config)
assert result["status"] in {"approved", "rejected"}
print(result["status"])运行与验收
分别运行两次:第一次输入 APPROVE,第二次输入其他文本。两条路径都应结束。本例默认返回格式从 interrupt 读取暂停;如果显式设置 version="v2",则应使用 result.interrupts 和 result.value,不要混用。
容易踩的坑
不要用宽泛 try/except 吞掉 interrupt 的控制信号,也不要把第一次 invoke 误当作完成。恢复时新建 thread_id 会找不到原暂停点。
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. 恢复应该使用哪个线程?
2. 恢复时节点从哪里开始?
3. 需要什么才能暂停恢复?
4. 审批超时可以默认批准吗?
动手实践
- 把审批值改为包含备注的 JSON 字典,并验证字段。
- 中断前打印一条日志,观察恢复时是否重复。
- 设计审批超时后的拒绝策略,不自动批准。
官方参考
核验日期:2026-09-17。