Create a sandbox from a public template

更新时间:
复制 MD 格式

PAI-Sandbox provides prebuilt public templates for common agent scenarios: code execution, browser automation, desktop automation, and AI coding. Select a template based on your use case and create a sandbox without additional setup.

Overview

Public templates are prebuilt by the EAS platform and can be used in any sandbox workspace without additional setup. Each template preconfigures the runtime environment for a specific agent scenario, including preinstalled libraries, prestarted services, and preset ports.

Template comparison

Agent scenario

Recommended template

Key capabilities

Code interpreter / data analysis

code-interpreter

Python 3.11 (with numpy, pandas, matplotlib, and sklearn), JavaScript, and TypeScript

Browser automation / web search

browser

Chromium with Playwright and MCP dual-protocol access

Desktop GUI automation / screenshots

desktop

Ubuntu 22.04 with remote desktop and UI automation SDK

AI coding (Claude)

claude-code

Claude CLI with Bailian API access

AI coding (open source)

opencode

opencode with opencode server

General-purpose task agent

openclaw / qwenpaw

General-purpose task agent framework with Bailian API access

Prerequisites

Before you use a public template:

  1. Create a sandbox space.

  2. On the Template Details page, click SDK Access in the upper-right corner to obtain the E2B_DOMAIN, E2B_API_KEY, and template ID.

    Note

    Public templates use the sandbox workspace-level token as the E2B_API_KEY. You can set a custom token when creating a sandbox workspace to replace the auto-generated one.

  3. Install the E2B SDK (only versions earlier than v2.25.0 are supported): pip install "e2b<2.25.0".

code-interpreter

The code-interpreter public template provides a development environment with Python 3.11, JavaScript, and TypeScript. Use the e2b_code_interpreter SDK to run code and manage files.

pip install e2b_code_interpreter       
import os
import time
from e2b_code_interpreter import Sandbox

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

sandbox = Sandbox.create(template="code-interpreter", timeout=300000)

# Wait for the sandbox to be ready
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

# Python code execution
execution = sandbox.run_code("print('hello world')")
print(execution.logs)

sandbox.run_code("x = 42")
execution = sandbox.run_code("print(f'x = {x}')")
print(execution.logs.stdout)

# JavaScript
result = sandbox.commands.run("node -e \"console.log('Hello from Node.js')\"")
print(result.stdout)

# TypeScript
result = sandbox.commands.run("tsx -e \"const msg: string = 'Hello from TypeScript'; console.log(msg)\"")
print(result.stdout)

# File operations
entries = sandbox.files.list("/home/user")
for e in entries:
    print(f"{e.type}\t{e.name}")

sandbox.files.write("/home/user/hello.txt", "Hello from sandbox")
content = sandbox.files.read("/home/user/hello.txt")
print(content)

browser

The browser public template includes a preinstalled Chromium browser with two access methods: CDP (Chrome DevTools Protocol) on port 8080 and MCP on port 50005. Agents can use these interfaces to automate browser interactions through Playwright or MCP.

Option 1: Playwright

Install Playwright on the client:

pip install playwright          

Connect to the sandbox browser via the CDP WebSocket endpoint and use the Playwright API for page navigation, screenshots, and element extraction:

import asyncio
import os
import time
from urllib.parse import quote_plus

import httpx
from e2b import Sandbox
from playwright.async_api import async_playwright

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
GATEWAY_PORT = 8080

sandbox = Sandbox.create(template="browser", timeout=300000)

print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

# Authentication headers (required for all requests)
def _auth_headers():
    return {
        "X-Access-Token": sandbox._envd_access_token,
        "E2b-Sandbox-Id": sandbox.sandbox_id,
    }

# Get the CDP WebSocket URL
def get_cdp_url():
    gateway_host = sandbox.get_host(GATEWAY_PORT)
    resp = httpx.get(
        f"https://{gateway_host}/json/version",
        headers=_auth_headers(), timeout=10, verify=False,
    )
    resp.raise_for_status()
    guid = resp.json()["webSocketDebuggerUrl"].rsplit("/", 1)[-1]
    return f"wss://{gateway_host}/devtools/browser/{guid}"

async def search_with_playwright(query: str):
    cdp_url = get_cdp_url()
    print(f"CDP URL: {cdp_url}")

    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(cdp_url, headers=_auth_headers())

        if browser.contexts and browser.contexts[0].pages:
            page = browser.contexts[0].pages[0]
        else:
            page = await (await browser.new_context()).new_page()

        # Navigate
        search_url = f"https://www.bing.com/search?q={quote_plus(query)}"
        await page.goto(search_url, wait_until="load", timeout=30000)
        print(f"Navigated to: {search_url}")

        # Wait for search results to render
        try:
            await page.wait_for_selector("li.b_algo", timeout=10000)
        except Exception:
            await page.wait_for_timeout(3000)

        # Take a screenshot
        with open("screenshot.png", "wb") as f:
            f.write(await page.screenshot(full_page=True))
        print("Screenshot saved: screenshot.png")

        # Extract search results
        results = await page.evaluate("""() => {
            const items = document.querySelectorAll('li.b_algo h2 a');
            return Array.from(items).slice(0, 5).map(a => ({
                title: a.textContent || '',
                url: a.href || ''
            }));
        }""")

        await browser.close()
    return results

if __name__ == "__main__":
    results = asyncio.run(search_with_playwright("Python latest release"))
    for i, r in enumerate(results, 1):
        print(f"  {i}. {r['title']} — {r['url']}")

Option 2: MCP

Install MCP on the client:

pip install mcp         

Use semantic browser tools through the MCP protocol without managing page objects directly:

import asyncio
import os
import time
from urllib.parse import quote_plus

import httpx
from e2b import Sandbox
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamable_http_client

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
MCP_PORT = 50005

sandbox = Sandbox.create(template="browser", timeout=300000)

print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

def _auth_headers():
    return {
        "X-Access-Token": sandbox._envd_access_token,
        "E2b-Sandbox-Id": sandbox.sandbox_id,
    }

def get_mcp_url():
    return f"https://{sandbox.get_host(MCP_PORT)}/mcp"

async def search_with_mcp(query: str):
    mcp_url = get_mcp_url()
    print(f"MCP URL: {mcp_url}")
    client = httpx.AsyncClient(headers=_auth_headers(), verify=False)

    async with client:
        async with streamable_http_client(mcp_url, http_client=client) as (
            read_stream, write_stream, _,
        ):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                print("MCP session initialized")

                # Navigate
                search_url = f"https://www.bing.com/search?q={quote_plus(query)}"
                await session.call_tool("browser_navigate", {"url": search_url})
                print(f"Navigated to: {search_url}")

                # Wait for page to load
                await session.call_tool("browser_wait_for", {"time": 2})

                # Get page snapshot
                snap = await session.call_tool("browser_snapshot", {})
                result_text = " ".join(
                    getattr(item, "text", "") for item in snap.content
                )

    return result_text

if __name__ == "__main__":
    text = asyncio.run(search_with_mcp("Python latest release"))
    print(f"Result:\n{text}")

desktop

The desktop public template runs Ubuntu 22.04. Use the e2b_desktop SDK to automate UI operations in the sandbox.

pip install e2b_desktop       
import os
import time
from e2b_desktop import Sandbox

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

create_kwargs = {
    "timeout": 3600,
    "resolution": (1920, 1080),
    "dpi": 96,
    "template": "desktop",
}
desktop = Sandbox.create(
    **create_kwargs,
    headers={"X-Sandbox-Create-Timeout": "100"},
)

print(f"Waiting for sandbox {desktop.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if desktop.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

# UI operations
print("Moving mouse to (100, 100)...")
desktop.move_mouse(100, 100)
time.sleep(0.5)

print("Left clicking...")
desktop.left_click()
time.sleep(0.5)

print("Typing 'Hello'...")
desktop.write("Hello")
time.sleep(0.5)

print("Pressing Enter...")
desktop.press("enter")
time.sleep(0.5)

print("Opening browser to example.com...")
desktop.open("https://example.com")
time.sleep(2)

# Take a screenshot and download it
print("Taking screenshot...")
screenshot = desktop.screenshot("bytes")
if screenshot:
    screenshot_path = "/tmp/desktop_screenshot.png"
    with open(screenshot_path, "wb") as f:
        f.write(screenshot)
    print(f"Screenshot saved to {screenshot_path}")
else:
    print("Screenshot returned empty")

On the sandbox details page, click Open VNC to connect to the sandbox GUI remotely.

claude-code

The claude-code public template includes the Claude CLI. Create a sandbox from this template and set the ANTHROPIC environment variables to run Claude for coding tasks in isolation.

import os
import time
from e2b import Sandbox

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

sandbox = Sandbox.create(
    template="claude-code",
    timeout=300000,
    envs={
        "ANTHROPIC_AUTH_TOKEN": "<YOUR-TOKEN>",
        "ANTHROPIC_BASE_URL": "<YOUR-BASE-URL>",
        "ANTHROPIC_MODEL": "<YOUR-MODEL>",
    },
)

print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

result = sandbox.commands.run(
    'claude -p "Write a Python program to calculate Fibonacci numbers"',
)
print(result.stdout)

opencode

The opencode public template ships with opencode and starts the opencode server on port 4096.

The opencode configuration file /root/.config/opencode/opencode.json includes preset model configurations for Alibaba Cloud Bailian Token Plan, Coding Plan, and pay-as-you-go mode. When creating a sandbox, provide the key through the BAILIAN-TOKEN-PLAN-KEY, BAILIAN-CODING-PLAN-KEY, or BAILIAN-API-KEY environment variable. You can then use the E2B SDK or HTTP requests to run opencode for coding tasks in the sandbox.

import os
import time
import requests
from e2b import Sandbox

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

OPENCODE_SERVER_PORT = 4096

sandbox = Sandbox.create(
    template="opencode",
    timeout=300000,
    envs={"BAILIAN-API-KEY": os.getenv("BAILIAN-API-KEY")},
)

print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

# Call the opencode CLI via the e2b SDK
result = sandbox.commands.run(
    'opencode run --model=bailian-payg/qwen3.7-max "Write a Python program to calculate Fibonacci numbers"',
    timeout=120,
)
print(result.stdout)

# Call via the opencode server API
BASE_URL = f"https://{sandbox.get_host(4096)}"
H = {"X-Access-Token": getattr(sandbox, '_SandboxBase__envd_access_token', None)}

# Set the API key (the opencode server does not automatically parse {env:...} templates)
api_key = sandbox.commands.run("printenv BAILIAN-API-KEY").stdout.strip()
requests.patch(f"{BASE_URL}/global/config", headers=H, json={
    "provider": {"bailian-payg": {"options": {
        "apiKey": api_key,
        "baseURL": "https://dashscope.aliyuncs.com/apps/anthropic/v1",
    }}}
})

# Create a session (specify the model)
session = requests.post(f"{BASE_URL}/session", headers=H, json={
    "model": {"id": "qwen3.7-max", "providerID": "bailian-payg"},
}).json()
session_id = session["id"]
print(f"Session: {session_id}")

# Send a coding task (synchronously wait for the complete response)
resp = requests.post(f"{BASE_URL}/session/{session_id}/message", headers=H, json={
    "parts": [{"type": "text", "text": "Write a Fibonacci program in Python, save it to /workspace/fibonacci.py and run it to verify"}]
}, timeout=300).json()

# Print the assistant response
for part in resp.get("parts", []):
    if part["type"] == "text":
        print(part["text"])
    elif part["type"] == "tool":
        print(f"  [tool: {part.get('name')}]")

# Verify the generated file
print("\n--- fibonacci.py ---")
print(sandbox.commands.run("cat /workspace/fibonacci.py").stdout)
print("--- run ---")
print(sandbox.commands.run("python3 /workspace/fibonacci.py", timeout=30).stdout)

openclaw

The openclaw public template includes the openclaw service. Use the following code to get the access URL for the openclaw service in the sandbox.

from e2b import Sandbox
import os
import time, httpx
from urllib.parse import urlparse, parse_qs

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

OPENCLAW_GATEWAY_PORT = 18789

def get_gateway_token(sandbox_id, api_key, domain):
    """Get gateway authentication token via API"""
    url = f"https://api.{domain}/sandboxes/{sandbox_id}"
    headers = {
        "X-API-KEY": api_key,
        "X-Generate-Dashboard-Url": "true",
    }
    resp = httpx.get(url, headers=headers, timeout=10)
    if resp.status_code != 200:
        return None

    data = resp.json()
    dashboard_url = None
    if "metadata" in data and isinstance(data["metadata"], dict):
        meta = data["metadata"]
        dashboard_url = meta.get("sandbox.alicloud.com/dashboard-url") or meta.get("dashboard_url")
    if not dashboard_url:
        dashboard_url = data.get("dashboard_url")

    if dashboard_url:
        parsed = urlparse(dashboard_url)
        qs = parse_qs(parsed.query)
        return qs.get("token", [None])[0]
    return None

sandbox = Sandbox.create(
    template="openclaw",
    timeout=300000,
)

print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

gateway_token = get_gateway_token(
    sandbox.sandbox_id, os.environ["E2B_API_KEY"], os.environ["E2B_DOMAIN"]
)
url = f"https://{sandbox.get_host(OPENCLAW_GATEWAY_PORT)}?token={gateway_token}"
print(f"    Sandbox Access URL: {url}")

qwenpaw

The qwenpaw public template includes the qwenpaw service, which starts automatically. Use the following code to get the access URL for the qwenpaw service in the sandbox.

import os
import time
from urllib.parse import urlparse, parse_qs

import httpx
from e2b import Sandbox

os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"

QWENPAW_PORT = 8088

def get_gateway_token(sandbox_id, api_key, domain):
    """Get the gateway authentication token via the API"""
    url = f"https://api.{domain}/sandboxes/{sandbox_id}"
    headers = {
        "X-API-KEY": api_key,
        "X-Generate-Dashboard-Url": "true",
    }
    resp = httpx.get(url, headers=headers, timeout=10)
    if resp.status_code != 200:
        return None

    data = resp.json()
    dashboard_url = None
    if "metadata" in data and isinstance(data["metadata"], dict):
        meta = data["metadata"]
        dashboard_url = meta.get("sandbox.alicloud.com/dashboard-url") or meta.get("dashboard_url")
    if not dashboard_url:
        dashboard_url = data.get("dashboard_url")

    if dashboard_url:
        parsed = urlparse(dashboard_url)
        qs = parse_qs(parsed.query)
        return qs.get("token", [None])[0]
    return None

sandbox = Sandbox.create(
    template="qwenpaw",
    timeout=300000,
)

# Wait for the sandbox to be ready
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
    try:
        if sandbox.is_running():
            print("Sandbox is ready.")
            break
    except Exception:
        pass
    time.sleep(2)

gateway_token = get_gateway_token(
    sandbox.sandbox_id, os.environ["E2B_API_KEY"], os.environ["E2B_DOMAIN"]
)
url = f"https://{sandbox.get_host(QWENPAW_PORT)}?token={gateway_token}"
print(f"Sandbox Access URL: {url}")