文章分析 Agent 跨 CRM、退款和消息工具执行任务时,长期密钥和宽泛权限如何放大非确定性行为的风险。提示错误、恶意工具响应或幻觉都可能在持有生产凭据时演变为事故。
AI 智能体正迅速进入生产系统,能够访问真实的客户数据、内部工具,以及影响营收的工作流程。一个客服自动化智能体可能在一次任务中,从 CRM 读取工单、总结对话历史、发起部分退款、创建内部升级处理工单,再向 Slack 发布状态更新。运行正常时,这些智能体可以简化工作流程,降低日常运营成本。出现故障时,它们的失效方式却与传统自动化不同:行为具有非确定性,影响范围可能很大,而且往往还持有完整的生产环境凭据。
要发挥作用,智能体需要足够的访问权限,才能跨多个工具执行动态的多步骤工作流程。但要保证安全,它就不能以“超级用户”的身份运行,对这些工具长期拥有广泛权限。大多数团队倾向于沿用自己熟悉的访问方式,比如将长期有效的 API 密钥写入环境变量,或使用为人类用户设计的 OAuth 流程,然后很快发现,这些方式从来就不是为非确定性软件设计的。配置不当的提示词、恶意的工具响应,甚至一次简单的幻觉,都可能演变成严重事故。
本指南将介绍如何把最小权限原则应用于自主系统。你将学习如何按能力而非资源限定权限范围,签发与特定执行计划绑定的短期凭据,将身份、授权和执行拆分为独立的层,以及为高风险操作引入人工介入审批。
传统的访问控制模型假设,操作主体属于以下两类之一:
第一类是人类用户:通过交互操作,活动范围受会话边界限制,受到 UI 约束,并且在单个工作流程中的行为通常可以预测。
第二类是后端服务:行为具有确定性,工作流程固定,而且可以通过静态代码路径进行审计。AI 智能体不属于其中任何一类。
智能体在设计上就具有非确定性。使用相同的提示词处理相同的数据,运行两次也可能产生不同的工具调用序列。它们会以开发者没有显式编写的方式串联工具调用,还可能通过两类已有充分记录的威胁受到不可信输入的影响:
此外,智能体越来越多地会创建子智能体,因此,一条始于单个用户请求的委派链,可能在完成之前扩展到多个相互独立的上下文中。
为上述两类主体构建的身份基础机制,例如会话令牌、服务账号和 OAuth scope,本身并没有问题;配合严格的权限范围限制、中介控制和运行时强制执行,它们可以很好地发挥作用。问题在于,它们并不是为非确定性的多步骤自主执行设计的,而其典型使用方式所依赖的有限会话或确定性工作流程,在这里并不成立。会话令牌假设交互流程有明确边界。服务账号假设行为具有确定性。像 billing:write 这样的 OAuth scope,则假设另一端的应用会按照用户的意图理解它。当智能体在没有额外控制层的情况下继承其中任何一种机制时,它获得了权限,却没有获得让原有模型得以安全运行的相应问责机制。
在讨论更好的模式之前,有必要先看看团队通常会采用哪些捷径。单独来看,每一种似乎都合理,但组合起来就可能带来危险。
第一种常见的反模式,是直接将长期有效的 API 密钥嵌入智能体配置或环境变量。智能体持有一个根凭据,可以广泛访问某个工具;一旦智能体进程、日志或内存遭到入侵,该凭据就会暴露。虽然这在非智能体软件中也是问题,但智能体面临的风险更高:根据具体配置,它们可能被诱骗或迫使执行不该执行的操作,或泄露自己能够访问的凭据,而做到这些,通常比利用持有长期凭据的传统软件漏洞容易得多。
即便通过 Vault 或 AWS Secrets Manager 等工具建立了凭据轮换机制,真正的问题仍然是影响范围;轮换往往执行不一致或速度缓慢,而从凭据泄露到完成轮换之间的时间,足以让攻击者,或行为失常的智能体,造成严重损害。
第二种反模式,是完整继承用户的 OAuth scope。用户以自己拥有的权限授权智能体访问 CRM,此后,无论智能体实际执行什么任务,它都会在所有未来操作中长期拥有这些 scope。智能体继承了用户原本只打算有选择地行使的权限。然而,智能体并不具备用户所拥有的判断力、上下文感知能力,也无法像用户一样承担责任,因此,智能体意外滥用这些过于宽泛的权限,只是时间问题。
第三种也是破坏性最强的反模式,是智能体对其编排的工具实际拥有超级用户权限,而且通常是系统级权限,而非单个用户级权限。当你直接使用可用的最宽泛 scope,而没有定义更窄的权限范围时,这种情况就会悄然发生。前面提到的客服智能体,可能因为唯一可用的账单 scope 是 billing:write,就被授予了这个 scope。于是,它可以发起任意金额的退款、修改任意客户的付款方式,以及取消任意订阅,尽管它的实际职责只是按照严格限定的政策,发放小额善意退款。
设想一下,一个拥有 billing:write 权限的客服运营智能体收到用户请求:“为订单 #12345 办理全额退款。”该订单的总金额为 10,000 美元。由于没有机制区分哪些退款金额允许处理、哪些不允许,智能体便直接执行了操作。访问控制层中没有任何机制阻止它。损失在几毫秒内就已发生,而唯一留下的审计痕迹是一条退款记录,看起来与正常的合法退款完全一样。
解决智能体权限问题的第一步,是不再从资源的角度思考权限,而是从能力的角度思考。像 billing:write 这样的 scope,描述的是一个资源类别和一个操作动词。而像 billing.refund.issue_under_50_usd 这样的能力,则明确表达了一种具体的操作类型、一个资源限制,以及隐含的风险边界。不过,通常不会像这样把能力检查编码进 scope,因为这必然会导致 scope 数量急剧膨胀。
按能力限定的权限,能够用业务实际采用的思考方式来表达授权。当产品经理决定客服智能体可以发放不超过 50 美元的善意退款时,这个决定应当体现在由授权系统评估的声明式策略中,而不是分散在调用 API 的应用代码里,成为一处处命令式检查。50 美元的阈值究竟由授权引擎本身强制执行,还是通过代码内的检查来执行,例如在签发令牌前查询一个专门的策略层,属于实现细节;关键在于,这条规则必须集中管理,并且可以审计。
这正是细粒度授权系统发挥作用的地方。虽然它们能解决其中一个具体问题,但并不总是能提供一体化的完整方案。
OpenFGA 是由 Auth0 的 FGA 团队创建的开源授权引擎,如今已成为 Cloud Native Computing Foundation(CNCF)的项目。它实现了基于关系的访问控制(Relationship-Based Access Control,ReBAC)。ReBAC 擅长建模“谁可以对什么执行操作”。客服智能体不只是一个角色,还是一个实体,与特定资源类型之间存在具体且有边界的关系,而这些关系会产生不同的权限。你可以表达这样的规则:“只有当订单所属客户与智能体之间存在一张仍在处理中的工单时,智能体才能为该订单退款。”

纯粹的 ReBAC 无法强制执行“退款金额低于 100 美元”这样的数值约束或基于属性的约束。OpenFGA 通过 Conditions 扩展了基础 ReBAC 模型,允许你为关系附加谓词,从而在授权检查时一并评估数值限制、有时限的授权,以及上下文属性。
在 OpenFGA 中,上图中的两层会合并为一次检查:关系及其条件会一起求值,其中任何一项都可以拒绝访问。像“智能体可以为金额小于配置上限的订单退款”这样的能力,可以直接通过 OpenFGA 的条件来表达,金额既可以存储在关系元组中,也可以在检查时通过上下文提供。对于希望将纯授权策略与声明式策略代码分开的团队,在上层叠加 Cedar 或 OPA 这样的专用引擎也是合理的替代方案,但并非必需;OpenFGA 可以同时处理关系图和属性约束。
具体来说,带有每个智能体独立额度限制的客服智能体退款能力,在 OpenFGA 的 DSL 中可以这样表示:
model
schema 1.1
type user
type agent
type customer
type order
relations
define owner: [customer]
define can_refund: [agent with refund_within_limit]
condition refund_within_limit(refund_amount: int, agent_limit: int) {
refund_amount <= agent_limit
}
要授予这项能力,需要写入一个关系元组,将为该智能体配置的上限作为持久化的条件上下文保存下来。对于退款上限为 50 美元的客服智能体:
await fgaClient.write({
writes: [
{
user: "agent:support_agent_01",
relation: "can_refund",
object: "order:12345",
condition: {
name: "refund_within_limit",
context: { agent_limit: 50 },
},
},
],
});
检查时,运行时会提供条件上下文中与本次请求相关的另一部分,也就是此次尝试退款的实际金额:
const { allowed } = await fgaClient.check({
user: "agent:support_agent_01",
relation: "can_refund",
object: "order:12345",
context: { refund_amount: 75 },
});
OpenFGA 在计算条件时会合并这两部分上下文;如果某个键同时出现在两者中,持久化的元组上下文优先于请求上下文。当元组中存储的是 agent_limit: 50,检查时提供的是 refund_amount: 75,条件 refund_amount <= agent_limit 的计算结果就是 false,返回的 allowed 也为 false;智能体无法自主发起 75 美元的退款,运行时接下来会触发人工审批流程(本文后面会介绍),而不是请求具备退款能力的令牌。
实际应用中的要点是,定义与真实业务边界相匹配的能力。不要用 billing:write,而要用 billing.refund.issue_under_50_usd。不要用 crm:read,而要用 crm.contact.read_for_assigned_tickets。当你发现自己定义的某项能力宽泛得让人不安时,不妨停下来想一想:能否在不影响所构建功能的前提下,将它收窄为权限更小的能力。
能力范围限定解决的是智能体能做什么。任务范围限定解决的是它何时能做,以及能做多久。这是两个独立的问题,混为一谈是常见的错误。
设计良好的智能体会尽可能减少或消除持久凭证。它不会长期持有访问权限,而是请求与每次操作的执行计划绑定的短期令牌。令牌只包含该计划所需的能力,很快就会过期(以分钟计,而不是以天计),并在使用后丢弃。在实践中,一些系统仍会缓存短期令牌,或依赖限定权限范围的服务账号来满足基础设施方面的需求。目标并不在于追求形式上的纯粹,而在于缩短任何一个凭证能够发挥作用的时间窗口。
这种模式有几个有用的特性:
凭证泄露事件的风险窗口大幅缩短,因此,从智能体内存中泄露的令牌只能在很短的时间内使用。
智能体被攻破后的影响范围缩小,因为它只能使用当前持有的短期令牌执行操作。
智能体本身从不接触根凭证。由一个独立组件代表智能体获取令牌,因此,即使智能体进程被攻破,底层的 OAuth 刷新令牌或 API 密钥也不会随之泄露。
Auth0 的 Token Vault 在身份层实现了这一模式。它安全地存储已连接服务(Google、Slack、Salesforce、GitHub 等)的 OAuth 令牌,智能体则按需请求限定于特定任务的访问令牌,而不必自行处理长期凭证。Token Vault 实现了 OAuth 2.0 Token Exchange(RFC 8693),其基础是标准,而非专有流程。根据所使用的流程,智能体可能完全不会接触底层刷新令牌;在为 SPA 加后端架构设计的访问令牌交换流程中,刷新令牌始终保留在 Auth0 一侧。
对于本文提到的客服智能体,这意味着,当它决定发起退款时,手中并没有现成的、具备退款能力的令牌。运行时首先会根据策略层评估请求,检查诸如以下问题:金额是否在智能体可自主处理的阈值内、客户是否未被标记为需要进行欺诈审查、智能体与该订单是否具有所需的关系。只有通过这些检查后,运行时才会请求一个限定于退款能力和该特定客户的短期访问令牌。如果智能体在两次任务之间被攻破,就没有可供利用的常驻退款权限;此前的令牌已经过期,而签发新令牌需要再次通过策略检查。
设计智能体访问控制时,一个有用的澄清是:三个不同的关注点往往被合并到了同一个实现中。
身份用于确定智能体是谁。它回答的问题是:“是哪个智能体,代表哪个用户,发起了这次请求?”身份相对稳定;智能体通常会在多个任务中保持一致的身份。
授权用于确定经过身份验证的主体被允许做什么。它回答的是:“对于这个身份,哪些能力适用?”授权是一项策略决策,应当针对每次操作重新评估,而不是沿用长期授权。
执行阶段的强制校验用于确定在特定运行时上下文中,哪些操作实际被允许。它回答的是:“对于这次具体调用及其具体载荷,策略是否允许执行?”这包括退款金额、目标资源,以及只有在实际发起请求时才能确定的上下文约束。
混淆这些层次,会导致一些常见的故障模式。如果身份与授权被视为一回事,那么权限就会在身份验证时固定下来,无法适应正在执行的任务。同样,如果授权与执行耦合在一起,策略就无法得到集中管理、审计或更新。每一层都需要自己的边界,而且每个边界都应由不同的组件负责强制执行。
在实践中,要处理这三个关注点,就需要在任何生产环境中的智能体系统里设计三个独立的层次。
身份提供方层负责身份验证和令牌签发。Auth0 验证用户身份,通过 Token Vault 管理已连接的账号,签发限定权限范围的令牌,并处理委托流程。身份提供方知道智能体是谁,以及它可以使用哪些能力,但不执行业务逻辑。
运行时环境层解析智能体的计划,向身份提供方请求凭证,并在调用下游工具之前执行策略校验。这通常就是你的智能体编排代码,往往基于 LangChain、LlamaIndex 或 Vercel AI SDK 等框架构建。
在设计良好的系统中,运行时与 LLM 进程本身是分离的:LLM 生成工具调用,而运行时决定这些调用是否被允许,为它们请求凭证,并执行实际的 API 调用。许多现实中的智能体系统并未做到这一点,它们会将 API 密钥传入工具封装,或通过工具配置暴露令牌。让凭证处于 LLM 无法触及的范围,是一个值得有意识地追求的设计目标,并不是系统天然具备的属性。
工具层是实际执行操作的下游系统,例如 CRM、计费服务或 Slack API。工具层负责自身的权限校验和日志记录,独立于智能体或运行时对操作是否被允许的判断。这种防御机制确保,即使运行时被攻破,工具仍会执行自身的授权规则。
保持这种分离会带来一个关键的安全收益:LLM 进程本身从不持有凭证。运行时负责获取凭证,在单次 API 调用中提交凭证,然后将其丢弃。试图诱导 LLM 泄露“它的凭证”的提示词注入攻击将无从下手,因为 LLM 从未获得过任何凭证。

即使凭证已经限定了能力范围和任务范围,有些操作仍然需要额外一步:在执行时获得明确的人工批准。这并不是权限模型失效后的补救措施,而是一种有意的设计选择,适用于风险程度足以证明额外流程有必要的操作。
问题在于,哪些操作越过了这条界线。客服智能体发放 5 美元的退款,大概不应该需要人工批准;自动化的价值就在于无需人工干预就能处理这些操作。但同一个智能体如果试图发放 2,000 美元的退款、向被标记为需要欺诈审查的账户退款,或对客户记录执行任何破坏性操作,情况就完全不同了。对于这些操作,做对比做得快更重要。
Auth0 使用客户端发起的后通道认证(Client-Initiated Backchannel Authentication,CIBA)标准实现异步审批。这是 OpenID Foundation 制定的一项规范,允许客户端(例如智能体后端)通过独立的可信设备,以带外方式请求用户批准。目前,大多数生产环境中的智能体系统都通过定制流程、内部策略引擎,或 Temporal 这样的工作流系统来处理审批。CIBA 提供了一种基于标准的替代方案,可以接入现有的 OAuth 基础设施,无需另外构建一套审批系统。当客服智能体判断某项操作超出了其自主执行的阈值时,其后端会向 Auth0 发送 CIBA 请求。用户会通过 Auth0 Guardian 应用,在已注册的移动设备上收到推送通知(也支持短信或电子邮件渠道),智能体则轮询等待响应。如果用户批准,智能体会收到一个作用范围限定为该特定操作的令牌。如果用户拒绝,或请求超时,则不会执行任何操作。

CIBA 与丰富授权请求(Rich Authorization Requests,RAR)结合使用时,能力会更强。RAR 是 OAuth 2.0 的一个扩展,允许授权请求携带详细的结构化上下文,说明究竟要批准什么。用户看到的不再是笼统的“批准访问账单系统?”,而是具体的提示,例如“批准为客户 Jane Doe 的订单 #12345 退款 2,000 美元”。结构化的 authorization_details 载荷让审批具备可验证性和可审计性,这是授予宽泛权限范围所无法做到的。
这种模式直接解决了 10,000 美元退款场景中的问题。智能体的能力范围允许它自主处理低于较低阈值的退款。对于任何超过该阈值的退款,智能体必须触发 CIBA 流程才能继续,用户也会在批准前看到确切的金额和退款对象。智能体无法绕过这一流程,因为执行退款所需的令牌只有在批准后才会签发。
最后一层是可观测性。即使智能体受到严格的权限控制,它仍然会跨多个系统自主运行,因此,了解它做了什么、为什么这么做,以及代表谁执行操作,对于调试、事件响应和合规都至关重要。
有三个要素很重要:
审计记录应该捕获智能体的决策,而不只是操作。仅仅知道发生了一笔退款还不够;你还需要知道智能体当时在执行哪个计划、哪些工具调用使流程走到了这一步,以及哪些上下文影响了这个决策。
委托链应该明确,使每一次 API 调用都能沿着用户 → 智能体 → 子智能体 → 工具 → 资源的链路追溯,并记录其中的每一跳。
撤销必须快速而精准。如果智能体行为异常,你需要撤销其长期授权、使执行中的令牌失效,并停止后续工具调用,同时不影响整个系统的运行。
Auth0 平台通过集中管理令牌签发来提供这套基础设施,这意味着每一个发给智能体的令牌都会经过统一的控制平面。撤销 Token Vault 中的连接会立即阻止后续令牌交换,而审计日志则会记录每一次令牌签发和使用。
AI 智能体需要专门为非确定性、自主工作流设计的访问控制模型。全盘继承人类用户的权限,或使用静态服务账户的权限,都无法满足这一需求。智能体会以用户和开发者都未曾预料的方式使用这些权限,而现有的访问控制层对此毫无察觉。
构成可用于生产环境的智能体架构基础的三种模式是:
限定能力范围的权限,明确规定具体操作及其限制,而不是宽泛的资源操作权限
限定任务范围的凭证,有效期短,并绑定到具体的执行计划,而不是长期有效的授权
分层执行控制,将身份、授权和执行分离为不同的组件
这些模式共同限制了智能体出错时的影响范围,同时保留了让智能体发挥价值的自主性。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。