Agent 审批必须绑定实际执行参数
文章提出将人工审批绑定到不可变、范围明确的操作提案,并在执行时核验操作一致性、审批有效期与审批人权限。内容包含风险矩阵、审批记录示例、参考校验器和测试方案。
AI 深度解读
人工审批按钮可能只记录了一次点击,却没有约束最终执行的操作。人看到的是「退款」,Agent 执行前却改了金额、客户或收款账户,这次审批就失去了作用。
作者提出的核心办法,是让审批绑定一份不可变的具体操作,并在执行前重新核验内容、权限、有效期和使用状态。它值得程序员花几分钟读:真正需要设计的,是从审核页面到后端执行器之间那条不能被绕过的授权链。
审批的对象应该是操作,而不是 Agent
「允许这个 Agent 继续运行」覆盖的范围太大。一次会话可能包含查询、修改资料和付款,而这些操作的影响、授权条件、可恢复程度各不相同。
作者把决策收窄成三个结果:自动执行、交给指定人员审核、直接阻止。分类依据是影响范围、可逆性和权限,而不是模型的置信度。模型再确信,也不能获得跨租户权限;金额再小,也不能让未知收款人变得可信。
每个工具应先定义自动执行的边界,包括操作类型、租户、记录范围、金额或大小上限、目标地址及前置条件。超出边界后,只有存在合格审核人和可靠审核路径,才能进入人工审批,否则应阻止。
为了让这些边界可检查,先把工具调用转换成结构化提案。原文的退款示例如下,它把一句「退款」展开成真正决定执行效果的字段:
{
"action_type": "refund",
"tenant_id": "tenant-42",
"customer_id": "cust-818",
"order_id": "ord-2026-9912",
"amount_minor": 12900,
"currency": "CHF",
"reason_code": "duplicate_charge",
"evidence_ids": ["case-7781", "payment-113"],
"destination_account_id": "original-payment-instrument",
"requested_by": "agent-run-abc",
"policy_version": "refund-policy-2026-10"
}
要留意 amount_minor、evidence_ids 和 destination_account_id:金额用最小货币单位表示,避免浮点小数;证据引用不可变 ID;退款目标采用稳定的内部标识。这里的 12900 对应 CHF 129.00。
审核页面展示的内容,必须来自执行器稍后读取的同一份冻结提案。
页面还要说明证据、不可撤销的后果、请求来源,以及批准后会发生什么。只给一个摘要或哈希值,审核人无法判断操作是否合理。
从点击批准到实际执行,中间还差哪些检查
作者列出六个主要状态:proposed、validated、pending review、approved、executing、completed,相关阶段还可以转入 rejected、expired 或 failed。状态转换由服务端控制,不能让模型直接填写。
流程先做确定性校验,检查 schema、身份、租户、允许列表、业务规则、证据时效、金额限制及重复操作。无效请求应在打扰审核人之前拦住。随后,集中策略只返回 allow、review 或 block,同时记录原因和策略版本。
需要审核时,系统保存规范化提案、摘要、证据引用和过期时间。审核人认证通过后,还要核验角色、委托关系、职责分离要求、当前租户和审批额度。批准记录绑定具体提案,只能使用一次。
原文的记录示例包含 approval_id、proposal_id、proposal_digest、approver_subject、approver_role、decision、reason_code、policy_version、nonce、used_at 和 execution_id。其中签发时间为 2026-10-25T07:15:00Z,过期时间为 2026-10-25T07:25:00Z,有效窗口是 10 分钟。这是示例值,不能直接当作所有业务的默认设置。
执行器应按提案 ID 读取存档,重新计算摘要,检查审批及当前策略,再调用下游服务并保存回执。
下面这段 Python 展示了内容绑定和执行前核验,原文明确说它只是示意:
import hashlib
import hmac
import json
import time
SECRET = b"load-from-a-secret-manager"
def canonical_bytes(proposal: dict) -> bytes:
# 此步骤之前必须完成 schema 校验和字段规范化。
return json.dumps(
proposal, sort_keys=True, separators=(",", ":"), ensure_ascii=False
).encode("utf-8")
def proposal_digest(proposal: dict) -> str:
return hashlib.sha256(canonical_bytes(proposal)).hexdigest()
def issue_approval(proposal_id: str, digest: str, approver: str,
policy_version: str, expires_at: int) -> dict:
payload = {
"proposal_id": proposal_id,
"proposal_digest": digest,
"approver": approver,
"policy_version": policy_version,
"expires_at": expires_at,
"nonce": hashlib.sha256(
f"{proposal_id}:{approver}:{time.time_ns()}".encode()
).hexdigest(),
}
signature = hmac.new(SECRET, canonical_bytes(payload), hashlib.sha256).hexdigest()
return {"payload": payload, "signature": signature}
def verify_before_execution(proposal: dict, approval: dict, *, now: int,
expected_approver: str, current_policy: str,
already_used: bool) -> bool:
payload = approval["payload"]
expected_signature = hmac.new(
SECRET, canonical_bytes(payload), hashlib.sha256
).hexdigest()
checks = [
hmac.compare_digest(expected_signature, approval["signature"]),
payload["proposal_digest"] == proposal_digest(proposal),
payload["approver"] == expected_approver,
payload["policy_version"] == current_policy,
now <= payload["expires_at"],
not already_used,
]
return all(checks)
最要小心的是 not already_used:检查未使用,再单独执行,并不能阻止两个 worker 同时通过。需要数据库事务或比较并交换操作,原子地占用审批,并在调用外部服务之前写入执行意图。
sort_keys=True 也不等于解决了所有序列化问题。数字格式、Unicode 规范化、缺省字段等仍需统一规则。HMAC 密钥不能交给模型;角色查询、租户授权、密钥轮换、审计持久化和并发处理,也都不在这段示例里。
防住参数篡改,还要防重复执行和证据不足
签名合法,不代表操作只能执行一次。单次审批、幂等键和结果核对解决的是不同问题,需要一起落实。
下游支持幂等键时,应从不可变的提案 ID 派生,或在冻结提案时分配,不能根据一次可重放的审批点击生成。下游不支持幂等时,需要本地操作台账和超时核对流程。网络超时只说明没有收到结果,不能据此断定操作失败并再次付款。
供应商银行账户变更的例子更能说明边界。Agent 读到一封邮件,准备修改收款账户并支付发票,不能只展示「批准供应商更新」。
作者要求把账户变更和付款拆成两个提案。账户变更需要供应商稳定 ID、新旧账户指纹、来源消息 ID、已知联系人核验记录、变更原因、请求人和生效时间。账户信息按需要脱敏,审批则绑定具体供应商和新账户指纹。
如果缺少独立核验,应阻止操作或补充证据。审核人点击同意,不能让一封可疑邮件变成可信的账户变更依据。变更完成后,还要保存并核对供应商系统回执,付款流程才能使用新目标。
审批能够授权一项操作,不能补齐缺失的证据,也不能扩大执行器原有的权限。
我的判断
作者抓住了一个容易被界面掩盖的问题:审批是否有效,要看执行端是否强制落实。相比会话级放行、笼统确认弹窗,这套方案把审核对象和检查条件变成了可测试的数据约束,对接 API 和内部系统时很有参考价值。
但它是一套工程建议,没有部署效果的实测背书。作者截至 2026 年 10 月 25 日整理了平台指南、安全资料、Anthropic 2026 年自主性研究及一则 n8n 社区讨论,没有测试具体审批产品、工作流、API 或密码学实现,也没有统计失败率。平台建议、厂商遥测和论坛问题,都不能证明这套方案已经有效运行。
落地还有前提:团队必须具备可信身份系统、可核验的证据来源、持久化存储和受限执行器。哈希能证明冻结内容没有变化,却不能证明内容真实;冻结证据 ID,也要求底层记录确实不可变。
输入原文提到风险矩阵和 14 项验收用例,但未附完整表格,不能把它们当作现成清单。能明确拿走的是验收方法:改动审核后的字段,应阻止下游调用;让两个执行器竞争同一审批,应只有一个取得原子执行资格。测试必须检查 API 请求和持久化状态,只验证弹窗出现远远不够。
能拿来做什么
- 给内部审批前端建立明确的数据契约:页面按提案 ID 展示服务端冻结版本,同时展示金额、目标、证据和到期时间;修改关键字段就生成新提案并重新审核。
- 在 Node 工具调用层集中实现
allow、review、block,把身份、租户、schema 和业务限制放进确定性代码,并阻止 Agent 直接访问高权限执行器。 - 为审批消费和重试建立服务端约束:用事务原子占用审批,以提案 ID 关联幂等键、执行意图和下游回执,超时后先核对结果。
- 把参数变更、并发竞争和重复请求加入集成测试,同时断言下游调用与数据库状态,验证审批确实约束了实际副作用。