尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【AgentScope 2.0】07-沙箱(Sandbox)详解

【AgentScope 2.0】07-沙箱(Sandbox)详解 版本基准:本文档基于 AgentScope 2.0 GA(v2.0.0)编写。具体版本号以 Release Notes 为准。一句话概括沙箱就是把 AI Agent 关在一个隔离的安全笼子里干活——它可以在笼子里随便折腾装软件、跑命令、写文件但不会影响到你的主机系统而且笼子里的东西还能被打包带走下次继续用。你能学到什么为什么需要沙箱Agent 直接在主机上跑不行吗5 种沙箱后端怎么选Docker、Kubernetes、Daytona、E2B、AgentRun4 种隔离级别IsolationScope的区别和使用场景快照机制5 种存储后端沙箱如何记住上次的状态分布式部署多副本场景下沙箱状态如何共享并发控制两个请求同时抢同一个沙箱怎么办自管理沙箱实例3 种我自己管的场景工作区投影宿主机文件如何同步到沙箱SandboxContext 编程 API前置知识通用前置详见 README.md。本篇额外需要01-overview理解 Middleware 机制和 HarnessAgent 整体架构05-filesystem理解 FilesystemSpec 声明式配置本文在沙箱模式下深入展开容器化基础可选基本了解 Docker 是什么理解镜像、容器等基本概念即可核心概念沙箱解决什么 —— 就像给鹦鹉准备独立工作间想象你养了一只聪明的鹦鹉Agent它能帮你干活查资料、写代码、整理文件。但是问题 1信任问题—— 鹦鹉可能会打翻花瓶、咬坏沙发、把厨房搞乱Agent 执行了rm -rf问题 2连续性问题—— 鹦鹉今天干了一半的活明天怎么让它从原地继续下次调用怎么恢复环境问题 3多副本问题—— 你开了三家分店每家都有鹦鹉在干活怎么让它们共享同一份进度沙箱就是给鹦鹉准备的一个独立工作间┌─────────────────────────────────────────────────────────────┐ │ 你的家宿主机 │ │ │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ 沙箱安全工作间 │ │ │ │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ │ │ │ │ 工作台 │ │ 工具箱 │ │ 储物柜 │ │ │ │ │ │ (文件) │ │ (命令) │ │ (已安装的依赖) │ │ │ │ │ └─────────┘ └─────────┘ └─────────────────┘ │ │ │ │ │ │ │ │ Agent 在这里干活随便折腾不影响外面 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ 你的保险柜、私人文件、生产环境 —— 完全安全 │ └─────────────────────────────────────────────────────────────┘沙箱给出的三个承诺问题沙箱的解决方案生活类比执行边界文件和命令都在沙箱内部执行碰不到宿主机鹦鹉只能在工作间里活动跨调用恢复每次对话结束把工作间打包下次打开继续用拍照记录工作间的状态多副本可用任意副本都能从共享存储恢复出同一份工作区三家分店共享同一份进度档案5 种沙箱后端 —— 就像不同级别的安保服务生活类比你需要租一个仓库来存放货物。市面上有 5 种仓库可选从自家地下室到五星级安保仓库价格和安全级别各不同。但不管选哪种你操作货物的方式是一样的——都是放进仓库、从仓库取出。┌──────────────────────────────────────────────────────────────────┐ │ 统一的沙箱接口 │ │ │ │ 不管后端是哪种Agent 代码、工具集、AGENTS.md 都不用变 │ └───────────────────────────────┬──────────────────────────────────┘ │ ┌─────────────────────┼─────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ 自建仓库 │ │ 托管仓库 │ │ 云仓库 │ │ │ │ │ │ │ │ • Docker │ │ • Daytona │ │ • E2B │ │ • K8s │ │ │ │ • AgentRun │ │ │ │ │ │ (阿里云FC) │ └─────────────┘ └──────────────┘ └──────────────┘后端适合场景生活类比Docker本地开发、单机部署、信任 shell自己家地下室自己管钥匙Kubernetes自建 K8s 集群、节点级 bind mount自建仓库大楼有电梯和货梯Daytona通用托管沙箱 HTTP API托管仓库打电话就能用E2B通用托管沙箱 平台原生快照高级托管仓库自带备份服务AgentRun阿里云函数计算 FC 3.0中国大陆低延迟NAS 动态挂载国内云仓库同城配送速度最快核心要点所有后端实现同一组接口你的 Agent 代码完全不用改——换后端只需要改一行配置。IsolationScope —— 就像酒店房间的分配策略沙箱怎么分配是每个人独享还是共享这由IsolationScope隔离范围决定。生活类比酒店有不同的房型策略——有人每次入住都开新房间有人长期包一间还有人整个酒店只有一间大通铺。隔离级别谁共享同一沙箱生活类比适用场景USER默认同一 userId 的多个 session 共享未传 userId 时回退为SESSIONVIP 客户专属房间跨会话共享工作区分布式部署SESSION每个 sessionId 独立每次入住开新房间多用户 SaaS对话完全隔离AGENT这个 agent 的所有用户/会话共享公司包下一间房公用公共知识库型 AgentGLOBAL一个 store 内全局共享整个酒店一个大通铺极少使用谨慎配置// 配置隔离级别——只需一行.filesystem(newDockerFilesystemSpec().image(ubuntu:24.04).isolationScope(IsolationScope.USER))并发安全提示SESSION天然并发安全每个会话自己一份USER/AGENT/GLOBAL在多副本部署时建议配合并发锁见后面的并发控制章节。快照机制 —— 就像游戏存档生活类比你在玩游戏每次退出前可以存档。下次进入时系统要判断从哪里继续游戏机还开着就直接继续最快游戏机关了但存档还在就读档继续存档也没了就从头开始。沙箱的快照机制就是这样工作的。每次call()结束时沙箱把工作区状态打包成快照存起来下次call()开始时按情况恢复沙箱 start() 被调用 │ ▼ ┌──────────────────────┐ │ workspaceRootReady? │ └──────────┬───────────┘ ╱ ╲ true false │ │ ▼ ▼ ┌───────────────┐ ┌───────────────┐ │ 容器内目录还在│ │ 快照可用 │ └───────┬───────┘ └───────┬───────┘ ╱ ╲ ╱ ╲ 是 否 是 否 │ │ │ │ ▼ ▼ ▼ ▼ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │BranchA│ │BranchB│ │BranchC│ │BranchD│ │ 热启动 │ │读快照 │ │读快照 │ │ 冷启动 │ │ 最快 │ │临时文件│ │全量 │ │ 全量 │ └───────┘ └───────┘ └───────┘ └───────┘四个分支的含义分支条件行为生活类比A容器还在 工作目录完好只刷新临时文件最快游戏机开着直接继续玩B容器还在 工作目录丢失从快照恢复 刷新临时文件游戏机开着但存档丢了读档C容器没了 快照可用创建新容器 从快照恢复游戏机关了读档重新开始D容器没了 快照不可用创建新容器 全量初始化最慢新游戏从头开始快照存到哪里取决于你配的snapshotSpecSpec存储位置适用场景生活类比NoopSnapshotSpec默认不持久化临时任务不需要恢复一次性用品用完即弃LocalSnapshotSpec宿主本地文件单机长期运行家里的储物间OssSnapshotSpecOSS/S3 兼容存储多副本分布式云端大仓库RedisSnapshotSpecRedis低延迟、小工作区快递柜存取快但空间小JdbcSnapshotSpecJDBC 数据库MySQL 等沉淀进关系型库、可审计企业档案柜OssSnapshotSpec/RedisSnapshotSpec/JdbcSnapshotSpec都继承自通用的RemoteSnapshotSpec你也可以基于它实现自己的远端快照后端。分布式沙箱 —— 就像连锁分店共享会员系统生活类比你开了一家连锁健身房。会员 Alice 今天在 A 店锻炼明天出差去了 B 店。她希望两个店都能看到她的锻炼记录B 店能接着 A 店的进度继续。这就是分布式沙箱解决的问题多个 Pod分店部署同一个 Agent要让任意 Pod 都能接住同一用户的对话。┌──────────────────────┐ │ 共享存储层 │ │ │ │ ┌────────────────┐ │ │ │ Redis AgentStateStore │ ← AgentState含会话状态 │ └────────────────┘ │ │ ┌────────────────┐ │ │ │ OSS 快照存储 │ │ ← 沙箱快照 │ └────────────────┘ │ └──────────┬───────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ Pod A │ │ Pod B │ │ Pod C │ │ (分店 A) │ │ (分店 B) │ │ (分店 C) │ └────────────┘ └────────────┘ └────────────┘ │ │ │ └───────────────┴───────────────┘ │ Alice 的请求 不管落在哪个 Pod 都能恢复同一份沙箱要实现分布式沙箱需要三样东西分布式 AgentStateStore例如基于 Redis 的实现远端快照存储OSS / Redis 等非 Noop 的SandboxSnapshotSpec合适的 IsolationScopeUSER/AGENT/GLOBALHarnessAgent 提供了.distributedStore(...)方法一次性把这分布式三件套打包声明传入一个DistributedStore聚合了AgentStateStore 文件存储baseStoreSandboxSnapshotSpecSandboxExecutionGuard框架在构建期自动把它们分别装配到对应位置。// Redis一条龙配齐分布式存储AgentStateStore 文件存储 沙箱快照 执行锁JedisPooledjedisnewJedisPooled(redis-host,6379);DistributedStorestoreRedisDistributedStore.fromJedis(jedis,agentscope:);HarnessAgentagentHarnessAgent.builder().name(assistant).model(model).distributedStore(store)// ← 一行配齐分布式三件套.filesystem(newDockerFilesystemSpec().image(ubuntu:24.04).isolationScope(IsolationScope.USER))// USER 级别共享.build();说明DistributedStore是分布式场景的统一入口。如果你显式配过.stateStore(...)、SandboxFilesystemSpec.snapshotSpec(...)等局部项会覆盖distributedStore提供的对应组件优先级显式 builder 方法 distributedStore 工作区默认值。这样既能在分布式场景一行搞定也能在需要时局部微调。并发控制 —— 就像会议室预约系统生活类比公司有一间公共会议室共享沙箱。Alice 在里面开会时Bob 不能冲进去。Bob 需要等 Alice 开完释放锁才能进去。会议室门口有个预约牌写着当前使用者和预计结束时间。当USER/AGENT/GLOBAL模式下两个 Pod 同时处理同一个用户的请求可能会把状态写到同一个 slot最后后写入覆盖前写入。加一把分布式锁就能解决用户 A 想用 → 检查会议室是否空闲 → 空闲获得使用权 → 用完释放 ↓ 不空闲 等待...轮询 retryInterval不同隔离级别的并发安全性隔离级别并发安全性说明SESSION天然安全每个会话独立沙箱互不干扰USER需要保护同一用户的多个会话可能冲突AGENT需要保护所有用户共享一个沙箱GLOBAL需要保护全局共享最容易冲突HarnessAgent 内置了一个基于 Redis 的分布式锁实现RedisSandboxExecutionGuard。锁的 key 会按 scope 自动分桶USER→ 按 userIdAGENT→ 按 agent 名。你也可以实现SandboxExecutionGuard接口接入其他锁后端数据库、Zookeeper、etcd 等。自管理沙箱实例 —— 就像自租房 vs 酒店托管生活类比默认情况下沙箱就像住酒店——前台框架帮你开门、关门、打扫。但有时候你想自己租一间房子自己拿钥匙、自己决定什么时候退租。默认沙箱的整个生命周期由框架托管。但 HarnessAgent 提供了三种我自己管的场景场景 1我已经启动好一个容器想让 Agent 用它// 你自己创建并启动沙箱SandboxmySandboxdockerClient.create(workspaceSpec,snapshotSpec,options);mySandbox.start();// 注入到调用中SandboxContextcallCtxSandboxContext.builder().client(dockerClient).externalSandbox(mySandbox)// 框架在 call 结束时只 stop()不 shutdown().build();agent.call(msgs,RuntimeContext.builder().sessionId(my-session).put(SandboxContext.class,callCtx)// 通过 RuntimeContext 注入 SandboxContext.build()).block();// 你自己决定什么时候销毁mySandbox.shutdown();场景 2我有一个具体的快照串想恢复到那个时刻// 从保存的状态恢复SandboxStatesavedStatedockerClient.deserializeState(savedStateJson);SandboxContextcallCtxSandboxContext.builder().client(dockerClient).externalSandboxState(savedState)// 框架按这个 state 恢复.build();// 生命周期仍由框架管场景 3多个 Agent 共享同一个沙箱把同一个externalSandbox透传给多个 Agent 的call()最后由你自己shutdown()。工作区投影 —— 就像给工作间配标准工具生活类比你给鹦鹉准备了一个工作间但每次都希望里面有一些基础工具手册、常用工具、参考资料。这些工具可能会更新你买了新工具但你不希望鹦鹉的工作间是空的。宿主机侧workspace/下的关键文件在每次沙箱启动时同步进去。按内容哈希增量同步——不变就跳过传输。宿主机工作区 沙箱工作区 ┌─────────────────┐ ┌─────────────────┐ │ workspace/ │ │ /workspace/ │ │ ├── AGENTS.md │ 投影同步 │ ├── AGENTS.md │ │ ├── skills/ │ ──────────▶ │ ├── skills/ │ │ ├── subagents/ │ 每次启动 │ ├── subagents/ │ │ └── knowledge/ │ │ └── knowledge/ │ └─────────────────┘ └─────────────────┘ 宿主机 沙箱内目录/文件用途AGENTS.mdAgent 的身份定义和指令skills/所有 Skill 的目录包含 SKILL.md 和脚本subagents/子 Agent 的定义文件knowledge/领域知识文件三个关键特性单向同步宿主机 → 沙箱。沙箱内修改不会反向同步回宿主机增量更新按 SHA-256 哈希比较不变则跳过每次启动执行保证沙箱内的文件是最新的GA 变更(marketplace skills 预阶段):如果配置了 marketplace 技能仓库(GitSkillRepository等),HarnessSkillMiddleware会在工作区投影之前先把 Layer-1/Layer-2 marketplace skills 的资源预阶段(stage)到工作区,然后再做宿主机 → 沙箱的投影同步。这保证 marketplace 技能的脚本/资源也能正确进入沙箱,而不是只投影 workspace 本地目录(源码 commit37bb6e7e)。如果你改了skills/里的脚本下次call()沙箱里就是新版。想取沙箱里的产物让 Agent 自己read_file读出来。关键代码解读1. 最小 Docker 沙箱示例// 创建一个使用 Docker 沙箱的 AgentHarnessAgentagentHarnessAgent.builder().name(code-agent)// Agent 名称.model(model)// 模型配置.filesystem(newDockerFilesystemSpec().image(ubuntu:24.04))// 指定 Docker 镜像.build();// 调用——每个 sessionId 对应一个独立沙箱agent.call(msg,RuntimeContext.builder().sessionId(user-1-conv-1)// 不同 sessionId → 不同沙箱.build()).block();sessionId不同 → 沙箱不同相同sessionId→ 自动复用同一沙箱或从快照恢复。2. USER 级别 OSS 快照 分布式部署// 配置 OSS 快照存储OssSnapshotSpecsnapshotSpecnewOssSnapshotSpec(ossClient,// OSS 客户端my-bucket,// Bucket 名称agentscope/// 存储路径前缀);// 创建 Agent支持分布式部署HarnessAgentagentHarnessAgent.builder().name(assistant).model(model).distributedStore(// ← 分布式存储配齐 stateStore 快照 锁RedisDistributedStore.fromJedis(jedis,agentscope:)).filesystem(newDockerFilesystemSpec().image(ubuntu:24.04).isolationScope(IsolationScope.USER)// USER 级别共享.snapshotSpec(snapshotSpec))// 显式指定 OSS 快照覆盖 distributedStore 提供的.build();// 两次调用不同 session但同一个 userId → 共享沙箱agent.call(msgs,RuntimeContext.builder().userId(alice)// ← 相同 userId.sessionId(session-1).build());agent.call(msgs,RuntimeContext.builder().userId(alice)// ← 恢复到同一个沙箱.sessionId(session-2).build());3. 并发控制——Redis 分布式锁// 配置 Redis 分布式锁UnifiedJedisjedisnewJedisPooled(redis-host,6379);SandboxExecutionGuardguardRedisSandboxExecutionGuard.builder(jedis).leaseTtl(Duration.ofMinutes(30))// 比最坏情况 call 时长稍长.retryInterval(Duration.ofMillis(500))// 轮询间隔.build();// 把锁配到沙箱上HarnessAgentagentHarnessAgent.builder().name(assistant).model(model).filesystem(newDockerFilesystemSpec().image(ubuntu:24.04).isolationScope(IsolationScope.USER)// USER 级别需要加锁.snapshotSpec(redisSnapshotSpec).executionGuard(guard))// ← 分布式锁.build();4. 自管理沙箱——自己控制生命周期// 你自己创建沙箱SandboxmySandboxdockerClient.create(workspaceSpec,snapshotSpec,options);mySandbox.start();// 构建调用上下文SandboxContextcallCtxSandboxContext.builder().client(dockerClient).externalSandbox(mySandbox)// 告诉框架这个沙箱我自己管.build();// 调用 Agent——框架只做 stop()不做 shutdown()agent.call(msgs,RuntimeContext.builder().sessionId(my-session).put(SandboxContext.class,callCtx)// 通过 RuntimeContext 注入 SandboxContext.build()).block();// 你自己决定什么时候销毁mySandbox.shutdown();整体流程图┌─────────────────────────────────────────────────────────────────────────────┐ │ 沙箱模式完整流程 │ └─────────────────────────────────────────────────────────────────────────────┘ ┌──────────────────┐ │ agent.call() │ └────────┬─────────┘ │ ▼ ┌───────────────────────────────────┐ │ SandboxLifecycleMiddleware │ │ (PreCall) │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ SandboxManager.acquire() │ │ │ │ Priority 1: externalSandbox? │──▶ 用户管理的沙箱 │ Priority 2: externalState? │──▶ 从指定状态恢复 │ Priority 3: stateStore 有状态? │──▶ 按隔离范围恢复 │ Priority 4: 创建新沙箱 │──▶ 冷启动 └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ Sandbox.start() │ │ │ │ ┌─────────────────────────┐ │ │ │ 4-分支恢复逻辑 │ │ │ │ │ │ │ │ A: 热启动容器目录都在│ │ │ │ B: 读快照容器在目录丢│ │ │ │ C: 读快照容器没了 │ │ │ │ D: 冷启动全量初始化 │ │ │ └─────────────────────────┘ │ │ │ │ 应用 WorkspaceProjectionEntry │ │ 同步 AGENTS.md, skills/ 等 │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ Agent 执行 │ │ │ │ ┌─────────────────────────────┐ │ │ │ ShellExecuteTool │ │ │ │ → 在沙箱内执行命令 │ │ │ │ │ │ │ │ FilesystemTool │ │ │ │ → 在沙箱内读写文件 │ │ │ └─────────────────────────────┘ │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ SandboxLifecycleMiddleware │ │ (PostCall/Error) │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ Sandbox.stop() │ │ │ │ 1. 打包工作区为 tar │ │ 2. 上传到快照后端OSS/Redis │ │ 3. 设置 workspaceRootReadytrue │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ 持久化 SandboxState │ │ 存到 SessionSandboxStateStore│ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ SandboxManager.release() │ │ │ │ 自管理调用 shutdown() 销毁容器 │ │ 用户管理不销毁容器继续运行 │ └─────────────────┬─────────────────┘ │ ▼ ┌──────────────────┐ │ call() 返回 │ └──────────────────┘模块关系与学习顺序文件系统SandboxFilesystemSpec把文件读写和 Shell 执行路由到隔离环境工作区工作区内容会投影到沙箱为 Agent 提供统一的目录和规则上下文沙箱句柄通过RuntimeContext在一次调用中传递快照用于跨调用恢复计划模式规划阶段限制写操作沙箱负责执行阶段的环境隔离两者解决不同层面的安全问题学习要点必须记住沙箱 隔离 可恢复 可分布三个承诺对应三个核心能力——执行边界、跨调用恢复、多副本部署5 种后端代码不用改Docker、Kubernetes、Daytona、E2B、AgentRun 都实现同一组接口换后端只改配置5 种快照后端NoopSnapshotSpec不存、LocalSnapshotSpec本地、OssSnapshotSpecOSS/S3、RedisSnapshotSpecRedis、JdbcSnapshotSpec数据库分布式三件套分布式 AgentStateStore 远端快照 合适的 IsolationScope用.distributedStore(...)一行配齐SESSION 天然安全其他级别要加锁USER/AGENT/GLOBAL在多副本下必须配SandboxExecutionGuard容易混淆IsolationScope vs SnapshotSpecIsolationScope决定谁和谁共享同一个沙箱隔离粒度SnapshotSpec决定快照存到哪里存储位置两个是独立的维度可以任意组合自管理 vs 用户管理自管理Self-managed默认行为框架全权负责沙箱的创建、启动、停止、销毁用户管理User-managed通过SandboxContext.externalSandbox()注入框架只stop()不shutdown()工作区投影 vs 快照工作区投影宿主机 → 沙箱的单向同步AGENTS.md、skills/等每次启动执行快照沙箱工作区的完整打包保存包括pip install的依赖、生成的文件等call()结束时执行实践建议开发阶段用 Docker SESSION最简配置本地就能跑不需要分布式组件上线前切换到 OSS 快照 USER 隔离生产环境必须持久化快照避免冷启动分布式场景一定配锁USER/AGENT/GLOBAL级别 多副本 必须加RedisSandboxExecutionGuard分布式场景用.distributedStore(...)一次配齐传入DistributedStore如RedisDistributedStore.fromJedis(jedis, prefix)框架自动把 AgentStateStore、文件存储、沙箱快照、执行锁都装配好避免漏配某一环导致跨副本状态不一致常见问题Q什么时候该用沙箱什么时候用本地文件系统维度沙箱模式本地文件系统执行位置隔离容器内宿主机Shell 命令在沙箱内执行在宿主机执行适用场景不可信输入、需要隔离、多副本可信环境、轻量级、单机复杂度较高需要容器基础设施较低Q5 种沙箱后端怎么选你的场景是什么 ├── 本地开发有 Docker → Docker ├── 自建 K8s 集群 → Kubernetes ├── 需要托管沙箱不关心基础设施 → Daytona / E2B ├── 阿里云用户中国大陆业务 → AgentRun低延迟 NAS └── 需要自定义隔离环境 → 实现自己的沙箱后端QNAS-first 模式为什么比 tar 快照快传统 tar 快照 沙箱工作区 → 打包 tar → 上传 OSS → 下次下载 → 解压到沙箱 大工作区可能需要几分钟 NAS-first 沙箱工作区 → 直接在 NAS 上 → 下次启动直接可用 秒级恢复因为文件一直都在 NAS 上Q如何调试沙箱问题查看SandboxLifecycleMiddleware日志确认走了 4-分支中的哪一个检查SessionSandboxStateStore中的状态是否正确持久化检查快照是否成功上传到 OSS/RedisAgentRun 可通过GetSandboxAPI 查看沙箱状态
返回列表