Agent 人工干预安全网关
Agent 人工干预安全网关
1. 问题定义
在 Agent 工具调用场景中,某些操作具有不可逆性或高风险(如删除文件、发送消息、执行系统命令)。如果让 Agent 自主执行所有工具调用,可能导致安全事故。人工干预安全网关的目标是:在工具执行前,根据策略决定是直接执行、跳过还是暂停等待用户审批。
2. 核心架构:7 阶段干预检查流水线
干预检查不是简单的”允许/拒绝”二元判断,而是一个多阶段的纵深防御体系。每个阶段按优先级依次检查,一旦某个阶段做出决定,后续阶段不再执行。
Phase 1:全局 Resolver(安全黑名单)
全局 Resolver 对所有工具调用统一检查,最典型的是安全黑名单——匹配已知危险的命令模式(如 rm -rf、DROP TABLE)。
// 伪代码:安全黑名单 Resolverconst DEFAULT_SECURITY_BLACKLIST = [ { command: /^rm\s+(-rf?|--?\S*)?\s/i, policy: "always" }, { command: /^mkfs\./, policy: "always" }, { command: /^curl.*\|\s*(sh|bash)/, policy: "overridable" },];全局 Resolver 的 policy 分为两种:
always:无条件阻止,即使在 headless 模式下也跳过该工具overridable:可覆盖的阻止,允许用户在知晓风险后手动批准
为什么需要全局 Resolver? 因为某些危险操作无论用户配置如何都不应自动执行。
Phase 2:Headless 模式
Headless 模式用于异步自动化任务(如定时 cron 任务),此时无人在场审批。策略是:
- 被
always阻止的工具:直接跳过 - 其他工具:全部直接执行
陷阱:headless 模式下跳过的工具不会通知用户,可能导致任务部分完成但用户不知情。应在任务完成报告中记录跳过的工具。
Phase 3:动态策略 Resolver
动态 Resolver 允许根据工具参数实时判断是否需要干预。例如:
// 伪代码:动态 Resolver{ dynamic: { type: "file-write", default: "never", // 默认不需要干预 policy: "always", // 匹配时总是需要干预 resolver: (args, metadata) => args.path.includes("/etc/") || args.path.includes(".env") }}动态 Resolver 的优势是上下文敏感——写普通文件不需要审批,但写入系统配置文件则需要。
设计权衡:动态 Resolver 的复杂度随规则数量增长,建议限制 Resolver 数量(如 5 个以内),并在配置文件中文档化每个 Resolver 的触发条件。
Phase 4:Always 策略匹配
工具自身的 manifest 可以声明 humanIntervention: "always" 或匹配规则。这些规则覆盖 auto-run 模式——即使用户选择了全自动模式,always 标记的工具仍需审批。
典型场景:发送外部消息(邮件、IM)、修改账户配置等不可逆操作。
Phase 5-7:用户配置模式
| 模式 | 行为 | 适用场景 |
|---|---|---|
auto-run | 所有工具直接执行 | 高信任环境、低风险工具 |
allow-list | 仅白名单中的工具直接执行 | 受控环境,明确知道哪些工具安全 |
manual(默认) | 使用工具自身的 manifest 配置 | 通用场景,信任工具作者的判断 |
未知工具守卫(Phase 5.5)
当工具不在 toolManifestMap 中时,manual/allow-list 模式下要求干预。这是防止未知工具被静默执行的安全兜底。
陷阱:在 auto-run 模式下不启用此守卫,因为选择 auto-run 的用户已经接受了未知工具的执行风险。
3. Turn-scoped 范围守卫
一个关键的安全陷阱是 stale pending tool messages。当用户发起工具审批但从未点击”批准/拒绝”时,这些 pending 状态的 tool message 会被加载回 state.messages。如果不做作用域限制,它们会在后续每个 tool_result 阶段劫持循环,导致 Agent 永远卡在 waiting_for_human 状态。
解决方案是 turn-scoped 范围限制:只检查与当前 assistant turn(最近的发出工具调用的 assistant 消息)相关的 pending tool messages。
// 伪代码:turn-scoped 范围守卫private getCurrentTurnPendingToolMessages(state) { // 找到最近发出工具调用的 assistant 消息 ID let currentAssistantId = findLatestAssistantWithToolCalls(state.messages); // 只返回 parentId 匹配该 ID 的 pending tool messages return state.messages.filter( m => m.role === "tool" && m.pluginIntervention?.status === "pending" && m.parentId === currentAssistantId );}4. 三种审批交互模式
| 模式 | 说明 | Agent 指令类型 |
|---|---|---|
| Approve | 用户批准或拒绝单个工具调用 | request_human_approve |
| Prompt | 用户输入文本反馈 | request_human_prompt |
| Select | 用户从多个选项中选择 | request_human_select |
5. 安全纵深总结
6. 参考来源
- OWASP Top 10 for LLM Applications — LLM-05: Supply Chain Vulnerabilities
- LobeHub
GeneralChatAgent.checkInterventionNeeded— 7 阶段干预检查的完整实现 - LobeHub
InterventionChecker.shouldIntervene— 静态策略判断逻辑