CubeSandbox 快照、回滚与克隆实战指南:cubesandbox Python SDK 端到端示例详解

CubeSandbox 快照、回滚与克隆实战指南:cubesandbox Python SDK 端到端示例详解 CubeSandbox 快照、回滚与克隆实战指南cubesandbox Python SDK 端到端示例详解【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox快照、回滚与克隆是 CubeSandbox 面向 AI Agent 场景提供的三组高级能力create_snapshot()把运行中沙箱的完整状态内存 文件系统持久化为可复用镜像clone()从运行中沙箱一行代码派生 N 个相互隔离的副本rollback()将沙箱原地还原到历史快照并继续执行。本文以仓库中的端到端示例目录 examples/snapshot-rollback-clone 为骨架逐脚本讲解每个 API 的用法、运行方式和验证点并结合 SDK 源码与 CubeAPI 后端实现说明其底层原理帮助你把这些能力直接落地到自己的 Agent 工作流中。能力总览三个 API 分别解决什么问题在 AI Agent 的迭代式执行中经常需要在状态可回溯、结果可复制、分支可并行之间取得平衡。CubeSandbox 用三个接口完成了这一闭环API作用对象Sandbox ID典型场景sb.create_snapshot()源沙箱自身不变创建检查点、持久化状态sb.clone(nN)从运行中的沙箱派生 N 个新沙箱N 个新 IDAgent 并行 rollout、可重复实验sb.rollback(snap_id)把当前沙箱还原到某个快照状态保持不变撤销失败步骤、从保存点分叉重试三者的协作关系可以用下面的流程图概括快照是三者共同的枢纽clone()内部通过快照一次、创建 N 次实现扇出rollback()则把沙箱进程从快照镜像重新启动来达成原地还原。完整的概念阐述可参考 docs/guide/snapshot-rollback-clone.md。需要说明的是快照、回滚与克隆是 CubeSandbox 独占能力——E2B 原版 SDK 没有对应接口。cubesandboxSDK 与 E2B SDK 兼容可以作为 drop-in 替换使用同时额外提供这些高级特性。环境准备与运行方式所有示例脚本都依赖cubesandboxPython SDK0.2.0 及以上版本可以直接安装或通过目录下的 requirements.txt内容为cubesandbox0.2.0安装pip install cubesandbox0.2.0 # 或 pip install -r requirements.txt export CUBE_API_URLhttp://127.0.0.1:3000 export CUBE_TEMPLATE_IDtpl-xxxxxxxxxxxxxxxxxxxxxxxx两个环境变量的用途CUBE_API_URLCubeAPI 服务的地址本地部署时默认为http://127.0.0.1:3000CUBE_TEMPLATE_ID创建沙箱所用的基础模板 ID格式为tpl-开头的字符串。共享环境辅助脚本 env.py 统一从环境变量读取配置未设置CUBE_TEMPLATE_ID时它会输出错误并以退出码 2 终止并提示可用cubemastercli tpl list查询模板当 CubeAPI 走 HTTPS 时还可以通过可选的SSL_CERT_FILE指定集群根 CA 证书路径。运行某个示例非常简单——每个脚本都是独立、自包含、可直接运行的python 01_create_snapshot.py python 04_state_preserved.py python 09_rollback.py # ...目录中还附带clone_demo.py、rollback_demo.py以及一组bench_*.py并发基准脚本如bench_snapshot_concurrency.py、bench_rollback_concurrency.py、bench_clone_concurrency.py方便在真实集群上评估各操作的并发吞吐。示例清单#脚本主题0101_create_snapshot.pysb.create_snapshot()基础用法0202_list_snapshots.pySandbox.list_snapshots()全量 / 按 sandbox_id 过滤 / 分页0303_clone_from_snapshot.py用template参数从快照启动新沙箱0404_state_preserved.py文件系统与内存状态在 snapshot clone 后均得以保留0505_snapshot_outlives_sandbox.py快照生命周期独立于源沙箱0606_clone_n.py一行sb.clone(nN)派生 N 个沙箱0707_clone_concurrent.pysb.clone(nN, concurrencyC)并发派生0808_fork_three_axis.py连续性 / 继承性 / 隔离性验证0909_rollback.pysb.rollback(snapshot_id)原地回滚1010_rollback_then_continue.py回滚后继续执行 在新分支上再次快照1111_delete_snapshot.pySandbox.delete_snapshot()基础用法快照创建、列出与删除创建快照Demo 01sb.create_snapshot()会短暂暂停沙箱捕获其完整状态内存 文件系统后恢复运行并返回一个包含snapshot_id的SnapshotInfo对象from cubesandbox import Sandbox from env import TEMPLATE_ID with Sandbox.create(templateTEMPLATE_ID) as sb: print(fsandbox: {sb.sandbox_id}) snapshot sb.create_snapshot() print(fsnapshot created: {snapshot.snapshot_id}) print(fuse as template: Sandbox.create(template{snapshot.snapshot_id})) # Cleanup Sandbox.delete_snapshot(snapshot.snapshot_id) print(snapshot deleted)从 SDK 源码看create_snapshot的实现位于 sdk/python/cubesandbox/sandbox.py它向POST /sandboxes/:sandboxID/snapshots发送请求返回的SnapshotInfo包含snapshot_id与names两个字段定义见 sdk/python/cubesandbox/_models.py。该方法还接受一个可选的name参数当同名模板已存在时新的快照构建会挂接到既有模板上而非新建模板。在后端侧CubeAPI 对应的处理逻辑位于 CubeAPI/src/handlers/snapshots.rs创建成功返回 HTTP 201 与SnapshotInfo沙箱不存在时返回 404。快照的生命周期独立于沙箱Demo 05快照创建后其生命周期与源沙箱解耦——即使源沙箱被kill()掉快照依然可用。Demo 05 完整演示了这一特性sb Sandbox.create(templateTEMPLATE_ID) snap sb.create_snapshot() snapshot_id snap.snapshot_id sb.kill() print(fsandbox killed: {sandbox_id}) # 遍历分页确认快照仍然存在 all_ids [] items, token Sandbox.list_snapshots() while True: all_ids.extend(s.snapshot_id for s in items) if not token: break items, token Sandbox.list_snapshots(next_tokentoken) if snapshot_id in all_ids: print(fOK: snapshot {snapshot_id} still exists after sandbox kill)这一点在 SDK 与后端文档中都有明确佐证delete_snapshot的 docstring 指出删除源沙箱不会级联删除其快照sandbox.py后端同样明确快照以模板形式存储必须显式删除。列出快照全量、过滤与分页Demo 02Sandbox.list_snapshots()返回(list[SnapshotInfo], next_token)二元组支持三种用法# 2a. 全量遍历分页 items, token Sandbox.list_snapshots() while True: for snap in items: print(f {snap.snapshot_id}) if not token: break items, token Sandbox.list_snapshots(next_tokentoken) # 2b. 按 sandbox_id 过滤 items, _ Sandbox.list_snapshots(sandbox_idsandbox_id)分页语义为next_token为None表示没有更多页非None则将其作为nextToken参数传回以获取下一页。从源码看sandbox.py该方法支持三个可选参数sandbox_id按源沙箱 ID 过滤对应请求参数sandboxID、limit页大小默认 100、next_token分页游标。请求路径为GET /snapshots下一页游标从响应头x-next-token中读取。在后端 CubeAPI/src/handlers/snapshots.rs 中分页令牌同样通过x-next-token响应头返回与 SDK 的读取逻辑一一对应。删除快照Demo 11Sandbox.delete_snapshot(snapshot_id)是类方法不需要某个特定沙箱实例sb Sandbox.create(templateTEMPLATE_ID) snap sb.create_snapshot() snapshot_id snap.snapshot_id sb.kill() Sandbox.delete_snapshot(snapshot_id) # 验证快照已从列表中消失 all_ids [] items, token Sandbox.list_snapshots() while True: all_ids.extend(s.snapshot_id for s in items) if not token: break items, token Sandbox.list_snapshots(next_tokentoken) assert snapshot_id not in all_ids print(fOK: snapshot {snapshot_id} removed from list)删除快照在 HTTP 层走的是DELETE /templates/:templateID而非独立的/snapshots/{id}端点——因为快照本质上以模板形式存储在系统中。后端代码 CubeAPI/src/handlers/snapshots.rs 对此有明确注释不额外暴露/snapshots/{id}的 DELETE 端点是为了避免两个入口返回不一致的响应形态同时把这个 ID 是不是快照的判定收敛到delete_template一处。从快照派生新沙箱状态保留验证用template从快照启动沙箱Demo 03快照 ID 可以直接当作模板 ID 传给Sandbox.create(template...)新沙箱将从快照捕获的内存 文件系统状态精确启动from cubesandbox import Sandbox from env import TEMPLATE_ID with Sandbox.create(templateTEMPLATE_ID) as src: snapshot src.create_snapshot() snapshot_id snapshot.snapshot_id print(fsnapshot created: {snapshot_id}) with Sandbox.create(templatesnapshot_id) as cloned: print(fcloned sandbox: {cloned.sandbox_id}) Sandbox.delete_snapshot(snapshot_id)状态保留的端到端验证Demo 04Demo 04 用写标记文件 → 快照 → 克隆 → 读标记的方式对状态保留做了断言式验证MARKER hello from snapshot with Sandbox.create(templateTEMPLATE_ID) as src: src.run_code(fopen(/tmp/marker.txt,w).write({MARKER})) snapshot src.create_snapshot() snapshot_id snapshot.snapshot_id with Sandbox.create(templatesnapshot_id) as cloned: result cloned.run_code(print(open(/tmp/marker.txt).read())) content result.logs.stdout[0].strip() if result.logs.stdout else assert content MARKER, fstate not preserved: got {content!r} print(OK: filesystem state preserved in cloned sandbox) Sandbox.delete_snapshot(snapshot_id)注意这里快照捕获的不仅是文件系统还包括内存状态——这意味着运行中的进程、已加载的模块、未刷盘的变量都处于同一快照语义下create_snapshot的 docstring 明确说明沙箱在快照创建期间会被临时暂停。如果你的 Agent 依赖内存态如已训练的模型、已建立的会话快照同样能保住这部分状态。克隆一行代码扇出 N 个沙箱基础克隆Demo 06sb.clone(nN)是快照 派生 清理的一站式封装调用后源沙箱保持运行临时快照自动被清理list_snapshots()不会再看到它。其内部执行步骤如下源码见 sandbox.py 的 docstringself.create_snapshot()—— 捕获当前状态Sandbox.create(templatesnapshot_id) × n—— 从该快照派生 N 个沙箱将共享清理状态挂到每个克隆上最后一个克隆被kill()时删除临时快照best-effort。N 3 src Sandbox.create(templateTEMPLATE_ID) src.run_code(open(/tmp/shared.txt,w).write(shared state)) # ★ 一行克隆 —— SDK 内部处理 snapshot/create/delete clones src.clone(nN) print(fcloned {len(clones)} sandboxes) for i, sb in enumerate(clones): result sb.run_code(print(open(/tmp/shared.txt).read())) content result.logs.stdout[0].strip() if result.logs.stdout else assert content shared state print(fOK: {N} sandboxes cloned via sb.clone(n{N}), all share initial state) src.kill() for sb in clones: sb.kill()并发克隆Demo 07默认情况下clone(nN)串行创建子沙箱。对于大规模扇出场景例如并行 Agent rollout可以传入concurrencyCimport os N int(os.environ.get(FORK_N, 10)) CONCURRENCY int(os.environ.get(FORK_CONCURRENCY, 5)) src Sandbox.create(templateTEMPLATE_ID) src.run_code(open(/tmp/origin.txt,w).write(I am from sandbox a)) clones src.clone(nN, concurrencyCONCURRENCY) # 验证每个克隆都继承了源沙箱写入的标记 expect I am from sandbox a ok 0 for i, sb in enumerate(clones): r sb.run_code(print(open(/tmp/origin.txt).read())) marker r.logs.stdout[0].strip() if r.logs.stdout else if marker expect: ok 1 assert ok N, some clones failed to inherit stateFORK_N与FORK_CONCURRENCY两个环境变量可调节扇出规模与并发度。并发语义上有几个关键点均来自 SDK 源码实现线程池concurrency 1时通过concurrent.futures.ThreadPoolExecutor分发实际工作线程数为min(n, concurrency)所以传一个大于 N 的值是无害的串行与并发等价concurrency1默认不启动任何线程行为与串行循环逐字节一致快照只做一次快照创建与删除各执行一次只有创建 N 个子沙箱这一步并行全有或全无的失败语义任一子任务失败时所有已成功创建的克隆都会被自动kill()随后抛出首个异常——调用方要么拿到恰好 N 个沙箱要么拿到一个异常不会产生孤儿资源。实现上sandbox.py会先排空所有 in-flight 的 future收集成功结果与首个异常再决定清理策略返回顺序concurrency 1时列表顺序不确定按后端 create 调用返回顺序排列concurrency 1时保持提交顺序。继承性、隔离性与连续性Demo 08Demo 08 用a.clone(n2)产生b、c两个克隆对三个性质逐一断言验证a Sandbox.create(templateTEMPLATE_ID) a.run_code(open(/tmp/origin.txt,w).write(from a)) b, c a.clone(n2) # 继承性b 和 c 都能看到 fork 之前 a 写入的文件 for sb, name in [(b, b), (c, c)]: r sb.run_code(print(open(/tmp/origin.txt).read())) assert marker from a # 隔离性b 的写入对 c 不可见 b.run_code(open(/tmp/b_only.txt,w).write(b)) r c.run_code(import os; print(os.path.exists(/tmp/b_only.txt))) assert leaked False, isolation violated # 连续性a 仍在运行且状态不受影响 r a.run_code(print(open(/tmp/origin.txt).read())) assert still from a三个性质的完整语义如下表性质含义继承性Inheritance每个克隆的初始状态与克隆调用瞬间的源沙箱完全一致内存 文件系统隔离性Isolation一个克隆中的写入对其他克隆及源沙箱均不可见连续性Continuityclone()返回后源沙箱继续运行状态不受影响回滚原地还原并继续执行基础回滚Demo 09sb.rollback(snapshot_id)将沙箱原地还原到指定快照状态文件系统被完整重置sandbox_id 保持不变sb对象在回滚后依然可用无需重新创建连接。Demo 09 通过v0 → 检查点 v1 → 写入 v2 → 回滚到 v1的完整时间线验证# Step 1: 创建基础快照 v0 with Sandbox.create(templateTEMPLATE_ID) as src: src.run_code(open(/tmp/v.txt,w).write(v0)) base src.create_snapshot() base_id base.snapshot_id # Step 2: 从基础快照启动沙箱 sb Sandbox.create(templatebase_id) # Step 3: 写入 v1打检查点 sb.run_code(open(/tmp/v.txt,w).write(v1)) checkpoint sb.create_snapshot() checkpoint_id checkpoint.snapshot_id # Step 4: 写入 v2确认生效 sb.run_code(open(/tmp/v.txt,w).write(v2)) before sb.run_code(print(open(/tmp/v.txt).read())).logs.stdout assert before[0].strip() v2 # Step 5: 回滚到 v1 检查点 sb.rollback(checkpoint_id) # Step 6: 验证状态回到 v1 after sb.run_code(print(open(/tmp/v.txt).read())).logs.stdout assert after[0].strip() v1 print(OK: rollback restored state to checkpoint (v1))回滚的底层机制值得展开说明源码见 sandbox.pyHTTP 请求为POST /sandboxes/:sandboxID/rollback请求体为{snapshotID: ...}成功后返回形如{sandboxID: ..., snapshotID: ..., status: success}的响应回滚后沙箱进程从快照镜像重新启动这会使此前对该沙箱保持的 TCP 连接全部失效包括沙箱内的 jupyter-server 与 CubeAPI 的 keep-alive 连接池为避免下一次run_code()与半关闭的 socket 竞争SDK 会主动调用_reset_connections()关闭底层 HTTP 客户端池使其在下一次使用时惰性重建——调用方不需要做任何事回滚后直接sb.run_code(...)即可_reset_connections()是幂等且 best-effort 的即使客户端从未构建从未调用过 run_code或close()抛错也不会影响回滚本身。这一行为在 sdk/python/tests/test_sandbox.py 的Sandbox.rollback测试族中有完整覆盖如test_rollback_closes_httpx_client_so_run_code_rebuilds、test_rollback_when_client_never_built_is_safe。后端侧回滚处理逻辑位于 CubeAPI/src/handlers/snapshots.rs沙箱或快照不存在时返回 404CubeAPI 与 CubeMaster 之间的回滚协议同步终端结果在 CubeAPI/src/cubemaster/mod.rs 有对应客户端实现。回滚后继续执行并再打快照Demo 10回滚后的沙箱依然可写可以继续执行、写入新状态并在新分支上再次创建快照。这正是 Agent 重试循环的理想模式在每个关键决策点打检查点失败时回滚重试另一条分支。Demo 10 的时间线为v1检查点→ v2 → 回滚到 v1 → 写入 v3 → 再次快照sb Sandbox.create(templateTEMPLATE_ID) # 写入 v1 并打检查点 sb.run_code(open(/tmp/v.txt,w).write(v1)) checkpoint sb.create_snapshot() # 推进到 v2然后回滚到 v1 sb.run_code(open(/tmp/v.txt,w).write(v2)) sb.rollback(checkpoint.snapshot_id) assert got v1 # 在回滚后的分支上继续写入 v3 sb.run_code(open(/tmp/v.txt,w).write(v3)) assert got v3 # 从该分支再打新快照并通过克隆验证 new_snap sb.create_snapshot() with Sandbox.create(templatenew_snap.snapshot_id) as forked: assert got v3 print(OK: rollback continue re-snapshot all consistent)最佳实践结合官方指南docs/guide/snapshot-rollback-clone.md与示例代码中的清理模式总结出以下四条实践经验快照不是免费的。每个快照都对应持久化存储中的一份完整镜像。不需要时应及时删除或定期运行list_snapshots()盘点清理避免存储膨胀。with块不会删除快照。上下文管理器只负责kill()沙箱本身用create_snapshot()创建的快照必须显式调用Sandbox.delete_snapshot()清理。观察所有示例可以发现每个脚本在结束时都严格执行delete_snapshot这正是推荐的习惯用法。clone()会自动清理内部临时快照。对于临时扇出场景无需手动管理快照生命周期直接调用clone()即可且失败时 SDK 保证不残留孤儿沙箱。大规模扇出优先使用clone(nN, concurrencyC)。SDK 内部处理失败清理与临时快照避免并发创建时出现部分成功、部分失败导致的资源泄漏concurrency1时行为与串行循环完全一致可以放心从默认值开始调参。API 速查from cubesandbox import Sandbox sb Sandbox.create(templateTEMPLATE_ID) # 快照 snap sb.create_snapshot() # → SnapshotInfo(snapshot_id...) items, token Sandbox.list_snapshots() # 分页token 为 None 表示结束 Sandbox.delete_snapshot(snap.snapshot_id) # 克隆从运行中的沙箱一对多派生 clones sb.clone(n5) # 串行 clones sb.clone(n10, concurrency4) # 通过线程池并发 # 回滚原地sandbox_id 保持不变 sb.rollback(snap.snapshot_id) # 把快照当作模板启动新沙箱 fresh Sandbox.create(templatesnap.snapshot_id)延伸阅读指南快照、回滚与克隆 —— 本文对应的官方完整指南跨节点快照 —— S3 后端、remote_statusready以及 Resume / FromSnap 跨节点调度的调度器规则SDK 源码sdk/python/cubesandbox核心实现集中在 sandbox.py模型定义见 _models.pySDK 测试sdk/python/tests/test_sandbox.py ——clone/rollback/ 连接重置行为的单元测试覆盖后端处理CubeAPI/src/handlers/snapshots.rs —— 快照创建、列表、回滚三个 HTTP 端点的 axum 实现基础用法示例examples/code-sandbox-quickstart —— E2B 兼容的基础流程。【免费下载链接】CubeSandboxInstant, Concurrent, Secure Lightweight Sandbox for AI Agents.项目地址: https://gitcode.com/GitHub_Trending/cu/CubeSandbox创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考