问题与目标
创建工单、发送邮件和修改数据库不应该因为模型一次选择就立即执行。Human-in-the-loop 让流程在副作用之前暂停,把拟执行内容交给用户或审核人批准、拒绝或编辑,再从 Checkpoint 恢复。
本篇实现“创建工单前审批”。重点不只是出现确认框,而是审批状态持久化、恢复值校验,以及节点恢复时会从头执行所带来的幂等要求。
核心概念

interrupt(payload) 会暂停当前图,把可序列化 payload 返回给调用者。恢复时使用 Command(resume=value);恢复值成为 interrupt() 的返回值。
Interrupt 依赖 Checkpointer 与稳定 thread_id。节点恢复时会从节点开头重新执行,而不是从源代码某一行继续,因此:
- 把外部副作用放在 Interrupt 之后;
- Interrupt 之前如果必须执行写操作,它必须幂等;
- 不要用普通
try/except吞掉 Interrupt; - 不要在版本升级中随意改变同一节点内 Interrupt 的顺序。
可运行实现
python -m pip install "langgraph>=1,<2"
from typing_extensions import TypedDict
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import END, START, StateGraph
from langgraph.types import Command, interrupt
class State(TypedDict):
device_id: str
reason: str
idempotency_key: str
result: dict
ORDERS: dict[str, dict] = {}
def approve_and_create(state: State) -> dict:
decision = interrupt({
"action": "create_work_order",
"device_id": state["device_id"],
"reason": state["reason"],
"allowed_decisions": ["approve", "reject", "edit"],
})
if decision["type"] == "reject":
return {"result": {"status": "rejected"}}
reason = decision.get("reason", state["reason"])
key = state["idempotency_key"]
if key not in ORDERS:
ORDERS[key] = {"status": "created", "device_id": state["device_id"], "reason": reason}
return {"result": ORDERS[key]}
builder = StateGraph(State)
builder.add_node("approve_and_create", approve_and_create)
builder.add_edge(START, "approve_and_create")
builder.add_edge("approve_and_create", END)
graph = builder.compile(checkpointer=InMemorySaver())
config = {"configurable": {"thread_id": "u-17:task-88"}}
paused = graph.invoke({
"device_id": "AX-3",
"reason": "设备离线",
"idempotency_key": "task-88:create-order",
"result": {},
}, config)
print(paused["__interrupt__"])
finished = graph.invoke(Command(resume={"type": "approve"}), config)
print(finished["result"])
生产审批接口必须验证审核人身份、任务归属、审批权限、任务是否仍处于等待状态,以及编辑后的参数是否重新通过 Schema 和业务校验。
常见问题与排查
在 Interrupt 前创建了工单
恢复时节点重跑会重复创建。把副作用移到批准之后,并使用数据库唯一幂等键作为第二道防线。
前端提交 approve 就直接恢复
服务端重新验证认证主体、审批角色、Thread 所有权和 payload 版本。客户端字段不能代替权限系统。
修改参数后跳过校验
编辑后的收件人、金额、设备或工单原因都视为新输入,重新执行 Schema、权限和业务规则检查。
内存 Checkpointer 等待数小时
进程重启会丢状态。长时间审批使用持久后端,设置过期时间、撤销方式、通知与审计记录。
小结
Interrupt 把高风险动作从模型决策变成可持久的审批流程。真正安全的关键是:副作用后置、恢复值重验、Thread 权限隔离和幂等执行,而不是简单增加一个确认按钮。
License: CC BY-NC 4.0
Updated 2 hours ago
Was this article helpful? Give it a like.
0 comments


