当 AI Agent 开始在你的服务器上执行命令、读写文件、创建容器时,你真的知道它做了什么吗?

链接

为什么做这件事

过去几个月,AI Agent 从"能聊天"进化到了"能干活"。Deep Agents、Manus、各种 Coding Agent 纷纷落地,它们需要真实的 Linux 环境来执行代码、安装依赖、运行测试。

但一个问题始终悬在工程师头上:

Agent 在沙箱里到底做了什么?出了问题怎么排查?删了什么文件?执行了什么命令?什么时候失败的?

市面上不缺沙箱运行时——E2B 用 Firecracker,Daytona 自研运行时,阿里开源了 OpenSandbox。但它们要么是纯 SaaS(数据不在自己手里),要么只提供基础 SDK(没有管理界面),没有一套方案能同时解决"给 Agent 用"和"给人看"这两个需求

所以我做了两个开源项目,组合起来解决这个问题:

  • Agent Sandbox Backend SDKagent-sandbox-backends)— 面向 AI Agent 的 OpenSandbox 适配 SDK,一行代码接入 Deep Agents 框架,内置沙箱内操作历史数据库、并发控制、文件上传安全策略

  • Sandbox Explorersandbox-explorer)— 自托管 Web 控制台,浏览器里管理沙箱、浏览文件、执行命令、打开终端、查看完整操作历史

两者通过沙箱内的 SQLite 历史数据库松耦合协作:SDK 写入,Console 读取,沙箱删除则历史一起删除,生命周期完全对齐


整体架构

先看全貌:

                    ┌─────────────────────────────────────────────┐
                    │            你的 Python 进程                  │
                    │                                             │
                    │   ┌───────────────────────────────────┐     │
                    │   │     Deep Agents / 你的 Agent 代码   │     │
                    │   └──────────────┬────────────────────┘     │
                    │                  │ as_deepagents_backend()  │
                    │   ┌──────────────▼────────────────────┐     │
                    │   │   agent-sandbox-backends (SDK)     │     │
                    │   │                                   │     │
                    │   │  ┌─────────┐  ┌───────────────┐   │     │
                    │   │  │Operation│  │   History     │   │     │
                    │   │  │Pipeline │──│   Store       │   │     │
                    │   │  └────┬────┘  └───────┬───────┘   │     │
                    │   │       │               │           │     │
                    │   │  ┌────▼────┐  ┌───────▼───────┐   │     │
                    │   │  │Provider │  │  Concurrency  │   │     │
                    │   │  │Adapter  │  │  KeyedRWLock  │   │     │
                    │   │  └────┬────┘  └───────────────┘   │     │
                    │   │       │               │           │     │
                    │   │  ┌────▼────┐  ┌───────▼───────┐   │     │
                    │   │  │ Upload  │  │   Security    │   │     │
                    │   │  │ Scanner │  │   Scanner     │   │     │
                    │   │  └─────────┘  └───────────────┘   │     │
                    │   └───────────────────────────────────┘     │
                    │                  │ HTTP/gRPC                │
                    └──────────────────┼─────────────────────────┘
                                       │
                    ┌──────────────────▼─────────────────────────┐
                    │        OpenSandbox 沙箱运行时                │
                    │                                            │
                    │   ┌──────────────────────────────────────┐  │
                    │   │     沙箱实例 (Sandbox)                │  │
                    │   │                                      │  │
                    │   │  /workspace/        ← Agent 工作目录  │  │
                    │   │  /.agent-history/                     │  │
                    │   │    └── history.sqlite3  ← 操作历史DB  │  │
                    │   │  /.agent-helper/      ← 历史写入工具  │  │
                    │   └──────────────────────────────────────┘  │
                    └──────────────────┬─────────────────────────┘
                                       │
                    ┌──────────────────▼─────────────────────────┐
                    │      Sandbox Explorer (Web Console)        │
                    │                                            │
                    │   ┌────────────┐ ┌────────┐ ┌──────────┐  │
                    │   │ File Browse │ │Command │ │ Terminal │  │
                    │   │ (Monaco)   │ │(SSE)   │ │(xterm.js)│  │
                    │   └────────────┘ └────────┘ └──────────┘  │
                    │                                            │
                    │   ┌──────────────────────────────────────┐ │
                    │   │     History Timeline (历史时间线)      │ │
                    │   │  SDK 操作 + Console 操作 统一展示     │ │
                    │   └──────────────────────────────────────┘ │
                    └────────────────────────────────────────────┘

核心设计思想

  1. SDK 不实现沙箱运行时,而是适配 OpenSandbox,让 Deep Agents 框架能用一行代码接入

  2. 操作历史写在沙箱内的 SQLite 里,而非 Web 后端,沙箱删除则历史自动清除,生命周期完全对齐

  3. Console 和 SDK 互不感知,通过沙箱内的历史数据库松耦合协作

  4. Console 是可选的,SDK 独立工作不受影响;Console 独立工作也能直连沙箱


项目一:Agent Sandbox Backend SDK

仓库Rainbow0328/agent-sandbox-backend PyPI 包名agent-sandbox-backends

一行代码接入 Deep Agents

from agent_sandbox_backends import CleanupPolicy, create_opensandbox_backend
from agent_sandbox_backends.integrations.deepagents import as_deepagents_backend
from deepagents import create_deep_agent
​
# 创建 Backend
core_backend = await create_opensandbox_backend(
    "http://your-opensandbox-service:8080",
    cleanup=CleanupPolicy.ON_CLOSE,
)
​
# 一行转换为 Deep Agents 后端
backend = as_deepagents_backend(core_backend)
​
# 交给 Deep Agents
agent = create_deep_agent(model=model, backend=backend)

Deep Agents 的文件查看、读取、写入、编辑、搜索、上传、下载和命令执行,全部自动适配到 OpenSandbox 沙箱。你不需要自己写任何 Adapter 代码

沙箱内操作历史(核心差异化)

这是我认为这个项目最有价值的部分。

问题:Agent 在沙箱里执行了 50 条命令、改了 20 个文件,中间某一步失败了。你怎么排查?E2B 不记录,Daytona 不记录,OpenSandbox 官方 SDK 也不记录。

方案:SDK 的 OperationPipeline 在每次操作前后自动写入沙箱内的 /.agent-history/history.sqlite3,完整记录:

字段 说明
event_id 全局唯一操作 ID(UUIDv7,时间有序)
operation_type 操作类型(command.execute / file.write / sandbox.create 等)
status started / succeeded / failed / cancelled / timeout
actor_type 执行者类型(agent / system / console / user)
actor_id 执行者标识
thread_id 会话线程 ID
run_id 运行批次 ID
correlation_id 关联 ID,用于追踪操作链
request_json 完整请求参数
result_json 完整结果
duration_ms 耗时
stdout/stderr 完整命令输出(分块存储,支持流式读取)
occurred_at 发生时间
completed_at 完成时间

关键设计

  • 四种历史模式NONE(不记录)、PROVIDER(记到 Provider 端)、DATABASE(记到外部 PostgreSQL)、SANDBOX(记到沙箱内 SQLite,默认)

  • TTL + 容量限制:默认 7 天过期,数据库最大 128MB,单次操作输出最大 16MB,超限自动删除最旧记录

  • 增量同步:基于 history_changes 变更日志 + history_consumers 消费者游标,Console 只拉取增量

  • 分布式租约history_leases 表防止多个消费者同时清理历史

  • 输出分块存储:大命令输出分 16KB 块存储,支持流式读取,不会因为一次 cat 大文件就把数据库撑爆

安全保证:API Key 不会写入历史,但命令字符串会——所以不要把密码拼进命令里,优先用环境变量传递。

并发控制:KeyedRWLock

当多个子 Agent 同时操作同一沙箱时,SDK 提供 Writer-preferring Keyed Read/Write Lock:

# 多个 Agent 可以同时读同一个文件(共享锁)
async with backend._lock.read("/workspace/data.json"):
    content = await backend.read_file("/workspace/data.json")
​
# 写操作排他(写优先,防止写饥饿)
async with backend._lock.write("/workspace/data.json"):
    await backend.write_file("/workspace/data.json", new_content)
  • Writer-preferring:有等待中的写锁时,新读锁会让路,防止写饥饿

  • Keyed:不同文件不互相阻塞,粒度细

  • Idle-state cleanup:无引用的锁自动清理,不泄漏内存

  • 超时控制:支持 timeout_seconds,防止死锁

文件上传安全

SDK 不会默认允许任意本地路径上传。必须显式声明允许的根目录:

from pathlib import Path
from agent_sandbox_backends import UploadConfig, UploadSpec, create_opensandbox_backend
​
backend = await create_opensandbox_backend(
    "http://your-opensandbox-service:8080",
    uploads=(UploadSpec(source="./project", target="/workspace/project"),),
    upload_config=UploadConfig(allowed_local_roots=(Path.cwd(),)),
)

上传安全策略覆盖

防护层 说明
路径逃逸防护 检查 .. 和绝对路径,防止写到沙箱任意位置
符号链接防护 检测 symlink,防止读到宿主机敏感文件
特殊文件防护 跳过 .env.ssh.aws.git、虚拟环境
归档炸弹防护 限制解压后总大小和文件数量
校验和验证 上传后重新读取验证 SHA256,防止传输损坏
Staging + 提交 先写暂存区,校验通过后原子提交,失败自动回滚
冲突策略 支持 skip / overwrite / if_changed(只覆盖有变化的)
文件数量限制 默认单次上传最多 10000 个文件
总大小限制 默认单次上传最大 1GB

Actor 上下文追踪

每个操作都携带完整的执行者上下文:

with backend.agent_context(
    agent_id="research-agent",
    thread_id="thread-1",
    run_id="run-1",
):
    await backend.execute("python --version")
    # 这条命令的历史记录会带上 agent_id="research-agent"

correlation_id 自动生成并贯穿整个上下文范围内的所有操作,方便在历史时间线中追踪一次完整的 Agent 运行。


项目二:Sandbox Explorer(Web 控制台)

仓库Rainbow0328/sandbox-web npm 包名sandbox-explorer

一键启动

# 生产模式:构建前端 + 后端服务
python start.py
​
# 开发模式:前后端热更新
python start.py --dev
​
# Docker
docker compose up -d

打开浏览器,就是完整的沙箱管理控制台。无需配置环境变量,认证默认关闭,开箱即用

全功能沙箱管理

沙箱生命周期:创建、暂停、恢复、删除,支持通过 Connection 快速创建,也支持直接输入 URL + API Key 创建。

文件浏览器

  • 目录树浏览,支持面包屑导航

  • Monaco Editor 在线编辑代码(和 VS Code 同款编辑器)

  • 上传文件(支持选择目标目录的自定义弹窗)

  • 下载文件

  • 创建文件夹(自定义弹窗,不用浏览器 prompt)

  • 删除文件/文件夹

命令执行器

  • SSE 实时流式输出,命令执行过程中可以看到逐行输出

  • 支持中断正在执行的命令

  • 命令历史记录

交互式终端

  • 基于 xterm.js + WebSocket 的完整交互终端

  • 支持 Ctrl+C、Tab 补全、颜色输出

  • 和真实终端体验一致

操作历史时间线(核心功能)

这是 Console 最有价值的页面。它会从沙箱内的 history.sqlite3 同步操作记录,并和 Console 自己的操作日志合并展示,形成一个统一的时间线。

你会看到什么

  • SDK 的操作:Agent 执行的每一条命令、每一次文件写入、每一次沙箱创建

  • Console 的操作:你在 Web 界面上执行的命令、上传的文件、创建的文件夹

  • 完整上下文:谁执行的(actor_type)、耗时多少、成功还是失败、完整的请求参数和结果

历史时间线的设计

┌─────────────────────────────────────────────────────────────┐
│  History Timeline                                           │
│                                                             │
│  ┌─ 14:32:01 ─ [SDK]  sandbox.create     ✅ succeeded  120ms│
│  │  actor: research-agent | run: run-1                       │
│  │  request: { image: "python:3.12", workdir: "/workspace" }│
│  └───────────────────────────────────────────────────────────│
│                                                             │
│  ┌─ 14:32:05 ─ [SDK]  command.execute      ✅ succeeded  2.1s│
│  │  actor: research-agent | run: run-1                       │
│  │  command: pip install fastapi                             │
│  │  ▸ stdout: Collecting fastapi... (点击展开)               │
│  └───────────────────────────────────────────────────────────│
│                                                             │
│  ┌─ 14:35:12 ─ [Console] file.write       ✅ succeeded   45ms│
│  │  actor: admin | path: /workspace/main.py                  │
│  └───────────────────────────────────────────────────────────│
│                                                             │
│  ┌─ 14:35:30 ─ [SDK]  command.execute      ❌ failed     5.0s │
│  │  actor: research-agent | run: run-1                       │
│  │  command: python main.py                                  │
│  │  ▸ stderr: ModuleNotFoundError: No module named 'uvicorn' │
│  └───────────────────────────────────────────────────────────│
└─────────────────────────────────────────────────────────────┘

技术实现

  1. Console 通过 SDK 的 SandboxHistoryStore 读取沙箱内 SQLite

  2. 基于 history_changes 变更日志做增量同步,只拉取上次同步之后的新记录

  3. 消费者游标(history_consumers)记录已确认的 seq,支持多 Console 实例独立消费

  4. Console 自己的操作也会写入沙箱历史(best-effort),确保 SDK 和 Console 的操作在同一个时间线里

  5. 历史同步是限流的,不会阻塞正常操作

连接管理

Console 可以管理多个 OpenSandbox 服务连接,凭据使用 Fernet 对称加密存储:

端口可配置

支持通过 CLI、环境变量或 .env 文件配置端口:

# CLI
python start.py --port 3000 --frontend-port 3001
​
# 环境变量
EXPLORER_PORT=3000 python start.py
​
# .env 文件
echo "EXPLORER_PORT=3000" > .env
python start.py

实战演示:Deep Agents + OpenSandbox + Web Console

场景:用 Deep Agents 在沙箱里做代码审查

import asyncio
from agent_sandbox_backends import CleanupPolicy, create_opensandbox_backend
from agent_sandbox_backends.integrations.deepagents import as_deepagents_backend
from deepagents import create_deep_agent
​
async def main():
    # 1. 创建沙箱 Backend
    backend = await create_opensandbox_backend(
        "http://your-opensandbox-service:8080",
        sandbox_name="code-review-session",
        image="python:3.12",
        cleanup=CleanupPolicy.NEVER,  # 不自动删除,方便后续在 Console 查看
    )
​
    # 2. 转换为 Deep Agents 后端
    da_backend = as_deepagents_backend(backend)
​
    # 3. 创建 Agent
    agent = create_deep_agent(
        model="claude-sonnet-4-20250514",
        backend=da_backend,
    )
​
    # 4. 设置 Actor 上下文
    with backend.agent_context(
        agent_id="code-reviewer",
        thread_id="review-001",
        run_id="run-20260720",
    ):
        # 5. 让 Agent 审查代码
        result = await agent.run(
            "Clone https://github.com/example/repo, "
            "review the codebase for security issues, "
            "and write a report to /workspace/report.md"
        )
​
    print(f"Review complete: {result}")
​
    # 6. 不关闭 Backend(cleanup=NEVER),沙箱保留
    # 7. 打开 Web Console 查看完整操作历史!
​
asyncio.run(main())

运行完毕后,打开 Sandbox Explorer:

  1. 在沙箱列表找到 code-review-session

  2. Files 标签页:查看 Agent 生成的 report.md

  3. History 标签页:查看 Agent 执行了哪些 git clonegreppython 命令,每条命令的完整输出

  4. Terminal 标签页:进入沙箱终端,自己验证 Agent 的发现

这就是"可观测的 AI Agent"——不是黑盒,是透明盒子。


安全设计

安全是这套系统的核心设计目标,不是事后补丁:

SDK 侧

安全机制 说明
上传路径白名单 必须显式声明 allowed_local_roots,默认不允许任何路径
符号链接检测 扫描所有上传文件,遇到 symlink 直接拒绝
特殊文件排除 自动排除 .env.ssh.aws.git__pycache__node_modules
归档炸弹防护 限制解压后总大小(默认 1GB)和文件数量(默认 10000)
校验和验证 上传后重新读取并校验 SHA256,防止传输损坏
Staging + 原子提交 先写暂存区,全部校验通过后原子提交,失败自动回滚
API Key 不入历史 历史记录中不保存 API Key,但命令字符串会保存(注意不要拼密码)
并发 KeyedRWLock Writer-preferring,防止写操作饥饿
操作活动门控 关闭/删除沙箱时等待所有活动操作完成,阻止新操作进入

Console 侧

安全机制 说明
凭据加密存储 API Key 使用 Fernet 对称加密(AES-128-CBC + HMAC-SHA256)
Master Key 可配 通过 EXPLORER_MASTER_KEY 环境变量配置加密密钥
Admin Token 认证 可选的 Bearer Token 认证,空则禁用(本地开发友好)
路径逃逸防护 文件操作做 POSIX 路径校验,防止 .. 逃逸
CORS 可配 通过 EXPLORER_CORS_ORIGINS 限制跨域
WebSocket Token 验证 终端 WebSocket 连接需要有效 Token

与同类产品的对比

能力 本方案 E2B Daytona OpenSandbox 官方 SDK
沙箱运行时 OpenSandbox E2B (Firecracker) Daytona 自研 OpenSandbox
Deep Agents 原生集成 ✅ 一行代码 ❌ 需自己封装 ❌ 需自己封装 ❌ 无
沙箱内操作历史 DB ✅ 完整 Schema
自托管 Web 控制台 ✅ 全功能 ❌ SaaS 为主 ❌ CLI 为主 ❌ 无
文件上传安全策略 ✅ 9 层防护
并发控制 (KeyedRWLock) ❌ 基础 ❌ 基础 ❌ 基础
文件浏览器 ✅ Monaco 编辑器
交互式终端 ✅ xterm.js
SSE 流式命令输出
多沙箱连接管理 ✅ 加密凭据
部署方式 Docker / 本地 SaaS / 自建 CLI / SaaS SDK only
开源协议 Apache-2.0 Apache-2.0 Apache-2.0 Apache-2.0

一句话总结差异化:E2B 和 Daytona 是"自己做运行时 + 自己做 SDK + 做 SaaS 管理界面"的全栈方案;本方案是"适配 OpenSandbox + 做 Deep Agents 桥梁 + 做自托管全功能控制台 + 做沙箱内操作历史"的补充方案。不竞争,互补


技术栈

SDK(agent-sandbox-backends)

  • Python 3.11+

  • Pydantic v2(领域模型)

  • SQLAlchemy 2.0 + aiosqlite / asyncpg(历史存储)

  • pathspec(上传安全扫描)

  • Hatchling(构建)

  • Ruff + Pyright(代码质量)

Console(sandbox-explorer)

  • 后端:FastAPI + SQLAlchemy + aiosqlite

  • 前端:React 18 + TypeScript + Vite

  • UI:Tailwind CSS + lucide-react

  • 状态管理:Zustand + TanStack Query

  • 编辑器:Monaco Editor (@monaco-editor/react)

  • 终端:xterm.js + WebSocket

  • 打包:Docker multi-stage build


快速上手

1. 安装 SDK

git clone https://github.com/Rainbow0328/agent-sandbox-backend.git
cd agent-sandbox-backend
pip install -e ".[deepagents]"

2. 启动 Web Console

git clone https://github.com/Rainbow0328/sandbox-web.git
cd sandbox-web
python start.py

3. 用 SDK 创建沙箱并执行操作

from agent_sandbox_backends import create_opensandbox_backend
​
backend = await create_opensandbox_backend(
    "http://your-opensandbox-service:8080",
    sandbox_name="my-first-sandbox",
)
​
with backend.agent_context(agent_id="my-agent"):
    result = await backend.execute("echo 'Hello from sandbox!'")
    print(result.stdout)

4. 打开浏览器查看

打开 http://localhost:8080,在沙箱列表中找到 my-first-sandbox,点击进入,切换到 History 标签页,你就能看到刚才那条 echo 命令的完整记录。


开源信息

项目 仓库 包名 协议
Agent Sandbox Backend SDK Rainbow0328/agent-sandbox-backend agent-sandbox-backends (PyPI) Apache-2.0
Sandbox Explorer Rainbow0328/sandbox-web sandbox-explorer (npm) Apache-2.0

欢迎 Star、Issue、PR!


路线图

SDK

  • 更多 Provider 适配器(E2B、Daytona)
  • 历史数据导出(JSON / CSV)
  • Webhook 通知(操作失败时回调)
  • 更丰富的 Actor 权限模型

Console

  • 沙箱快照与回滚
  • 多用户协作(同时操作同一沙箱)
  • 操作历史搜索与过滤
  • 暗色主题
  • 国际化(i18n)

写在最后

AI Agent 正在从"能聊天"走向"能干活"。当 Agent 真正开始操作你的服务器时,可观测性不是锦上添花,是安全底线

这套方案的核心价值不是"又造了一个沙箱",而是:

  1. 让 Agent 的每一步操作都有据可查——操作历史是设计的,不是事后补的

  2. 让数据生命周期对齐——沙箱删除,历史一起删除,不残留

  3. 让管理界面可以自托管——数据在自己手里,不在 SaaS 平台

  4. 让 Deep Agents 一行代码接入——降低 Agent 开发门槛

如果你也在做 AI Agent 相关的项目,如果你也需要在沙箱里运行 Agent 代码,欢迎试试这套方案。

如果觉得有用,给个 Star 是对开源作者最大的鼓励 🙏


本文涉及的两个项目均为 Apache-2.0 开源协议,可免费用于商业用途。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐