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、授权是安全;前者每次问人、后者按身份与作用域确定性放行或拒绝,二者不可互相替代。
这是整篇文章的支点,也是最容易踩的坑。
payment.refund、金额 9999、给用户 U"丢给人,人点确认才继续。它是逐跳、一次性、依赖人在线的。LangGraph 的 interrupt()、OpenAI 的 needs_approval、AutoGen 的 Intervention Handler 都属于这一层。ALLOW / DENY / APPROVE,并落审计日志。AGT 的 govern()、APP 的加密授权凭证、Claude Code 的 permission rules 属于这一层。二者最致命的差异在劝服性:批准可以被模型用话术绕("我已确认用户同意""这是紧急退款"),而确定性授权拦截根本不让模型看到放行逻辑——它只是一个 if policy.denies(ctx): raise 的函数调用,模型再怎么"劝说"也改不了一段已经编译进运行时的代码。OWASP 把这种"授权只在 provisioning 时给、runtime 无二次校验"称为结构性漏洞,APP 白皮书一句话点破:prompt guardrails are advisory and rely on model compliance。
把这条理清后,六栈的强弱立刻分层:几乎没有主流框架原生提供"授权"层,它们提供的其实是"批准"层。下面逐一拆。
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 审计直接对口,单价高、复购强,是训练营里"安全合规模块"的最佳锚点。
批准问"行不行",授权问"允不允许",作用域层则问"以谁的身份、能碰哪些数据"。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 租户隔离与数据权限"专题,客单价高于纯功能课。
授权闸门最容易被忽略的下游问题:策略拒绝之后,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;
三道闸门串联、不可互换:批准挡"人没确认的",授权挡"策略没允许的",作用域挡"身份够不着的"。任何一道缺位,下游就裸奔。审计日志贯穿全程——合规审计看的不是"你做了什么",而是"每一次拒绝/放行,你能不能证明它经过了确定性策略"。
按场景给选型,避免"全家桶"式空话:
govern() 包裹所有破坏性工具 + APP 凭证做跨服务调用。default=DENY,宁可误杀不可漏放。审计接入 SIEM。interrupt(),并手写一个 policy 节点插在 tool node 之前(社区已有"Agent Permission Protocol 应用到 LangGraph 模式"的成熟范式),补齐缺失的授权层。needs_approval,并在 README 标红"未含授权层、禁止接入生产数据"。tools/list,让模型连越权选项都看不到——这是最省心的纵深防御。落地优先级:① 先上审计(无审计不谈安全)→ ② 再上默认拒绝 + 白名单(一行配置挡住 80% 裸奔)→ ③ 最后上确定性授权拦截(AGT/APP)补齐模型不可贿赂的那一环。
一句话总结本期:没有 govern() 的工具就是裸的,没有 default=DENY 的 Agent 就是越权的,而"批准"无论做多漂亮,都换不来"授权"那一票。
govern() 确定性拦截、默认拒绝、审计与拒绝处理总览interrupt() / Command resume 的批准机制needs_approval 的边界[!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 等场景全覆盖。
