在这里插入图片描述

从工具不可见到数据不越权:AgentScope工具权限的三层防线

AgentScope 工具权限的三层防线

前置阅读:《若依框架和阿里 AgentScope 的权限封装》

上一篇主要讨论如何把若依的角色权限接入 Agent 工具体系,重点解决“无权限用户不应该在模型上下文中看到 Tool Schema”,以及为什么不能只依赖工具执行阶段的 @PreAuthorize。本文继续向下拆解,给出一套覆盖工具可见性、调用决策和业务数据范围的三层权限模型。

一、为什么 Agent 工具权限不能只做一次校验

传统 Web 接口的权限控制通常发生在 Controller 或 Service 执行之前:

@PreAuthorize("@ss.hasPermission('inventory:stock:query')")

但 Agent 的执行链路比普通 HTTP 请求更长:

用户问题
  → Agent 获取 Tool Schema
  → 大模型选择工具
  → Agent 发起 Tool Call
  → Java 工具方法执行
  → Service 查询业务数据
  → 结果返回大模型

如果只在 Java 方法执行前校验权限,会留下三个不同的问题:

  1. 用户没有工具权限,但模型已经看到了工具名称、描述和参数结构。
  2. 用户可以使用工具,但某次调用属于高风险操作,需要动态拒绝或人工确认。
  3. 两个角色都能调用同一个查询工具,但能够读取的数据范围不同。

这三个问题对应三个不同的安全边界,不能用一个注解或一次拦截全部替代。

二、三层权限模型

一套完整的 Agent 工具权限体系,可以拆分为以下三层:

层级核心问题控制时机典型结果
第一层:工具可见性这个角色对应的 Agent 是否拥有该工具Tool Schema 发送给模型之前保留或移除 Tool Schema
第二层:调用决策当前这一次工具调用是否允许执行每次 Tool Call 执行之前ALLOW、DENY、ASK
第三层:数据权限工具执行后允许访问哪些业务数据Service、Mapper 或查询条件构建阶段本人、本地区、本部门或跨区域数据

三层权限分别保护不同对象:

第一层保护模型上下文
第二层保护工具执行入口
第三层保护真实业务数据

三、第一层:角色与 Agent 的工具可见性

第一层解决的是:

当前角色对应的 Agent,究竟应不应该拥有这个工具?

假设系统中存在三个工具:

query_inventory        查询库存
allocate_spare_part    调拨配件
delete_maintenance     删除维修记录

普通维修工可能只拥有 query_inventory,区域管理员拥有查询和调拨工具,系统管理员才拥有删除工具。

在模型推理之前,应根据当前用户的角色与权限生成本轮可见工具集合:

普通维修工:
  query_inventory

区域管理员:
  query_inventory
  allocate_spare_part

系统管理员:
  query_inventory
  allocate_spare_part
  delete_maintenance

没有授权的工具不仅不能执行,而且不应该把以下内容发送给模型:

  • 工具名称;
  • 工具用途描述;
  • 参数名称;
  • 参数 JSON Schema;
  • 可能暴露内部业务能力的示例。

这一层属于“能力披露控制”。它能够减少模型上下文噪声,也能降低提示词注入诱导模型尝试越权工具的风险。

需要特别注意:隐藏 Tool Schema 不等于完成了全部安全控制。模型不可见只是第一道门,工具执行入口仍然需要独立校验。

四、第二层:AgentScope Permission System 的调用决策

AgentScope Java 提供了 io.agentscope.core.permission 权限系统。根据官方文档,Permission System 会拦截 Agent 的每一次工具调用,并返回三种决策之一:

  • ALLOW:允许执行;
  • DENY:拒绝执行;
  • ASK:暂停执行并询问用户是否确认。

这意味着同一个已经对模型可见的工具,不同调用仍然可以得到不同结果。

例如,维修工拥有配件调拨工具,但不同参数代表不同风险:

查询本地区配件库存                 → ALLOW
调拨一个普通配件                   → ASK
跨区域调拨高价值配件               → DENY

1. Permission System 的三个判断来源

官方权限系统综合以下三个组件进行决策。

Rules

PermissionRule 针对具体工具及调用模式配置 ALLOW、DENY 或 ASK。

规则可以在 PermissionContextState 中预先配置,也可以在用户确认 ASK 时接受建议规则,动态加入当前权限上下文。

PermissionContextState permissionContext =
        PermissionContextState.builder()
                .mode(PermissionMode.DEFAULT)
                .addAllowRule(
                        "query_inventory",
                        new PermissionRule(
                                "query_inventory",
                                null,
                                PermissionBehavior.ALLOW,
                                "rolePolicy"))
                .addAskRule(
                        "allocate_spare_part",
                        new PermissionRule(
                                "allocate_spare_part",
                                null,
                                PermissionBehavior.ASK,
                                "rolePolicy"))
                .addDenyRule(
                        "delete_maintenance",
                        new PermissionRule(
                                "delete_maintenance",
                                null,
                                PermissionBehavior.DENY,
                                "rolePolicy"))
                .build();

这里的 ruleContent 不一定只能匹配工具名称。工具可以通过 matchRule() 对实际调用参数进行匹配,从而实现“同一工具、不同参数、不同决策”。

Mode

PermissionMode 决定没有命中显式规则时的默认行为。

Mode行为适用场景
DEFAULT未明确允许的调用进入确认流程默认安全策略
ACCEPT_EDITS自动允许安全范围内的编辑操作用户在线的开发场景
EXPLORE允许只读,拒绝写操作和命令只读探索、规划
BYPASS默认放行,但拒绝规则和危险检查仍有效完全可信沙箱
DONT_ASK将所有 ASK 转换成 DENY定时任务、无人值守流程

对于没有实时交互界面的后台任务,不应该让 ASK 无限等待。此时可以使用 DONT_ASK,把需要确认的调用安全降级为拒绝。

Built-in Checks

工具还可以通过 ToolBase#checkPermissions,基于本次真实输入进行动态风险分析。

它适合检查无法仅靠静态角色表达的风险,例如:

  • 操作目标是否属于危险目录;
  • 调拨数量是否超过阈值;
  • 是否涉及高价值配件;
  • 目标设备是否处于锁定状态;
  • 当前调用是否会产生不可逆副作用。

官方文档强调,工具自身的危险检查属于运行时检查,不应被普通规则或模式绕过。

2. ASK 不是弹一个确认框那么简单

ASK 表示 Agent 的本次执行被挂起,系统需要保存:

  • 用户身份;
  • 会话身份;
  • Tool Call 编号;
  • 工具名称;
  • 工具参数;
  • 风险说明;
  • 建议规则;
  • 确认有效期。

用户确认时,必须恢复原来的调用,而不是重新让模型生成一次工具参数,否则确认的内容和最终执行的内容可能不一致。

确认页面还应该明确展示:

准备执行的工具:allocate_spare_part
调拨配件:智能马桶主控板
调拨数量:2
来源区域:华东一区
目标区域:华东三区
风险提示:跨区域调拨

用户确认的是一笔确定的操作,而不是模糊的“是否允许 Agent 继续”。

五、第三层:工具背后的业务数据权限

第三层解决的是:

用户拥有这个工具,也允许执行这次调用,但他最终可以看到哪些数据?

以库存查询为例,普通维修工和区域管理员都拥有:

inventory:stock:query

他们也都可能被 Permission System 判定为 ALLOW,但数据范围并不相同:

普通维修工:
  只能查询所属地区的库存

区域管理员:
  可以查询管辖区域内多个地区的库存

总部管理员:
  可以查询全国库存

因此,数据权限必须在业务 Service 或数据库查询条件中落实:

Set<Long> readableRegionIds =
        dataScopeService.getReadableRegionIds(currentUserId);

return inventoryService.getInventory(
        request.getProductId(),
        readableRegionIds);

查询条件必须取自服务端可信身份,而不能直接相信大模型生成的 regionId。

错误方式:

return inventoryMapper.selectByRegionId(toolRequest.getRegionId());

正确思路:

模型传入目标区域
  → 服务端计算当前用户可访问区域
  → 判断目标区域是否在授权集合内
  → 带数据范围条件查询

即使模型伪造了其他区域编号,也无法越过服务端的数据范围限制。

六、三层权限如何协同

以“区域管理员跨区域调拨配件”为例:

第一层:工具可见性
  区域管理员拥有 allocate_spare_part
  → Tool Schema 可以进入模型上下文

第二层:调用决策
  当前调用属于跨区域调拨
  → Permission System 返回 ASK
  → 用户确认后才继续

第三层:数据权限
  校验来源区域和目标区域是否都在管理员管辖范围
  → 在范围内执行
  → 超出范围拒绝

任何一层失败,都必须停止调用:

Tool 不可见
  OR Permission = DENY
  OR 用户拒绝 ASK
  OR 数据范围不满足
  → 不执行工具

七、角色权限矩阵示例

角色Tool SchemaPermission 决策数据范围
三方维修工查询维修手册、查询本人维修单查询 ALLOW,写操作 DENY本人被派发的工单
普通维修工查询库存、申请配件查询 ALLOW,申请 ASK所属地区
区域管理员查询库存、配件调拨查询 ALLOW,跨区调拨 ASK管辖区域
总部管理员全部管理工具普通操作 ALLOW,高风险操作 ASK全国
无人值守任务仅白名单工具DONT_ASK,其余拒绝任务配置的数据范围

这个矩阵中的三列不能合并:

  • Tool Schema 决定模型“知不知道有这个能力”;
  • Permission 决定本次调用“能不能做、是否要问”;
  • 数据范围决定最终“能操作哪些业务对象”。

八、工程落地时最容易踩的坑

1. 只隐藏工具,不校验执行入口

攻击者可能绕过模型入口直接构造 Tool Call,因此执行阶段仍需权限控制。

2. 只做 ALLOW / DENY,忽略 ASK

完全放行和完全拒绝之间需要有人机协同边界。配件调拨、删除记录、批量修改等操作更适合 ASK。

3. 把数据权限写进 Prompt

Prompt 只能提示模型,不是安全边界。地区、部门、用户等数据范围必须由后端身份和查询条件决定。

4. 共享可变权限上下文

如果 Agent 是单例,不能把某个用户的动态权限直接修改到全局共享 Toolkit 或全局规则集合中,否则并发请求可能串权。

权限状态至少应按用户和会话隔离:

(userId, sessionId) → PermissionContextState

5. ASK 恢复时重新生成参数

确认前后的工具名称和参数必须一致。建议保存原始 Tool Call,并在确认后恢复执行。

6. 把 BYPASS 当作生产默认值

BYPASS 更适合完全可信的隔离环境。生产系统应从最小权限开始,根据明确规则逐步放行。

九、推荐的安全原则

可以用一句话概括三层工具权限:

模型只看见应该看见的工具,每次调用都经过明确决策,工具最终只能访问授权范围内的数据。

落地时建议遵循:

  1. 默认不暴露未授权 Tool Schema。
  2. 默认使用安全的 Permission Mode。
  3. 高风险写操作优先使用 ASK。
  4. 无人值守场景将 ASK 降级为 DENY。
  5. 数据权限在 Service 和 SQL 查询中强制执行。
  6. 所有身份和数据范围均从服务端可信上下文获取。
  7. 记录 ALLOW / DENY / ASK、用户确认和数据范围审计日志。
  8. 单例 Agent 下的权限状态必须按用户与会话隔离。

十、总结

Agent 工具权限不是一个简单的“能不能调用”问题,而是一条完整的安全链路:

角色与 Agent 工具授权
  → Tool Schema 可见性
  → Permission System 调用决策
  → 用户确认
  → 业务数据权限
  → 审计记录

第一层让无权限工具不进入模型上下文;第二层使用 AgentScope Permission System 对每次调用执行 ALLOW / DENY / ASK 决策;第三层在真实业务查询中落实地区、部门、用户等数据范围。

三层共同工作,才能真正实现工具能力最小化、调用过程可控制、业务数据不越权。

参考资料

  1. AgentScope Java:Permission System
  2. 若依框架和阿里 AgentScope 的权限封装
Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐