AI Agent 接入企业系统的第一个方案通常是:让用户登录一次,把拿到的令牌交给 Agent 用。这个方案能跑通,但它有三个无法回答的问题——Agent 用了谁的权限?用户撤销授权后 Agent 还能不能用?出事时怎么区分是人的操作还是 Agent 的操作?
Agent 是主体,不是工具
第一步是把 Agent 建模成独立主体。令牌里带 principal_type=machine,与人类用户走同一套内核但主体类型不同。Agent 有自己的凭据、自己的角色、自己的调用配额。
uiam-agent — MACHINE 主体
01// Agent 不再伪装成 USER,它有独立的 subject_type02{03 "sub": "agent:sales-copilot",04 "principal_type": "machine",05 "tenant_id": "acme",06 "roles": ["order:read", "customer:read"],07 "quota": { "tool_calls_per_min": 120 }08}委托链:权限取交集,不取并集
当 Agent 代表某个用户行动时,走 RFC 8693 Token Exchange 换一张新令牌。关键在权限计算:新令牌的权限是「Agent 被授予的应用权限」与「该用户实际拥有的权限」的交集,再按 maxPermissionLevel 裁剪上限。
这一点非常重要。如果取并集,Agent 就成了权限放大器——用户没有的权限,Agent 反而有了。取交集意味着 Agent 永远不可能比「委托它的用户」做得更多。
TokenExchangeServiceImpl — 权限交集
01val appPerms = appPermissionResolver.resolve(clientId)02val userPerms = userPermissionResolver.resolve(subjectToken.sub)0304// 交集:Agent 不能比被委托的用户做得更多05val granted = appPerms.intersect(userPerms)06 .filter { it.level <= maxPermissionLevel }0708// act claim 记录委托关系,多跳场景可嵌套09val act = ActClaim(sub = subjectToken.sub, nested = subjectToken.act)工具级授权写在代码里
Agent 能调哪些工具,不是写在文档里靠人遵守,而是写在注解上由框架强制。agent_tools 注册表登记每个工具的归属与权限要求,调用前逐次校验。
uiam-agent — 工具级授权
01@RequiresToolPermission("order:refund")02@PostMapping("/tools/order-refund/invoke")03fun invokeRefund(@RequestBody req: ToolRequest): ToolResult {04 // 到这里说明调用方持有该工具权限,且未超配额05 return toolExecutionService.execute(req)06}代价是什么
- 每个 Agent 都要先注册、配角色,不能「先跑起来再说」
- 委托链多了一次令牌交换,链路更长、排查更复杂
- 多跳委托的 act 嵌套需要小心处理,否则会丢失中间委托关系
这些成本换来的是:任何一次 Agent 的数据访问,都能回答「谁授权、用哪个工具、用了哪些权限」。在合规场景里,这个回答能力是准入门槛。