跳转到内容

多层人工干预安全网关在 LobeHub 中的实现

学习目标

理解 LobeHub 如何在 GeneralChatAgent 中实现 7 阶段工具调用干预检查流水线,包括全局安全黑名单、动态策略解析、多种用户模式和 turn-scoped 范围守卫。

前置知识

本章涉及 Agent 工具调用安全网关的通用原理,建议先阅读:

下文假设你已理解上述概念,直接聚焦 LobeHub 的具体实现。

项目实践

LobeHub 的 7 阶段流水线

GeneralChatAgent.checkInterventionNeeded() 中,LobeHub 实现了完整的 7 阶段检查:

全局安全黑名单

LobeHub 的默认安全黑名单包括危险命令模式:

// 伪代码:默认安全黑名单
const DEFAULT_SECURITY_BLACKLIST = [
{ pattern: /^rm\s+(-rf?|--?\S*)?\s/i, policy: "always" },
{ pattern: /^mkfs\./, policy: "always" },
{ pattern: /^curl.*\|\s*(sh|bash)/, policy: "overridable" },
];

黑名单 resolver 作为默认全局 Resolver:

// 伪代码:创建默认全局审计器
function createDefaultGlobalAudits() {
return [{
resolver: (args, metadata) => {
const blacklist = metadata.securityBlacklist ?? DEFAULT_SECURITY_BLACKLIST;
return blacklist.some(rule => matchesPattern(args.command, rule.pattern));
},
policy: "always",
}];
}

Turn-scoped 范围守卫

LobeHub 解决 stale pending tool messages 问题的具体实现:

// 伪代码:LobeHub turn-scoped 范围守卫
private getCurrentTurnPendingToolMessages(state): ToolMessage[] {
// 1. 找到最近的发出工具调用的 assistant 消息
let currentAssistantId: string | undefined;
for (let i = state.messages.length - 1; i >= 0; i--) {
const m = state.messages[i];
if (m.role === "assistant" && (m.tool_calls?.length > 0 || m.tools?.length > 0)) {
currentAssistantId = m.id;
break;
}
}
if (!currentAssistantId) return [];
// 2. 只返回 parentId 匹配的 pending tool messages
return state.messages.filter(
m => m.role === "tool"
&& m.pluginIntervention?.status === "pending"
&& m.parentId === currentAssistantId
);
}

这个实现的关键点:通过 parentId 关联 tool message 和发出调用的 assistant message,确保只检查当前 turn 的 pending 工具。

工具三级分类

LobeHub 对内置工具进行三级分类管理:

分类说明典型工具
defaultToolIds默认启用的核心工具activator、skills、web-browsing、knowledge-base、memory
alwaysOnToolIds始终启用、不可禁用activator、skills、skill-store
runtimeManagedToolIds由运行时条件决定cloud-sandbox、knowledge-base、memory、local-system

三种审批交互

LobeHub 的 AgentRuntime 支持三种审批指令:

// 伪代码:审批指令类型
type AgentInstruction =
| { type: "request_human_approve"; pendingToolsCalling: ToolCall[] }
| { type: "request_human_prompt"; message: string }
| { type: "request_human_select"; options: string[] };

Runtime 执行 request_human_approve 时将状态设为 waiting_for_human,暂停事件流,等待用户决策后通过新 operationId 恢复。

问题与规避

问题场景规避策略
stale pending messages用户未审批就离开页面turn-scoped 范围守卫:只检查当前 assistant turn 的 pending 工具
headless 模式跳过关键工具安全黑名单 always 阻止在任务完成报告中记录跳过的工具,供事后审计
auto-run 模式未知工具用户选择 auto-run 接受风险未知工具守卫在 auto-run 下不启用,用户自行承担风险
JSON 解析失败的工具参数工具调用参数非合法 JSON捕获异常,按空参数处理(toolArgs = {}

设计取舍

为什么需要 7 阶段而不是 3 阶段?

7 阶段的设计是为了覆盖不同的安全信任层级:

  1. 全局黑名单(Phase 1):无论用户配置如何都阻止的危险操作
  2. headless 模式(Phase 2):自动化任务,无人在场
  3. 动态策略(Phase 3):上下文敏感的精细控制
  4. Always 策略(Phase 4):工具作者声明的必须审批操作
  5. 用户模式(Phase 5-7):用户自主选择的信任级别

每一阶段都有独特的信任假设,不能简化为统一的”允许/拒绝”。

为什么 unknown tool 在 auto-run 下不拦截?

Auto-run 模式的用户已经明确接受了所有工具的执行风险。如果对未知工具进行拦截,反而破坏了 auto-run 的语义。Manual 和 allow-list 模式下,用户没有明确接受未知风险,因此需要拦截。

参考来源

  • LobeHub GeneralChatAgent 干预检查 — packages/agent-runtime/src/agents/GeneralChatAgent.tscheckInterventionNeeded 方法
  • LobeHub 内置工具分类 — packages/builtin-tools/src/index.ts
  • Agent 人工干预安全网关 — 通用干预机制原理