在智能体开发的过程中,有一个问题绕不过,就是当需要人操作/审批执行的时候,怎么设计这个流程?
如果要实现这种人机交互的功能,有几个关键要素:
-
在AI需要人操作的时候,需要保存当前AI的所有状态。
-
让人执行审批
-
恢复审批前所有的状态,并根据审批结果继续执行。
就类似Cursor编程,当需要修改代码的时候需要人审批,Chatgpt深度研究模式也需要在执行前审批。
借助Langchain的记忆能力,和智能体编排能力,结合一个中间件(Human-in-the-Loop),可以轻松实现这种人机交互的功能。
下面学习下这种智能体的搭建过程,以及有哪些配置、可以实现哪些能力。
什么是Human-in-the-Loop (HITL)?
Human-in-the-Loop(人在回路中)是一种设计模式,让 AI Agent 在执行关键操作前暂停并等待人工审批,确保安全性和可控性。
核心流程:
使用humanInTheLoopMiddleware中间件开启HITL
humanInTheLoopMiddleware 接受一个配置对象,包含以下参数:
humanInTheLoopMiddleware({
interruptOn?: Record<string, boolean | InterruptOnConfig>,
descriptionPrefix?: string
})
interruptOn
作用: 配置哪些工具需要人工审批,以及审批的规则
默认值: undefined(所有工具自动批准)
值类型:支持布尔和
interruptOn: {
// false 不需要审批
"read_file": false,
// true 需要审批,允许所有决策类型
"edit_file": true,
// 复杂配置案例
"write_file": {
// (必需)审批允许的决策类型
// - `"approve"`: 批准执行原始操作
// - `"edit"`: 修改参数后执行
// - `"reject"`: 拒绝执行
allowedDecisions: ["approve", "edit"],
// (可选) 审批提示 string | DescriptionFactory
description: "⚠️ 文件写入需要审批",
// description: (toolCall, state, runtime) => {
// const { filename, content } = toolCall.args;
// return `准备写入文件: ${filename}`;
// }
// (可选)当允许 `"edit"` 决策时,用于验证修改后的参数
argsSchema: { /* JSON Schema */ }
}
}
descriptionPrefix
默认的审批提示前缀,用于没有自定义 description 的工具。
如果工具配置了 description,则使用工具的描述,忽略此前缀
示例
1. 定义工具
2. 配置 HITL Middleware
3. 创建 Agent
4. 调用
进阶技巧
动态审批提示
根据toolCall获取其他参数,返回不同的提示内容。
humanInTheLoopMiddleware({
interruptOn: {
"send_email": {
allowedDecisions: ["approve", "edit", "reject"], // 动态生成提示
description: (toolCall, state, runtime) => {
const { to, subject } = toolCall.args;
return 📧 发送邮件\n收件人: ${to}\n主题: ${subject}\n\n请确认后批准;
}
}
}
})
条件审批
通过description以及toolCall的参数args,返回的结果不同提示文本,代码再根据description进行不同处理。
humanInTheLoopMiddleware({
interruptOn: {
"transfer_money": {
allowedDecisions: ["approve", "reject"],
description: (toolCall) => {
const { amount } = toolCall.args;
if (amount > 10000) {
return "🚨 大额转账!需要经理审批";
}
return "💰 转账操作需要审批";
}
}
}
})
常见问题
1. 为什么需要 checkpointer?
HITL 使用 LangGraph 的中断机制,需要保存中断时的状态。没有 checkpointer,Agent 无法在恢复时找到之前的执行上下文。
2. thread_id 必须一致吗?
是的。初始调用和恢复调用必须使用相同的 thread_id,否则 Agent 找不到对应的中断状态。
3. 可以跳过某些工具的审批吗?
可以。设置 `interruptOn: { "tool_name": false }` 即可自动允许该工具。
4. 如何在生产环境使用?
-
使用持久化的 checkpointer(如 PostgresSaver)
-
为每个用户会话生成唯一的 thread_id
-
将中断请求发送到队列或数据库
-
构建审批界面让管理员处理
-
收到审批后使用相同的 thread_id 恢复执行
5. 能否批量审批?
可以。一次中断可能包含多个待审批操作,为每个操作提供决策即可。