何时需要给 Agent 设置权限边界
讨论 Agent 权限控制的必要性与时机,涉及安全和可信度,对生产级 Agent 系统的架构决策有指导意义。
讨论 Agent 权限控制的必要性与时机,涉及安全和可信度,对生产级 Agent 系统的架构决策有指导意义。
欢迎各位的到来。您可以期待我们所有的 TNS 内容会在周一至周五准时送达,让您掌握最新资讯和行业动向。
请查收确认邮件,您可以在邮件中调整偏好设置,甚至加入其他订阅组。
在您喜爱的社交媒体网络上关注 TNS。
成为 TNS 在 LinkedIn 上的关注者。
等待首封 TNS 通讯期间,您可以浏览最新的精选和热门文章。
AI agent 在仅生成文本时看起来是无害的,但一旦它调用工具,风险概况就会改变。
起初,那个工具似乎很轻微:读取日志、总结工单、搜索文档或分类事件。然而,范围蔓延几乎是必然的。很快,agent 就需要更新工单、打开部署请求、重启 worker,或查询内部系统。
此时,agent 不再只是聊天界面。它成了一个执行表面。
许多团队在这里仍然几乎完全关注 prompt 工程。Prompt 很重要,但它已经不再是最强有力的控制手段。一旦 agent 通过工具执行操作,工具访问就成为了生产访问。架构必须回答一组完全不同的问题:
谁在请求这个工具调用?
工具是否已注册且有意公开?
该操作是否被不可变策略阻止?
调用者的角色是否具有所需范围?
参数是否有效?
该操作是只读的、可逆的、有风险的,还是具破坏性的?
该操作是否需要人工批准?
系统是否会原生审计该决定?
"一旦 agent 通过工具执行操作,工具访问就成为了生产访问。"
如果您的系统依赖自然语言工具描述来回答这些问题,它对于生产环境来说太脆弱了。
工具描述很有用,因为它们帮助模型决定何时工具是相关的。但是,描述绝不是权限边界。
考虑一个名为 infra_tool 的工具。它的描述声称可以"帮助检查和管理基础设施"。这可能足以进行本地演示,但作为执行契约就不足了。"管理"是否意味着读取日志、重启 worker、打开部署请求或删除资源?谁被授权执行每项操作?
安全的设计将工具选择与授权解耦。模型提议一个工具调用,但确定性策略层决定该调用是否继续。
最小可行架构看起来像这样:
Agent 请求
↓
可信工具注册表查询和风险元数据
↓
静态风险策略
↓
角色/范围授权
↓
严格参数验证
↓
批准门控
↓
工具执行器
↓
审计日志
(您可以在 GitHub 上查看该模式的离线参考实现。)
关键是,agent 永远不会直接调用工具。它只请求一个调用。治理层拥有最终执行决定权。
参考实现使用确定性 Python 包。它避免了云调用和外部 API 密钥,以演示一个狭窄的目的:在执行前阻止不安全的路径。
核心角色策略刻意设计得很简单:
DEFAULT_ROLE_SCOPES = {
"viewer": frozenset({"logs:read"}),
"operator": frozenset({"logs:read", "worker:restart:request"}),
"admin": frozenset(
{"logs:read", "worker:restart:request", "deploy:request"}
),
}
这个映射不是全面的企业 IAM 系统。它只是证明了边界。viewer 读取日志。operator 请求重启 worker。admin 打开部署请求。未知角色会以关闭的方式失败,因为它们收到一个空范围集。该实验室还完全阻止了具破坏性的工具,即使有人试图通过注册表公开它们。
从可信注册表中获取工具规范后,策略以快速失败的顺序评估请求:
# 1. 首先强制执行不可变的静态策略。
if spec.risk_level == RiskLevel.DESTRUCTIVE:
return block(
call,
spec.risk_level,
"destructive tools are disabled",
)
# 2. 强制执行基于身份和角色的授权。
allowed_scopes = role_scopes.get(call.user_role, frozenset())
if spec.scope not in allowed_scopes:
return block(
call,
spec.risk_level,
"role does not have required scope",
)
# 3. 在成本更低的策略检查之后验证调用者控制的参数。
schema_errors = registry.validate_arguments(spec, call.arguments)
if schema_errors:
return block(
call,
spec.risk_level,
"; ".join(str(error) for error in schema_errors),
)
# 4. 对于有风险的或明确受门控的操作,需要批准。
if spec.requires_approval or spec.risk_level == RiskLevel.RISKY:
approval_status = approval_gate.status_for(
call.approval_id,
call.request_id,
)
if approval_status != ApprovalStatus.APPROVED:
return approval_required(call, spec.risk_level)
仅有授权还不够。授权用户仍然可以传递无效参数。
例如,一个 read_logs 工具接受服务和限制。服务必须与已知值匹配,系统必须拒绝意外参数。这防止了模糊的工具接口悄悄扩展其自身范围。
在该实验室中,系统允许这个请求:
{
"user_role": "viewer",
"tool_name": "read_logs",
"arguments": {
"service": "api",
"limit": 5
}
}
但它在执行前阻止了这个请求:
{
"user_role": "viewer",
"tool_name": "read_logs",
"arguments": {
"service": "api",
"limit": 5,
"write": true
}
}
不要将额外的 write 字段视为无害的。在生产环境中,当下游代码解释这些参数时,意外参数可能会导致意外的权限提升。严格的 schema 将歧义转化为明确的工程决策。
排序在这里很重要。验证仍然在执行前进行,但仅在请求通过静态风险和授权检查之后。未授权的调用者不应消耗验证工作或接收有关工具接受的有效负载的不必要细节。
agent 绝不应该决定操作是否有风险。可信注册表拥有该定义。
"agent 绝不应该决定操作是否有风险。可信注册表拥有该定义。"
在参考实现中,每个工具都附带一个硬编码的风险级别:read_only、reversible_write、risky 或 destructive。策略在评估调用者控制的参数之前读取该元数据。
重启 worker 或打开部署请求是有风险的,需要人工批准。具破坏性的工具仅存在于注册表中,以证明策略原生地阻止它。
这个区别很重要。agent 可能为危险请求幻觉出安全描述,或者用户可能将具破坏性的操作伪装成常规维护。策略层必须忽视该叙述,依赖由工程控制的元数据。
产品团队通常将人工批准视为 UI 功能:按钮、模态框或 Slack 消息。UX 很重要,但批准从根本上是一个架构问题。
批准门控必须有状态地或密码学地绑定到请求 ID。泛型批准令牌不足够,因为它可以被重放到不相关的操作中。
该系统产生三个有效结果:allow、block 或 approval_required。
第三个状态至关重要。没有它,团队会将工作流坍缩成二元成功或失败。有风险的操作暂停以获得批准不是失败。这是一个有效的工作流状态,表明调用者被授权请求该操作、有效负载有效,且执行仍需人工验证。
大多数系统记录成功执行,但很少捕获预防性决定。对于 AI agent,被阻止的调用通常产生最有价值的遥测数据。
强大的审计记录捕获:
如果 viewer 尝试部署,系统记录阻止决定。有风险的重启触发 approval_required 记录。批准的重启记录允许决定。这条记录线允许安全团队重建 agent 执行了什么以及它尝试做了什么。
生产系统还应对审计记录应用数据最小化。参数可能包含敏感值,所以保留、编辑、访问控制和篡改抵抗是治理边界的一部分。
这个架构不会神奇地保护 agent 免受 prompt 注入、身份欺骗或合规失败。它通过删除 agent 对执行的最终权限来关闭一个危险的缺口。
它也改变了安全对话。工程师不再希望"prompt 能保护",而是可以指向确定性注册表、静态策略、严格 schema、角色控制、批准状态和审计线索。这是用于生产安全审查的更强大基础。
权衡是摩擦。
严格的 schema 需要维护。角色映射需要明确所有权。风险级别需要定期审查。批准流程增加延迟,审计日志需要保留和访问策略。宽容的工具比受治理的工具更快演示,但宽容工具是生产风险隐藏的地方。
"让模型提议;让确定性策略决定。"
治理规则很简单:随着工具改变状态的能力增加,对模型授权判断的信任必须减少。让模型提议;让确定性策略决定。
在授予 agent 访问真实工具的权限前,确保您可以回答以下问题:
如果您对任何一个回答"否",您就是在过早地将 agent 连接到生产。
初始 AI agent 演示可能看起来像一个聪明的 UI。生产现实是不同的。一旦 agent 调用工具,它就成为了真实系统控制平面的一部分。
这并不意味着工程团队应该放弃使用工具的 agent。这意味着他们必须停止仅将其视为 prompt 设计问题。工具访问就是生产访问。相应地进行架构设计。