AI Agent安全治理避坑:审批按钮不是护栏,最小权限落地实战指南

0 阅读11分钟

REPLACE_WITH_REAL_COVER_enterprise-agent-permission-governance.png

很多团队上线 Agent 时,权限设计一句话带过:"高风险动作都加审批,让人点。"本文站在工程视角,拆穿"审批=治理"的幻觉,给出一套最小权限 + 动作矩阵 + 不可变审计 + 回滚的落地方案。

0. 一个 ¥3,800 的教训

先上结论再讲故事。我们给一家跨境客服客户做退款 Agent,第一版权限设计是这样的:

refund_agent_token:
  scopes:
    - orders:read
    - refund:issue

理由朴素:"退款总得先读订单吧。"上线第二周,一笔不该退的 ¥3,800 退出去了。复盘发现审批人在 11 分钟点了 23 次"批准",事后说"我以为系统筛过风险了"。

问题不在人,在架构orders:readrefund:issue 绑在同一个身份上,意味着任何一个拿到 token 的环节都能一气呵成完成"看 + 退"。审批按钮只是把责任从系统推给人,人疲劳了就盲签。

这就是企业 Agent 权限管理最常见的死法——用审批假装兜住了权限。

1. 最小权限原则的工程落地

最小权限(least privilege)在 Agent 场景不是哲学,是三个可验证的约束:

1.1 读 / 写 / 资金必须拆成三个独立身份

风险等级完全不同:

权限出错代价
最多答错,≈0
人工可改回,一次客服
资金钱出去了,实打实损失

拆法:

read_token:   { scopes: [orders:read, policy:read] }
write_token:  { scopes: [ticket:create, status:update] }
fund_token:   { scopes: [refund:issue], requires: [approval, idempotency_key] }

Agent 运行时按动作类型挑对应 token,绝不让一个身份同时持有读和资金。

1.2 权限写在工具签名里,不写在提示词里

这是最关键的一点。提示词是给人看的,工具签名才是系统执行的边界。

@tool(scope="read-only", market="US")
def lookup_refund_policy(order_id: str) -> Policy: ...

@tool(scope="fund", required=["approver_id", "idempotency_key"])
def issue_refund(order_id: str, amount: float,
                 approver_id: str, idempotency_key: str) -> Receipt: ...

即使模型被越狱、提示词被改写,issue_refund 仍然强制要求 approver_ididempotency_key,缺参直接 400。工具层的硬约束不依赖模型乖不乖。

1.3 分阶段发放 token

最稳的节奏:

  • Phase 1(只读):只发 read_token,Agent 跑两周,把所有"想做"的动作记成 proposed 日志,不落库。
  • Phase 2(受限写):逐个开 write_token,每个动作配审批档。
  • Phase 3(资金):最后开 fund_token,强制双签 + 幂等。

跳过 Phase 1 直接全开,然后用审批兜底——这是后面所有事故的起点。

2. 动作分级:auto / single / dual

按"是否可逆 + 金额上限"分三档:

def tier(action):
    if action.reversible and action.cap == 0:
        return "auto"      # 查询、草稿、建工单
    if action.reversible:
        return "single"    # 改状态、非资金通知
    return "dual"          # 退款、改预算、删数据、跨市场写

一句话:做错了 5 分钟内能不能无损撤回?不能,就别自动跑。

注意改广告预算——钱不"直接飞走"但会烧。比一次退款更隐蔽:退款立刻报警,预算被悄悄改可能三天后才在报表露馅。所以资金边界不能只看"是否直接转账"。

agent-permission-audit-rollback.webp

3. 审批页必须渲染的五类证据

有效的企业 Agent 权限管理,审批页要一次性给审核人五样东西,少一样就是盲签:

  1. intent:哪条消息/政策触发?
  2. evidence:看了哪些数据,可点击溯源?
  3. tool_call:调哪个工具、传什么参数?
  4. blast_radius:动多少钱、几个订单、哪个市场?
  5. reversibility:点批准之后还能不能撤、怎么撤?

前端实现上,这要求把 Agent 的"思考轨迹"结构化存储,而不是只存最终动作:

{
  "trace_id": "tr_8f2a",
  "intent": "user asked refund for order A",
  "evidence": ["order:A:status=delivered", "policy:v2025:refund_window=30d"],
  "tool": "issue_refund",
  "params": {"amount": 3800},
  "blast_radius": {"orders": 1, "market": "US", "money": 3800},
  "reversible": true,
  "rollback": "reverse_refund(tr_8f2a)"
}

审核人看到的不是"是否批准",而是"基于以上证据,是否批准"。

4. 四道防线:deny / retry / rollback / audit

权限管理终点不是"批了就完",是"出事能兜住"。

def execute(action):
    if not has_evidence(action):       # deny
        raise Denied("insufficient evidence")
    if action.idempotency_key in seen: # retry-safe
        return seen[action.idempotency_key]
    result = call_tool(action)
    seen[action.idempotency_key] = result
    audit.log(trace_id=action.trace_id, actor=action.approver_id, ...)  # audit
    return result

def rollback(trace_id):
    compensations[trace_id]()          # reverse / snapshot / soft-delete
  • deny:证据不足或超权限,系统直接拦,不进队列。
  • retry:幂等键保证重复调用不产生二次副作用。
  • rollback:退款有冲正、预算有快照、删除有软删+回收站。
  • audit:每次调用带 trace_id,串起决策、参数、审批人、时间。

没有幂等键的 retry 是灾难,没有快照的 rollback 是空话,没有 trace_id 的 audit 是甩锅。三样必须和权限同设计。

5. 动作矩阵(可直接抄)

动作分级角色上限证据审批幂等键回滚
查询订单Agent系统query_id
生成建议读(出)Agent政策版本系统draft_id
创建工单Agent订单+原因系统ticket_id关工单
修改状态Agent+1原/新+依据单人op_id还原
发起退款资金Agent+2≤¥500单/>¥500双订单+政策+时效+确认双人refund_id冲正
改广告预算资金Agent+2日预算封顶旧/新+理由双人budget_id快照还原

每周复盘:审批人离职?上限半年没调?动作从"单人"偷升"自动"?

6. 五个失效模式

  • 审批疲劳:高低风险混排,真人变橡皮图章。
  • 共享账号:一堆 Agent 共用 admin token,出事分不清谁。
  • 不可回滚:删库、发外邮、改全局,点了不可逆。
  • 日志不全:只记"调了什么",不记"凭什么、谁批的"。
  • 权限漂移:业务变了权限没变,临时写权限忘了收。

四条与"审批"无关,只和"证据/边界/可逆/可审计"有关。

7. 独家观点:治理最小单位是动作,不是模型

行业谈治理总盯"模型安不安全""提示词越狱没"。但企业真正要审计的最小单位,是动作——它看了什么、调了什么、改了什么、能否恢复。

沙盒里对答如流 vs 能改你广告预算,风险不在一个量级。决定量级的是动作,不是模型。把治理下沉到动作层,每个动作配齐角色/上限/证据/审批/幂等键/回滚,模型再抽风也被框在小格子里。

8. 亚马逊场景的落地提示

运营 Agent 能碰:查 BSR、改竞价、调预算、读评论、代发邮件。建议:

  • 实时数据(价格/排名/广告位/评论)交给 Amazon Data API 这类数据层提供"看"的事实;
  • 任何改后台的动作走最小权限 token + 双人审批 + 幂等键 + 回滚。

很多误退、误改预算,根因是"看"的事实层和"动"的权限层混在一起。亚马逊政策随市场/季节/类目剧烈变化,外部事实统一从可信数据层拉,至少"基于哪版政策决策"可追溯,避开权限漂移。

第二现场:广告预算是怎么被"悄悄改掉"的

退款事故容易被看见,因为它立刻报警。还有一种更隐蔽的事故:预算被悄悄改。

我们见过一个案例,某团队的 Agent 有"改广告预算"权限,分级在"写"而不是"资金",理由是"它只是调个数字,又不直接转账"。结果一次策略误判,把某 ASIN 的日预算从 ¥200 顶到了 ¥2000,连续烧了三天才在周报里被发现。三天 × ¥1800 ≈ ¥5400 的无声损耗,还没算连带的转化浪费。

这个案例说明两件事:第一,资金边界不能只看"是否直接转账",任何"钱会烧"的动作都该进双签 + 回滚;第二,不可回滚的动作一旦点下,靠审批是追不回来的,必须靠快照还原在分钟级止损。把预算当"写"而不是"资金",是第二常见的权限事故源。

90 天落地节奏(可直接排期)

别想着一次到位。我们给客户排的节奏是这样的:

  • 第 1–2 周(只读观察):只发 read_token,Agent 跑起来,把所有"拟执行"动作记成 proposed 日志,不落库。这阶段你会看清它到底想碰什么。
  • 第 3–6 周(受限写):逐个开 write_token,每个动作配单人/双签档与审批页五证据。先开"改状态""建工单"这类可逆动作。
  • 第 7–10 周(资金 + 回滚):最后开 fund_token,强制双签 + 幂等键 + 回滚(退款冲正、预算快照)。同时把 trace id 串起全链路。
  • 第 11–12 周(常态化审计):动作矩阵进周会,跑"权限使用记录"周审,清离职审批人、收临时写权限。

绝大多数团队把前 6 周压缩成"上线即全开",于是把事故也压缩进了前 2 周。

自检清单:上线前问自己 10 个问题

  1. 读、写、资金是三个独立 token 吗?
  2. 权限写在工具签名里,还是只写在提示词里?
  3. 第一阶段是不是只读?
  4. 每个动作的审批档(auto/single/dual)写清楚了吗?
  5. 审批页是否展示意图、证据、调用、影响、可逆性?
  6. 每个动作有幂等键吗?
  7. 每个可逆动作有回滚方案吗?
  8. 每次调用有 trace id 吗?
  9. 有没有不可回滚动作被放进了自动档?
  10. 动作矩阵这周有人复盘吗?

10 个里答不上 3 个,就先别让 Agent 碰资金。

成本账:治理 vs 不治理

还是用前面 5 万咨询量/月的运营来算。盲签导致 10% 误动作,其中 5% 变成 ¥50 投诉,就是 ¥12,500/月、¥150,000/年;再加 10 个复核人力(¥50k/人)= ¥500 万。治理的代价是一个兼职 owner + 一个周脚本。这笔账不用算第二遍——"免费"的放任,其实是最贵的方案。

五个关于权限的误区

  • 误区一:"有审批就安全"——审批是责任转移,不是边界。
  • 误区二:"模型乖就不会乱动"——边界必须在工具层,不靠模型表现。
  • 误区三:"预算不是资金"——会烧钱的动作必须进资金档。
  • 误区四:"日志记了调用就够了"——还要记证据、审批人、trace id。
  • 误区五:"上线配一次永久有效"——权限会随业务漂移,必须周审。

金融等强监管行业的额外要求

做金融、医疗、政务的团队,除了上面这些,还要多两层:一是职责分离(SoD)要有书面审批留痕,审计师会逐项核;二是外部实时事实必须来自可信数据层,不能让 Agent 自己抓网页猜政策版本——这一点上,像 Amazon Data API 这种实时数据层,价值就在于把"它基于哪版政策决策"变成可举证的事实,而不是模型的一句"我认为"。

怎么衡量治理健康度

别只看"有没有审批"。真正该盯的指标:审批拒绝率(长期为 0 说明审批形同虚设)、平均审批耗时(过长说明证据不够、审核人卡壳)、不可回滚动作占比(应趋近于 0)、trace id 覆盖率(必须 100%)、权限漂移次数(周审发现的问题数)。这五个数字,比"我们加了审批"这句话诚实得多。

审批页前后对比:同一笔退款,两种页面

坏的页面:"Agent 请求退款 ¥3,800,是否批准?" 好的页面:意图(买家说货损)、证据(订单 A 已签收;政策 v2025 30 天窗口可退)、工具调用(issue_refund 3800 市场 US)、影响范围(1 单 ¥3,800)、可逆性(24h 内可冲正)。同一个动作,两种决策——坏页面产出盲签,好页面产出真实判断,也常常产出证据不足时的果断"拒绝"。

反模式清单

  • 审批人写进提示词:软请求,模型可忽略。
  • 共享 admin token:出事分不清谁。
  • 日志只记动作名:不记证据、审批人、trace id。
  • "模型拒绝了"当"安全":拒绝 ≠ 已验证边界。
  • 小额定额自动批:仍在训练盲签习惯。

落地小结:治理不是按钮,是系统

把上面这些串起来,企业 Agent 权限管理就是一套可重复的机制:分级的动作、每次审批都亮证据、每个动作带幂等键、每个可逆动作有回滚、每次调用有 trace id,并且每周复盘一次。它不是一个"批准"按钮,而是一套让"批准"这两个字终于有意义的系统。做到这些,Agent 才从玩具变成能托付业务的同事。

小结

企业 Agent 权限管理治的是"责任",不是"开关"。当"批准"不再是盲签、出错一定能撤、出事查得到谁,Agent 才从玩具变同事。

系列前作:客服 Agent 拒答能力、端到端集成、Agent 不是交付单位、项目管理 Agent 系统集成、AI 转型 SOP 还是进入流程、企业 AI 知识库为什么会失效。欢迎在评论区交流你们的工具签名怎么设计。