前言
因为业务涉及很多资产和金额相关的操作,如果把所有能力直接开放给Agent,很容易造成资损,所以涉及高风险的操作,都必须需要人来确认,这个确认要求,不是Agent自己发起,而是由Harness层做的限制。
一、背景
1.1 什么是 HITL?
在基于大语言模型的 Agent 系统中,模型的行为由推理动态决定而非程序员显式编码。这意味着:
- Agent 可能自主发起批量删除 、不可逆变更 、高金额操作等高风险调用
- 纯自动化的"放行/拦截"二态决策无法覆盖合理但需确认的中间地带
- 合规要求关键操作必须有人工审计轨迹
HITL(Human-In-The-Loop)填补了这一空白:它不是简单阻断,而是暂停 → 通知 → 等待人工决策 → 恢复/终止的可控协同机制。
1.2 在安全体系中的位置
Harness 安全框架采用四层纵深防御,HITL 是 L3 OperationGuard 的四种处置动作之一:
| 处置动作 | 语义 | 是否阻断 | 典型场景 |
|---|---|---|---|
| PASS | 无条件放行 | ❌ | 低风险只读操作 |
| WARN | 记录警告日志,不阻断 | ❌ | 中风险但可逆操作 |
| HITL | ⭐ 暂停执行,等待人工审批 | ⏸️ 暂停 | 高风险 / 不可逆 / 需合规审批 |
| BLOCK | 硬性阻断,返回错误 | ✅ | 恶意命令 / 越权操作 |
二、架构总览
2.1 组件职责划分
2.2 核心数据流

三、HITL 触发场景详解
场景 1:风险评分触发(主路径)
触发机制:L3 OperationGuard 采用多维度加权风险评分模型(0-100 分),当评分达到阈值时触发 HITL:
| 维度 | 权重 | 说明 |
|---|---|---|
| 操作类型风险 | 40% | DELETE/PURGE 比 QUERY 风险更高 |
| 影响范围 | 30% | 批量操作比单条操作风险更高 |
| 可逆性 | 20% | 不可逆操作无论评分高低均触发 HITL |
| 数据敏感度 | 10% | 涉及敏感数据的操作加分 |
阈值动态调整:根据 Skill 风险等级(LOW/MEDIUM/HIGH)动态降低 HITL 触发阈值,HIGH 等级 Skill 仅需 40 分即触发。
场景 2:前端敏感操作触发
触发机制 :LLM 通过 click 工具调用前端 UI 操作时,系统将目标元素 ID 映射到对应的 配置,检查其 sensitive 标记。支持两个危险等级:
- 🔴 danger:发布、删除等不可逆操作 → 强制人工确认
- ⚠️ soft_confirm:保存草稿等可逆操作 → 轻量提示确认
场景 3:沙箱危险命令触发
触发机制 :代码沙箱执行环境检测到 warn 级危险命令(非 block 级但需确认)时抛出 WarnCommandError,由 ToolExecutor 统一捕获并转入 HITL 流程。典型场景包括:访问非白名单域名、执行潜在破坏性命令等。
场景 4:规则引擎配置触发
触发机制:运营人员通过管理 API 配置通用业务规则(generic 类型),当工具调用的参数匹配规则条件且 action 配置为 HITL 时触发。支持两种审批子模式:
| 子模式 | approvalType | 交互流程 | 适用场景 |
|---|---|---|---|
| simple(默认) | "simple" 或不填 |
创建 Token → 前端弹框 → 用户 WS 确认 → 恢复 | 操作人即审批人,快速确认 |
| standard | "standard" |
发送 IM 通知给指定审批人列表 → 返回 WAITING 状态 → 审批页回调 → 恢复/终止 | 审批人 ≠ 操作人,正式审批流 |
场景 5:多工具并发聚合
触发机制 :AI 在同一轮响应中并发调用多个工具,其中多个同时触发 HITL 时,系统将它们聚合成一个 hitl_request 事件(携带工具名数组),仅创建一次 HITL Token,用户一次确认即可全部放行。已完成的其他工具结果不受影响。
四、核心流程设计
4.1 完整生命周期
css
Agent 发起了一个需要确认的操作
│
▼
┌─────────┐
│ 暂停等待 │ ← 系统生成一张「一次性审批凭证」,同时通知用户
└────┬────┘
│
┌────┼──────────────┬────────────┐
▼ ▼ ▼ ▼
用户在线 用户离线 等太久没人管
点了[批准] 收到消息 (默认30分钟)
点了[拒绝] 点卡片审批 → 自动拒绝
│ │ │ │
▼ ▼ ▼ ▼
继续 取消 继续 取消
执行 操作 执行 操作
操作对照表:
| 操作类型 | 流程 | 对 Agent 的影响 |
|---|---|---|
| ✅ 在线批准 | 用户在页面上点了「批准」 | Agent 收到放行信号,继续执行刚才暂停的操作 |
| ❌ 在线拒绝 | 用户在页面上点了「拒绝」 | Agent 收到拒绝信号,该操作以失败返回,但对话不中断 |
| ✅ 离线批准 | 用户不在页面,通过IM消息/卡片点了「批准」 | 系统「唤醒」Agent,让它从暂停的地方继续干活 |
| ❌ 离线拒绝 | 通过IM点了「拒绝」 | 同上,操作以失败返回 |
| ⏰ 超时未处理 | 30 分钟内没有任何人处理 | 自动视为「拒绝」,防止任务永远卡住 |
4.2 示例:一次完整的 HITL 流程
场景:Agent 要帮你删除一批数据
Agent 决定动手,系统喊停
Agent 在推理过程中判断需要调用「批量删除」工具。安全系统评估后发现这是一个高风险操作(不可逆、影响范围大),于是:
- 系统立即叫停 Agent,不让它真的去删
- 生成一张一次性审批凭证(类似银行转账的验证码,用过即作废)
- 把这个凭证和操作说明保存到数据库
- 同时通知用户:「Agent 请求删除 XX 数据,请确认」
用户在线 ------ 最简单的路径
如果用户此刻正在页面上:
- 页面弹出一个确认框,显示「Agent 请求执行:删除 XX 条数据」,有批准 和拒绝两个按钮
- 用户点批准 :
- 系统验证凭证有效(防重复点击)
- 标记为「已批准」
- 通知 Agent:「准了,继续吧」
- Agent 收到信号后,继续执行删除操作
- 用户点拒绝 :
- 同样验证凭证
- 标记为「已拒绝」
- 通知 Agent:「不行,这个操作被拒了」
- Agent 把这次操作标记为失败,然后可以继续聊别的事
用户离线 ------ 系统自动切换到备用方案
如果用户此时已经关掉了页面(WebSocket 断开):
- 系统等 60 秒,发现用户确实不在线
- 自动把 Agent 当前的状态存档落库,然后让 Agent 先「休息」(释放资源)
- 通过IM 给用户发一条消息,附带一个交互式卡片
- 用户在手机/电脑上看到消息,进行反馈
- 系统收到反馈后:
- 验证凭证有效性
- 用之前的存档恢复 Agent 的工作现场
- 把用户的决定(批准/拒绝)告诉 Agent
- Agent 从中断的地方继续工作
超时 ------ 不能让任务永远等着
如果用户既没在线点确认,也没在IM上审批:
- 默认等待 30 分钟(可配置)
- 超时后系统自动拒绝该操作
- Agent 收到超时拒绝信号,操作失败,对话可以继续
超时机制有两道保险:
- 第一道(精确):内存里的定时器,到期立刻触发,精确到毫秒级
- 第二道(兜底):每分钟扫一次数据库,找出所有超时未处理的请求。这道保险是为了防止服务重启导致内存定时器丢失的情况
4.3 安全设计:不能伪造审批
HITL 涉及人工审批,安全性至关重要。
| 问题 | 解决 |
|---|---|
| 有人伪造一个「批准」请求? | 每次审批都带一次性凭证(Token),用完立即作废。重复使用会被拒绝 |
| 有人拦截请求修改参数? | 凭证通过 CAS(比较并交换)原子操作消费,不存在竞态窗口 |
| 服务重启后已发出的凭证失效? | 凭证存在数据库里,重启不影响。超时有兜底扫描机制 |
| 审批记录被篡改? | 每次操作都写审计日志(含操作人、时间、结果),日志独立存储不可覆盖 |
| 用户重复点「批准」? | 第一次点击成功消费 Token 后,后续点击因 Token 已作废而返回「已处理,请勿重复操作」 |
4.4 多个操作同时需要审批怎么办?
AI 有时候会在一轮回复中同时发起多个操作(比如同时删除 A 和 B),如果每个都弹一个确认框会很烦。
系统的做法是:合并成一个确认框。
- 把所有待确认的操作列在一起:「Agent 请求执行以下操作:① 删除 A ② 删除 B」
- 用户点一次「批准」就全部放行,或点一次「拒绝」全部拦掉
- 只生成一个凭证、注册一个超时定时器、发一条通知
最后
HITL(人机协同)是整个安全防护框架中最关键的一道人工防线,本文分享了笔者在业务开发流程中做的一些设计,如果你也有类似的思考,欢迎一起交流~