🔥 实战 | OpenSandbox × OpenClaw 强强联合:给你的 AI Agent 装上「钢铁笼子」

AI Agent 能写代码,但你敢让它直接在你的服务器上跑吗?
本文手把手带你用 OpenSandbox 为 OpenClaw 构建一套完整的安全执行环境,从架构设计到代码实战,全程无废话。


📢 广告

OpenClaw U盘 一键部署‼️
还在为 OpenClaw 的环境配置头疼?Node.js 版本冲突、npm 安装失败、Windows 权限问题……这些烦恼统统不存在。
OpenClaw U盘 一键部署‼️
预装完整运行环境,插上 U 盘即刻启动,真正做到开箱即用。
适合开发者、企业内网部署、送给不懂技术的朋友。
OpenClaw U盘 一键部署‼️
— 让 AI 生产力触手可及。
需要的可私信我哦‼️


一、为什么 OpenClaw 需要 OpenSandbox?

OpenClaw(社区昵称"龙虾")是 2026 年最火爆的开源 AI Agent 框架,它赋予了 AI 真正的「双手」——可以操作文件、执行命令、控制浏览器、调用 API。短短数月,GitHub Stars 突破 150K,腾讯、阿里、小米等大厂纷纷基于它构建自己的 Agent 产品。

然而,能力越强,风险越大

OpenClaw 的核心 Skill Engine 直接在宿主机上执行代码。这意味着:

  • 大模型产生幻觉,生成了 rm -rf / ——宿主机文件系统直接清空;
  • 恶意 Prompt 注入(Indirect Prompt Injection),攻击者通过网页内容向 Agent 下达指令,窃取本地文件;
  • AI 生成的爬虫代码未经审查就运行,触发目标网站的反爬机制,IP 被封;
  • 多个用户共用一个 Agent 实例,A 用户的代码意外读取了 B 用户的数据。

Andrej Karpathy 曾评价 OpenClaw:“genuinely the most incredible sci-fi takeoff-adjacent thing I have seen recently”,随后又补充:“it’s a dumpster fire… I definitely do not recommend that people run this stuff on their computers.” 这句话精准地道出了问题所在。

OpenSandbox 就是为了解决这个问题而生的。

它是阿里巴巴开源的通用 AI 沙箱平台,专门为 AI Agent 的代码执行提供隔离、安全、可扩展的运行环境。OpenSandbox 的 examples/openclaw 目录就是专门为 OpenClaw 准备的集成示例,两者的结合可以说是天作之合:

  • OpenClaw 负责「想什么、做什么」—— AI 大脑 + 任务编排;
  • OpenSandbox 负责「怎么安全地做」—— 执行隔离 + 安全防护。

二、架构设计:两者如何协同工作

在深入实战之前,先理解整体架构,这是避免踩坑的关键。

2.1 整体架构图

 ┌─────────────────────────────────────────────────────────────────┐
 │                      用户层(User Layer)                        │
 │          Telegram / WhatsApp / Discord / DingTalk               │
 └──────────────────────────────┬──────────────────────────────────┘
                                │ 消息
                                ▼
 ┌─────────────────────────────────────────────────────────────────┐
 │                    OpenClaw Gateway(宿主机)                    │
 │  ┌─────────────────┐   ┌──────────────────┐  ┌──────────────┐  │
 │  │  Agent Core     │   │  Memory Bank     │  │  Skill Engine│  │
 │  │  (LLM 推理)     │   │  (记忆管理)      │  │  (技能调度)  │  │
 │  └────────┬────────┘   └──────────────────┘  └──────┬───────┘  │
 │           │                                          │          │
 └───────────┼──────────────────────────────────────────┼──────────┘
             │ 任务规划                                  │ 代码执行请求
             │                                          ▼
 ┌───────────┴──────────────────────────────────────────────────────┐
 │                   OpenSandbox Server(隔离层)                    │
 │                                                                   │
 │   ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐  │
 │   │  沙箱实例 1  │  │  沙箱实例 2  │  │     沙箱实例 N       │  │
 │   │  (Python)    │  │  (Browser)   │  │  (Code Interpreter)  │  │
 │   │  execd + JK  │  │  Chrome+VNC  │  │  execd + Jupyter     │  │
 │   └──────────────┘  └──────────────┘  └──────────────────────┘  │
 │                                                                   │
 │   ← Docker/Kubernetes 隔离边界,gVisor/Kata Containers 可选 →    │
 └───────────────────────────────────────────────────────────────────┘

2.2 数据流向:一次代码执行请求的完整旅程

以「用户让 OpenClaw 爬取数据并分析」为例,完整流程如下:

Step 1:用户发送指令

用户(Telegram)→ "帮我爬取豆瓣电影 Top10,分析评分分布,生成图表"

Step 2:OpenClaw 大脑规划

Agent Core 调用 LLM → 生成执行计划:
{
  "step_1": "使用 Playwright 爬取豆瓣数据",
  "step_2": "用 Pandas 分析评分分布",
  "step_3": "用 Matplotlib 生成柱状图",
  "step_4": "返回图片给用户"
}

Step 3:Skill Engine 触发沙箱

Skill Engine → 调用 OpenSandbox SDK
→ 创建隔离沙箱(opensandbox/code-interpreter:v1.0.2)
→ 在沙箱内执行 AI 生成的代码
→ 通过 SSE 实时回传执行日志
→ 返回生成的图片文件

Step 4:结果回传

OpenClaw → 将图片发送给用户
→ 沙箱自动销毁,不留任何痕迹

整个过程中,AI 生成的代码从未接触宿主机,即使代码中包含恶意指令,也只会在沙箱内部「自爆」,不会影响外部系统。


三、环境搭建:从零开始的完整部署

3.1 前置条件

# 检查 Docker 版本(需要 24.0+)
docker --version
# Docker version 24.0.7, build afdd53b

# 检查 Python 版本(需要 3.10+)
python3 --version
# Python 3.11.8

# 安装 uv(推荐的 Python 包管理器)
curl -LsSf https://astral.sh/uv/install.sh | sh

3.2 部署 OpenSandbox 服务端

方式一:快速安装(推荐新手)

# 安装 OpenSandbox 服务端
uv pip install opensandbox-server

# 初始化配置文件(Docker 模式)
opensandbox-server init-config ~/.sandbox.toml --example docker

# 查看并编辑配置(可选)
cat ~/.sandbox.toml

生成的配置文件 ~/.sandbox.toml 关键内容如下:

[server]
host = "0.0.0.0"
port = 8090
log_level = "INFO"

[runtime]
type = "docker"
execd_image = "opensandbox/execd:latest"

[egress]
image = "opensandbox/egress:latest"

[docker]
# 网络隔离模式(bridge 提供更好的隔离性)
network_mode = "bridge"
# 剥夺危险的 Linux Capabilities
drop_capabilities = [
  "AUDIT_WRITE", "MKNOD", "NET_ADMIN", "NET_RAW",
  "SYS_ADMIN", "SYS_MODULE", "SYS_PTRACE", "SYS_TIME"
]
# 禁止特权升级
no_new_privileges = true
# 限制进程数,防御 Fork Bomb
pids_limit = 512

[ingress]
mode = "direct"

方式二:从源码启动(适合开发调试)

git clone https://github.com/alibaba/OpenSandbox.git
cd OpenSandbox/server

# 安装依赖
uv sync

# 复制配置文件
cp example.config.toml ~/.sandbox.toml

# 启动服务
uv run python -m src.main

服务启动后,你会看到:

INFO:     OpenSandbox Server v0.1.8
INFO:     Listening on http://0.0.0.0:8090
INFO:     Docker runtime initialized
INFO:     Ready to accept sandbox requests

验证服务是否正常:

curl http://localhost:8090/health
# {"status": "ok", "version": "0.1.8"}

3.3 拉取必要的镜像

# 代码解释器镜像(支持 Python/Java/JS 等多语言)
docker pull opensandbox/code-interpreter:v1.0.2

# 浏览器自动化镜像(带 Chrome + Playwright)
docker pull opensandbox/playwright:latest

# 桌面环境镜像(带 VNC)
docker pull opensandbox/desktop:latest

3.4 安装 OpenClaw

# 使用官方安装脚本
curl -fsSL https://install.openclaw.ai | bash

# 验证安装
openclaw --version
# OpenClaw v2026.3.x

# 初始化配置
openclaw init

3.5 配置 OpenClaw 使用 OpenSandbox

编辑 OpenClaw 配置文件 ~/.openclaw/config.yaml

# LLM 模型配置
model:
  default: claude-sonnet-4
  providers:
    anthropic:
      api_key: ${ANTHROPIC_API_KEY}

# 沙箱配置 —— 关键部分
agents:
  defaults:
    # 启用沙箱模式(强烈推荐!)
    sandbox:
      enabled: true
      # 指向 OpenSandbox 服务端
      server_url: "http://localhost:8090"
      # 默认使用的沙箱镜像
      default_image: "opensandbox/code-interpreter:v1.0.2"
      # 沙箱超时时间(分钟)
      timeout: 30
      # 网络出站策略
      egress_policy:
        default_action: deny
        allow:
          - "pypi.org"
          - "*.github.com"
          - "douban.com"
          - "*.douban.com"

# 安全配置
security:
  # 开启沙箱模式后,代码执行不再在宿主机上进行
  sandbox: true
  # 敏感信息脱敏(防止 API Key 泄露到日志)
  redact:
    - api_key
    - password
    - token

# 通信渠道
channels:
  - name: telegram
    type: telegram
    token: ${TELEGRAM_BOT_TOKEN}

四、核心实战:五个典型场景的完整代码

场景一:安全执行 AI 生成的 Python 代码

这是最基础的场景:让 AI 写代码,在沙箱中执行,安全地获取结果。

import asyncio
from datetime import timedelta
from code_interpreter import CodeInterpreter, SupportedLanguage
from opensandbox import Sandbox
from opensandbox.models import WriteEntry

async def safe_code_execution(ai_generated_code: str) -> dict:
    """
    在 OpenSandbox 隔离环境中安全执行 AI 生成的代码
    
    Args:
        ai_generated_code: AI 生成的 Python 代码字符串
    
    Returns:
        包含执行结果、标准输出、错误信息的字典
    """
    
    # 1. 创建隔离沙箱
    sandbox = await Sandbox.create(
        "opensandbox/code-interpreter:v1.0.2",
        entrypoint=["/opt/opensandbox/code-interpreter.sh"],
        env={"PYTHON_VERSION": "3.11"},
        timeout=timedelta(minutes=10),
        # 资源限制:防止 AI 代码耗尽系统资源
        cpu_limit="1.0",      # 最多使用 1 个 CPU 核心
        memory_limit="512m",  # 最多使用 512MB 内存
    )
    
    result = {
        "success": False,
        "stdout": [],
        "stderr": [],
        "result": None,
        "error": None
    }
    
    async with sandbox:
        try:
            # 2. 创建代码解释器
            interpreter = await CodeInterpreter.create(sandbox)
            
            # 3. 执行 AI 生成的代码(完全隔离)
            execution = await interpreter.codes.run(
                ai_generated_code,
                language=SupportedLanguage.PYTHON,
            )
            
            # 4. 收集执行结果
            result["success"] = True
            result["stdout"] = [line.text for line in execution.logs.stdout]
            result["stderr"] = [line.text for line in execution.logs.stderr]
            if execution.result:
                result["result"] = execution.result[0].text
                
        except Exception as e:
            result["error"] = str(e)
        
    # 沙箱在 async with 块结束后自动销毁
    return result


# 使用示例:让 AI 计算斐波那契数列
async def main():
    # 模拟 AI 生成的代码(可能包含任何内容,但在沙箱中安全运行)
    ai_code = """
import time

def fibonacci(n):
    if n <= 1:
        return n
    a, b = 0, 1
    for _ in range(2, n + 1):
        a, b = b, a + b
    return b

# 计算前 20 个斐波那契数
start = time.time()
results = [fibonacci(i) for i in range(20)]
elapsed = time.time() - start

print(f"斐波那契数列(前20项): {results}")
print(f"计算耗时: {elapsed*1000:.2f}ms")

# 返回最后一个值
results[-1]
"""
    
    print("正在安全沙箱中执行 AI 生成的代码...")
    result = await safe_code_execution(ai_code)
    
    if result["success"]:
        print("执行成功!")
        for line in result["stdout"]:
            print(f"  输出: {line}")
        if result["result"]:
            print(f"  返回值: {result['result']}")
    else:
        print(f"执行失败: {result['error']}")

asyncio.run(main())

输出结果:

正在安全沙箱中执行 AI 生成的代码...
执行成功!
  输出: 斐波那契数列(前20项): [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]
  输出: 计算耗时: 0.23ms
  返回值: 4181

即使 AI 生成的代码中包含 os.system("rm -rf /")import subprocess; subprocess.run(["curl", "evil.com"]) 这样的危险操作,也只会在沙箱内部执行,不会影响宿主机。


场景二:OpenClaw + OpenSandbox 数据爬取与分析工作流

这是最典型的联合使用场景:OpenClaw 负责理解用户意图和编排任务,OpenSandbox 负责安全执行爬虫和数据分析代码。

import asyncio
from datetime import timedelta
from opensandbox import Sandbox
from opensandbox.models import WriteEntry
from code_interpreter import CodeInterpreter, SupportedLanguage

class OpenClawSandboxAgent:
    """
    OpenClaw + OpenSandbox 联合 Agent
    负责安全地执行数据爬取和分析任务
    """
    
    def __init__(self, sandbox_server_url: str = "http://localhost:8090"):
        self.sandbox_server_url = sandbox_server_url
    
    async def analyze_data_safely(self, task_description: str, code: str) -> dict:
        """
        在隔离沙箱中安全执行数据分析任务
        
        这模拟了 OpenClaw 将 AI 生成的代码投递到 OpenSandbox 的完整流程
        """
        print(f"\n[OpenClaw] 接收到任务: {task_description}")
        print("[OpenClaw] 正在将代码投递到 OpenSandbox 安全沙箱...")
        
        sandbox = await Sandbox.create(
            "opensandbox/code-interpreter:v1.0.2",
            entrypoint=["/opt/opensandbox/code-interpreter.sh"],
            env={"PYTHON_VERSION": "3.11"},
            timeout=timedelta(minutes=15),
        )
        
        async with sandbox:
            # 预安装分析所需的依赖包(在沙箱内,不影响宿主机)
            print("[OpenSandbox] 正在沙箱内安装依赖...")
            install_result = await sandbox.commands.run(
                "pip install requests beautifulsoup4 pandas matplotlib -q"
            )
            
            if install_result.exit_code != 0:
                return {"success": False, "error": "依赖安装失败"}
            
            print("[OpenSandbox] 依赖安装完成,开始执行分析代码...")
            
            # 执行 AI 生成的分析代码
            interpreter = await CodeInterpreter.create(sandbox)
            execution = await interpreter.codes.run(
                code,
                language=SupportedLanguage.PYTHON,
            )
            
            # 收集输出
            stdout_lines = [line.text for line in execution.logs.stdout]
            stderr_lines = [line.text for line in execution.logs.stderr]
            
            # 尝试读取生成的图表文件
            chart_data = None
            try:
                chart_data = await sandbox.files.read_file("/tmp/chart.png")
                print("[OpenSandbox] 检测到图表文件,正在提取...")
            except Exception:
                pass
            
            print("[OpenSandbox] 任务执行完毕,沙箱即将销毁")
            
            return {
                "success": True,
                "stdout": stdout_lines,
                "stderr": stderr_lines,
                "chart": chart_data,
                "task": task_description
            }


async def main():
    agent = OpenClawSandboxAgent()
    
    # 模拟 OpenClaw 根据用户指令生成的分析代码
    analysis_code = """
import json

# 模拟爬取到的电影数据(实际场景中会用 requests + BeautifulSoup)
movies_data = [
    {"title": "肖申克的救赎", "rating": 9.7, "votes": 2850000},
    {"title": "霸王别姬",     "rating": 9.6, "votes": 1950000},
    {"title": "阿甘正传",     "rating": 9.5, "votes": 2100000},
    {"title": "这个杀手不太冷","rating": 9.4, "votes": 2400000},
    {"title": "美丽人生",     "rating": 9.6, "votes": 1100000},
    {"title": "泰坦尼克号",   "rating": 9.4, "votes": 2200000},
    {"title": "千与千寻",     "rating": 9.4, "votes": 2050000},
    {"title": "辛德勒的名单", "rating": 9.5, "votes": 1450000},
    {"title": "盗梦空间",     "rating": 9.3, "votes": 2350000},
    {"title": "忠犬八公的故事","rating": 9.4, "votes": 1300000},
]

import statistics

ratings = [m["rating"] for m in movies_data]
votes = [m["votes"] for m in movies_data]

print("=" * 50)
print("豆瓣电影 Top10 数据分析报告")
print("=" * 50)
print(f"\\n评分统计:")
print(f"  平均评分: {statistics.mean(ratings):.2f}")
print(f"  最高评分: {max(ratings)}")
print(f"  最低评分: {min(ratings)}")
print(f"  评分中位数: {statistics.median(ratings):.2f}")
print(f"\\n投票数统计:")
print(f"  总投票数: {sum(votes):,}")
print(f"  平均投票数: {statistics.mean(votes):,.0f}")
print(f"  最高投票数: {max(votes):,} ({movies_data[votes.index(max(votes))]['title']})")
print(f"\\n评分分布:")
for movie in sorted(movies_data, key=lambda x: x["rating"], reverse=True):
    bar = "█" * int((movie["rating"] - 9.0) * 20)
    print(f"  {movie['title'][:10]:<12} {movie['rating']} {bar}")

# 尝试生成图表(如果 matplotlib 可用)
try:
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    import matplotlib.font_manager as fm
    
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
    
    titles = [m["title"] for m in movies_data]
    ratings_list = [m["rating"] for m in movies_data]
    votes_list = [m["votes"] / 10000 for m in movies_data]
    
    # 评分柱状图
    bars = ax1.barh(titles, ratings_list, color='steelblue', alpha=0.8)
    ax1.set_xlabel('评分')
    ax1.set_title('豆瓣 Top10 评分对比')
    ax1.set_xlim(9.0, 10.0)
    for bar, rating in zip(bars, ratings_list):
        ax1.text(bar.get_width() + 0.01, bar.get_y() + bar.get_height()/2,
                f'{rating}', va='center', fontsize=9)
    
    # 投票数散点图
    scatter = ax2.scatter(ratings_list, votes_list, 
                         s=[v*2 for v in votes_list],
                         alpha=0.6, c=ratings_list, cmap='YlOrRd')
    ax2.set_xlabel('评分')
    ax2.set_ylabel('投票数(万)')
    ax2.set_title('评分 vs 投票数分布')
    plt.colorbar(scatter, ax=ax2, label='评分')
    
    plt.tight_layout()
    plt.savefig('/tmp/chart.png', dpi=150, bbox_inches='tight')
    print("\\n图表已生成: /tmp/chart.png")
    
except ImportError:
    print("\\n提示: matplotlib 未安装,跳过图表生成")

print("\\n分析完成!")
"""
    
    result = await agent.analyze_data_safely(
        task_description="分析豆瓣电影 Top10 的评分和投票数据",
        code=analysis_code
    )
    
    if result["success"]:
        print("\n[OpenClaw] 任务执行成功,整理结果中...")
        for line in result["stdout"]:
            print(line)
        if result["chart"]:
            print(f"\n[OpenClaw] 图表文件已提取,大小: {len(result['chart'])} bytes")
            print("[OpenClaw] 正在将图表发送给用户...")
    else:
        print(f"\n[OpenClaw] 任务失败: {result.get('error')}")

asyncio.run(main())

场景三:浏览器自动化 —— 在沙箱中安全运行 Playwright

GUI Agent 是 OpenClaw 的强大能力之一,但让 AI 控制浏览器访问网页,风险极高(恶意网页可能通过 Prompt Injection 劫持 Agent)。OpenSandbox 的浏览器沙箱完美解决了这个问题。

import asyncio
from datetime import timedelta
from opensandbox import Sandbox

async def browser_automation_in_sandbox(url: str, task: str) -> dict:
    """
    在 OpenSandbox 隔离浏览器环境中执行网页自动化任务
    
    即使目标网页包含恶意 Prompt Injection,
    也无法逃出沙箱影响宿主机
    """
    
    # 使用带 Playwright 的浏览器沙箱镜像
    sandbox = await Sandbox.create(
        "opensandbox/playwright:latest",
        timeout=timedelta(minutes=5),
        # 网络策略:只允许访问指定域名
        network_policy={
            "default_action": "deny",
            "allow": [url.split("/")[2]]  # 只允许访问目标域名
        }
    )
    
    playwright_code = f"""
import asyncio
from playwright.async_api import async_playwright

async def run():
    async with async_playwright() as p:
        browser = await p.chromium.launch(
            headless=True,
            args=['--no-sandbox', '--disable-dev-shm-usage']
        )
        page = await browser.new_page()
        
        print(f"正在访问: {url}")
        await page.goto('{url}', wait_until='networkidle')
        
        # 执行任务:{task}
        title = await page.title()
        print(f"页面标题: {{title}}")
        
        # 截图保存
        await page.screenshot(path='/tmp/screenshot.png', full_page=True)
        print("截图已保存到 /tmp/screenshot.png")
        
        # 提取页面文本(过滤 HTML 标签)
        content = await page.evaluate('''() => {{
            return document.body.innerText.substring(0, 2000);
        }}''')
        print(f"页面内容预览(前500字):\\n{{content[:500]}}")
        
        await browser.close()

asyncio.run(run())
"""
    
    async with sandbox:
        # 在隔离浏览器沙箱中执行
        result = await sandbox.commands.run(
            f"python3 -c \"{playwright_code.replace(chr(34), chr(39))}\"",
            timeout=60
        )
        
        screenshot = None
        try:
            screenshot = await sandbox.files.read_file("/tmp/screenshot.png")
        except Exception:
            pass
        
        return {
            "success": result.exit_code == 0,
            "output": [line.text for line in result.logs.stdout],
            "errors": [line.text for line in result.logs.stderr],
            "screenshot": screenshot
        }

# 使用示例
async def main():
    print("在隔离沙箱中启动浏览器自动化任务...")
    result = await browser_automation_in_sandbox(
        url="https://example.com",
        task="提取页面标题和主要内容"
    )
    
    if result["success"]:
        print("任务执行成功!")
        for line in result["output"]:
            print(f"  {line}")
        if result["screenshot"]:
            print(f"截图已获取,大小: {len(result['screenshot'])} bytes")
    else:
        print("任务执行失败:")
        for line in result["errors"]:
            print(f"  错误: {line}")

asyncio.run(main())

场景四:强化学习训练 —— 并行沙箱的威力

RL Training 是 OpenSandbox 的明星场景之一。训练时需要并行运行大量独立的环境实例,传统方案要么资源竞争严重,要么冷启动太慢。OpenSandbox 的 BatchSandbox 机制让这一切变得轻而易举。

import asyncio
from datetime import timedelta
from opensandbox import Sandbox

# DQN CartPole 训练代码(将在沙箱中执行)
RL_TRAINING_CODE = """
import random
import math
import json

# 简化版 CartPole 环境模拟
class CartPoleEnv:
    def __init__(self):
        self.state = [0.0, 0.0, 0.0, 0.0]
        self.steps = 0
        self.max_steps = 200
    
    def reset(self):
        self.state = [random.uniform(-0.05, 0.05) for _ in range(4)]
        self.steps = 0
        return self.state[:]
    
    def step(self, action):
        x, x_dot, theta, theta_dot = self.state
        force = 10.0 if action == 1 else -10.0
        
        costheta = math.cos(theta)
        sintheta = math.sin(theta)
        
        temp = (force + 0.05 * theta_dot**2 * sintheta) / 1.1
        thetaacc = (9.8 * sintheta - costheta * temp) / (0.5 * (4/3 - 0.1 * costheta**2 / 1.1))
        xacc = temp - 0.05 * thetaacc * costheta / 1.1
        
        x = x + 0.02 * x_dot
        x_dot = x_dot + 0.02 * xacc
        theta = theta + 0.02 * theta_dot
        theta_dot = theta_dot + 0.02 * thetaacc
        
        self.state = [x, x_dot, theta, theta_dot]
        self.steps += 1
        
        done = (abs(x) > 2.4 or abs(theta) > 0.2095 or self.steps >= self.max_steps)
        reward = 1.0 if not done else 0.0
        return self.state[:], reward, done

# 简化版 DQN Agent(Q-Table 近似)
class SimpleAgent:
    def __init__(self, epsilon=0.3):
        self.q_table = {}
        self.epsilon = epsilon
        self.lr = 0.1
        self.gamma = 0.95
    
    def discretize(self, state):
        bins = [
            [-2.4, -1.2, 0, 1.2, 2.4],
            [-3.0, -1.5, 0, 1.5, 3.0],
            [-0.2095, -0.1, 0, 0.1, 0.2095],
            [-3.0, -1.5, 0, 1.5, 3.0]
        ]
        key = tuple(sum(1 for b in bin_list if s > b) for s, bin_list in zip(state, bins))
        return key
    
    def act(self, state):
        if random.random() < self.epsilon:
            return random.randint(0, 1)
        key = self.discretize(state)
        if key not in self.q_table:
            return random.randint(0, 1)
        q = self.q_table[key]
        return 0 if q[0] >= q[1] else 1
    
    def learn(self, state, action, reward, next_state, done):
        key = self.discretize(state)
        next_key = self.discretize(next_state)
        if key not in self.q_table:
            self.q_table[key] = [0.0, 0.0]
        if next_key not in self.q_table:
            self.q_table[next_key] = [0.0, 0.0]
        
        target = reward + (0 if done else self.gamma * max(self.q_table[next_key]))
        self.q_table[key][action] += self.lr * (target - self.q_table[key][action])

# 开始训练
env = CartPoleEnv()
agent = SimpleAgent(epsilon=0.3)
episode_rewards = []

print("开始 DQN CartPole 训练...")
print("-" * 40)

for episode in range(100):
    state = env.reset()
    total_reward = 0
    
    while True:
        action = agent.act(state)
        next_state, reward, done = env.step(action)
        agent.learn(state, action, reward, next_state, done)
        state = next_state
        total_reward += reward
        if done:
            break
    
    # 逐渐降低探索率
    agent.epsilon = max(0.05, agent.epsilon * 0.99)
    episode_rewards.append(total_reward)
    
    if (episode + 1) % 20 == 0:
        avg_reward = sum(episode_rewards[-20:]) / 20
        print(f"Episode {episode+1:3d} | 平均奖励: {avg_reward:6.1f} | ε: {agent.epsilon:.3f}")

# 最终评估
print("-" * 40)
final_avg = sum(episode_rewards[-20:]) / 20
print(f"训练完成!最终20轮平均奖励: {final_avg:.1f}")
print(f"Q-Table 状态数: {len(agent.q_table)}")

# 输出检查点数据(JSON 格式)
checkpoint = {
    "final_avg_reward": final_avg,
    "total_episodes": 100,
    "q_table_size": len(agent.q_table),
    "final_epsilon": agent.epsilon,
    "reward_history": episode_rewards
}
print(f"CHECKPOINT:{json.dumps(checkpoint)}")
"""

async def run_single_training(worker_id: int, seed: int) -> dict:
    """在独立沙箱中运行单次 RL 训练"""
    sandbox = await Sandbox.create(
        "opensandbox/code-interpreter:v1.0.2",
        entrypoint=["/opt/opensandbox/code-interpreter.sh"],
        env={
            "PYTHON_VERSION": "3.11",
            "WORKER_ID": str(worker_id),
            "RANDOM_SEED": str(seed)
        },
        timeout=timedelta(minutes=5),
    )
    
    async with sandbox:
        from code_interpreter import CodeInterpreter, SupportedLanguage
        interpreter = await CodeInterpreter.create(sandbox)
        
        result = await interpreter.codes.run(
            RL_TRAINING_CODE,
            language=SupportedLanguage.PYTHON,
        )
        
        # 解析检查点数据
        checkpoint = None
        for line in result.logs.stdout:
            if line.text.startswith("CHECKPOINT:"):
                import json
                checkpoint = json.loads(line.text.replace("CHECKPOINT:", ""))
                break
        
        return {
            "worker_id": worker_id,
            "seed": seed,
            "checkpoint": checkpoint,
            "stdout": [l.text for l in result.logs.stdout if not l.text.startswith("CHECKPOINT:")]
        }


async def parallel_rl_training():
    """
    并行启动多个沙箱进行 RL 训练
    模拟分布式强化学习的典型场景
    """
    NUM_WORKERS = 4
    print(f"启动 {NUM_WORKERS} 个并行训练沙箱...")
    print("=" * 50)
    
    # 并发创建多个沙箱并行训练
    tasks = [
        run_single_training(worker_id=i, seed=i * 42)
        for i in range(NUM_WORKERS)
    ]
    
    results = await asyncio.gather(*tasks, return_exceptions=True)
    
    print("\n所有 Worker 训练完成,汇总结果:")
    print("=" * 50)
    
    valid_results = [r for r in results if isinstance(r, dict) and r.get("checkpoint")]
    
    if valid_results:
        avg_rewards = [r["checkpoint"]["final_avg_reward"] for r in valid_results]
        best = max(valid_results, key=lambda x: x["checkpoint"]["final_avg_reward"])
        
        print(f"{'Worker':<10} {'最终平均奖励':<15} {'Q-Table大小':<12}")
        print("-" * 40)
        for r in valid_results:
            cp = r["checkpoint"]
            marker = " ← 最优" if r["worker_id"] == best["worker_id"] else ""
            print(f"Worker-{r['worker_id']:<4} {cp['final_avg_reward']:<15.1f} {cp['q_table_size']:<12}{marker}")
        
        print("-" * 40)
        print(f"全局平均奖励: {sum(avg_rewards)/len(avg_rewards):.1f}")
        print(f"最优 Worker: Worker-{best['worker_id']} (奖励: {best['checkpoint']['final_avg_reward']:.1f})")
    
    return results

asyncio.run(parallel_rl_training())

场景五:TypeScript 版集成 —— 前端 Agent 的安全执行

OpenSandbox 提供完整的 TypeScript SDK,非常适合 Node.js 后端或前端 Agent 项目。

import { Sandbox } from '@alibaba-group/opensandbox';
import { CodeInterpreter } from '@alibaba-group/opensandbox-code-interpreter';

interface ExecutionResult {
  success: boolean;
  stdout: string[];
  stderr: string[];
  result?: string;
  executionTime: number;
}

/**
 * OpenClaw TypeScript 集成示例
 * 在 OpenSandbox 中安全执行 AI 生成的代码
 */
async function executeInSandbox(
  code: string,
  language: 'python' | 'javascript' | 'typescript' = 'python'
): Promise<ExecutionResult> {
  const startTime = Date.now();

  // 创建隔离沙箱
  const sandbox = await Sandbox.create(
    'opensandbox/code-interpreter:v1.0.2',
    {
      entrypoint: ['/opt/opensandbox/code-interpreter.sh'],
      env: { PYTHON_VERSION: '3.11' },
      timeout: 600_000, // 10 分钟(毫秒)
    }
  );

  const result: ExecutionResult = {
    success: false,
    stdout: [],
    stderr: [],
    executionTime: 0,
  };

  try {
    await sandbox.open();

    // 创建代码解释器
    const interpreter = await CodeInterpreter.create(sandbox);

    // 执行代码,支持流式输出
    const execution = await interpreter.codes.run(code, {
      language,
      onStdout: (line) => {
        // 实时处理标准输出(适合流式 UI 展示)
        result.stdout.push(line.text);
        process.stdout.write(`[沙箱输出] ${line.text}\n`);
      },
      onStderr: (line) => {
        result.stderr.push(line.text);
        process.stderr.write(`[沙箱错误] ${line.text}\n`);
      },
    });

    result.success = true;
    if (execution.result && execution.result.length > 0) {
      result.result = execution.result[0].text;
    }
  } catch (error) {
    result.stderr.push(`执行异常: ${error}`);
  } finally {
    // 确保沙箱被销毁,释放资源
    await sandbox.kill();
    result.executionTime = Date.now() - startTime;
  }

  return result;
}

// 集成到 OpenClaw Skill 的示例
async function openClawCodeSkill(userRequest: string): Promise<string> {
  console.log(`[OpenClaw] 用户请求: ${userRequest}`);

  // 模拟 LLM 生成代码(实际场景中调用 Claude/GPT API)
  const generatedCode = `
# AI 生成的代码 - 在 OpenSandbox 中安全执行
import datetime
import platform
import sys

print(f"执行时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Python 版本: {sys.version}")
print(f"运行平台: {platform.system()} {platform.release()}")
print(f"处理请求: ${userRequest}")

# 模拟业务逻辑
result = {
    "status": "success",
    "processed": True,
    "message": "任务在安全沙箱中完成"
}
print(f"执行结果: {result}")
result
`;

  console.log('[OpenSandbox] 将代码投递到隔离沙箱...');
  const execResult = await executeInSandbox(generatedCode, 'python');

  if (execResult.success) {
    const output = execResult.stdout.join('\n');
    console.log(`[OpenClaw] 执行成功,耗时: ${execResult.executionTime}ms`);
    return output;
  } else {
    const errors = execResult.stderr.join('\n');
    console.error(`[OpenClaw] 执行失败: ${errors}`);
    return `执行失败: ${errors}`;
  }
}

// 主函数
(async () => {
  const response = await openClawCodeSkill('分析当前系统环境信息');
  console.log('\n最终回复给用户:');
  console.log(response);
})();

五、安全加固:生产环境的七道防线

将 OpenSandbox + OpenClaw 部署到生产环境,安全配置是重中之重。以下是经过实践验证的七层防护体系。

5.1 第一道防线:OpenClaw Gateway Token 认证

# 生成强随机 Token
openclaw doctor --generate-gateway-token

# 配置 Gateway 仅允许本地访问
openclaw config set gateway.mode local

# 如需远程访问,务必配置 TLS
# nginx 反向代理 + Let's Encrypt 证书

对应的 nginx 配置:

server {
    listen 443 ssl;
    server_name your-agent.example.com;

    ssl_certificate /etc/letsencrypt/live/your-agent.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-agent.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:18789;
        proxy_set_header Authorization "Bearer YOUR_GATEWAY_TOKEN";
        proxy_set_header X-Real-IP $remote_addr;
        # 禁止直接暴露 Gateway Token 到外部
        proxy_hide_header X-Gateway-Token;
    }
}

5.2 第二道防线:沙箱资源硬限制

~/.sandbox.toml 中配置严格的资源上限,防止 AI 代码耗尽系统资源:

[docker]
# 进程数限制(防御 Fork Bomb)
pids_limit = 512

# 剥夺所有危险的 Linux Capabilities
drop_capabilities = [
  "AUDIT_WRITE",   # 禁止写入审计日志
  "MKNOD",         # 禁止创建设备文件
  "NET_ADMIN",     # 禁止修改网络配置
  "NET_RAW",       # 禁止原始套接字
  "SYS_ADMIN",     # 禁止广泛系统管理
  "SYS_MODULE",    # 禁止加载内核模块
  "SYS_PTRACE",    # 禁止进程追踪
  "SYS_TIME",      # 禁止修改系统时间
  "SYS_TTY_CONFIG" # 禁止 TTY 配置
]

# 禁止特权升级(防止容器逃逸)
no_new_privileges = true

# 使用 AppArmor 配置文件(如果系统支持)
apparmor_profile = "docker-default"

5.3 第三道防线:网络出站白名单

[egress]
image = "opensandbox/egress:latest"

# 默认拒绝所有出站流量
[egress.policy]
default_action = "deny"

# 按需开放白名单
[[egress.policy.allow]]
target = "pypi.org"       # Python 包下载
action = "allow"

[[egress.policy.allow]]
target = "*.github.com"   # GitHub 访问
action = "allow"

[[egress.policy.allow]]
target = "api.openai.com" # OpenAI API(如需)
action = "allow"

# 强制禁用 IPv6(防止绕过 IPv4 规则)
[egress.network]
disable_ipv6 = true

5.4 第四道防线:沙箱生命周期管理

# 在应用层实现沙箱使用审计
import logging
from datetime import datetime

class AuditedSandbox:
    """带审计日志的沙箱封装"""
    
    def __init__(self, user_id: str, task_id: str):
        self.user_id = user_id
        self.task_id = task_id
        self.sandbox = None
        self.logger = logging.getLogger("sandbox.audit")
    
    async def __aenter__(self):
        self.logger.info(
            f"SANDBOX_CREATE | user={self.user_id} | task={self.task_id} | time={datetime.now()}"
        )
        self.sandbox = await Sandbox.create(
            "opensandbox/code-interpreter:v1.0.2",
            timeout=timedelta(minutes=10),
        )
        return self.sandbox
    
    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if self.sandbox:
            await self.sandbox.kill()
            self.logger.info(
                f"SANDBOX_DESTROY | user={self.user_id} | task={self.task_id} | "
                f"exception={'yes' if exc_type else 'no'} | time={datetime.now()}"
            )

# 使用示例
async def handle_user_request(user_id: str, code: str):
    async with AuditedSandbox(user_id=user_id, task_id="code_exec_001") as sandbox:
        interpreter = await CodeInterpreter.create(sandbox)
        return await interpreter.codes.run(code, language=SupportedLanguage.PYTHON)

5.5 第五道防线:Prompt Injection 防护

OpenClaw 在处理来自网页、邮件、文档的内容时,需要防范间接 Prompt Injection 攻击。

import re

class PromptInjectionGuard:
    """
    Prompt Injection 防护层
    在将外部内容传入 LLM 之前进行清洗
    """
    
    # 常见注入模式
    INJECTION_PATTERNS = [
        r"(?i)(ignore|forget|disregard)\s+(previous|above|all)\s+(instructions?|commands?)",
        r"(?i)system\s*:\s*",
        r"(?i)\[INST\]|\[/INST\]",
        r"(?i)<\|im_start\|>|<\|im_end\|>",
        r"(?i)you are now|act as|pretend to be",
        r"(?i)(execute|run|eval)\s*(this|the following)\s*(code|command|script)",
    ]
    
    @classmethod
    def sanitize(cls, content: str, source: str = "unknown") -> tuple[str, bool]:
        """
        清洗外部内容,返回 (清洗后内容, 是否检测到注入)
        """
        injection_detected = False
        sanitized = content
        
        for pattern in cls.INJECTION_PATTERNS:
            if re.search(pattern, content):
                injection_detected = True
                # 将可疑内容替换为无害文本
                sanitized = re.sub(
                    pattern,
                    "[CONTENT FILTERED]",
                    sanitized,
                    flags=re.IGNORECASE
                )
        
        if injection_detected:
            logging.warning(
                f"PROMPT_INJECTION_DETECTED | source={source} | "
                f"original_length={len(content)}"
            )
        
        return sanitized, injection_detected

5.6 第六道防线:升级到 MicroVM 级隔离

对于多租户生产环境,容器级隔离不够,需要升级到 MicroVM:

# 安装 gVisor(用户态内核,更强的 Syscall 隔离)
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list > /dev/null
sudo apt-get update && sudo apt-get install -y runsc

# 配置 Docker 使用 gVisor
sudo runsc install
sudo systemctl restart docker

~/.sandbox.toml 中启用:

[docker]
# 使用 gVisor 运行时(更强隔离,略有性能损耗)
runtime = "runsc"

对于最高安全要求场景,使用 Kata Containers(硬件级 MicroVM):

[docker]
# 使用 Kata Containers(每个沙箱独立 VM,物理级隔离)
runtime = "kata-runtime"

5.7 第七道防线:定期安全审计

# 检查是否有僵尸沙箱残留
docker ps -a --filter "label=opensandbox.sandbox_id" --format "table {{.ID}}\t{{.Status}}\t{{.CreatedAt}}"

# 检查 OpenClaw 版本(及时修复 CVE)
openclaw --version
npm update -g openclaw

# 审计已安装的 Skills(每个 Skill 都是潜在的攻击面)
openclaw skill list
cat ~/.openclaw/skills/*/skill.md | grep -i "permission\|exec\|shell\|command"

# 检查 Gateway 暴露情况(确保没有公网暴露)
ss -tlnp | grep 18789

六、性能调优:让沙箱飞起来

6.1 连接池预热(Roadmap 功能预览)

OpenSandbox 即将推出的连接池功能,可以将沙箱创建延迟从秒级降低到毫秒级:

# 预热沙箱池(即将支持的功能)
from opensandbox import SandboxPool

pool = SandboxPool(
    image="opensandbox/code-interpreter:v1.0.2",
    min_size=3,      # 最少保持 3 个预热沙箱
    max_size=10,     # 最多 10 个并发沙箱
    idle_timeout=300 # 空闲 5 分钟后自动回收
)

await pool.initialize()

# 从池中获取沙箱(近乎零延迟)
async with pool.acquire() as sandbox:
    interpreter = await CodeInterpreter.create(sandbox)
    result = await interpreter.codes.run(code, language=SupportedLanguage.PYTHON)

6.2 镜像预拉取优化

# 在部署时预拉取所有需要的镜像,避免首次创建沙箱时的拉取延迟
docker pull opensandbox/code-interpreter:v1.0.2
docker pull opensandbox/playwright:latest
docker pull opensandbox/desktop:latest
docker pull opensandbox/execd:latest
docker pull opensandbox/egress:latest

# 验证镜像完整性
docker images | grep opensandbox

6.3 Kubernetes 生产部署配置

对于大规模生产环境,推荐使用 Kubernetes 部署并启用 Pool CRD:

# kubernetes/pool.yaml
apiVersion: sandbox.opensandbox.io/v1alpha1
kind: Pool
metadata:
  name: code-interpreter-pool
  namespace: opensandbox
spec:
  template:
    spec:
      containers:
        - name: sandbox
          image: opensandbox/code-interpreter:v1.0.2
          resources:
            requests:
              cpu: "500m"
              memory: "256Mi"
            limits:
              cpu: "1000m"
              memory: "512Mi"
  # 池化配置
  capacitySpec:
    bufferMin: 5     # 始终保持 5 个预热实例
    bufferMax: 20    # 最多预热 20 个
    poolMax: 100     # 池子最大容量
  # 使用 gVisor 增强隔离
  runtimeClass: gvisor
# 应用配置
kubectl apply -f kubernetes/pool.yaml

# 查看池状态
kubectl get pool -n opensandbox
kubectl describe pool code-interpreter-pool -n opensandbox

七、常见问题排查

Q1:沙箱创建超时

# 检查 Docker 服务状态
systemctl status docker

# 检查 OpenSandbox 服务日志
opensandbox-server --log-level DEBUG

# 检查镜像是否已拉取
docker images | grep opensandbox

# 手动测试沙箱创建
curl -X POST http://localhost:8090/sandboxes \
  -H "Content-Type: application/json" \
  -d '{"image": "opensandbox/code-interpreter:v1.0.2", "timeout": 600}'

Q2:代码执行结果为空

# 确保等待 SSE 流完整结束
result = await interpreter.codes.run(
    code,
    language=SupportedLanguage.PYTHON,
    # 增加超时时间(复杂计算可能需要更长时间)
    timeout=300,
)

# 检查 stderr 是否有错误信息
if result.logs.stderr:
    for line in result.logs.stderr:
        print(f"错误: {line.text}")

Q3:网络访问被拒绝

# 检查 ~/.sandbox.toml 中的 egress 配置
# 添加需要访问的域名到白名单
[[egress.policy.allow]]
target = "your-required-domain.com"
action = "allow"

Q4:OpenClaw 无法连接 OpenSandbox

# 验证 OpenSandbox 服务是否正常运行
curl http://localhost:8090/health

# 检查端口是否监听
ss -tlnp | grep 8090

# 检查 OpenClaw 配置中的 server_url 是否正确
cat ~/.openclaw/config.yaml | grep server_url

八、总结:构建生产级 AI Agent 的正确姿势

经过以上的完整实战,我们可以总结出 OpenSandbox + OpenClaw 联合部署的最佳实践:

架构层面: OpenClaw 作为 AI 大脑负责理解意图和编排任务,OpenSandbox 作为执行层负责安全隔离。两者职责清晰,互不耦合,可以独立升级和扩展。

安全层面: 七道防线层层递进,从 Gateway Token 认证到 MicroVM 硬件隔离,从网络白名单到 Prompt Injection 过滤,构建起完整的纵深防御体系。任何单一防线被突破,其他防线仍然有效。

性能层面: 利用 Pool CRD 预热机制消灭冷启动延迟,利用 BatchSandbox 实现 O(1) 时间复杂度的并行沙箱创建,在保证安全的同时不牺牲性能。

工程层面: 完整的多语言 SDK 支持(Python/TypeScript/Java/C#)、标准化的 OpenAPI 规范、详尽的审计日志,让整个系统可观测、可调试、可维护。

AI Agent 的时代已经到来。让 AI 拥有能力,让沙箱守护安全——这正是 OpenSandbox × OpenClaw 组合所代表的工程哲学。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐