1. 为什么 SubAgent 一多安全边界就开始漏风OpenClaw 的 SubAgent 机制本质上是一个「父代理主动委派、子代理独立会话、单向结果回流」的架构。父 Agent 通过spawn_subagent工具发起子任务系统会生成一个独立的子会话 ID比如session:xxx/subagent:uuid内存、沙箱、工作区、记忆全部隔离子代理跑完之后只把结构化摘要回传给父会话中间推理和工具日志不回灌。听起来很干净但真正跑起来之后问题往往出在三个地方权限扩散、递归雪崩、上下文污染。权限扩散是指子代理默认继承了父代理的全部高危工具一个订单查询子代理理论上能拿到删库、执行 shell、访问内网的权限递归雪崩是指子代理继续派生子代理父生子、子生孙、孙生重孙会话数量指数增长算力直接被吃干上下文污染则是父子上下文互通、记忆跨代理读写、敏感凭证泄露子任务的中间日志把父窗口撑爆。这篇要解决的就是这三件事。我会给出一套可以直接复制的config.toml骨架配合rules.md、AGENTS.md和子代理私有白名单把三层安全约束在本地跑通并且逐层验证越权调用和递归调用确实被拦截。适合已经在用 OpenClaw 做多代理任务委派、但还没把安全边界收紧的开发者。2. 前置准备TaoToken 接入与 OpenClaw 环境确认在开始配置安全约束之前需要先确保模型调用链路是通的。OpenClaw 本身不绑定特定模型服务你可以通过 TaoToken 的 API 来接入 Claude 系列模型这样在验证子代理行为时能拿到稳定的推理结果。TaoToken 的 API 地址是https://taotoken.net/api兼容 Anthropic 的接口格式。如果你还没配置过先去控制台创建一个 API Key然后在 OpenClaw 的模型配置里指向这个地址。具体操作路径是登录后进入控制台在 API Keys 页面生成一个 key复制下来填到 OpenClaw 的模型 provider 配置里。这里要注意一点TaoToken 的 API 地址不带任何 UTM 参数直接写https://taotoken.net/api就行。如果你在配置里看到别人写的带一堆参数的地址那是给官网链接用的API 调用不需要。环境确认部分你需要保证本地 OpenClaw 版本支持onSubAgentDispatch钩子和rules.md分层加载。可以用下面这个命令检查当前版本和已加载的规则文件openclaw --version openclaw config show --section subagent如果subagent段是空的说明你还没配置过子代理约束接下来从零开始写就行。如果已经有内容建议先备份一份config.toml避免覆盖掉现有配置。3. 三层安全约束的 config.toml 骨架三层约束的核心逻辑是全局层兜底、父代理层准入、子实例层收缩。权限遵循「黑名单叠加追加、白名单取交集」的原则子代理实际可用工具 父白名单 ∩ 子自定义白名单 − 全局/父/子三层黑名单。子代理永远只能拿到父代理权限的子集不可能扩权。3.1 全局层rules.md 强制黑名单全局层的作用是永久拦截所有子代理的高危能力不可绕过。在项目根目录的rules.md里写入# Global SubAgent Rules ## Blocked Tools (Global) subagent.blockGlobalTools: - exec - shell - fs_delete - drop_table - network_scan - spawn_subagent - delegate_task - cred_read - secrets_export ## Recursion Guard subagent.maxDepth: 1 subagent.maxPingPongTurns: 8 subagent.maxConcurrentSubAgents: 3 subagent.maxRuntimeSeconds: 300这里把spawn_subagent和delegate_task放进全局黑名单是关键一步。子代理默认没有创建孙代理的入口从根源上阻断垂直嵌套递归。maxDepth: 1表示只允许父到子单层委派企业场景如果确实需要两层可以放宽到 2但必须配合人工审批。3.2 父代理层AGENTS.md 委派准入白名单父代理层管控的是「允许创建哪些子代理」和「父能下放哪些工具集」。在父 Agent 目录的AGENTS.md里配置# Parent Agent Delegation Policy ## Allowed SubAgents allowAgents: - order_query_sub - report_sub - refund_audit_sub ## Delegation Tool Allowlist delegateToolAllowlist: - query_order - read_customer - generate_report - audit_refund ## Elevated Approval requireApprovalElevatedTools: - write_database - send_notificationallowAgents列表之外的子代理 ID 请求会被直接拦截不会创建子会话。delegateToolAllowlist定义了父代理允许下放给子代理的工具集合这个集合会和子代理自身的白名单取交集。requireApprovalElevatedTools里的工具需要人工复核子代理无法自主执行生产变更。3.3 子实例层子代理私有白名单每个子 Agent 目录下有自己的rules.md和TOOLS.md用来进一步收缩权限。以订单查询子代理为例# order_query_sub Private Rules ## Allowed Tools tools.allowlist: - query_order - read_customer ## Sandbox sandbox.mode: non-main sandbox.workspaceAccess: ro sandbox.networkWhitelist: - api.internal.order ## Memory memory.allowParentRead: false memory.shard: subagent/order_query_sub子代理强制使用non-main轻量隔离沙箱禁止 elevated 特权模式。workspaceAccess: ro表示只读隔离子代理无法读写父 Agent 的工作目录。记忆分片独立默认禁止读取父代理记忆。3.4 三层交集计算的实际效果把三层配置串起来看假设父代理可用工具是{query_order, read_customer, write_database, generate_report}父下放白名单是{query_order, read_customer, generate_report}子代理自身白名单是{query_order, read_customer}全局黑名单是{exec, spawn_subagent, ...}。那么子代理实际可用工具 {query_order, read_customer, write_database, generate_report}∩{query_order, read_customer, generate_report}∩{query_order, read_customer}−{exec, spawn_subagent, ...}{query_order, read_customer}。write_database被父下放白名单挡掉generate_report被子代理自身白名单挡掉exec和spawn_subagent被全局黑名单挡掉。子代理只能更少不能更多。4. 防递归与防污染的关键配置项防递归分两类垂直嵌套递归和水平乒乓递归。垂直嵌套靠maxDepth和全局移除spawn_subagent来阻断水平乒乓靠maxPingPongTurns、消息 TTL 和拓扑闭环检测来拦截。4.1 递归防护配置在config.toml的[subagent]段里加上[subagent] max_depth 1 max_ping_pong_turns 8 max_concurrent_subagents 3 max_runtime_seconds 300 max_hop_count 5 enable_topology_cycle_detection truemax_hop_count控制 SFA2A 跨代理消息的最大跳数每条消息携带hopCount每跨一次 Agent 加一超过 5 就丢弃。enable_topology_cycle_detection开启路由层的通信拓扑图检测一旦识别到 A↔B 闭环就直接中断子代理执行并生成告警日志。4.2 防污染隔离配置防污染分五个维度上下文视图隔离、长期记忆读写隔离、凭证与配置隔离、MD 配置提示词隔离、工作区文件系统隔离。在config.toml里对应的配置[subagent.isolation] context_mode independent result_summary_only true boundary_marker [SUBAGENT_RESULT_BOUNDARY] memory_shard_per_subagent true allow_parent_memory_read false credential_mount parent_only env_strip_secrets true workspace_mode temp_ephemeral workspace_access noneresult_summary_only true表示子代理执行完成后只返回结构化结论中间推理和工具日志永久不回传。boundary_marker是子代理返回内容的隔离标记ContextEngine 解析时识别这个边界禁止子输出伪造 System Prompt 或 rules 约束。env_strip_secrets true会在创建沙箱时剔除父代理的全部密钥类环境变量。4.3 生命周期钩子的介入时机所有约束统一在onSubAgentDispatch钩子执行前置拦截未创建子会话前直接拦截不分配沙箱、不消耗模型算力。完整的介入点如下生命周期钩子权限收缩防递归防污染onSessionCreate预加载全局/父权限策略初始化 nestDepth 计数器初始化父独立记忆、工作区onSubAgentDispatch三级白名单交集计算深度、乒乓、并发、拓扑四重检测分配独立上下文、沙箱、记忆分片before_tool_run二次校验子工具白名单子无 spawn_subagent工具路径白名单before_memory_rw管控记忆访问权限无限制仅读写自身记忆分片after_subagent_finish回收临时下放权限重置子递归计数器摘要过滤中间日志session:after_close清空子权限快照释放并发配额销毁临时沙箱/工作区5. 逐层验证越权与递归是否真的被拦截配置写完不代表生效需要逐层验证。下面给出三个验证动作分别对应权限收缩、垂直递归、水平乒乓。5.1 验证权限收缩先创建一个订单查询子代理然后尝试让它调用write_database工具。预期结果是before_tool_run钩子拦截返回越权错误。openclaw subagent spawn --agent order_query_sub --task 查询订单 12345 并更新状态如果配置生效你会看到类似这样的输出{ status: blocked, hook: before_tool_run, reason: tool write_database not in subagent allowlist, allowed_tools: [query_order, read_customer], trace_id: subagent:uuid }allowed_tools只显示交集计算后的结果write_database不在里面说明权限收缩生效。5.2 验证垂直递归阻断尝试让子代理调用spawn_subagent创建孙代理。预期结果是全局黑名单直接拦截子会话根本不会创建。openclaw subagent spawn --agent order_query_sub --task 创建一个子代理去查询库存预期输出{ status: blocked, hook: onSubAgentDispatch, reason: tool spawn_subagent is in global blocklist, nest_depth: 1, max_depth: 1 }nest_depth显示当前深度是 1max_depth也是 1达到阈值直接熔断。即使全局黑名单没拦住深度检测也会在第二层拦截。5.3 验证水平乒乓阻断构造一个场景子代理反复向父代理请求补充信息看maxPingPongTurns是否在 8 轮后终止。这个验证需要模拟双向消息可以用 OpenClaw 的测试模式openclaw subagent test-pingpong --agent order_query_sub --turns 12预期在第 8 轮之后收到熔断消息{ status: terminated, reason: max_ping_pong_turns exceeded, turns_used: 8, max_turns: 8, trace_id: subagent:uuid }如果三轮验证都通过说明三层约束在本地已经跑通。接下来可以接入 TaoToken 的模型对话接口做一次真实任务委派观察 Langfuse 追踪链路里SubAgentDispatchSpan和RulePolicySpan的记录是否完整。6. 本篇常见错排查配置过程中最容易踩的坑集中在规则加载顺序、交集计算为空、递归检测误报这三个地方。6.1 规则文件没被加载现象是子代理仍然能调用exec或spawn_subagent。排查步骤先确认rules.md放在项目根目录然后检查config.toml里有没有指定规则文件路径。可以用openclaw config show --section rules查看当前加载的规则列表。如果列表为空说明路径不对需要在config.toml里显式指定[rules] global_rules ./rules.md agent_rules ./agents/{agent_id}/AGENTS.md subagent_rules ./agents/{agent_id}/rules.md6.2 交集计算为空导致子代理无法创建现象是onSubAgentDispatch返回「no available tools」子会话创建失败。这通常是因为父下放白名单和子代理自身白名单没有交集。比如父只下放了generate_report但子代理白名单里只有query_order交集为空。解决办法是检查两边的工具名是否拼写一致或者适当放宽父下放白名单。6.3 递归检测误报现象是正常的单层委派被判定为递归。检查maxDepth是否被设成了 0或者nestDepth计数器没有在onSessionCreate时正确初始化。另外如果父代理和子代理使用了相同的traceId前缀拓扑检测可能会误判为闭环。确保每个子会话有独立的parentTraceId和nestDepth元数据。6.4 记忆分片读写失败现象是子代理无法写入自身记忆。检查memory.shard路径是否存在以及文件系统权限是否为 700。子代理的记忆分片目录需要独立创建不能和父代理共用。如果用了allow_parent_memory_read: false子代理读取父记忆会直接返回空这是预期行为不是 bug。6.5 凭证剥离后子代理无法调用业务 API现象是子代理调用内部订单 API 时返回 401。这是因为env_strip_secrets true把父代理的密钥环境变量全部剔除了。解决办法是在子代理的沙箱配置里单独下放业务只读凭证而不是直接剥离所有环境变量。可以在sandbox.networkWhitelist旁边加上sandbox.envAllowlist只放行必要的业务配置项。排查完这些之后如果还想进一步验证模型在子代理场景下的行为一致性可以用 TaoToken 的模型对话接口单独跑几轮委派任务观察子代理的摘要回流是否符合预期。对于需要长期跑编码类 Agent 的场景可以考虑用 Coding Plan 来管理多代理的调用配额避免递归失控时算力被瞬间吃光。接入文档里有完整的钩子签名和配置字段说明遇到不确定的字段名可以直接查。