沙盒代理
基于Docker的沙盒代码执行的LangGraph代理。每个会话都在一个隔离的、强化的Docker容器中运行,该容器具有一个持久内核——Python的IPython, vm.createContext Node.js和专用的R环境。支持3个运行时、与提供者无关的LLM配置和视觉(多模式模型的自动检测)。可用作交互式CLI、MCP服务器(Cursor、Claude Desktop)、REST API(艾格拉),以及React前端。
特性
- Docker隔离 --每个会话都在自己的容器中运行,没有暴露的端口,也没有主机卷
- 硬化容器 --非根用户(UID 65532)、PID限制、内存+交换限制、仅tmpfs可写目录、,
no-new-privileges - 碰撞检测 --检测到OOM杀伤、分叉炸弹、分段故障,并明确报告给特工
- 持久状态 --变量在代码执行之间存活(如Jupyter单元格)
- PostgreSQL检查点 --对话历史记录在重新启动后仍然存在(与Aegra共享)
- 异步支持 --Promise(Node.js)和协程(Python)会自动等待
- 多运行时 --Python、Node.js和R
- 丰富的显示输出 --捕获matplotlib/ggplot图形、Plotly图表、IPython音频、HTML小部件等;自动将图像发送到多模式LLM
- 提供者不可知 --通过以下方式与OpenAI、Anthropic、Google Gemini、Ollama或任何兼容提供商合作
langchain init_chat_model - 运行时软件包安装 —
pip install/npm install/install.packages()在会话创建时或通过终端 - 6工具 —
create_session,execute_code,execute_terminal,import_files,export_files,stop_session - MCP服务器 --通过模型上下文协议(stdio传输)公开相同的工具
- REST API -完整的LangGraph平台API,通过 艾格拉 使用OpenAPI文档、流媒体、线程管理
- 输入验证 --Pydantic模式在执行前验证所有工具输入,失败时返回结构化错误
- React前端 --SPA聊天、工具可视化、文件上传/下载、设置对话框(React 19+Vite+Tailwind CSS)
- 文件上传 -将文件上载到API以导入沙箱会话(
POST /threads/{id}/files/upload) - 文件导出 --注册文件以供下载(无主机副本);通过API下载或在交叉会话导入中使用
- 文件导入 --从主机路径、内联内容或其他会话(在同一对话中导出的文件)导入
- 跨会话传输 --从会话A导出,导入到会话B
{session_id, path} - 会话垃圾回收 --空闲超时、最大生存期、线程驱逐、孤立容器清理
- 自动清理 --当代理退出时,所有容器都会停止并被移除
先决条件
- Python 3.11+
- Docker引擎
- LLM提供商的API密钥(
CHAT_MODEL_API_KEY) - PostgreSQL(适用于API/CLI模式-checkpointer+Aegra)
- Node.js 18+和npm(用于React前端)
设置
# Docker — installs (if needed), configures permissions, and builds all 3 images
sudo ./setup-docker.sh
# Install Python dependencies (open a new terminal so the docker group is active)
uv sync
# Install frontend dependencies
cd frontend && npm install && cd ..
# Configure environment
cp .env.example .env
# Edit .env with your CHAT_MODEL_API_KEY, POSTGRES_PASSWORD, and other settings
# Docker images are also built automatically on first use if not already presentPostgreSQL(CLI、API和UI都需要)
PostgreSQL在使用时通过Docker Compose自动启动 localhostCLI检测PostgreSQL是否可访问并自动启动:
# Manual start (if needed)
docker compose up postgres -d或者通过以下方式指向现有的PostgreSQL实例 POSTGRES_* env变量 .env.
用法
所有命令都使用统一 sandbox-agent 入口点:
uv run sandbox-agent cli # Interactive CLI (default)
uv run sandbox-agent mcp # MCP server (Cursor, Claude Desktop)
uv run sandbox-agent api # REST API (Aegra, no reload)
uv run sandbox-agent api dev # REST API with hot reload
uv run sandbox-agent ui # React UI (auto-starts API if needed)命令行界面
uv run sandbox-agent cli
# or simply
uv run sandbox-agentCLI作为Aegra REST API之上的瘦客户端运行。要求API正在运行(uv run sandbox-agent api).特征:
- 富有的 带有语法高亮显示的工具I/O的面板(每个运行时词法分析器)
- Markdown渲染流媒体代理输出
- 跨重启的持久线程(
~/.local/state/sandbox-agent/cli-thread.json) /new开始新对话的命令- 通过将模型/提供商/密钥设置传递给API
configurable
MCP 服务器
运行MCP服务器(stdio传输)以与Cursor、Claude Desktop或任何兼容MCP的客户端集成:
uv run sandbox-agent mcp光标或克劳德桌面
添加以下MCP配置:
{
"mcpServers": {
"sandbox-agent": {
"command": "uv",
"args": ["--directory", "/path/to/sandbox-agent", "run", "sandbox-agent", "mcp"]
}
}
}MCP服务器公开了与CLI代理相同的6个工具,具有相同的行为。它在中维护一个持久的thread_id ~/.local/state/sandbox-agent/mcp-thread.json 为了确保导出URL的一致性。
这 import_files 该工具直接接受文件内容(如文本或base64) file_content/encoding 密钥)、主机路径(通过 source/destination),或跨会话引用(session_id+path).这 export_files 该工具通过注册文件进行下载 GET /threads/{thread_id}/files/download?session_id=...&path=....
REST API(Aegra)
将代理作为REST API运行,通过 艾格拉 (自托管LangGraph平台替代方案):
uv run sandbox-agent api # Production mode (no reload, auto-starts PostgreSQL)
uv run sandbox-agent api dev # Development mode (hot reload via aegra dev)如果无法在本地主机上访问PostgreSQL,则生产命令会通过Docker Compose自动启动PostgreSQL。服务器运行在 http://localhost:8000 OpenAPI文档位于 /docs。使用LangGraph SDK或curl创建助手、线程和流运行。与Agent Chat UI、LangGraph Studio和CopilotKit兼容。
自定义端点:
GET /threads/{thread_id}/files/download?session_id=...&path=...--流式传输从容器导出的文件POST /threads/{thread_id}/files/upload--上传文件以供导入沙盒会话DELETE /threads/{thread_id}--还清理该线程的Docker会话和存储(通过中间件)GET /settings--返回在后端合并的持久化前端设置.env默认值PUT /settings--将前端设置持久化到PostgreSQL(加密)
反应前端
通过Aegra API与代理商聊天的web UI(React 19+Vite+Tailwind CSS):
# Install frontend dependencies (if not done during setup)
cd frontend && npm install && cd ..
# Start the UI (auto-starts API + PostgreSQL if needed)
uv run sandbox-agent ui前端运行在 http://localhost:5173 (带有API代理的Vite dev服务器 :8000).特征:
- 通过侧边栏进行话题管理(创建、恢复、删除对话)
- 使用可扩展的工具块流式传输响应(每次运行时突出显示语法)
- 文件上传和下载支持
- 思维块可视化
- 设置对话框(模型、提供商、API密钥、基本URL、视觉切换)
- 通过服务器端API进行持久设置(
GET/PUT /settings),带后端.env默认为回退
程序化
from sandbox_agent.sandbox import SandboxManager
manager = SandboxManager()
info = manager.create_session(
runtime="python",
dependencies={"pandas": "2.2.3", "matplotlib": ""},
)
sid = info.session_id
r1 = manager.execute_code(sid, """
import pandas as pd
df = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})
print(df.describe())
""")
print(r1.stdout)
# Variables persist between calls
r2 = manager.execute_code(sid, "df.shape")
print(r2.result)
# Export files from the sandbox (registers for download, no host copy)
manager.execute_code(sid, "df.to_csv('/workspace/output.csv', index=False)")
export = manager.export_files(sid, [{"source": "output.csv"}])
print(export.files[0].session_id, export.files[0].path)
manager.stop_session(sid)导出文件
export_files 注册文件以供下载和跨会话导入(无主机副本)。文件可通过API获得(GET /threads/{thread_id}/files/download?session_id=...&path=...)以及 import_files 在其他会议中:
# Export a single file
result = manager.export_files(sid, [{"source": "report.pdf"}])
# Export an entire directory
result = manager.export_files(sid, [{"source": "results/"}])
# Export multiple files at once
result = manager.export_files(sid, [
{"source": "data.csv"},
{"source": "chart.png"},
{"source": "/workspace/logs/"},
])
for f in result.files:
print(f"{f.session_id}:{f.path} ({'OK' if f.success else f.error})")跨会话文件传输
使用 export_files + import_files 在会话之间移动文件(甚至在不同的运行时之间):
# Session A (Python): produce data
sid_a = manager.create_session(runtime="python", dependencies={"pandas": ""}).session_id
manager.execute_code(sid_a, """
import pandas as pd
df = pd.DataFrame({'x': [1,2,3], 'y': [4,5,6]})
df.to_csv('/workspace/data.csv', index=False)
""")
export = manager.export_files(sid_a, [{"source": "data.csv"}])
path = export.files[0].path # /workspace/data.csv
# Session B (R): consume the same data
sid_b = manager.create_session(runtime="r", dependencies={"readr": ""}).session_id
manager.import_files(sid_b, [{"session_id": sid_a, "path": path, "destination": "data.csv"}])
manager.execute_code(sid_b, 'df API["Aegra REST API
(LangGraph Platform)"]
UI --> API
API --> Agent["LangGraph ReAct Agent"]
Agent --> Tools["LangChain Tools"]
MCP --> Core["Core Tool Functions"]
Tools --> Core
Core --> SM["SandboxManager
Docker SDK"]
SM -->|"docker exec -i + JSON pipe"| Docker
subgraph Docker ["Docker Containers
isolated, hardened"]
direction LR
PY["Python
IPython · UNIX socket"]
JS["Node.js
vm.createContext · UNIX socket"]
R["R
R env · TCP :8765"]
end
subgraph Storage ["Persistence"]
PG["PostgreSQL
checkpoints, exports"]
end
API --> PG
SM --> PG在每个容器内,都有一个持久的 内核 (PID 1)保持执行状态 客户端 通过UNIX套接字(Python/Node.js)或TCP(R)连接到它 docker exec 呼叫:
flowchart TB
SM["SandboxManager"] -->|"docker exec -i"| Client["Client (ephemeral)"]
subgraph container ["Container"]
Client -->|"UNIX socket / TCP"| Kernel["Kernel (PID 1, persistent)"]
Kernel --- State["State
variables, imports, data"]
end测试
# Unit tests (no Docker required)
uv run pytest tests/test_cli.py tests/test_http_app.py -v
# Integration tests (requires Docker)
uv run pytest tests/test_manager.py tests/test_tools.py tests/test_export_files.py tests/test_mcp.py -v
# LangGraph debug trace (requires Docker + LLM API key)
uv run pytest tests/test_langgraph_debug.py -v -s
# API integration tests (requires Docker + running API: uv run sandbox-agent api dev)
uv run pytest tests/test_api.py -v -s
# Full suite
uv run pytest tests/ -v生产部署
A制作 Dockerfile 和 docker-compose.yml 包括:
# Start PostgreSQL + API
docker compose up -d
# Or build and run manually
docker build -t sandbox-agent-api .
docker run -p 8000:8000 --env-file .env sandbox-agent-api生产图像使用 aegra serve 具有非根 app 用户。
许可证
麻省理工学院 --爱德华多·拉蒙·雷瑟
