很多东西光靠嘴上说永远没有体感。我最近在调一个多 Agent 项目,主 Agent 给一个财务子 Agent 派了个“整理本月对账数据”的任务,子 Agent 为了拿到报表,直接尝试写文件到生产配置目录——还好它跑在容器里,写入路径被只读挂载挡住了。那一刻我特别明白一个道理:Agent 一旦有了手,能调工具、能写文件、能执行命令,安全就不是加分项,而是底线。本文就从 Sub-Agents 这个具体场景切入,聊聊安全沙箱怎么设计、怎么实操、怎么排查问题。
关注 Agent 开发的朋友应该都有感受:现在各家框架都把工具调用(Function Calling / Tool Use)做得很顺,Agent 从“一问一答”进化成“自主执行”。这个进化非常爽,但也把一个大问题摆到台面上:大模型本身只是一段程序,真正危险的是它手上的工具权限。尤其是 Sub-Agents,主 Agent 派生出多个子 Agent 并行干活,攻击面瞬间扩大好几倍。如果你正打算在生产环境跑多 Agent 系统,或者自己搭工具型 Agent 项目,这篇文章能帮你把安全兜底的那层网补上。
1. 先聊清楚:Agent 的“手”到底是什么,Sub-Agents 凭什么更危险
很多人聊 Agent 安全,上来就谈大模型幻觉、Prompt 注入,这些当然重要,但我觉得真正的分水岭是从“只输出文本”变成“能操作系统资源”那一刻。没有工具调用能力的模型,就算被注入恶意指令,最多也就说几句离谱的话;一旦接上工具,恶意指令就能变成真实的文件删除、数据外发、命令执行。所以要谈安全,得先理解这双手是怎么长出来的。
1.1 工具调用把 Agent 从“嘴”变成“手”
以前的聊天机器人只有一张嘴,用户问什么,它答什么。现在的 Agent 不一样,它背后接了一批工具函数,比如读文件、写数据库、调 API、执行 Shell 命令、发消息、下单。模型的推理结果会转成一次真实的函数调用,这个调用能改变系统状态。
我习惯用一个类比:老式聊天机器人是前台接待,只会指路;现在的 Agent 是拿着工牌和门禁卡的实习生,不仅能问路,还能自己刷卡进门、开电脑、改文件。问题在于,大模型本身并不具备“道德判断”能力,它对工具的使用完全取决于上下文里的指令和它学到的模式。一旦上下文里混入了恶意指令,或者模型理解偏差,一个看似合理的操作可能带来不可逆后果。
更麻烦的一点是,工具调用往往是链式的。Agent 为了完成一个目标,可能先调用搜索工具,再调用代码解释器,然后写文件,最后发消息。任何一个环节被污染,整条链路都可能被带偏。而且工具调用的执行速度快,出错之后的反应窗口非常短,人工介入往往来不及。
1.2 Sub-Agents 让攻击面成倍变大
那 Sub-Agents 又特殊在哪?我理解 Sub-Agents 是主 Agent 为了分解复杂任务而派生的子任务执行单元,它可以有自己的上下文窗口、工具集、执行环境,甚至独立的记忆空间。听起来很灵活,但安全上它带来了几个实打实的风险点。
第一,攻击面放大。一个主 Agent 只有一个执行链,一旦派出 5 个子 Agent,就有 5 条执行链在并行操作。哪怕每个子 Agent 只暴露一个漏洞点,整体被攻破的概率也大幅上升。第二,权限继承失控。很多框架默认让子 Agent 继承主 Agent 的工具集和凭证,等于一个只需要阅读文档的子 Agent 拿到了删除文件的权限,一个只需要调用翻译 API 的子 Agent 拿到了数据库连接串。这是典型的权限过度授予。第三,上下文隔离困难。子 Agent 可能会把父任务里的上下文全部带上,如果其中一个子 Agent 被恶意 Prompt 注入污染,它回传给主 Agent 的内容也可能夹带私货,形成横向污染。第四,生命周期残留。子 Agent 跑完之后,它派生出的临时进程、临时文件、缓存数据如果不清理,就像系统里留着后门。
所以我的观点很明确:Sub-Agents 这种架构本身没什么问题,但必须默认不安全来设计。每个子 Agent 的每一次工具调用,都应该被当作不可信行为来做边界控制。这也是“给 Agent 戴手套”这句话的真正含义。
2. 不需要“绝对隔离”,需要“恰好够用的隔离”:沙箱设计思路
聊到沙箱,很多人第一反应是上重方案:虚拟机、独立物理机、或者干脆每个任务起一个 Kubernetes Pod。这些方案安全当然安全,但成本和复杂度也很高。我更推荐按需设计,先想清楚要隔离什么、威胁边界在哪,再选择对应强度的方案。
2.1 沙箱要隔离什么
沙箱的本质是构建一个受限的执行环境,让 Agent 在里面的操作只影响临时资源,不碰宿主核心资产。具体到 Agent 场景,我觉得有五类东西必须隔离:
- 文件系统:子 Agent 能读哪些目录、能写哪些目录,必须是白名单制。生产配置、密钥文件、其他用户的私有数据,都不该被无差别访问。
- 网络:子 Agent 能访问哪些域名、IP、端口,必须有明确策略。一个做文本生成的子 Agent,没有任何理由去访问内部管理系统。
- 进程与系统调用:子 Agent 能不能创建新进程、能不能执行 Shell、能不能加载内核模块,这决定了代码执行类攻击的破坏半径。
- 资源配额:CPU、内存、磁盘、GPU、文件描述符、进程数,全部要设上限。否则一个失控子 Agent 能把整个宿主机拖垮。
- 凭据与环境变量:数据库密码、API Key、云厂商密钥,绝不能直接以环境变量形式塞给子 Agent。要用临时凭证或代理转发的方式。
你可以把沙箱想象成一个带水槽的厨房操作台:在里面切菜洗菜没问题,但水只能从水槽里流走,不能漫到整个房间。隔离做得好的沙箱,就是水槽足够深、管道足够窄,溢出概率被压到最低。
2.2 四层沙箱方案怎么选
我把实践中常见的沙箱方案分成四层,从轻到重分别是操作系统用户隔离、容器沙箱、微虚机沙箱、外部云沙箱。选型时除了看隔离强度,还要看启动速度和运维成本。
第一层是操作系统用户隔离。给每个子 Agent 建一个独立系统用户,用文件权限和 Linux capabilities 控制它能做什么。优点是无额外开销,缺点是隔离能力有限,一个用户提权成功就能影响整个系统。它适合内部信任度较高、Agent 只跑少量受限命令的场景。
第二层是容器沙箱。这是目前性价比最高的方案,Docker、Podman、containerd 都行。容器靠 Linux 内核的 Namespace、Cgroups、Capabilities、Seccomp 提供隔离,启动在毫秒到秒级,资源限制完善,对 Agent 这种短生命周期任务非常友好。需要注意:容器共享宿主机内核,如果一个子 Agent 拿到了内核漏洞利用能力,理论上仍可能逃逸。但对 90% 的业务 Agent 场景,容器沙箱已经够用了。
第三层是微虚机沙箱,代表方案有 Firecracker、Kata Containers、gVisor。它们用硬件虚拟化或用户态内核拦截系统调用,隔离强度比容器高一个量级,适合执行不可信代码、第三方插件脚本的场景。缺点是启动相对慢、内存开销更大,运维复杂度也更高。
第四层是外部云沙箱。把 Agent 的执行环境完全放到独立外部服务或专用集群,通过 API 传入代码和任务,服务端只返回结果。这种方案适合对数据隔离要求极高的场景,比如同时处理多家客户的数据,不能有任何串台可能。
我整理了一张选型对照表,方便你快速判断:
| 方案 | 隔离强度 | 启动开销 | 适