前面我们已经解决了:
- Graph 如何组织 Agent 流程
- State 如何在节点之间流转
- Conditional Edge 如何动态选择路径
- Checkpointer 如何保存 State
但是实际 Agent 还有一个重要问题:
不是所有事情都应该让 Agent 自动完成。
有些操作需要人确认。
例如:
- 转账
- 删除文件
- 发布文章
- 发送邮件
- 修改数据库
这时候就需要:
Human-in-the-loop(人在循环中)
1. 为什么 Agent 需要人工介入?
假设一个自动 Agent:
用户:
帮我给张三转账100元
Agent:
调用转账工具
完成
如果 Agent 判断错误:
转错账户
转错金额
后果很严重。
所以更合理:
用户
↓
Agent准备执行
↓
暂停
↓
用户确认
↓
继续执行
2. Interrupt 是什么?
LangGraph 提供:
scss
interrupt()
它的作用:
暂停当前 Graph 执行,并等待外部输入。
例如:
ini
const awaitConfirm = (state)=>{
const input = interrupt({
action:"向张三转账100元"
});
}
执行:
scss
Node
↓
interrupt()
↓
暂停
3. Interrupt 发生了什么?
注意:
interrupt 不是普通函数返回。
它不会:
kotlin
return "等待"
而是:
暂停 Graph
此时:
diff
当前 State
+
当前执行位置
需要保存。
所以:
interrupt
必须依赖
checkpointer
4. 一个转账 Demo
State:
javascript
const StateAnnotation = Annotation.Root({
actionSummary:Annotation({
reducer:(prev,next)=>next,
default:()=> ""
}),
userInput:Annotation({
reducer:(prev,next)=>next,
default:()=> ""
})
});
保存两个数据:
actionSummary
用户要确认的动作
userInput
用户输入
第一个节点
dart
const showTransfer = ()=>({
actionSummary:
"向张三转账 $100"
});
执行:
State:
bash
{
actionSummary:
"向张三转账 $100"
}
第二个节点
arduino
const awaitConfirm=(state)=>{
const text=interrupt({
actionSummary:
state.actionSummary
});
return {
userInput:text
}
}
读取:
state.actionSummary
得到:
bash
向张三转账 $100
然后:
scss
interrupt()
暂停。
5. Graph 暂停之后发生什么?
第一次:
arduino
await graph.invoke({},config)
执行:
sql
START
↓
showTransfer
↓
awaitConfirm
↓
interrupt暂停
返回:
类似:
css
{
__interrupt__:[
{
value:{
actionSummary:
"向张三转账 $100"
}
}
]
}
此时:
Agent 等待用户。
例如:
终端:
markdown
是否确认?
> 确认
6. 如何恢复?
恢复需要:
Command
例如:
arduino
await graph.invoke(
new Command({
resume:"确认"
}),
config
);
含义:
告诉 LangGraph:
上次暂停的位置继续执行,并把这个值交给 interrupt。
恢复后:
原来的:
scss
const text = interrupt(...)
会得到:
ini
text="确认"
然后继续:
kotlin
return {
userInput:"确认"
}
最终:
bash
{
actionSummary:
"向张三转账 $100",
userInput:
"确认"
}
流程结束。
7. 为什么一定需要 thread_id?
因为可能有很多用户同时暂停:
例如:
用户A:
等待确认转账
用户B:
等待确认删除文件
LangGraph 必须知道:
恢复哪个任务。
所以:
css
{
configurable:{
thread_id:"user-a"
}
}
表示:
恢复 user-a 的那一次执行。
8. Interrupt 完整流程
scss
用户请求
↓
Agent执行
↓
准备执行危险操作
↓
interrupt()
↓
保存 State + 执行位置
↓
等待用户
↓
Command(resume)
↓
恢复 Graph
↓
继续执行
↓
结束
9. Interrupt 和普通函数暂停的区别
普通代码:
javascript
function test(){
console.log("开始");
await input();
console.log("继续");
}
如果程序关闭:
状态没了。
LangGraph:
interrupt
↓
checkpointer保存现场
↓
之后恢复
它保存的是:
- State
- 当前节点
- 执行路径
所以它适合长流程 Agent。
10. LangGraph 到这里形成完整 Agent 工作流
现在完整模型:
vbnet
State
↓
START
↓
Node
↓
Edge
↓
Conditional Edge
↓
Loop
↓
Checkpointer
↓
Interrupt
↓
Resume
↓
END
总结
一句话:
Interrupt 让 LangGraph 支持人工介入,它可以暂停 Agent 工作流,保存当前状态,等待用户输入后继续执行,是构建安全可靠 Agent 系统的重要能力。