
最近折腾了 AI Agent 的权限继承问题,踩了几个坑,这篇把核心问题说清楚。
当一个 AI Agent 需要做实际工作时,最危险但省事的做法就是给它一个 API Key,然后祈祷 prompt 能管住它。
这条路走不通。一个有用的 Agent 现在可以读取工单、创建 issue、更新 CRM 记录、运行查询、触发工作流、调用内部工具。如果这些都通过一个宽泛的 Key 来做,你有的不是用户权限,而是一个拿着借来的万能钥匙的机器人。
更安全的做法是 AI Agent 权限继承:Agent 以当前用户的受限权限来行动,仅限一个任务,有明确的边界、可撤销、有审计记录。
这篇说说怎么给生产环境的 AI 工具设计这套模式。
最近的 AI 平台讨论一直在绕同一个问题:Agent 变得有用是因为它们能行动,而不只是回答问题。但行动需要访问权限。
压力来自多个方向:
实战教训不是"永远不让 Agent 行动"。那太粗暴了。教训是:Agent 应该从用户和任务继承受限的权限,而不是从永久的共享密钥继承。
小团队经常从这个架构起步:
User request -> AI agent -> tool router -> service API key -> internal systems
看起来很快。演示也容易。还避免了 OAuth 的复杂性。但同时也埋下了一堆问题:
| 问题 | 实际会发生什么 |
|---|---|
| 没有用户边界 | Agent 可以访问用户正常情况下访问不到的数据 |
| 撤销能力弱 | 禁用一个用户不会停掉共享的 Key |
| 审计困难 | 日志显示"agent-service",而不是真正的行动者和目的 |
| prompt 注入爆炸半径大 | 恶意输入可以引导 Agent,而工具仍然有广泛权限 |
| 多租户隔离困难 | 多租户过滤器变成可选的应用逻辑,而不是强制的策略 |
| 密钥暴露 | Key 可能通过 trace、错误、截图或调试 prompt 泄露 |
这不仅是安全问题,也是产品质量问题。如果用户无法理解一个 Agent 被允许做什么、实际做了什么、如何撤销或回滚,那在关键时刻他们不会信任这个功能。
AI Agent 权限继承意味着 Agent 收到一个临时的、任务作用域的授权,这个授权派生自用户的身份、角色、租户、同意和当前工作流。
Agent 不会拿到用户的原始密码。不会拿到宽泛的服务 Key。它收到的是一个委托令牌或权限信封,里面写着:
一个简单的模型是这样的:
User session -> permission service -> task-scoped delegation token -> agent runtime -> policy-checked tool calls -> audit log + revocation path
核心思想:Agent 的权限不由 prompt 决定。prompt 可以解释任务,但后端强制执行什么被允许。
从一个普通对象开始。不要把设计藏在 prompt 里。
{ "delegation_id": "dlg_01J8...", "actor_user_id": "user_123", "tenant_id": "tenant_456", "agent_id": "support_refund_agent", "task_id": "task_789", "purpose": "draft_refund_response", "allowed_tools": ["tickets.read", "orders.read", "refunds.draft"], "resource_scope": { "ticket_ids": ["ticket_234"], "customer_ids": ["cust_987"], "max_order_age_days": 90 }, "action_mode": "draft_only", "budget": { "max_model_calls": 12, "max_tool_calls": 20, "max_runtime_seconds": 180 }, "approval_required_for": ["refunds.execute", "emails.send"], "expires_at": "2026-08-06T10:30:00Z", "policy_version": "agent-policy-v4"
} 这个对象成了产品、安全、工程、支持之间的契约。它也比"只访问用户能访问的内容"这种模糊指令更容易测试。
Agent 可能会声称它需要一个工具。这个声称不够。
每个工具调用都应该通过一个策略检查,结合:
一个简化版的 TypeScript 风格策略检查:
type ToolCall = { tool: string; args: Record<string, unknown>;
}; type Delegation = { actorUserId: string; tenantId: string; allowedTools: string[]; actionMode: "read_only" | "draft_only" | "supervised" | "bounded_autopilot"; resourceScope: { ticketIds?: string[]; customerIds?: string[]; }; approvalRequiredFor: string[]; expiresAt: string;
}; function authorizeToolCall(call: ToolCall, delegation: Delegation) { if (new Date(delegation.expiresAt) < new Date()) { return deny("delegation_expired"); } if (!delegation.allowedTools.includes(call.tool)) { return deny("tool_not_delegated"); } if (!userCanUseTool(delegation.actorUserId, delegation.tenantId, call.tool)) { return deny("user_lacks_permission"); } if (!resourceInScope(call.args, delegation.resourceScope)) { return deny("resource_out_of_scope"); } if (delegation.approvalRequiredFor.includes(call.tool)) { return requireApproval("human_approval_required"); } if (delegation.actionMode === "draft_only" && isStateChanging(call.tool)) { return deny("draft_mode_blocks_state_change"); } return allow();
} 注意漏掉了什么:没有"LLM 说这安全"这个分支。
模型可以提议。运行时决定。
委托令牌应该很无聊。这是夸奖。
好的委托令牌是:
避免那种变成影子账户的长期"Agent 令牌"。如果需要一个后台 Agent 继续后续工作,持久化工作流状态,然后在重新检查策略后发放新的委托。
对于长时间运行的工作,使用租约:
run starts -> delegation valid for 10 minutes
run pauses -> lease released
run resumes -> policy re-check -> new delegation issued
这有助于处理用户角色变更、客户记录被锁定、租户禁用了集成、或支持经理撤销了审批的情况。
大多数有用的 Agent 工作流在第一天并不需要完全自主。
使用模式:
| 模式 | Agent 可以做 | 适用于 |
|---|---|---|
| 只读 | 搜索、摘要、检查 | 研究、客服分流、分析解释 |
| 仅草稿 | 准备变更但不应用 | 邮件草稿、退款草稿、CRM 更新预览 |
| 监督模式 | 审批后执行 | 账单变更、用户消息、账户更新 |
| 受限自动驾驶 | 在硬限制内执行低风险操作 | 打标签、路由、丰富、小型内部更新 |
权限信封应该包含模式。工具处理器应该强制执行它。
不要只依赖 UI 标签。如果产品说"草稿模式",后端必须拒绝状态变更调用。
撤销是很多 Agent 系统变得模糊的地方。
你需要至少四条撤销路径:
Agent 运行时应该在每次工具调用前检查撤销,而不只是运行时开始时。
async function beforeToolCall(call, delegation) { const status = await delegationStore.getStatus(delegation.delegation_id); if (status.revoked) { throw new Error(`Delegation revoked: ${status.reason}`); } return authorizeToolCall(call, delegation);
} 这看起来严格,但能防止 Agent 失败的最坏版本:工作流在用户以为停止后继续行动。
每个有意义的 Agent 操作都应该留下一张收据。
好的收据回答:
收据示例:
{ "receipt_id": "rcpt_01J9...", "delegation_id": "dlg_01J8...", "actor_user_id": "user_123", "tenant_id": "tenant_456", "agent_id": "support_refund_agent", "tool": "refunds.draft", "resource_ids": ["order_555"], "decision": "allowed", "approval_id": null, "policy_version": "agent-policy-v4", "created_at": "2026-08-06T10:18:14Z"
} 收据不只是为了审计。它们帮助开发者调试错误输出、支持团队解释 Agent 行为、产品团队判断哪些工作流可以进一步自动化。
围绕 AI Agent、OAuth、MCP、API 安全的高排名内容经常把某一层讲得很好:
缺失的实战价值是这些层之间的连接。权限继承就是那个连接。
问题不只是"我把密钥存在哪?"而是:
这个 Agent 应该从用户继承什么确切权限来做这个任务,系统如何证明它强制执行了那个权限?
这是小团队应该尽早填补的架构缺口。
在给 Agent 访问真实工具之前用这个清单检查。
按风险给工具打标签:
如果一个工具无法分类,它就不应该对自主 Agent 可用。
不要让 Agent 从 prompt 发现自己的权限。在检查用户会话、租户、角色、套餐、同意和集成状态后,在后端生成权限信封。
如果 Agent 想退款一个订单,order_id 应该来自可信的查询或选中的 UI 记录,而不是单纯从自由文本获得。
审批应该显示 diff、来源证据和确切操作。"批准 Agent"太模糊了。"批准退款草稿 order_555 金额 $42.00"才可以审查。
模型 trace 显示 Agent 想了什么。收据显示系统允许了什么。两个都需要。
添加回归测试,让 Agent 尝试在错误的租户、错误的客户、错误的记录或过期的委托上使用有效工具。这些测试应该失败并关闭。
可见的停止按钮应该撤销委托、取消排队的步骤、阻止同一运行的未来工具调用。
客服 Agent 可以读取当前工单、检查近期订单、起草退款、准备回复。它不能执行退款或给客户发邮件,直到人类审批确切的操作。
分析 Agent 可以查询用户已经能访问的指标。委托包含行级租户过滤器、指标定义、查询预算和屏蔽列。Agent 不能通过写原始 SQL 绕过分析权限来访问更宽泛的仓库角色。
编码 Agent 可以读 issue、检查仓库文件、创建分支、起草 PR。它不能轮换密钥、改部署设置或未经审批合并。
销售 Agent 可以丰富线索、起草 CRM 备注、建议下一步。它不能导出完整客户列表或不经同意和速率限制发外联消息。
[User Session] | v
[Permission Service] ---- checks roles, tenant, consent, integration status | v
[Delegation Token / Envelope] | v
[Agent Runtime] | v
[Tool Policy Gateway] ---- checks tool, resource, mode, budget, approval | v
[Internal Tools / MCP Servers / APIs] | v
[Delegation Receipts + Audit Logs] 这个架构适用于 REST API、MCP 服务器、队列、浏览器操作、SQL 查询或内部 SDK 调用。重要的是每条行动路径都经过策略网关。
追踪揭示权限继承是否正常工作的指标:
如果每个操作都被批准,你的 Agent 可能限制太严了。如果没有任何操作被拒绝,你的策略可能没有真正在工作。
AI Agent 需要访问权限才能有用。但访问不等于给模型宽泛的密钥、隐形的权限或永久的授权。
权限继承给了开发者更好的中间路径:Agent 可以用用户的受限权限行动,针对特定任务,有收据、可撤销、每次工具调用都有策略检查。
这才是从炫酷 Demo 走向用户真正能信任的 Agent 工作流的方式。
AI Agent 权限继承是一种模式,Agent 收到临时、任务作用域的权限,派生自当前用户的权限、租户、同意和工作流模式。Agent 不会收到宽泛的 API Key 或原始凭证。
不一样。OAuth 可以是实现的一部分,但权限继承是更广泛的产品和运行时模式。它包括任务范围、资源限制、工具策略、审批门、撤销、预算和审计收据。
有时候可以,但服务账号仍然应该被租户、工具、任务和策略约束。一个能做所有事的宽泛服务账号是有风险的。偏好用户发起的工使用用户作用域的委托。
Prompt 可以描述规则,但不应该作为执行层。权限必须在工具调用执行前由后端服务检查。
保持短。很多交互任务只需要几分钟。长时间运行的工作流应该使用租约,在发放新委托前重新检查策略。
最大的错误是因为 Demo 更容易就给了 Agent 一个强力密钥。这个捷径通常会产生薄弱的审计日志、糟糕的撤销、大的爆炸半径和跨租户风险。