首页 › ai-agents › 批准非授权:工具闸门六栈实测

批准非授权:工具闸门六栈实测

概述

2026 年 4 月,微软开源了 Agent Governance Toolkit(AGT),核心只有一句口号:用 govern() 把任意工具函数包起来,所有动作在执行前被确定性拦截、按策略评估、自动审计与拒绝。三个月前,Crittora 发布了 Agent Permission Protocol(APP),主张"提示词护栏是劝告、依赖模型服从;APP 提供协议级、加密可验证的执行时授权"。这两件事共同指向同一个被严重低估的结论:绝大多数团队以为给 Agent 加了"批准"(approval)就等于有了"授权"(authorization),其实没有。

OWASP LLM Top 10 for LLM Applications 2025 的 LLM06「过度代理(Excessive Agency)」把根因拆成三条:功能过度、权限过度、自主性过度。本期六栈实测聚焦中间这条——工具权限闸门。我们用四枚探针(裸奔探针、批准探针、策略探针、越权探针)压测 LangGraph、OpenAI Agents SDK、CrewAI、AutoGen、Claude(Code/API 双形态)以及 AGT/APP 结构层,核心发现:批准是 UX、授权是安全;前者每次问人、后者按身份与作用域确定性放行或拒绝,二者不可互相替代。

核心区分:批准 ≠ 授权

这是整篇文章的支点,也是最容易踩的坑。

二者最致命的差异在劝服性:批准可以被模型用话术绕("我已确认用户同意""这是紧急退款"),而确定性授权拦截根本不让模型看到放行逻辑——它只是一个 if policy.denies(ctx): raise 的函数调用,模型再怎么"劝说"也改不了一段已经编译进运行时的代码。OWASP 把这种"授权只在 provisioning 时给、runtime 无二次校验"称为结构性漏洞,APP 白皮书一句话点破:prompt guardrails are advisory and rely on model compliance。

把这条理清后,六栈的强弱立刻分层:几乎没有主流框架原生提供"授权"层,它们提供的其实是"批准"层。下面逐一拆。

第一闸门:批准层(HITL)三栈实测

LangGraph 把批准做成了一等公民,这是它在这条赛道上最稳的地方:

from langgraph.types import interrupt, Command

def refund_node(state):
    action = {"tool": "payment.refund", "amount": state["amount"], "to": state["user"]}
    verdict = interrupt({"pending": action})          # 暂停,挂起 checkpoint
    if verdict.get("approve"):
        return do_refund(action)
    return {"rejected": True, "reason": verdict.get("note")}
# 恢复:graph.invoke(Command(resume={"approve": True, "note": "..."}), config)

OpenAI Agents SDK 走的是 needs_approval + guardrail 路线,AutoGen Core 则用 Intervention Handler 在 tool_agent 与 tool_use_agent 之间拦截请求,二者都能 approve/reject。但批准探针(reject 后看 Agent 是否绕路)暴露了一个共同弱点:拒绝只是当跳的回值,没有跨调用的策略记忆。模型完全可以下一轮换个参数、换个工具路径再调一次,形成"批准疲劳"循环——人按了十次拒绝,第十一次按漏了,破坏性操作就落地了。更糟的是,三家默认都不拒绝(default allow),破坏性工具裸跑零摩擦。

优点:上手快,单步可控,审计轨迹清晰(配合 LangSmith/Langfuse)。缺点:粒度粗、依赖人在线、可被话术软化、无结构化策略。变现/训练营评估:适合做"HITL 合规"入门课,但作为安全卖点偏弱——客户问到"模型能不能自己绕过批准"时答不上来。

第二闸门:授权层(确定性拦截)实测

这是 2026 年真正的增量。微软 AGT 的设计哲学一句话概括:默认拒绝、白名单制、确定性应用层拦截。

from agent_governance_toolkit import govern, Policy, Decision

policy = Policy(
    rules=[
        ("payment.refund", lambda c: c.identity.has_scope("payments:write")),
        ("db.drop",        lambda c: Decision.DENY),   # 硬拒绝,永不可调
    ],
    default=Decision.DENY,                              # 默认拒绝
    require_approval={"payment.refund"},                # 放行后仍需人工批准
)

@govern(policy)
def refund(order_id, amount):
    return stripe.refund(order_id, amount)

每次调用前:拦截 → 策略评估 →(放行 / 批准 / 拒绝)→ 审计。关键在于这个判定发生在 Python 函数边界、而非 prompt 里——策略探针(让模型用各种话术诱导退款)在 AGT/APP 上直接失效,因为模型根本接触不到放行逻辑。APP 更进一步,给每次工具调用附带加密签名授权凭证(绑定 principal、scope、过期时间),运行时验签,连"调用方身份是否被冒用"都覆盖了。

为什么这是结构性而非劝告性:沙箱(见往期"落地即隔离")解决的是"代码在哪跑、能不能碰文件系统";回滚("做错能退")解决的是"出事后怎么撤销";失败恢复("调挂即编")解决的是"工具报错怎么重试"。授权闸门解决的是更上游的一问:这个调用,到底允不允许发生。它在前三者之前挡住,是第一道也是唯一一道"模型不可贿赂"的防线。

缺点:策略编写是额外工程债,误配 default=DENY 会让合法流程全线卡死;APP 的加密凭证对短任务偏重。变现评估:企业合规刚需,金融/医疗/法务的 SOC2、ISO27001 审计直接对口,单价高、复购强,是训练营里"安全合规模块"的最佳锚点。

第三闸门:作用域与身份层(ScopeGate / RBAC)

批准问"行不行",授权问"允不允许",作用域层则问"以谁的身份、能碰哪些数据"。Claude Code 的权限模型是这条赛道上少数把三者都做实的产品:typed tool system + allow/deny rules + MCP allowlist + settings.json 的 hooks,可以做到"这个 Agent 只能读 db.customers、且只对欧盟租户生效"。

// settings.json —— 工具级 allowlist,默认拒绝未列出的工具
{
  "permissions": {
    "allow": ["mcp__stripe__read_invoice", "db.read:customers:eu"],
    "deny":  ["mcp__stripe__refund", "bash:rm:*"],
    "ask":   ["mcp__stripe__charge"]            // 命中即触发批准
  }
}

allow / deny / ask 三态把批准(ask)与授权(allow/deny)压进了同一份声明式配置,运维可在组织级 settings 锁定,开发者无法在代码里覆盖——这是 越权探针(让 Agent 调用 scope 外工具)唯一能稳定拦住的形态。ScopeGate 把同样的思路推广到 MCP/OAuth:每个 Agent 持有不同 scope 的令牌,tools/list 只返回它有权看到的工具,模型连"还有别的工具存在"都不知道,从源头消除越权。

优点:声明式、可审计、可版本化、与身份系统(SSO/OAuth)打通。缺点:scope 粒度设计难,过细则配置爆炸、过粗则形同虚设;MCP allowlist 需要服务端配合(tools/list 过滤)。变现评估:多租户 SaaS 的必备能力,可包装成"Agent 租户隔离与数据权限"专题,客单价高于纯功能课。

拒后恢复实测:deny 之后 Agent 会怎样

授权闸门最容易被忽略的下游问题:策略拒绝之后,Agent 是优雅重规划,还是死循环、还是把拒绝原因泄露给用户。这与往期"调挂即编"(工具报错的恢复)不同——那是异常路径,这里是正常的业务拒绝(policy DENY 不是 bug)。

实测四种表现:① 优雅重规划(推荐):deny 结果作为结构化消息回灌,Agent 改走合规路径,例如退款被拒→改发起工单。② 死循环:模型把 deny 当成"再试一次"的信号,反复换参数重调,token 烧穿。③ 泄露:把内部策略名(rule: db.drop denied by policy P-7)原样吐给终端用户,暴露安全拓扑。④ 降级欺骗:模型发现 payment.refund 被拒,转而调 payment.credit(语义等价的旁路工具)绕过——这正是往期"撞名即错调"的同义碰撞在授权场景的复现,只有作用域层把同类工具整体 deny 才防得住。

拒后表现 LangGraph OpenAI SDK CrewAI AutoGen Claude Code AGT/APP
结构化 deny 回灌 需自接 需自接 弱 中 强(hooks) 强(内置)
默认不暴露策略细节 △ △ ✗ △ ✓ ✓
同义旁路兜底 ✗ ✗ ✗ ✗ △(需配 deny 组) ✓(按 scope)

结论:拒后恢复的成熟度与授权层是否原生强相关。没有原生 deny 语义的栈,几乎都倒在"泄露"和"死循环"上。

六栈闸门矩阵:四探针总评

用四枚探针横评(✓ 默认拦住 / △ 可配置拦住 / ✗ 默认裸奔):

探针 \ 栈 LangGraph OpenAI Agents SDK CrewAI AutoGen(Core) Claude(Code/API) AGT/APP
裸奔探针(破坏性工具零摩擦) ✗ ✗ ✗ ✗ ✓ ✓
批准探针(reject 可被话术绕/循环) △ △ ✗ △ ✓ ✓
策略探针(DENY 对模型不可绕过) △(自建节点) ✗ ✗ △ ✓ ✓
越权探针(scope 外调用被拦) ✗ ✗ ✗ ✗ ✓ ✓
默认拒绝(default deny) ✗ ✗ ✗ ✗ △ ✓
审计日志一等公民 △ △ ✗ △ ✓ ✓

读法:前四栈在"批准"上有差异,在"授权"上集体缺席;Claude Code 与 AGT/APP 是仅有的、把确定性授权做实的产品形态。一个反直觉但重要的发现——框架越"易用"(CrewAI),闸门越虚;因为易用意味着默认放行、最少配置,这与"默认拒绝"的安全哲学天然冲突。这是"易用性税"在安全维度的具象化。

架构图:三层闸门与拦截流

flowchart TD
    U[用户指令] --> AGENT[Agent / LLM]
    AGENT -->|tool_use 请求| G1{第一闸门·批准<br/>HITL: interrupt / needs_approval}
    G1 -->|reject| R1[拒后恢复: 重规划或终止]
    G1 -->|approve / auto| G2{第二闸门·授权<br/>确定性拦截: govern / APP 签名}
    G2 -->|DENY<br/>模型不可绕过| R2[拒后恢复 + 审计]
    G2 -->|ALLOW| G3{第三闸门·作用域<br/>ScopeGate / allow-deny rules}
    G3 -->|scope 外| R3[403 + 审计]
    G3 -->|scope 内| TOOL[工具执行]
    TOOL --> AUDIT[(审计日志)]
    R1 --> AUDIT
    R2 --> AUDIT
    R3 --> AUDIT
    classDef gate fill:#ffe,stroke:#c80,stroke-width:2px;
    class G1 G2 G3 gate;

三道闸门串联、不可互换:批准挡"人没确认的",授权挡"策略没允许的",作用域挡"身份够不着的"。任何一道缺位,下游就裸奔。审计日志贯穿全程——合规审计看的不是"你做了什么",而是"每一次拒绝/放行,你能不能证明它经过了确定性策略"。

推荐方案

按场景给选型,避免"全家桶"式空话:

落地优先级:① 先上审计(无审计不谈安全)→ ② 再上默认拒绝 + 白名单(一行配置挡住 80% 裸奔)→ ③ 最后上确定性授权拦截(AGT/APP)补齐模型不可贿赂的那一环。

一句话总结本期:没有 govern() 的工具就是裸的,没有 default=DENY 的 Agent 就是越权的,而"批准"无论做多漂亮,都换不来"授权"那一票。

参考来源

  1. Introducing the Agent Governance Toolkit(微软开源博客,2026-04) — AGT 的 govern() 确定性拦截、默认拒绝、审计与拒绝处理总览
  2. Agent Governance Toolkit 架构文档(microsoft.github.io) — 应用层拦截:每个 agent action 执行前按策略评估
  3. Agent Permission Protocol(APP)白皮书(Crittora,2026-01) — 协议级、加密可验证的执行时授权;"prompt guardrails are advisory"
  4. OWASP LLM06:2025 Excessive Agency — 过度代理三根因:功能/权限/自主性过度
  5. Agentic Trust Framework:面向 AI Agent 的零信任(CSA,2026-02) — Zero Trust 治理规范
  6. Human-in-the-Loop(LangGraph 官方文档) — interrupt() / Command resume 的批准机制
  7. Guardrails and human review(OpenAI Agents SDK 官方指南) — guardrails 与 needs_approval 的边界
  8. User Approval for Tool Execution using Intervention Handler(AutoGen) — tool_agent 拦截工具调用请求做批准
  9. Tool System & Permissions(Claude Code 架构,DeepWiki) — typed tool system、allow/deny/ask 规则与 MCP allowlist
  10. Securing LangGraph Multi-Agent Workflows: Tool-Level Permissions(dev.to) — 在 LangGraph 中手建 policy 节点补齐授权层的工程范式

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入