AI Agent 上生产前先加三道闸门:审批、限权、可回放的工程实践

AI Agent 上生产前先加三道闸门:审批、限权、可回放的工程实践

关于"模型说执行就执行"的争论,其实很少停留在模型层。真正把团队拦在生产环境门外的,是这样一个问题:当 Agent 的一次工具调用产生不可逆副作用时,系统能不能在毫秒级拦住它、让人介入、并在三天后完整复现当时发生了什么。这不是提示词工程问题,而是运行时的可控性工程问题。近期社区里"先加三道闸门再碰真实业务"的提法引起了不少讨论 1,本文在此基础上向后端纵深推进:把审批做成可持久化的状态机,把限权落到工具治理的实处,把第三道闸门升级为可重放的 Harness。

一、Demo 能跑,与能上生产之间的距离

1.1 一次失控调用的典型链路

先看一个合成的示意场景,它不是真实事故,但结构上接近团队最常见的失控路径:

业务人员输入:"把 A 仓的超储部分调拨到 B 仓。"

Agent 规划后选择工具 create_stock_transfer,构造参数时把数量字段理解为"整仓全量",同时漏掉了目标仓的容量校验参数,直接提交了一张调拨单。

把这条链路拆开,失控通常发生在四段:

阶段 发生了什么 失控的表现 缺失的约束
意图理解 自然语言转成任务目标 目标被放大或曲解 意图澄清、确认
工具选择 从工具集中挑一个 选中语义相近但危险的工具 工具风险分级
参数构造 填写调用参数 字段含义错配、范围越界 参数校验、权限收窄
副作用执行 调用下游系统 不可逆写入已完成 审批、可回滚、留痕

注意,四段里只有一段是"模型能力"问题,另外三段都是工程问题。指望通过更强的模型、更精细的提示词消除后三段风险,在工程上不成立:模型输出本质上是概率性的,而生产系统要求的是确定性边界。可控性工程的思路,正是把确定性约束放在运行时,而不是放在模型的"自觉"上。

1.2 三道闸门分别解决什么

本文所说的三道闸门是一个工程化提法,不是行业标准术语,但它们各自对应一类明确的失效模式:

  • 审批(决策授权):解决"该不该做"。在有风险的动作执行前中断,把决策权交给人或策略引擎,而不是交给模型。
  • 限权(能力边界):解决"能做什么"。即使用错工具、参数错配,也只能在被允许的范围内造成影响。
  • 可回放(行为可追可复现):解决"事后怎么说清"。让每一次运行都能被审计、被复现、被用于回归评估。

三者缺一不可,且互相不能替代。只有审批没有限权,审批者面对的是海量高危请求,最终变成无脑点"同意";有限权没有审批,不可逆动作仍然会以合法权限被执行;两者都有但无法回放,出事之后既不能定位根因,也不能证明系统当时的行为符合规则。

1.3 适用边界与阅读路线

本文聚焦运行时工程约束,不覆盖提示注入的系统化治理、模型越狱防御、训练侧安全对齐等主题;这些是另一条战线,与本文措施互补。代码示例均为结构示意的伪代码,具体 API 需以所用框架的官方文档为准。

章节 主要闸门 可直接取用的产出
第二章 审批 风险分级矩阵、审批状态机、持久化要点
第三章 限权 权限收窄维度、MCP 坑位清单、运行时硬约束
第四章 可回放 事件日志 schema、回放边界
第五、六章 三者串联 端到端时序、Checklist、实施路径

二、闸门一:把审批做成状态机,而不是弹窗

2.1 哪些动作必须审批:风险分级矩阵

审批不是所有调用都要走,否则系统会被拖死。判断依据主要是三个维度:可逆性 (能否撤销)、影响面 (影响单条记录还是全局)、敏感度(是否涉及资金、合规、对外触达)。

以开源项目 double-ai-agent_1 的业务域为参照:该项目基于 Spring AI Alibaba Graph 构建,涵盖自然语言驱动的 BI 查询与智能库存调拨,并宣称支持 RAG、人工审批与图状态持久化 3。其中两类工具的风险等级显然不同:

工具 动作类型 可逆性 影响面 风险级 策略
query_bi_metrics 只读查询 完全可逆 单一数据域 低 自动放行,仅留痕
create_stock_transfer 业务单据写入 流程内可撤销,但已触发下游作业则代价高 跨仓、跨部门 中高 强制人工审批
send_notification_external 对外触达 不可逆 外部客户 高 双人审批 + 限额

一个可复用的空白模板如下,建议在工具注册时就强制填写,未填写的工具默认最高风险级:

yaml 复制代码
# 配置示意,字段为设计建议,非任何框架的原生格式
tool: create_stock_transfer
risk_level: high            # low / medium / high / critical
reversible: partial
impact_scope: cross_domain  # single_record / single_domain / cross_domain / global
approval_policy:
  required: true
  approvers: 1              # 或 2 表示双人复核
  sla_minutes: 30
  on_timeout: reject        # 超时默认拒绝,优于默认放行
param_constraints:
  max_quantity: 5000
  allowed_warehouses: ["A", "B", "C"]
rollback:
  method: compensating_order   # 补偿式撤销,而非假装删除

这里有一个容易被忽略的取舍:审批粒度 。工具级审批实现简单但粗糙,参数级审批精准但实现和体验成本高,业务单据级审批最贴近组织流程但需要与单据系统集成。实践中常见做法是工具级定策略、参数级做校验、单据级做留痕,三层叠加。double-ai-agent_1 的实际审批粒度究竟落在哪一层,需要读源码确认,仓库描述本身不能作为实现细节的证据 3。

2.2 中断-等待-恢复:审批的运行时模型

审批的本质是一次长事务:Agent 执行到高危动作前挂起,把执行上下文序列化,等待外部决策,再从断点恢复。它必须处理的状态迁移至少包括:批准、驳回、超时、改参重试。

text 复制代码
运行中 ──触发高危工具──> 待审批 ──批准──> 运行中
                          │
                          ├──驳回──────> 终止(或回退到上一步重新规划)
                          ├──超时──────> 终止(默认拒绝)
                          └──改参重试──> 运行中(生成新 step_id,保留原请求记录)

对应的骨架逻辑(结构示意):

python 复制代码
# 伪代码:展示状态迁移骨架,不对应任何具体 SDK
async def run_step(ctx, action):
    policy = policy_engine.evaluate(action)
    if policy.level >= RISK_MEDIUM:
        checkpoint = persist.save(
            run_id=ctx.run_id,
            step_id=ctx.step_id,
            snapshot=ctx.snapshot(),      # 含计划、中间变量、工具候选
            pending_action=action,
        )
        decision = approval_bus.wait(checkpoint.id, timeout=policy.sla)
        if decision.kind == "rejected":
            return terminate(ctx, reason=decision.reason)
        if decision.kind == "timeout":
            return terminate(ctx, reason="approval_timeout")
        if decision.kind == "edited":
            action = decision.action        # 改参后重新校验,而不是直接放行
            policy = policy_engine.evaluate(action)
        ctx.restore(checkpoint.id)
    return tool_executor.execute(action)   # 仍需经过限权层,见第三章

四个容易踩空的点:

  1. 审批不是授权的终点。改参后的动作必须重新过策略引擎,否则"改参"就成了绕过审批的后门。
  2. 超时默认应为拒绝。默认放行会在审批人力不足时把风险全部释放到生产。
  3. 并发互不阻塞。多次运行、同一运行的多个待审批点应各自独立,不应互相排队。
  4. 驳回要有语义。"驳回并终止"与"驳回并重新规划"是不同策略,需按动作类型区分。

关于 Spring AI Alibaba Graph 是否提供官方的中断、人工介入与恢复 API,本次研究材料不足以给出确切结论;double-ai-agent_1 声称具备人工审批与图状态持久化能力 3,但具体 API 形态需以框架官方文档和仓库源码为准。若框架不提供原生支持,采用上述自研 checkpoint 模式是可行路径,代价是要自己解决序列化、幂等与恢复语义。

2.3 状态持久化:进程重启后审批还有效

如果审批状态只存在于内存里,服务一次发布就会丢掉所有待办,用户看到的是"调用凭空消失"。持久化设计至少要回答三件事:

  • 存什么:执行快照(计划、上下文摘要、待执行动作)+ 决策记录(谁、何时、批准/驳回/改参、理由)。
  • 幂等键怎么设计 :run_id + step_id + action_hash,保证同一动作重复提交不会生成多张审批单。
  • 恢复语义:重启后从最近快照恢复到断点,已执行的副作用不得重放,这要求工具调用本身也带幂等键。

一个可行的事件与快照组合是:审批单与决策记录用关系型数据库落表(便于查询与权限控制),执行过程用事件日志追加(见第四章)。double-ai-agent_1 把"图状态持久化"列为其企业级能力之一 3,可作为参照实现的方向,但其是否只存快照、是否含事件流、崩溃恢复语义如何,需要核对源码后才能判断。

2.4 审批体验的工程细节

审批系统最常见的失败不是技术失败,而是审批疲劳。要让人真正判断,审批单必须包含:调用工具名、参数差异(相对系统建议值或上一版本)、预期副作用、影响对象数量、回滚方式与成本、该动作的历史批准/驳回统计。

降低疲劳的手段包括:低风险动作按策略自动放行并事后抽查、同类批量请求合并审批、设置 SLA 超时告警、对反复驳回的任务类型直接降低 Agent 的自主级别。审批率与驳回率应作为持续指标观察:驳回率过低往往意味着审批者在无脑批准,而不是 Agent 越来越准。

三、闸门二:最小权限与 MCP 的真实坑位

3.1 工具即权限:按数据域 × 动作 × 环境收窄

"给模型全部工具,然后用提示词约束它不要乱用"是可控性工程里的第一反模式。提示词是概率性的,工具集是确定性的;把安全边界放在概率性的一侧,等于没有边界。正确的做法是在注册层就做裁剪:模型看到的工具集,本来就只是它被允许使用的那一部分。

收窄的三个维度:

维度 取值示例 收窄方式
数据域 订单 / 库存 / 财务 / 用户 按租户与角色绑定可见工具
动作类型 读 / 写 / 对外触达 写与触达类单独注册、单独审批
环境 dev / staging / prod 生产工具集最小化,禁止跨环境混用
yaml 复制代码
# 工具集组装示意,需替换为实际框架的注册写法
contexts:
  - name: inventory_agent_prod
    tenant: tenant_a
    allowed_tools:
      - query_bi_metrics        # 只读
      - create_stock_transfer   # 写入,强制审批
    denied_tools:
      - drop_table
      - send_notification_external
    network_egress:
      allow: ["erp.internal.example"]

另外,凭据不应随工具描述暴露给模型。正确形态是:Agent 只拿到工具句柄,真实凭据由执行侧在调用时注入,且按会话隔离、可轮换、可吊销。

3.2 MCP 接入的真实坑位清单

MCP 解决的是"模型如何发现和调用工具",不解决"工具该不该被调用"。从"能连上"到"生产可用",社区实践反馈中反复出现的问题包括以下几类 26:

坑位 表现 应对
工具描述歧义 语义相近的工具被误选 描述中写明边界与副作用;风险级标注
工具数量爆炸 上下文被工具 schema 填满,成本与误选率上升 按场景动态裁剪工具集,分层注册
schema 与实际行为不一致 声称只读,实际触发写入 契约测试;执行侧二次校验
鉴权凭据传递 会话间串用、无法轮换 执行侧注入、按会话隔离、短时效
长会话与服务重启 连接断开、工具结果丢失、无法恢复 重连策略 + 事件落盘(见第四章)
工具结果超长 返回被截断后模型基于残缺信息继续决策 摘要与原文分存,关键字段强制校验
多 Server 版本漂移 同名工具在不同环境下行为不同 版本锁定 + 环境隔离 + 变更审计

需要说明的是:上表是根据社区讨论与实践文章归纳的候选清单 26,其中具体条目与归因仍建议对照 MCP 官方规范逐条核实,本次研究材料未提供规范原文链接,本文不对其版本号与条款细节作断言。社区文章的单点经验也不能当作普遍结论 7。

3.3 运行时硬约束:沙箱、出网与审计

权限收窄之外,还有一类约束必须在运行时硬性执行,任何提示词都不应能绕过:

  • 命令执行沙箱:涉及 shell 或代码执行的工具运行在隔离容器中,限制文件系统写入范围与 CPU/内存配额。
  • 出网白名单:Agent 的网络出口默认拒绝,仅放行业务必要域名,防止数据外带。
  • 工具调用强制入审计:无论成功、失败还是被拦截,都留下记录。没有记录的拦截等于没有拦截。
  • 资源配额:单次运行的工具调用次数、总 token 消耗、执行时长设上限,超限即中断。多智能体编排中尤其需要轮次上限,避免子任务互相驱动而无限扩张 68。

有团队在数月落地实践中总结过类似经验:权限与隔离问题往往比模型效果问题更早爆发 7。这也解释了为什么限权应当排在效果调优之前。

四、闸门三:可重放的 Harness

4.1 为什么要回放:三种诉求,三种保真度

"可回放"经常被简化成"有日志",但三类诉求对保真度的要求并不相同:

诉求 需要什么 保真度要求
排障 完整执行过程、每步输入输出 高,要能还原分支与中间状态
回归评估 可比对的输入输出对 中,重点是可重复执行同一用例
合规审计 不可篡改的决策留痕 高,且要求完整性与时间顺序可验证

Harness 的概念正在从"测试脚手架"演变为运行时基础设施:它负责驱动 Agent 循环、管理状态、记录事件,并支持从任意断点恢复。SmartAI/ava 将自己描述为面向 Python 的"durable、replayable 的编码智能体 Harness" 4,CSDN 上关于 hermes-agent 的文章也把 Harness 作为框架组件单独讨论 5。这两个来源印证了"Harness 正在成为独立关注点"这一趋势,但其内部录制格式、恢复语义与成熟度需要阅读源码或文档后才能判断,本文不依据标题推断实现细节。

4.2 Harness 里到底录什么

一次运行至少要记录以下事件,才能支撑上述三类诉求:

json 复制代码
{
  "schema_version": "1",
  "run_id": "run_20260318_ab12",
  "step_id": "step_007",
  "event_type": "tool_call",
  "ts": "2026-03-18T10:21:33.412Z",
  "actor": "agent:inventory_bot",
  "payload": {
    "tool": "create_stock_transfer",
    "params_digest": "sha256:...",
    "risk_level": "high",
    "policy_decision": "require_approval",
    "approval_id": "apr_9f31"
  },
  "env": {"app_version": "1.8.2", "model": "recorded", "mcp_server_versions": {"erp": "2.3.0"}}
}

字段清单为设计建议,不代表任何现有框架的格式。要点有三个:run_id / step_id 串联全链路 ;payload 存摘要,原文进对象存储并以哈希关联 ,避免日志库爆炸;环境信息必须记录,包括应用版本、模型标识、各 MCP Server 版本,否则换一次依赖就无法复现。

此外,审批决策点必须作为事件记录,而不是只留在审批系统的界面上。审计要回答的问题是"当时系统为什么允许这个动作",这需要决策依据与执行动作在同一时间线上。

4.3 事件日志与状态快照的组合

只存最终结果无法排障,只存事件流在长链路里恢复成本高。实践中的取舍通常是:事件日志追加写,快照定期存。

方案 优点 代价 适用
仅结果 成本最低 无法复盘 不推荐用于生产
仅事件流 完整可追 长链路恢复需重放全量 短链路、低频任务
事件流 + 快照 恢复快、可追溯 双写一致性要处理 多数生产场景

一致性上的稳妥做法是:快照只记录"已确认完成的 step 边界",任何进行中的步骤都不进快照,恢复时从最近快照加后续事件重建。

4.4 确定性回放的边界

必须明确一点:回放验证的是编排、权限与工具链路,不是模型能力。LLM 输出存在随机性,同一输入两次调用可能得到不同计划。要做到可重放,录制模式下需要把不确定性来源全部固定:

  • 模型原始请求与响应全文录制,重放时直接使用录制结果,不再调用模型;
  • 时间、随机数、外部服务响应在录制模式下一并捕获,重放时替换真实调用;
  • 工具执行分两类:纯函数型可真正重跑,有副作用型在重放中以录制结果代替,并显式标注"模拟执行"。

这里有一条红线:回放时绝不能修改录制数据来"让测试通过"。一旦允许编辑录制内容,回放就从验证手段退化成了自我安慰。录制数据应只读、可校验哈希、与原始执行环境的版本信息绑定。

五、端到端走查:一次库存调拨如何穿过三道闸门

沿用前文的示例场景(示例,非真实实现),把整条链路串起来:

text 复制代码
用户输入:"把 A 仓超储部分调拨到 B 仓"
   │
   ├─ 1. 意图澄清:确认品种、数量口径、目标仓
   ├─ 2. 规划:生成计划,选取工具 create_stock_transfer
   ├─ 3. 限权检查:工具在该租户工具集内?参数通过数量与仓别校验?
   │        └─ 不通过 → 拦截并记录事件,结束
   ├─ 4. 风险判定:high → 触发审批
   │        ├─ 保存 checkpoint(快照 + 待执行动作)
   │        ├─ 挂起,进入待审批
   │        └─ 批准 → 恢复;驳回 → 终止;超时 → 拒绝
   ├─ 5. 执行:调用下游系统,携带幂等键
   ├─ 6. 落盘:工具调用、结果、审批决策写入事件日志
   └─ 7. 可回放:任意时间可从事件与快照重建本次运行

配置层面建议"先严后松":生产环境初期对中高风险动作全部强制审批,灰度稳定后按动作类型逐步放开低风险自动放行;权限白名单只增不减需要经过变更评审,所有配置变更本身也要入审计。上线后重点观测的指标:

指标 观察意义 建议关注点(非行业基准)
待审批平均时长 审批是否成为瓶颈 持续接近 SLA 需扩容审批人力或下调自主级别
审批驳回率 计划质量与风险识别 长期接近零需警惕无脑批准
工具调用拦截率 权限与参数校验是否生效 突然归零可能意味着校验被绕过
回放成功率 留痕完整性 低于预期优先检查事件丢失
异常恢复次数 状态机健壮性 频繁恢复需检查外部依赖稳定性

阈值应由各团队按自身业务风险设定,本文不提供通用基准值。

六、上线清单与反模式

6.1 上线前 Checklist

审批

  • 所有工具已标注风险等级,未标注者默认最高级
  • 中高风险动作的审批策略已配置,超时默认拒绝
  • 审批单包含参数摘要、预期副作用与回滚方式
  • 改参后动作会重新过策略引擎

限权

  • 工具集按租户、角色、环境裁剪,生产工具集最小化
  • 凭据由执行侧注入,按会话隔离,支持轮换与吊销
  • 命令执行有沙箱,网络出口默认拒绝并有白名单
  • 工具调用(含被拦截的)全部进入审计日志
  • 单次运行的调用次数、时长、消耗设上限

可回放

  • 事件日志含 run_id / step_id / 环境版本信息
  • 审批决策作为事件记录,与执行动作同时间线
  • 录制模式固定模型响应、时间、随机数与外部响应
  • 录制数据只读、可校验,禁止为通过测试而修改

观测

  • 待审批时长、驳回率、拦截率、回放成功率有看板与告警
  • 配置与权限变更本身有审计记录

6.2 常见反模式

  1. 审批疲劳:所有调用都审批,最终人人无脑批准。应按风险分级,把人力留给高风险动作。
  2. 权限一刀切后偷偷开白名单:临时豁免没有记录、没有到期时间,等于长期漏洞。
  3. 只存结果不存过程:出问题只能看到"失败了",看不到"为什么失败"。
  4. 把审批做成前端弹窗:进程重启即丢状态,审批记录与执行动作对不上账。
  5. 回放时修改录制数据:验证机制失效,且掩盖了真实缺陷。
  6. 把提示词当权限:任何"请不要调用 X 工具"都不构成安全边界。

6.3 分阶段实施路径

资源有限时,建议顺序是审计留痕 → 权限收窄 → 审批状态机 → 可重放 Harness。理由是:留痕成本最低、收益立刻可见,且是后续所有工作的数据基础;权限收窄直接削减爆炸半径;审批状态机依赖前两者的策略与事件能力;完整 Harness 的投入最大,但能同时服务排障、回归与合规。

每阶段的"够了"标准:留痕阶段能回答"过去 24 小时 Agent 调用了哪些工具";限权阶段能在测试中证明越权调用被拦截;审批阶段能演示进程重启后审批单仍可恢复并继续执行;Harness 阶节能对任一历史运行重放并给出与录制一致的行为序列。

七、结语

三道闸门的共同本质,是把模型的自由度关进运行时的确定性约束里。模型负责提出方案,系统负责决定方案能不能执行、执行到什么范围、事后如何还原。这不是对模型能力的不信任,而是工程系统的基本要求:任何产生真实副作用的组件,无论它内部是规则引擎还是神经网络,都必须服从同样的授权、边界与审计规则。

值得强调的是,本文的技术判断基于公开的社区文章与仓库描述,其中不少实现细节尚未逐条核对源码与官方规范 23456。把这些当作方向性参考,落到自己的系统前先做验证,本身就是可控性工程的一部分。

参考资料

1 前端开发者做 Agent:模型说执行就执行?先加 3 道闸门再碰真实业务,掘金。https://juejin.cn/post/7636280185700990986

2 热榜都在吹 MCP Skills 万能,我搭了个 AI Agent 才发现最大的坑不是 Skill,掘金。https://juejin.cn/post/7605807405308051519

3 豆芽/double-ai-agent_1:企业级双引擎 AI 智能体(Spring AI Alibaba Graph,RAG、人工审批、图状态持久化),Gitee。https://gitee.com/mitouhao/double-ai-agent_1

4 SmartAI/ava:A durable, replayable coding-agent harness for Python,GitHub。https://github.com/SmartAI/ava

5 【Agent】构建 Harness | hermes-agent 框架组件,CSDN。https://blog.csdn.net/qq_35812205/article/details/160281584

6 2026年AI Agent开发实战:MCP协议深度解析与多智能体协作架构完全指南,掘金。https://juejin.cn/post/7628131346834063410

7 从 3 个月 AI Agent 落地实战,聊透我踩过的坑和摸到的门道,掘金。https://juejin.cn/post/7601034810313130038

8 AI Agent 工作流编排:用 Subagent 实现复杂任务分解,掘金。https://juejin.cn/post/7616650313060155443

注:本次研究资料未提供 MCP 官方规范原文链接,文中涉及规范版本、能力协商与鉴权机制的内容未作具体断言,建议实施时另行对照官方文档核实;上述来源多为社区文章与仓库描述,其中的实现细节需以源码与官方文档为准。

相关推荐
遥感知识服务1 小时前
沙漠和洪水在SAR里都很黑,AI到底怎么分?
人工智能
小成很成1 小时前
Skills:AI 时代必备技能,让大模型从聊天工具变成专业助手
人工智能
yu_zhidun1 小时前
企业管理者看:员工上网行为管理软件,别再只靠网关“赌安全”
大数据·人工智能·安全·域智盾
-cywen-1 小时前
MaskCLIP
人工智能
努力的小雨1 小时前
被游戏虐烦后,我让 Seed-2.1-pro 帮我做了一款自己当王的 3D 游戏
人工智能
上海锝秉工控1 小时前
微米级精准捕捉,激光测振解锁产线新效率
人工智能
思茂信息1 小时前
CST软件BCI仿真模型及仿真案例
开发语言·单片机·嵌入式硬件·算法·emc
wshzd2 小时前
LLM之Agent(103)|当「快思考」遇上「深理解」:Laya 和 BERT 到底有什么区别?
人工智能·深度学习·bert
AI砖家2 小时前
Claude Code Skill 质量检查实战:用 /skill-doctor + Plugin Evals 找出“看似能用、实际没被调用“的问题
人工智能·ai编程·claude·codex·skill