版本基准:本文档基于 AgentScope 2.0 GA(
v2.0.0)编写。具体版本号以 Release Notes 为准。
一句话概括
沙箱就是把 AI Agent 关在一个隔离的安全笼子里干活——它可以在笼子里随便折腾(装软件、跑命令、写文件),但不会影响到你的主机系统,而且笼子里的东西还能被"打包带走"下次继续用。
你能学到什么
- 为什么需要沙箱?Agent 直接在主机上跑不行吗?
- 5 种沙箱后端怎么选:Docker、Kubernetes、Daytona、E2B、AgentRun
- 4 种隔离级别(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 时回退为SESSION) | VIP 客户专属房间 | 跨会话共享工作区,分布式部署 |
SESSION | 每个 sessionId 独立 | 每次入住开新房间 | 多用户 SaaS,对话完全隔离 |
AGENT | 这个 agent 的所有用户/会话共享 | 公司包下一间房公用 | 公共知识库型 Agent |
GLOBAL | 一个 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 | 容器没了 + 快照不可用 | 创建新容器 + 全量初始化(最慢) | 新游戏,从头开始 |
快照存到哪里?取决于你配的snapshotSpec:
| Spec | 存储位置 | 适用场景 | 生活类比 |
|---|---|---|---|
NoopSnapshotSpec(默认) | 不持久化 | 临时任务,不需要恢复 | 一次性用品,用完即弃 |
LocalSnapshotSpec | 宿主本地文件 | 单机长期运行 | 家里的储物间 |
OssSnapshotSpec | OSS/S3 兼容存储 | 多副本分布式 | 云端大仓库 |
RedisSnapshotSpec | Redis | 低延迟、小工作区 | 快递柜,存取快但空间小 |
JdbcSnapshotSpec | JDBC 数据库(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) - 合适的 IsolationScope(
USER/AGENT/GLOBAL)
HarnessAgent 提供了.distributedStore(...)方法,一次性把这"分布式三件套"打包声明:传入一个DistributedStore(聚合了AgentStateStore+ 文件存储baseStore+SandboxSnapshotSpec+SandboxExecutionGuard),框架在构建期自动把它们分别装配到对应位置。
// Redis:一条龙配齐分布式存储(AgentStateStore + 文件存储 + 沙箱快照 + 执行锁)JedisPooledjedis=newJedisPooled("redis-host",6379);DistributedStorestore=RedisDistributedStore.fromJedis(jedis,"agentscope:");HarnessAgentagent=HarnessAgent.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→ 按 userId,AGENT→ 按 agent 名)。你也可以实现SandboxExecutionGuard接口,接入其他锁后端(数据库、Zookeeper、etcd 等)。
自管理沙箱实例 —— 就像自租房 vs 酒店托管
生活类比:默认情况下,沙箱就像住酒店——前台(框架)帮你开门、关门、打扫。但有时候你想自己租一间房子,自己拿钥匙、自己决定什么时候退租。
默认沙箱的整个生命周期由框架托管。但 HarnessAgent 提供了三种"我自己管"的场景:
场景 1:我已经启动好一个容器,想让 Agent 用它
// 你自己创建并启动沙箱SandboxmySandbox=dockerClient.create(workspaceSpec,snapshotSpec,options);mySandbox.start();// 注入到调用中SandboxContextcallCtx=SandboxContext.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:我有一个具体的快照串,想恢复到那个时刻
// 从保存的状态恢复SandboxStatesavedState=dockerClient.deserializeState(savedStateJson);SandboxContextcallCtx=SandboxContext.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.md | Agent 的身份定义和指令 |
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 沙箱的 AgentHarnessAgentagent=HarnessAgent.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 快照存储OssSnapshotSpecsnapshotSpec=newOssSnapshotSpec(ossClient,// OSS 客户端"my-bucket",// Bucket 名称"agentscope/"// 存储路径前缀);// 创建 Agent,支持分布式部署HarnessAgentagent=HarnessAgent.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 分布式锁UnifiedJedisjedis=newJedisPooled("redis-host",6379);SandboxExecutionGuardguard=RedisSandboxExecutionGuard.builder(jedis).leaseTtl(Duration.ofMinutes(30))// 比最坏情况 call 时长稍长.retryInterval(Duration.ofMillis(500))// 轮询间隔.build();// 把锁配到沙箱上HarnessAgentagent=HarnessAgent.builder().name("assistant").model(model).filesystem(newDockerFilesystemSpec().image("ubuntu:24.04").isolationScope(IsolationScope.USER)// USER 级别需要加锁.snapshotSpec(redisSnapshotSpec).executionGuard(guard))// ← 分布式锁.build();4. 自管理沙箱——自己控制生命周期
// 你自己创建沙箱SandboxmySandbox=dockerClient.create(workspaceSpec,snapshotSpec,options);mySandbox.start();// 构建调用上下文SandboxContextcallCtx=SandboxContext.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. 设置 workspaceRootReady=true │ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ 持久化 SandboxState │ │ (存到 SessionSandboxStateStore)│ └─────────────────┬─────────────────┘ │ ▼ ┌───────────────────────────────────┐ │ SandboxManager.release() │ │ │ │ 自管理:调用 shutdown() 销毁容器 │ │ 用户管理:不销毁,容器继续运行 │ └─────────────────┬─────────────────┘ │ ▼ ┌──────────────────┐ │ call() 返回 │ └──────────────────┘模块关系与学习顺序
- 文件系统:
SandboxFilesystemSpec把文件读写和 Shell 执行路由到隔离环境 - 工作区:工作区内容会投影到沙箱,为 Agent 提供统一的目录和规则
- 上下文:沙箱句柄通过
RuntimeContext在一次调用中传递,快照用于跨调用恢复 - 计划模式:规划阶段限制写操作,沙箱负责执行阶段的环境隔离,两者解决不同层面的安全问题
学习要点
必须记住
- 沙箱 = 隔离 + 可恢复 + 可分布:三个承诺对应三个核心能力——执行边界、跨调用恢复、多副本部署
- 5 种后端,代码不用改:Docker、Kubernetes、Daytona、E2B、AgentRun 都实现同一组接口,换后端只改配置
- 5 种快照后端:
NoopSnapshotSpec(不存)、LocalSnapshotSpec(本地)、OssSnapshotSpec(OSS/S3)、RedisSnapshotSpec(Redis)、JdbcSnapshotSpec(数据库) - 分布式三件套:分布式 AgentStateStore + 远端快照 + 合适的 IsolationScope;用
.distributedStore(...)一行配齐 - SESSION 天然安全,其他级别要加锁:
USER/AGENT/GLOBAL在多副本下必须配SandboxExecutionGuard
容易混淆
IsolationScope vs SnapshotSpec:
IsolationScope:决定"谁和谁共享同一个沙箱"(隔离粒度)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 命令 | 在沙箱内执行 | 在宿主机执行 |
| 适用场景 | 不可信输入、需要隔离、多副本 | 可信环境、轻量级、单机 |
| 复杂度 | 较高(需要容器基础设施) | 较低 |
Q:5 种沙箱后端怎么选?
你的场景是什么? ├── 本地开发,有 Docker → Docker ├── 自建 K8s 集群 → Kubernetes ├── 需要托管沙箱,不关心基础设施 → Daytona / E2B ├── 阿里云用户,中国大陆业务 → AgentRun(低延迟 + NAS) └── 需要自定义隔离环境 → 实现自己的沙箱后端Q:NAS-first 模式为什么比 tar 快照快?
传统 tar 快照: 沙箱工作区 → 打包 tar → 上传 OSS → 下次下载 → 解压到沙箱 (大工作区可能需要几分钟) NAS-first: 沙箱工作区 → 直接在 NAS 上 → 下次启动直接可用 (秒级恢复,因为文件一直都在 NAS 上)Q:如何调试沙箱问题?
- 查看
SandboxLifecycleMiddleware日志,确认走了 4-分支中的哪一个 - 检查
SessionSandboxStateStore中的状态是否正确持久化 - 检查快照是否成功上传到 OSS/Redis
- AgentRun 可通过
GetSandboxAPI 查看沙箱状态