Agent 权限控制:哪些动作可以自动做,哪些必须确认?
模型可以提出动作,但不能给自己授权。本文从身份、资源、策略、批准和隔离五层,建立可执行、可审计的 Agent 权限边界。

文章目录
- 导语
- 一、先分清五个经常混在一起的概念
- 1. 身份认证:你是谁
- 2. 资源授权:你能操作什么
- 3. 运行时策略:在当前条件下是否允许
- 4. 人工批准:这一次是否放行
- 5. 隔离:即使出错,最多能影响哪里
- 二、为什么“读、写、危险”不够
- 三、工具能被模型看到,不等于它能被执行
- 四、一次权限判断到底需要什么输入
- 为什么返回三种结果
- 五、一个不过度绑定场景的执行骨架
- 权限检查必须靠近执行点
- 工具不能继承平台的万能凭证
- 拒绝也要成为可理解的观察结果
- 六、哪些动作适合自动执行
- 七、哪些动作通常值得人工确认
- 八、前端和后端分别负责什么
- 前端:让边界可见,但不充当安全边界
- 后端:在每个真实入口强制执行
- 九、用一个例子检查是否真的理解
- 十、常见误区
- 把工具风险写进 Prompt 就算完成权限控制
- 认为 read 天然安全
- 让模型提供 userId、tenantId 或 role
- 批准一次后永久放行
- 先检查权限,等待很久后直接执行
- 有策略检查就给工具管理员密钥
- 十一、本篇总结
- 参考资料
导语
上一篇讨论 Planning 时,我们留下了一个重要边界:
计划说明 Agent 想做什么,却不能证明它有权这样做。
假设模型生成了下面的工具调用:
{
"name": "deploy_service",
"arguments": {
"service": "checkout-api",
"environment": "production"
}
}
这只表示模型提议部署生产环境,不表示部署已经获得许可。真正执行前,系统至少还要回答:
- 当前用户是谁,他能否操作这个服务?
- 这次会改哪个环境,影响多大,能否回滚?
- 组织策略允许自动执行,还是要求人工批准?
- 执行进程实际拿到了什么凭证和网络能力?
这些问题不能交给模型凭自然语言自我约束。它们要由模型之外的确定性系统检查。
这一篇,我们把 Agent 的“聪明”与系统的“权力”分开。
一、先分清五个经常混在一起的概念
1. 身份认证:你是谁
登录态、服务身份、租户信息回答的是 Authentication 问题。例如当前请求来自用户 u_123,属于租户 tenant_a。
这些信息应来自应用服务器验证过的会话或令牌,不能相信模型从对话中提取的“我是管理员”。
2. 资源授权:你能操作什么
Authorization 判断这个身份对具体资源是否拥有具体动作权限:
用户 u_123 能否对 tenant_a 的 checkout-api 执行 deploy?
这里的关键不是“能否调用 deploy 工具”这么粗,而是动作、资源和范围的组合。
3. 运行时策略:在当前条件下是否允许
用户可能有部署权限,但组织仍规定:生产环境只能在发布窗口操作,或金额超过一万元必须复核。
这类规则关注当前环境、参数、风险和业务状态。
4. 人工批准:这一次是否放行
权限通过不等于一定自动执行。对有明显外部影响、难以撤销或责任敏感的动作,系统可以暂停并请有资格的人批准。
批准不是补发权限。无权访问资源的人,即使点击“同意”,也不应让动作越权执行。
5. 隔离:即使出错,最多能影响哪里
最小权限凭证、文件系统沙箱、网络白名单、速率限制和资源配额,限制的是执行器真正能触达的范围。
权限判断可能有缺陷,工具也可能有漏洞。隔离层负责缩小最坏结果。
把这五层连起来,可以得到一条更准确的控制链:

模型位于这条链的起点,不是裁判。
二、为什么“读、写、危险”不够
很多系统会把工具标成 read、write、dangerous。这种标签有助于快速形成风险直觉,但不能直接当授权策略。
同一个读取动作,风险可能完全不同:
| 动作 | 看起来 | 实际差异 |
|---|---|---|
| 读取公开产品文档 | read | 通常影响较小 |
| 读取某客户合同 | read | 涉及租户隔离和敏感信息 |
| 导出全部员工资料 | read | 没有写入,却可能造成严重泄露 |
同一个写入动作也不一定都要确认。把草稿保存到个人工作区,通常可以自动完成;向十万名客户群发邮件,则需要更严格的控制。
所以风险至少与这些因素有关:
- 主体:谁发起,代表个人还是服务账号;
- 动作:读取、修改、删除、发布、付款等;
- 资源:个人草稿、生产服务、客户数据等;
- 范围:一条记录还是全量数据;
- 可逆性:能否撤销,恢复代价多大;
- 外部影响:是否通知他人、花钱或改变生产状态;
- 环境:开发、测试还是生产;
- 时间与上下文:是否在允许的窗口内,是否来自可信网络。
工程上可以保留粗粒度风险标签,供 UI 提示和默认策略使用;最终决定仍应结合真实参数和可信上下文。
三、工具能被模型看到,不等于它能被执行
这是 Agent 开发里很容易忽略的一层。
工具列表解决的是:模型现在可以提出哪些动作?
授权系统解决的是:这个具体调用现在是否允许执行?
两者可以共同收紧边界:
- 只向模型暴露当前任务可能需要的工具,减少误调用;
- 每次执行前仍做服务端授权与策略检查,防止越权。
不能只做第一步。模型看不到某个工具可以降低误用概率,却不是安全边界;攻击者仍可能绕过聊天界面直接请求后端接口。
也不能只把规则写进 System Prompt,例如:
不要在未经允许时部署生产环境。
这句话可以帮助模型少提不合适的动作,但它不是强制控制。通用型 Agent 的 System Prompt 更应该描述稳定的行为原则;具体用户、资源和临时审批状态,应由 Runtime 在每次调用时提供并校验,而不是塞进一个针对单场景过拟合的 Prompt。
四、一次权限判断到底需要什么输入
可以把策略输入整理为下面的结构。字段名只是本文示例,并非行业统一标准。
type ActionRequest = {
actor: {
userId: string;
tenantId: string;
roles: string[];
};
action: {
toolName: string;
arguments: unknown;
};
resource: {
type: string;
id: string;
environment?: "dev" | "staging" | "production";
};
context: {
runId: string;
requestedAt: string;
};
};
type PolicyDecision =
| { outcome: "allow"; constraints?: Record<string, unknown> }
| { outcome: "deny"; reasonCode: string }
| { outcome: "require_approval"; reasonCode: string };
注意各字段的来源:
actor来自服务端验证过的身份上下文;toolName和arguments来自模型提出的调用,经 Schema 校验;resource由应用代码根据参数解析,并在服务端查证;context由 Runtime 生成;PolicyDecision由确定性策略代码或专门的策略引擎产生。
模型可以辅助做风险分类,但不应凭自己的分类结果给自己放行。
为什么返回三种结果
allow、deny、require_approval 分别表示:
- 系统已经有充分规则可以自动放行;
- 当前动作不被允许,用户确认也不能绕过;
- 动作在权限范围内,但当前策略要求由人承担这次决定。
这比一个布尔值更贴近真实产品,也为下一篇的 Human-in-the-loop 留出了明确接口。
五、一个不过度绑定场景的执行骨架
下面代码只表达控制边界,不绑定 PR Review、客服或部署等单一业务。
async function executeRequestedAction(
call: ToolCall,
trustedContext: TrustedContext,
): Promise<ToolResult> {
const tool = toolRegistry.get(call.name);
if (!tool) return failure("TOOL_NOT_FOUND", false);
const parsed = tool.inputSchema.safeParse(call.arguments);
if (!parsed.success) {
return failure("INVALID_ARGUMENTS", false, parsed.error);
}
const request = await buildActionRequest({
tool,
arguments: parsed.data,
trustedContext,
});
const decision = await policyEngine.evaluate(request);
if (decision.outcome === "deny") {
await audit("action_denied", request, decision);
return failure(decision.reasonCode, false);
}
if (decision.outcome === "require_approval") {
const pending = await savePendingAction(request, decision);
return { ok: false, status: "waiting_for_approval", pendingId: pending.id };
}
// 等待期间资源和权限都可能变化,真正执行前再次检查。
await assertStillAuthorized(request);
return tool.execute(parsed.data, leastPrivilegeCredentials(request));
}
这里有三个容易漏掉的细节。
权限检查必须靠近执行点
如果检查发生在计划生成时,而执行发生在十分钟后,资源所有者、订单状态或用户角色都可能已经变化。执行前要重新校验,避免“检查时可以,执行时已经不可以”的竞态。
工具不能继承平台的万能凭证
即使策略层判断正确,执行器拿着全局管理员密钥也会放大缺陷。更好的做法是使用用户委托凭证、短期令牌或按任务裁剪的服务身份。
拒绝也要成为可理解的观察结果
模型需要知道动作为什么没执行,才能改走安全路径。但返回内容不应泄露敏感策略细节。例如告诉模型“当前身份无权访问该资源”通常足够,不必暴露完整角色配置。
六、哪些动作适合自动执行
没有一张适用于所有公司的固定清单。更稳妥的判断是:当系统可以确定身份和资源范围,动作符合显式策略,影响有限且可以审计时,才考虑自动执行。
典型例子包括:
- 在授权范围内读取非敏感资料;
- 查询订单状态、测试结果或构建日志;
- 在个人工作区创建可撤销的草稿;
- 对临时目录做范围受限的转换;
- 执行已有明确配额和速率限制的低影响操作。
“可自动执行”仍不代表无限执行。批量读取也要受分页、频率和数据范围限制。
七、哪些动作通常值得人工确认
当动作满足下面一个或多个条件时,人工确认往往有价值:
- 会向外部人员发送、发布或提交内容;
- 会产生费用、法律承诺或合规责任;
- 会修改生产环境或共享数据;
- 难以撤销,或撤销代价很高;
- 影响范围由运行时参数决定,而且这次范围较大;
- 模型的选择包含重要业务判断,系统无法用规则完全确定。
人工确认也不是越多越安全。如果用户每次都机械点击“允许”,确认会退化成没有信息价值的仪式。合理策略应尽量让低风险动作自动流转,把注意力留给真正需要判断的节点。
下一篇会进一步回答:确认界面要展示什么、Run 如何暂停,以及批准后如何安全恢复。
八、前端和后端分别负责什么
前端:让边界可见,但不充当安全边界
前端可以展示:
- 当前 Agent 正准备执行的动作;
- 目标资源、环境和影响范围;
- 自动放行、需要批准或被拒绝的原因;
- 动作执行后的结果与审计编号。
但隐藏按钮、禁用菜单都不能替代后端授权。
后端:在每个真实入口强制执行
后端需要:
- 从可信会话获取身份和租户;
- 校验工具参数并解析真实资源;
- 执行授权与策略判断;
- 使用最小权限凭证调用外部系统;
- 记录提议、决定、执行结果和关联 Run;
- 对并发、重试和过期批准做防护。
前端负责让人看懂,后端负责让规则无法绕过。
九、用一个例子检查是否真的理解
用户说:
把 checkout-api 部署到生产环境。
请不要背术语,按参与者重建过程:
- 模型根据任务和工具说明,提出
deploy_service调用; - Runtime 校验参数,并从服务端登录态取得用户与租户;
- 应用代码把服务名解析成真实项目和生产资源;
- 授权系统确认用户是否有部署该资源的权限;
- 策略发现生产部署需要人工批准,于是保存待批准动作,而不是调用部署接口;
- 批准后,Runtime 再检查权限、资源版本和发布窗口;
- 受限执行器使用短期凭证部署,并记录结果。
如果用户没有该服务的部署权限,第 4 步就应该拒绝。此时不能靠一个确认按钮把拒绝变成允许。
这个例子做了一个简化:真实组织还可能接入 RBAC、ABAC、策略即代码、变更审批和云厂商 IAM。实现可以不同,但“模型提议,可信系统授权,执行器受限”这条边界不应消失。
十、常见误区
把工具风险写进 Prompt 就算完成权限控制
Prompt 是行为引导,不是不可绕过的授权机制。
认为 read 天然安全
读取可能越租户、泄露隐私或造成批量导出。风险要结合资源和范围判断。
让模型提供 userId、tenantId 或 role
模型输出不是可信身份源。这些字段必须由服务端上下文注入。
批准一次后永久放行
批准应绑定具体主体、工具、参数、资源和有效期。范围过大的永久批准会悄悄扩大权限。
先检查权限,等待很久后直接执行
等待期间环境可能变化。执行前需要重新校验关键条件。
有策略检查就给工具管理员密钥
策略和最小权限执行是互补关系,不能互相替代。
十一、本篇总结
Agent 权限控制的核心不是给工具贴三个标签,而是回答一个完整问题:
一个已经验证的主体,在当前上下文中,能否以给定参数对某个具体资源执行这次动作?
请记住四个结论:
- 模型提出动作,不给自己授权;
- 工具可见性可以减少误用,但真正的安全边界在执行端;
- 权限、策略、人工批准和隔离解决不同问题;
- 决定必须绑定真实身份、资源和参数,并在执行前重新校验。
当策略返回 require_approval 时,Agent 不能继续偷偷执行,也不能丢掉当前进度。它需要暂停,把决策交给人,再从原位置恢复。
这正是下一篇的主题:Human-in-the-loop:如何设计用户确认、接管和反馈?
参考资料
- Model Context Protocol:Tools https://modelcontextprotocol.io/specification/2025-06-18/server/tools
- OpenAI Agents SDK:Human-in-the-loop https://openai.github.io/openai-agents-js/guides/human-in-the-loop/
- NIST:Zero Trust Architecture(SP 800-207) https://csrc.nist.gov/pubs/sp/800/207/final