16 安全护栏与权限:Agent 能规划不等于能执行
·1169 字·3 分钟
结论先说:安全护栏不是 prompt 附件,而是运行时控制系统 #
一个 Agent 系统里,最危险的误解就是:
只要在 prompt 里告诉模型“不要做危险操作”,系统就安全了。
这当然不够。
真正的安全护栏必须在运行时落地,至少包括:
- 权限
- 人工确认
- 幂等性
- 预算
- 注入防护
一、为什么“能规划”不等于“能执行” #
LLM 可以很擅长做这些事:
- 理解意图
- 提出动作
- 组合多步计划
但它不该拥有这些权力:
- 决定自己有没有权限
- 决定高风险动作是否可以直接落地
- 决定同一操作失败后能否无限重试
- 决定哪段用户输入可以直接进入系统调用
所以最核心的原则是:
规划权可以给模型,执行权必须收回到系统。
二、权限护栏:动作白名单而不是善意假设 #
class PermissionRule(BaseModel):
action: Literal["read", "write", "external", "modify_file"]
entities: list[str]
def validate_permissions(plan, manifest):
for step in plan.steps:
if step.action not in manifest.allowed_actions:
return False
if step.entity not in manifest.allowed_entities:
return False
return True
这一步不是“提醒”,而是拒绝条件。
为什么这层必须在运行时 #
因为 prompt 规则无法保证:
- 模型不幻觉
- 工具名不漂移
- 用户输入不诱导模型越权
只有运行时白名单,才是硬边界。
三、人工确认:高风险动作为什么必须暂停 #
有些动作根本不该自动通过:
- 删除数据
- 改权限
- 发外部邮件
- 写敏感路径文件
- 对外联网请求高风险地址
if manifest.risk_level == "high":
request = HumanConfirmRequest(plan=step, timeout=30)
approved = wait_for_confirmation(request)
if not approved:
return Denied("confirmation timeout")
这一步的重要性在于:
- 把组织责任留在人
- 把不可逆动作从模型自动化里摘出来
- 给系统留一个最后的刹车点
四、幂等性:恢复不能放大事故 #
恢复机制如果没有幂等护栏,会非常危险。
if execution_records.exists(idempotency_key):
return execution_records.get(idempotency_key)
这层防的是什么 #
- 重试导致重复写库
- 重试导致重复发信
- 重试导致重复外呼
- 同一失败路径被反复放大
所以幂等性不是优化,而是安全护栏的一部分。
五、预算护栏:安全不只是权限,也是成本边界 #
if token_budget.remaining <= 0:
return Stop("budget exhausted")
if tool_cost[tool_name] > remaining_cost:
return Denied("tool budget exceeded")
很多人把预算理解成成本控制,其实它同时是防滥用边界:
- 防无限循环
- 防外部 API 被打爆
- 防单个用户问题拖垮系统资源
从系统安全角度看,它和权限一样重要。
六、注入防护:为什么不能让用户输入直接影响执行层 #
在 Agent 场景里,注入不只是 prompt injection,也包括:
- 用户输入污染工具参数
- 外部文档内容反向影响控制流
- 检索结果把系统带偏
所以防护至少应该包括:
6.1 参数白名单 #
只允许结构化参数进入工具
6.2 用户输入与系统约束分离 #
用户能影响目标,不能影响执行边界
6.3 检索证据不直接等同于命令 #
RAG 结果是证据,不是执行指令
这也是为什么“自然语言拼系统命令”是危险设计。
七、为什么安全护栏不能全放在 manifest 里 #
manifest 可以声明:
- allowed actions
- allowed fields
- risk level
- idempotent
但它不能成为最终安全边界,因为:
- manifest 本身也可能出错
- manifest 只是框架层声明
- 真正执行动作的权限最终还在基础设施层
所以更准确的分层是:
manifest: 声明策略
runtime: 执行策略
infra: 最终兜底
少任何一层,护栏都会变脆。
八、这一篇真正要记住的框架 #
安全护栏可以压成这五个问题:
这个动作允许吗?
这个动作需要人工确认吗?
这个动作失败后能安全重试吗?
这个动作值得继续消耗预算吗?
用户输入会不会污染执行层?
如果系统不能系统性回答这五个问题,那它只是“带工具的 LLM”,还不是可上线 Agent。
九、验证命令 #
pytest tests/ -k "permission or guardrail or idempotent or confirm or budget" -v