智能体面试准备(六十三):智能体人在回路(HITL)与审批流工程——中断、审批、回滚与可审计闭环

引言

前面(六十二)讲了工具沙箱与执行安全,把"智能体能做什么"关进了笼子;前面(五十五)讲了工程化交付。但生产里还有一类更现实的问题:有些动作不能让智能体自己拍板——发邮件、改数据库、批准报销、调用付费 API、对外发布。这些必须有人确认。这就是人在回路(Human-in-the-loop, HITL)。

HITL 不是"加个弹窗"那么简单。它要的是:在正确的节点停下来等人、把上下文讲清楚让人能决策、人决策后能从断点续跑、人拒绝后能安全回滚、全程可追溯可审计。本文讲一套工程上能落地的审批流设计,含状态机、断点续跑、回滚、审计。结尾给速答。

一、为什么 HITL 是生产智能体的刚需

不需要 HITL 的:              需要 HITL 的:
只读查询(搜文档)            写操作(发邮件/改库)
低风险生成(草稿)           对外发布/付费调用
可回滚的试探              不可逆动作(转账/删除)
内部分析                  涉及隐私/合规/他人

核心判据:动作是否不可逆、是否涉及他人、是否花钱、是否合规敏感。任一命中就进审批。这和(六十二)沙箱的"默认拒绝 + 白名单"是互补的:沙箱管"能不能执行",审批流管"要不要先问人"。

二、审批流的状态机

把一次需要审批的工具调用建模成状态机,是工程上最清晰的做法:

工具调用审批状态机
        plan
         |
         v
    [pending]  -- 自动白名单 --> [executed] --ok--> done
         |                         |
      需审批                      被拒/失败
         |                         |
         v                         v
    [awaiting_human]          [rolled_back] -> done
         |
    人批准 -> [executed]
    人拒绝 -> [rolled_back]
    人改参 -> [pending](新参数)

要点:
- pending 是计划态,尚未产生副作用。
- awaiting_human 是阻塞态,智能体线程挂起,不能继续往下走(否则会出现"人还没批,它已经发了下一封邮件"的事故)。
- 任何"已产生副作用"的状态都要能对应一个 rollback 动作。

三、中断与续跑:把执行拆成可恢复步骤

智能体不能一次性跑完再问人。要把流程拆成"步骤 + 检查点",在检查点阻塞。工程上用一个持久化的执行上下文(checkpoint)保存:当前到第几步、每步输入、已产生副作用列表。

执行上下文(检查点)结构
{
  run_id, step_index,
  plan: [step0, step1(需审批), step2],
  side_effects: [ {step:0, kind:"db_update", undo:"UPDATE ... SET old"} ],
  status: "awaiting_human",
  human_decision: null
}

续跑:人批准后在 step_index 处 resume,加载上下文继续;不丢历史、不重算已过的步骤。这呼应(二十二)长时任务断点续跑——审批流是断点续跑的一个特例。

代码(阻塞式审批桩):

import json, time

def request_approval(ctx, step):
    ctx["status"] = "awaiting_human"
    ctx["pending_step"] = step
    save(ctx)                         # 持久化, 等待人
    # 真实系统: 发消息给审批人(IM/工单), 然后阻塞或返回
    decision = block_until_human(ctx["run_id"])
    ctx["human_decision"] = decision
    if decision["action"] == "approve":
        ctx["status"] = "executing"
        return True
    if decision["action"] == "reject":
        rollback(ctx)                 # 回滚已产生副作用
        ctx["status"] = "done"
        return False
    if decision["action"] == "modify":
        step["args"] = decision["new_args"]   # 人改参数后重跑该步
        return True

四、回滚:副作用必须可撤销

不可逆动作(转账、删除)原则上不应进智能体自动链路;必须做也要"预扣 + 确认 + 限时"。对可回滚动作,每条副作用记 undo 脚本:

side_effect 记录
kind: db_update
do:   UPDATE account SET balance=balance-100 WHERE id=1
undo: UPDATE account SET balance=balance+100 WHERE id=1
kind: email_send
do:   send(to, body)
undo: 撤回(若服务商支持) 或 标记"待确认未发"
kind: file_write
do:   write(path, content)
undo: write(path, backup)   # 先备份原文件

拒绝或超时时按 side_effects 倒序执行 undo。关键:undo 本身可能失败,要有"补偿 + 告警 + 人工兜底",不能假设回滚一定成功。

五、审批人体验:让人能决策

人不会点"批准"如果看不懂要批什么。审批消息必须把决策所需信息给全:

【待审批】智能体想执行: 发送邮件
收件人: zhang@corp.com
主题: 关于 Q3 报销的确认
正文摘要: ...(前200字)
影响: 对外、不可逆程度=低、预估成本=0
[批准] [拒绝] [修改参数] [查看完整上下文]

信息密度原则:默认给"做了什么、影响谁、能否撤销、完整上下文链接"。否则人只能盲批,HITL 退化成形式。

六、可审计:留痕是合规底线

审计日志(每条审批)
run_id, step, actor(智能体/人), action,
decision_by, decision_at,
before_hash, after_hash,
side_effects, rollback_status

审计要防篡改(append-only + 哈希链),能回答"谁在什么时候批准了什么、产生了什么后果"。这在金融/医疗/政企场景是合规硬要求(呼应六十安全合规)。

七、降级与超时

  • 审批人离线:设超时策略——超时拒绝(保守)或转交备用审批人(生产)。默认"超时拒绝 + 告警"。
  • 高优路径:把不可逆/高成本动作默认设为"必须审批",低风险动作走白名单自动过,平衡效率与安全。
  • 批量审批:同类低风险动作合并成一次审批("允许接下来 10 分钟发送最多 5 封同类邮件"),降低人负担。

八、和(五十八)多智能体编排的关系

在 DAG/状态机编排里,审批节点是一个特殊节点:它把"自动边"变成"人工边"。编排引擎要支持"human node"作为一等公民——可挂起、可恢复、可超时、可回滚。否则 HITL 只能塞在单智能体里,多智能体流程一碰到人工就断。

面试速答

问:HITL 和沙箱(六十二)什么关系?
答:沙箱管"能不能执行"(默认拒绝+白名单),审批流管"要不要先问人"(不可逆/花钱/合规敏感动作)。一个管权限,一个管决策。

问:审批流核心状态机?
答:pending → awaiting_human →(批准)executed /(拒绝)rolled_back /(改参)回到 pending。阻塞态不能继续往下跑。

问:回滚为什么不能假设一定成功?
答:undo 本身可能失败(如邮件已读无法撤回)。要补偿+告警+人工兜底,并留审计。

高频追问清单

  1. 哪些动作必须进审批?判据是什么?
  2. 智能体在 awaiting_human 时线程怎么处理?能继续别的任务吗?
  3. 断点续跑的上下文要持久化哪些字段?
  4. 不可逆动作(转账/删除)怎么在智能体链路里安全处理?
  5. 回滚失败怎么办?补偿事务怎么设计?
  6. 审批消息要包含哪些信息才不至于让人盲批?
  7. 审计日志为什么要用 append-only + 哈希链?
  8. 审批人离线/超时,策略怎么定?
  9. 批量审批怎么设计既安全又不烦人?
  10. 多智能体编排里 human node 如何作为一等公民支持挂起/恢复?
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐