赋予 Agent 自主执行能力的同时,必须建立清晰的安全边界。Addy Osmani 警告过"cognitive surrender"风险——当人类过度依赖 Agent 自主决策时,可能忽视其行为的安全影响。TrueFoundry 在企业级部署中强调的权限分层和审计追踪,是 Loop Engineering 安全体系的核心。本节构建完整的权限管理框架。
Agent 不应拥有完成当前任务所需之外的任何权限。在 Loop Engineering 中,Agent 在 discover 阶段不需要写权限,在 execute 阶段不需要部署权限,在 verify 阶段不需要网络权限。这是对风险的主动管理。
构建支持多维度权限控制的 Agent 权限管理器,按阶段×环境动态调整。
"""permission_manager.py — Agent 权限管理器""" import json, time from dataclasses import dataclass, field from enum import Enum from typing import Optional class Environment(Enum): DEVELOPMENT = "development" STAGING = "staging" PRODUCTION = "production" class ApprovalStatus(Enum): AUTO_APPROVED = "auto_approved" REQUIRES_APPROVAL = "requires_approval" DENIED = "denied" @dataclass class Permission: tool_name: str allowed_paths: list[str] = field(default_factory=lambda: ["/"]) denied_paths: list[str] = field(default_factory=list) network_allowed: bool = False requires_approval: bool = False @dataclass class PermissionPolicy: stage: str; environment: Environment permissions: list[Permission] = field(default_factory=list) def get_permission(self, tool_name: str) -> Optional[Permission]: for p in self.permissions: if p.tool_name == tool_name: return p return None def is_tool_allowed(self, tool_name: str) -> bool: return any(p.tool_name == tool_name for p in self.permissions) class PermissionManager: """权限管理器: 四层防御""" def __init__(self, environment: Environment = Environment.DEVELOPMENT): self.environment = environment self.policies: dict[str, PermissionPolicy] = {} self._setup_defaults() def _setup_defaults(self): """设置默认权限策略""" # 开发环境: execute阶段拥有大部分权限 dev_exec = [ Permission("read", allowed_paths=["/"], network_allowed=True), Permission("write", allowed_paths=["/tmp/","./","/root/"], denied_paths=["/etc/","/var/"]), Permission("edit", allowed_paths=["/tmp/","./","/root/"], denied_paths=["/etc/","/var/"]), Permission("exec", network_allowed=True), ] self.policies["discover"] = PermissionPolicy( "discover", Environment.DEVELOPMENT, [ Permission("read"), Permission("exec", network_allowed=True)]) self.policies["plan"] = PermissionPolicy( "plan", Environment.DEVELOPMENT, [Permission("read")]) self.policies["execute"] = PermissionPolicy( "execute", Environment.DEVELOPMENT, dev_exec) self.policies["verify"] = PermissionPolicy( "verify", Environment.DEVELOPMENT, [Permission("read"), Permission("exec")]) # 生产环境: 严格限制 self.policies["discover_prod"] = PermissionPolicy( "discover", Environment.PRODUCTION, [Permission("read", allowed_paths=["/app/logs/"])]) self.policies["plan_prod"] = PermissionPolicy( "plan", Environment.PRODUCTION, [Permission("read")]) self.policies["execute_prod"] = PermissionPolicy( "execute", Environment.PRODUCTION, [ Permission("read", allowed_paths=["/app/"]), Permission("write", allowed_paths=["/app/tmp/"], requires_approval=True)]) self.policies["verify_prod"] = PermissionPolicy( "verify", Environment.PRODUCTION, [Permission("read", allowed_paths=["/app/"])]) def check_permission(self, tool: str, path: str = "", stage: str = "execute") -> ApprovalStatus: """检查操作权限""" key = stage + ("_prod" if self.environment == Environment.PRODUCTION else "") policy = self.policies.get(key) if not policy: return ApprovalStatus.DENIED perm = policy.get_permission(tool) if not perm: return ApprovalStatus.DENIED if path: if any(path.startswith(dp) for dp in perm.denied_paths): return ApprovalStatus.DENIED if not any(path.startswith(ap) for ap in perm.allowed_paths): return ApprovalStatus.DENIED return (ApprovalStatus.REQUIRES_APPROVAL if perm.requires_approval else ApprovalStatus.AUTO_APPROVED) # 演示 if __name__ == "__main__": for env_name, env in [("开发", Environment.DEVELOPMENT), ("生产", Environment.PRODUCTION)]: mgr = PermissionManager(env) print(f"\n=== {env_name}环境 ===") tests = [ ("write", "/tmp/test.py", "execute"), ("write", "/etc/passwd", "execute"), ("read", "/app/config.yaml", "execute"), ("exec", "rm -rf /", "execute")] for tool, path, stage in tests: s = mgr.check_permission(tool, path, stage) icon = {"auto_approved":"✅","requires_approval":"⏳","denied":"🚫"}[s.value] print(f" {icon} {stage}.{tool}({path}): {s.value}")
记录 Agent 的所有操作,为安全审查提供完整证据链。
"""audit_trail.py — Agent 操作审计追踪系统""" import json, time, hashlib, os from dataclasses import dataclass, field from enum import Enum class AuditLevel(Enum): INFO = "info"; WARNING = "warning"; CRITICAL = "critical" @dataclass class AuditEntry: timestamp: float = field(default_factory=time.time) agent_id: str = "main"; iteration: int = 0 stage: str = ""; action: str = ""; tool: str = "" target: str = ""; permission_status: str = "" level: AuditLevel = AuditLevel.INFO; risk_score: float = 0.0 hash: str = "" def __post_init__(self): self.hash = hashlib.sha256( f"{self.timestamp}{self.action}{self.target}".encode() ).hexdigest()[:12] def to_dict(self): return {"time": time.strftime("%H:%M:%S", time.localtime(self.timestamp)), "agent": self.agent_id, "iter": self.iteration, "action": self.action, "tool": self.tool, "target": self.target, "perm": self.permission_status, "level": self.level.value, "risk": self.risk_score, "hash": self.hash} class AuditTrail: """审计追踪系统""" def __init__(self, log_dir: str = "/tmp/loop_audit"): self.log_dir = log_dir; self.entries: list[AuditEntry] = [] self.risk_events: list[AuditEntry] = [] os.makedirs(log_dir, exist_ok=True) def log_action(self, agent_id: str, iteration: int, stage: str, action: str, tool: str = "", target: str = "", perm_status: str = "", level: AuditLevel = AuditLevel.INFO): """记录审计事件""" risk = self._assess_risk(action, target, perm_status) entry = AuditEntry(agent_id=agent_id, iteration=iteration, stage=stage, action=action, tool=tool, target=target, permission_status=perm_status, level=level, risk_score=risk) self.entries.append(entry) if risk >= 0.7: self.risk_events.append(entry) self._write_alert(entry) return entry def _assess_risk(self, action: str, target: str, perm: str) -> float: risk = 0.1 high_risk = ["/etc/","/var/","~/.ssh/",".env","password","secret"] destructive = ["delete","remove","rm","drop","truncate","overwrite"] if any(p in target for p in high_risk): risk += 0.4 if any(a in action.lower() for a in destructive): risk += 0.3 if perm == "denied": risk += 0.2 return min(1.0, risk) def _write_alert(self, entry: AuditEntry): with open(f"{self.log_dir}/alerts.jsonl", 'a') as f: f.write(json.dumps(entry.to_dict(), ensure_ascii=False) + "\n") def get_summary(self) -> dict: by_level = {} for e in self.entries: by_level[e.level.value] = by_level.get(e.level.value, 0) + 1 return {"total": len(self.entries), "high_risk": len(self.risk_events), "by_level": by_level} def export_report(self, path: str = None): path = path or f"{self.log_dir}/report_{int(time.time())}.json" report = {"summary": self.get_summary(), "entries": [e.to_dict() for e in self.entries]} with open(path, 'w') as f: json.dump(report, f, ensure_ascii=False, indent=2) return path # 演示 if __name__ == "__main__": trail = AuditTrail() ops = [ ("main",1,"discover","读取项目文件","read","/app/src/main.py","auto_approved"), ("main",3,"execute","写入测试文件","write","/tmp/test.py","auto_approved"), ("main",5,"execute","修改系统配置","edit","/etc/nginx.conf","denied"), ("main",6,"execute","删除日志","exec","rm -rf /app/logs/*","requires_approval"), ("main",8,"verify","验证结果","exec","pytest","auto_approved")] for op in ops: e = trail.log_action(*op) bar = "█"*int(e.risk_score*5)+"░"*(5-int(e.risk_score*5)) print(f" [{e.hash}] iter{e.iteration} {e.action} risk:[{bar}] {e.risk_score:.1f}") print(f"\n摘要: {trail.get_summary()}")
"""secure_loop.py — 安全感知循环引擎""" from permission_manager import PermissionManager, ApprovalStatus, Environment from audit_trail import AuditTrail, AuditLevel class SecureLoopEngine: def __init__(self, name: str, env: Environment = Environment.DEVELOPMENT, max_iter: int = 20): self.perm = PermissionManager(env) self.audit = AuditTrail() self.max_iter = max_iter self.iteration = 0 self.blocked = 0 def safe_exec(self, tool: str, target: str, stage: str, fn, *args, **kwargs): """安全的工具调用包装""" status = self.perm.check_permission(tool, target, stage) self.audit.log_action("main", self.iteration, stage, f"调用{tool}", tool=tool, target=target, perm_status=status.value, level=AuditLevel.WARNING if status.value=="denied" else AuditLevel.INFO) if status == ApprovalStatus.DENIED: self.blocked += 1 raise PermissionError(f"权限拒绝: {tool}({target})") if status == ApprovalStatus.REQUIRES_APPROVAL: print(f" ⏳ 等待审批: {tool}({target})") return None return fn(*args, **kwargs) def run(self, step_fn): """step_fn(iteration, safe_exec) -> (output, done)""" print(f"🔒 安全循环启动 | 环境: {self.perm.environment.value}") while self.iteration < self.max_iter: self.iteration += 1 try: output, done = step_fn(self.iteration, self.safe_exec) print(f" ✅ 迭代{self.iteration}: {output[:50]}") if done: print("🏁 完成"); break except PermissionError as e: print(f" 🚫 {e}") summary = self.audit.get_summary() print(f"\n审计: {summary['total']}次操作, {summary['high_risk']}高风险, " f"{self.blocked}被阻止") self.audit.export_report() # 使用示例 if __name__ == "__main__": def my_step(i, safe): safe("read", "/app/src/main.py", "discover", lambda: "OK") safe("write", "/tmp/test.py", "execute", lambda: "OK") if i >= 3: try: safe("write", "/etc/config", "execute", lambda: "blocked") except PermissionError: pass return f"步骤{i}完成", i >= 5 SecureLoopEngine("安全测试", Environment.DEVELOPMENT).run(my_step)
"""e2e_security_demo.py — 环境安全差异对比""" from permission_manager import PermissionManager, ApprovalStatus, Environment tests = [("read","/app/src/main.py","discover"), ("write","/tmp/test.py","execute"), ("write","/etc/nginx.conf","execute"), ("exec","rm -rf /tmp/old","execute"), ("exec","curl https://api.example.com","execute")] for tool, path, stage in tests: dev = PermissionManager(Environment.DEVELOPMENT).check_permission(tool, path, stage) prod = PermissionManager(Environment.PRODUCTION).check_permission(tool, path, stage) d_icon = {"auto_approved":"✅","requires_approval":"⏳","denied":"🚫"}[dev.value] p_icon = {"auto_approved":"✅","requires_approval":"⏳","denied":"🚫"}[prod.value] print(f"{stage}.{tool}({path})") print(f" 开发:{d_icon} {dev.value:20s} | 生产:{p_icon} {prod.value}")
Cognitive surrender 指人类逐渐放弃对 Agent 决策的审查,完全依赖其自主性。在 Loop Engineering 中表现为:不审查代码变更就提交、跳过安全扫描、忽略审计异常。防范:(1)保留关键操作的审批通道;(2)定期审查审计报告;(3)设置强制人工检查点(如部署前必须确认)。
实用策略:(1)按环境区分——开发宽松、生产严格;(2)按阶段动态调整——discover 只需读、execute 需要写;(3)用 requires_approval 代替 denied——不确定时等人工审批比直接拒绝更合理;(4)积累信任后逐步放宽。
高风险事件永久保留;常规操作 30-90 天后归档。每次工具调用约 200-500 字节审计记录,100 次迭代约 20-50KB,存储压力很小。关键是使用结构化存储(JSONL)方便分析。
默认拒绝,显式允许。 未被明确允许的操作应被拒绝。永远不要用"允许所有"的通配符。
按阶段最小化权限。 discover→read/search,execute→write/exec,verify→read/exec。阶段切换时自动更新权限集。
高风险操作必须有审批通道。 删除文件、修改配置、执行未验证命令即使在开发环境也应人工确认。
审计日志不可篡改。 一旦写入就不应被 Agent 修改或删除,使用只追加模式。
不要在权限系统中使用通配符。 allowed_paths=["/"] 等同于没有权限控制。
不要让 Agent 修改自己的权限配置。 Agent 不应有权限修改 .claude/、.codex/ 或权限配置文件。
不要忽视审计告警。 短时间内大量 denied 事件说明系统或 Agent 行为有问题,应自动告警。
本节构建了 Loop Engineering 的完整安全体系:按阶段×环境动态控制的权限管理器、完整的审计追踪系统、四层防御模型。
核心要点:权限最小化是管理风险而非限制能力;按阶段动态调整权限;审计追踪是不可缺失的安全基础设施;cognitive surrender 是需持续警惕的风险。
至此第3章"Loop Engineering 高级概念与技巧"全部完成——从状态持久化、漂移检测、多代理编排、Token 经济学、错误恢复到安全边界,掌握了构建生产级循环系统所需的全部核心高级能力。
关键词:安全边界、权限管理、权限最小化、审计追踪、Audit Trail、Loop Engineering、cognitive surrender、Agent安全、环境隔离
难度:⭐⭐⭐⭐⭐ 高级
预计阅读时间:25 分钟