在Python中直接exec()运行AI生成代码等于将整个进程权限交给模型;作者对比了E2B、Modal和开源Piston三种安全沙箱方案的接入方式与边界。
当你让 LLM 写代码然后在实际环境中运行它的那一刻,你已经把一个决策交给了一个根本不知道你的 .env 文件里有什么的模型。一个只会聊天的 chatbot 生来就是安全的,而一个会自己执行 Python 来验证计算或绘制图表的就完全是另一回事了——必须有某个东西来决定这些代码可以在哪里运行。
大多数人的第一反应是 exec(),而这恰恰是错误的选择:exec() 运行在你自己的进程内部,它会带着你的环境变量和文件系统权限一起"搭车"。我在 DevToolLab 上写了一篇更详细的完整版教程,涵盖了 E2B、Modal 和 Piston——这是一个可以自托管的开源引擎,让你不用依赖任何一家供应商。下面的每一个代码片段都对照着通过 pip 实际安装的 SDK 版本验证过,而不是从某个营销页面上复制来的。
把模型生成的字符串直接塞进 exec(),它能读取你的进程能读取的任何东西:
model_output = """
import os
print(f"I can see {len(os.environ)} environment variables from this process.")
"""
exec(model_output)
在一台普通的笔记本上,这会打印出类似 I can see 61 environment variables from this process 的内容,而这个调用完全不需要任何提升的权限。它只是一个普通 Python 进程在读取自己的环境——这通常就是 API 密钥和各种密钥存放的地方。沙箱的存在就是为了给模型的代码提供一个独立的"废弃机器",这样读取 os.environ 就什么有意义的东西都拿不到了。
E2B 将每个沙箱作为 Firecracker microVM 运行,这就是 AWS 为 Lambda 构建的同款虚拟化技术。安装 code interpreter SDK,从 E2B 仪表盘获取一个 API key,然后导出为 E2B_API_KEY:
pip install e2b-code-interpreter
from e2b_code_interpreter import Sandbox
with Sandbox.create(timeout=60) as sandbox:
execution = sandbox.run_code("data = [4, 8, 15, 16, 23, 42]\nprint(sum(data))")
print(execution.text)
务必设置一个明确的超时时间。run_code() 返回的是一个 Execution 对象,而不是普通字符串,.text 属性用于获取打印结果,.results 用于获取更丰富的输出(如 matplotlib 生成的 PNG),.logs 用于获取 stdout/stderr,.error 用于获取结构化的异常(.name、.value、.traceback)而不是抛出一个 Python 异常。检查 .error 是这里的惯用做法,因为一个失败的代码片段并不是传输故障,而是 SDK 会正确报告回来的一个正常结果。
E2B 的 Hobby 层级免费,包含一次性 $100 的使用额度、20 个并发沙箱和一小时的会话时长。Pro 层级 $150/月,会话延长到 24 小时,并发数提升到 100。

Modal 走的是另一条路:Sandbox 是运行在 Modal 通用计算平台上的一个容器,所以如果你已经在用 Modal 部署函数,同样的镜像构建工具在这里也完全适用。
pip install modal
modal setup
modal setup 会打开一个浏览器并将 token 写入 ~/.modal.toml,不需要手动管理 API key。Modal 沙箱启动时处于空闲状态,没有入口点,你用 .exec() 在里面运行命令:
import modal
app = modal.App.lookup("demo", create_if_missing=True)
image = modal.Image.debian_slim(python_version="3.12")
sandbox = modal.Sandbox.create(image=image, app=app, timeout=60, block_network=True)
process = sandbox.exec("python", "-c", "print(sum([4, 8, 15, 16, 23, 42]))")
print(process.stdout.read())
sandbox.terminate()
如果你省略 timeout,Sandbox.create() 默认会使用 300 秒的超时时间;对于只需要计算而不需要对外调用的场景,block_network=True 值得明确设置。Modal 的 Starter 计划免费,包含 $30/月的额度且无空闲费用;Sandbox 的计费大约是标准按秒费率的 3 倍,因为它们是不可抢占的。
Piston 是一个真正开源(MIT 许可)的代码执行引擎,多年来一直为 Discord 上的代码机器人提供动力。它的公开演示 API 曾经可以免费调用,但截至 2026 年 2 月 15 日,execute 端点已改为白名单制,这一点已得到直接确认:
curl -s -X POST "https://emkc.org/api/v2/piston/execute" \
-H "Content-Type: application/json" \
-d '{"language":"python","version":"3.10.0","files":[{"name":"main.py","content":"print(1)"}]}'
返回的信息会引导你去自托管。好消息是 Piston 提供了一个现成的 Docker 镜像:
docker run --privileged -v $PWD:/piston -dit -p 2000:2000 --name piston_api ghcr.io/engineer-man/piston
运行起来之后,把请求发到你自己的主机而不是 emkc.org,使用完全相同的请求结构:
import requests
response = requests.post(
"http://localhost:2000/api/v2/execute",
json={"language": "python", "version": "3.10.0", "files": [{"name": "main.py", "content": "print('hi')"}]},
)
print(response.json()["run"]["stdout"])
Piston 用 isolate 为每次执行创建沙箱——这就是竞赛编程裁判使用的同款工具,但你需要自己负责修补主机和配置容器权限,这些是 E2B 和 Modal 作为服务价格一部分帮你吸收掉的工作。

如果你想最少地考虑基础设施问题,从 E2B 开始,它的 run_code() 模型是为这个场景量身打造的。如果你需要自定义镜像、挂载卷,或者你已经在 Modal 上部署,Modal 基于容器的 Sandbox 可以自然融入。如果你不需要任何供应商,Piston 自托管是真正的备选方案,代价是你需要自己运行它。无论你选哪一个,完整的 DevToolLab 指南包含了全部三个平台的完整代码以及精确的计费计算,DevToolLab 的 .env File Generator 可以快速帮你搭建一个存放 API key 的文件,而不用手工逐个输入。
比选哪个 SDK 更重要的模式是:始终设置明确的超时时间,始终检查结构化错误而不是靠猜,慎重决定沙箱是否获得网络访问权限,并在用完后始终将其销毁。
Building an AI Code Interpreter: E2B and Modal Sandboxes in Python - 原文、完整版指南
E2B code interpreter 文档
Modal Sandboxes 指南