#AI Agent#MCP#DevOps

Agent 审批防重放:把人工批准绑定到唯一执行与资源状态

用户确认一次高风险动作后,重复点击、队列重投和参数替换都可能复用批准。本文用规范化 action digest、一次性消费、状态版本和幂等执行封闭窗口。

本文要点

  • 主题聚焦:AI Agent、MCP、DevOps
  • 适合人群:独立开发者、全栈工程师、AI 应用开发者
  • 核心价值:快速理解背景、实践路径、常见误区和工程建议
作者 @admin·发布于 2026年7月20日·更新于 2026年7月20日

审批页面显示“删除 18 个草稿”,不代表后续任意删除都获授权。重复提交、队列重投、参数替换和资源变化,可能让批准执行两次或作用到另一批对象。approved=true 或可复用 ID 无法封闭这些窗口。

Agent 人工审批门 已定义动作范围、期限和实时权限。本篇补齐防重放:批准服务端规范化动作的唯一摘要,消费与建立 execution 必须原子发生。

先生成不可含糊的 action manifest

模型只提交候选计划。可信服务加载资源、授权和策略后生成 manifest,包含租户、run、工具版本、参数、目标 ID 或快照、影响及资源/策略版本。UI 直接渲染这些事实。

json
{
  "tenantId": "tenant-a",
  "runId": "run-92",
  "tool": "draft.delete.v2",
  "arguments": { "projectId": "p-7", "recordIds": ["d-2", "d-9"] },
  "expectedState": { "projectVersion": 41, "aclVersion": 12, "recordVersions": [7, 3] },
  "impact": { "count": 2, "reversibleUntil": "2026-07-18T10:00:00Z" },
  "policyVersion": "approval-v8"
}

对象按 RFC 8785 JCS 生成 canonical bytes 再计算 SHA-256。JCS 不做 Unicode normalization,字符串原样保留;仅接收 I-JSON,parser 先拒绝重复键,数字遵循 IEEE 754/ECMAScript。金额和大整数使用领域规范的十进制字符串。builder 持久化唯一 canonical bytes,UI 与 executor 共用,不能各自序列化 manifest。

批准凭证绑定人、动作和期限

approval artifact 包含 ID、digest、tenant、run、请求者、批准者、期限、nonce、策略、scope、max_uses=1 和 key ID;双人批准需不同身份签同一 digest。

JWT 可作传输格式,但按 RFC 8725 固定 iss、aud、私有 typ=agent-approval+jwt 和非对称 alg allowlist。验证器只从本地可信 key set 解析已知 kid,拒绝 none、对称/非对称算法混淆、未知 kid,以及 token 指定的 jku、x5u 或远程 key。TTL 以分钟计算且不进入模型;撤回、run 取消、权限或策略变化都会 revoke。

人工复核队列 选择审批人,execution binding 防止判断被移植;执行前仍重验身份。

消费与创建执行记录必须原子化

executor 对持久化 canonical bytes 复算 digest,再验证签名、身份、期限、tenant、run、scope 与策略。本地副作用必须在同一事务中锁定 approval、目标资源和授权版本,重查 ACL 与资源版本;匹配时才更新业务行、消费 approval、创建 execution/outbox。若不匹配,则提交 terminal STALE 且不做业务修改,不能留下可复用批准。

sql
BEGIN;
SELECT * FROM approvals WHERE id = $approval FOR UPDATE;
SELECT version, acl_version, tenant_id FROM resources WHERE id = $resource FOR UPDATE;
-- verify live ACL, tenant, digest and expected version
UPDATE resources SET ..., version = version + 1
  WHERE id = $resource AND version = $expected; -- exactly one row
UPDATE approvals SET status = 'CONSUMED', execution_id = $execution
  WHERE id = $approval AND status = 'APPROVED'; -- exactly one row
INSERT INTO executions (...) VALUES (...);
COMMIT;

任一条件更新不是一行就走 STALE 分支,不执行副作用。唯一约束覆盖 approval 与 digest,工具用 execution id 作幂等键;MCP 超时、重试与幂等 要求重投只查询原 execution。

外部副作用使用 If-Match/ETag、版本 precondition、reservation 或目标系统等价的条件操作,并传 execution id。开始调用前 approval 已进入不可复用的 execution;412、reservation 失效或 ACL 拒绝都终止为 STALE/FAILED,必须重新批准。结果未知进入 RECONCILING,只能按同一 execution id 查询或幂等恢复,不能创建第二次逻辑执行。

资源变化必须要求重新批准

批准后查询可能从 18 条变成 200 条。manifest 应绑定 ID 与版本;必须绑定查询时,保存规范、快照和最大影响数,任一变化都使本次批准终止为 APPROVAL_STALE。

工具版本、金额、收件人、附件哈希或回滚能力变化也使 digest 无效。MCP 审计日志设计 记录 manifest 版本、批准者、execution、结果与补偿。

验证所有重放窗口

验收覆盖双击、并发、重投、过期/撤回、参数替换、字段重排、资源变化、跨租户、换 audience、权限撤销和外部成功但本地超时,并断言副作用次数与状态。

跨语言 golden vectors 覆盖组合字符、不规范 Unicode、重复键、IEEE 754 边界、金额与大整数,并注入事务崩溃、outbox 重发和对账恢复。同一 approval 最多产生一个逻辑 execution。

事故时撤销未消费批准并对账

发现缺陷时停止高风险工具,提升 issuance epoch 使未消费批准失效。对已消费无终态或外部结果未知的 execution 逐项对账,不能批量重试。

回滚到上一版已签名 builder、JCS 库和消费事务,经并发/故障矩阵验证后逐步开放。不得接受旧 digest、延长 TTL 或重置 CONSUMED;旧任务必须重算并重批。批准只有绑定同一动作、状态和唯一执行才是授权。

延伸阅读

Related

相关推荐