Agent 工具权限管理
Agent 需要调用外部工具(执行命令、读写文件、调用 API),但把所有权限都开放意味着用户可以让它执行 rm -rf /。"全有或全无"的权限模型不够用——危险工具需要限制,安全工具需要放开,某些工具只在特定模式下可用。
怎么设计一套灵活的工具权限管理?
权限分级
最简单的方式:把工具分成几类。
class ToolCategory(Enum):
READ = "read" # 只读,安全
WRITE = "write" # 写入,有风险
EXECUTE = "execute" # 执行命令,危险
ADMIN = "admin" # 管理员专用
工具注册时标记分类:
registry.register("read_file", read_file, ToolCategory.READ)
registry.register("write_file", write_file, ToolCategory.WRITE)
registry.register("execute_command", execute_command, ToolCategory.EXECUTE)
registry.register("delete_file", delete_file, ToolCategory.ADMIN)
用户角色
定义用户角色,不同角色有不同权限:
class UserRole(Enum):
GUEST = "guest" # 只读
USER = "user" # 读写
ADMIN = "admin" # 全部权限
PERMISSIONS = {
UserRole.GUEST: [ToolCategory.READ],
UserRole.USER: [ToolCategory.READ, ToolCategory.WRITE],
UserRole.ADMIN: [ToolCategory.READ, ToolCategory.WRITE, ToolCategory.EXECUTE, ToolCategory.ADMIN],
}
权限检查
执行工具前检查权限:
def execute_with_permission(tool_name: str, args: dict, role: UserRole) -> str:
category = registry.categories.get(tool_name)
allowed = PERMISSIONS.get(role, [])
if category not in allowed:
return f"错误:权限不足,无法执行 {tool_name}"
return registry.execute(tool_name, args)
模式控制
除了用户角色,还可以用"模式"控制权限。
比如 jojo-code 有两种模式:
- PLAN 模式:只读,不执行写操作
- BUILD 模式:完整权限
class PlanMode(Enum):
PLAN = "plan" # 只读
BUILD = "build" # 完整权限
def execute_with_mode(tool_name: str, args: dict, mode: str) -> str:
category = registry.categories.get(tool_name)
if mode == PlanMode.PLAN.value and category != ToolCategory.READ:
return f"PLAN 模式下不允许执行 {tool_name}"
return registry.execute(tool_name, args)
这样,用户可以在 PLAN 模式下安全地让 Agent 分析问题,不用担心它执行危险操作。
完整示例
from enum import Enum
from typing import Dict, Callable
class ToolCategory(Enum):
READ = "read"
WRITE = "write"
EXECUTE = "execute"
class UserRole(Enum):
GUEST = "guest"
USER = "user"
ADMIN = "admin"
PERMISSIONS = {
UserRole.GUEST: [ToolCategory.READ],
UserRole.USER: [ToolCategory.READ, ToolCategory.WRITE],
UserRole.ADMIN: [ToolCategory.READ, ToolCategory.WRITE, ToolCategory.EXECUTE],
}
class ToolRegistry:
def __init__(self):
self.tools: Dict[str, Callable] = {}
self.categories: Dict[str, ToolCategory] = {}
def register(self, name: str, func: Callable, category: ToolCategory):
self.tools[name] = func
self.categories[name] = category
def execute(self, name: str, args: dict, role: UserRole) -> str:
# 检查工具是否存在
if name not in self.tools:
return f"错误:工具 {name} 不存在"
# 检查权限
category = self.categories[name]
allowed = PERMISSIONS.get(role, [])
if category not in allowed:
return f"错误:权限不足,无法执行 {name}(需要 {category.value} 权限)"
# 执行
return self.tools[name](**args)
# 使用
registry = ToolRegistry()
registry.register("read_file", read_file, ToolCategory.READ)
registry.register("write_file", write_file, ToolCategory.WRITE)
registry.register("execute_command", execute_command, ToolCategory.EXECUTE)
# Guest 用户尝试执行命令
result = registry.execute("execute_command", {"cmd": "ls"}, UserRole.GUEST)
# 输出: 错误:权限不足,无法执行 execute_command(需要 execute 权限)
# Admin 用户执行命令
result = registry.execute("execute_command", {"cmd": "ls"}, UserRole.ADMIN)
# 输出: 文件列表...
我踩过的坑
坑一:权限检查遗漏
有些地方直接调用工具,忘了检查权限。
解决:把权限检查放在统一的地方(Registry),而不是每个工具里。
坑二:权限粒度太粗
一开始只有"能用"和"不能用"两种,后来发现不够用。
解决:细分权限:读、写、执行、管理员。
坑三:权限配置硬编码
权限规则写死在代码里,改一下要重新部署。
解决:把权限配置抽出来,做成可配置的(文件或数据库)。
下一步行动
- 列出你的工具:每个工具的风险等级是什么
- 定义用户角色:哪些用户能用哪些工具
- 加上权限检查:统一在 Registry 里检查
权限管理不是一次性的,每加一个新工具,都要评估它的风险等级。