人工介入 (Human-in-the-loop)
在许多实际的 Agent 应用中,完全自动化并不总是理想的,甚至可能是危险的。你可能需要人工介入来批准关键操作(如转账)、提供 Agent 缺失的信息,或者修正 Agent 的错误推理。LangGraphGo 将"人"视为图中的一等公民。
1. 静态中断 (InterruptBefore)
背景与功能
这是最常见的人工介入模式:在执行某个特定节点之前,总是暂停下来等待批准。这类似于 CI/CD 流水线中的人工审批环节。
实现原理
在编译或运行图时,我们配置 InterruptBefore
列表。当运行时准备执行列表中的节点时,它会检查是否收到了恢复指令。如果没有,它会保存当前状态(Checkpoint)并挂起执行,返回一个特殊的错误。
代码展示
// 1. 设置中断配置
config := &graph.Config{
InterruptBefore: []string{"human_approval_node"},
}
// 2. 运行图
// 当执行到 "human_approval_node" 时,Invoke 调用将返回 GraphInterrupt 错误
res, err := runnable.InvokeWithConfig(ctx, initialState, config)
// 3. 检查中断
var interrupt *graph.GraphInterrupt
if errors.As(err, &interrupt) {
fmt.Println("图已暂停,等待人工审批...")
// 此时可以将 ThreadID 返回给前端,让用户进行操作
}
2. 动态中断 (Dynamic Interrupt)
背景与功能
有时我们无法预知何时需要中断。例如,只有当 Agent 的置信度低于某个阈值,或者检测到潜在的敏感内容时,才需要人工介入。动态中断允许节点在运行时自主决定是否暂停。
实现原理
节点函数可以返回一个 graph.Interrupt 类型的错误。运行时捕获到这个错误后,会立即停止后续执行,并保存当前状态。
代码展示
func sensitiveActionNode(ctx context.Context, state interface{}) (interface{}, error) {
data := state.(MyState)
// 动态检查:如果金额超过 1000,触发中断
if data.Amount > 1000 {
return nil, graph.Interrupt("Amount too high, requesting human review")
}
// 否则直接执行
return executeTransfer(data), nil
}
3. 恢复执行 (Resume)
背景与功能
暂停只是为了更好地继续。在人工完成审批或提供输入后,我们需要恢复图的执行。LangGraphGo 允许你带着新的输入或修改后的状态恢复执行。
实现原理
使用 ResumeFrom 配置或直接调用 Invoke 并传入相同的 ThreadID。框架会加载最新的 Checkpoint。如果在这个
Checkpoint 上有挂起的中断,框架会清除中断标志,并使用可选的新输入继续执行被暂停的节点。
代码展示
// 用户批准了操作
// 我们更新状态(例如设置 Approved = true)
runnable.UpdateState(ctx, config, map[string]interface{}{"approved": true})
// 恢复执行
// 注意:这里不需要再次指定 InterruptBefore,除非你想再次中断
resumeConfig := &graph.Config{
ThreadID: "conversation-123",
}
// 再次调用 Invoke,图会从上次暂停的地方继续
runnable.Invoke(ctx, nil, resumeConfig)