「智能体开发入门」如何让AI智能体在执行关键操作前,暂停并等待人工审批,确保安全性和可控性。

65 阅读3分钟

在智能体开发的过程中,有一个问题绕不过,就是当需要人操作/审批执行的时候,怎么设计这个流程?

如果要实现这种人机交互的功能,有几个关键要素:

  1. 在AI需要人操作的时候,需要保存当前AI的所有状态。

  2. 让人执行审批

  3. 恢复审批前所有的状态,并根据审批结果继续执行。

就类似Cursor编程,当需要修改代码的时候需要人审批,Chatgpt深度研究模式也需要在执行前审批。

借助Langchain的记忆能力,和智能体编排能力,结合一个中间件(Human-in-the-Loop),可以轻松实现这种人机交互的功能。

下面学习下这种智能体的搭建过程,以及有哪些配置、可以实现哪些能力。

什么是Human-in-the-Loop (HITL)?

Human-in-the-Loop(人在回路中)是一种设计模式,让 AI Agent 在执行关键操作前暂停并等待人工审批,确保安全性和可控性。

核心流程:

使用humanInTheLoopMiddleware中间件开启HITL

源码位置 github.com/langchain-a…

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. 如何在生产环境使用?

  1. 使用持久化的 checkpointer(如 PostgresSaver)

  2. 为每个用户会话生成唯一的 thread_id

  3. 将中断请求发送到队列或数据库

  4. 构建审批界面让管理员处理

  5. 收到审批后使用相同的 thread_id 恢复执行

5. 能否批量审批?

可以。一次中断可能包含多个待审批操作,为每个操作提供决策即可。