主题
11. 沙箱系统概览:安全的代码执行环境
让代理安全地执行代码,保护你的系统
引言
代理可以生成代码、操作文件、运行 Shell 命令。但这也意味着巨大的安全风险——如果代理被恶意输入误导,可能会:
- 读取你的密钥和凭据
- 删除重要文件
- 发起网络攻击
- 消耗无限资源
沙箱(Sandbox)通过在代理执行环境和宿主系统之间建立隔离边界来解决这个问题。
┌─────────────────────────────────────────────────────────────┐
│ 宿主系统 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 沙箱边界 │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 代理 │ │ │
│ │ │ • 文件系统操作 ✅ │ │ │
│ │ │ • Shell 命令 ✅ │ │ │
│ │ │ • 网络请求 (可限制) ✅ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ❌ 无法访问宿主文件 │ │
│ │ ❌ 无法读取宿主环境变量 │ │
│ │ ❌ 无法影响其他进程 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
为什么需要沙箱?
安全威胁分析
| 威胁 | 无沙箱 | 有沙箱 |
|---|---|---|
| 读取系统密钥 | ⚠️ 可能 | ✅ 被阻止 |
| 删除重要文件 | ⚠️ 可能 | ✅ 隔离 |
| 执行恶意代码 | ⚠️ 可能 | ✅ 隔离 |
| 消耗无限资源 | ⚠️ 可能 | ✅ 可限制 |
| 网络数据泄露 | ⚠️ 可能 | ⚠️ 需配置 |

适用场景
编码代理:
- 克隆仓库、运行 git 命令
- 执行构建和测试流水线
- 运行 Docker-in-Docker
- 自主修改和运行代码
数据分析代理:
- 加载和处理数据文件
- 安装数据分析库(pandas、numpy)
- 运行统计计算
- 生成报告和可视化
沙箱在 DeepAgents 中的定位
在 DeepAgents 中,沙箱是一种特殊的后端(Backend):
┌─────────────────────────────────────────────────────────────┐
│ 后端类型 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ StateBackend│ │ Filesystem │ │ StoreBackend│ │
│ │ (临时) │ │ Backend │ │ (持久化) │ │
│ │ │ │ (本地) │ │ │ │
│ │ 文件工具 ✅ │ │ 文件工具 ✅ │ │ 文件工具 ✅ │ │
│ │ execute ❌ │ │ execute ❌ │ │ execute ❌ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 沙箱后端 │ │
│ │ │ │
│ │ 文件工具 ✅ (ls, read_file, write_file...) │ │
│ │ execute ✅ (运行 Shell 命令) │ │
│ │ 安全边界 ✅ (隔离宿主系统) │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘沙箱后端提供:
- 所有标准文件系统工具(ls、read_file、write_file、edit_file、glob、grep)
execute工具(在沙箱中运行任意 Shell 命令)- 保护宿主系统的安全边界

集成模式
有两种将代理与沙箱集成的架构模式:
模式一:沙箱内运行代理
代理运行在沙箱内部,你通过网络与其通信。
┌─────────────────────────────────────────────────────────────┐
│ 你的应用 │
│ │ │
│ WebSocket/HTTP │
│ │ │
│ ┌───────────────────────▼─────────────────────────────┐ │
│ │ 沙箱 │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 代理 │ │ │
│ │ │ LLM API Key 在沙箱内 ⚠️ │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘优点:
- ✅ 更贴近本地开发体验
- ✅ 代理与环境强耦合
缺点:
- 🔴 API Key 必须放在沙箱内(安全风险)
- 🔴 更新需要重建镜像
- 🔴 需要额外基础设施处理通信
Dockerfile 示例:
dockerfile
FROM python:3.11
RUN pip install deepagents-cli模式二:沙箱作为工具(推荐)
代理运行在你的机器/服务器上,需要执行代码时调用沙箱。
┌─────────────────────────────────────────────────────────────┐
│ 你的应用 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 代理 │ │
│ │ LLM API Key 在宿主 ✅ │ │
│ │ │ │
│ │ execute("npm run build") │ │
│ │ │ │ │
│ └──────────│──────────────────────────────────────────┘ │
│ │ Provider API │
│ ┌──────────▼──────────────────────────────────────────┐ │
│ │ 沙箱 │ │
│ │ 执行命令并返回结果 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘优点:
- ✅ 无需重建镜像即可更新代理代码
- ✅ API Key 留在沙箱外部
- ✅ 沙箱故障不会丢失代理状态
- ✅ 仅为执行时间付费
缺点:
- 🔴 每次执行调用有网络延迟
推荐使用此模式,本教程的示例都基于"沙箱作为工具"模式。

可用的沙箱提供商
| 提供商 | 特点 | 适用场景 |
|---|---|---|
| Modal | ML/AI 工作负载,GPU 访问,Python | 机器学习、数据分析 |
| Daytona | TypeScript/Python 开发,快速冷启动 | 编码代理、团队协作 |
| Deno | JavaScript 工作负载,microVM | 轻量级脚本执行 |
| Node VFS | 本地虚拟文件系统,无需云服务 | 本地开发、测试 |
提供商选择指南
开始
│
├─ 需要 GPU?
│ │
│ └─ 是 ──→ Modal
│
├─ 需要快速冷启动?
│ │
│ └─ 是 ──→ Daytona
│
├─ 主要是 JavaScript/TypeScript?
│ │
│ └─ 是 ──→ Deno
│
└─ 只是本地开发/测试?
│
└─ 是 ──→ Node VFS
基础用法
创建沙箱代理
typescript
import { createDeepAgent } from "deepagents";
import { DenoSandbox } from "@langchain/deno";
const sandbox = await DenoSandbox.create({
memoryMb: 1024,
lifetime: "10m",
});
try {
const agent = createDeepAgent({
backend: sandbox,
systemPrompt: "你是一个可以访问沙箱的 JavaScript 编码助手。",
});
const result = await agent.invoke({
messages: [{
role: "user",
content: "创建一个 HTTP 服务器并测试它",
}],
});
console.log(result.messages[result.messages.length - 1].content);
} finally {
await sandbox.close();
}execute 工具
当代理需要执行命令时,会调用 execute 工具:
typescript
execute("npm install express")
execute("npm run build")
execute("git clone https://github.com/user/repo.git")返回结果:
// 成功
npm notice created a lockfile...
[命令成功,退出码为 0]
// 失败
bash: foobar: command not found
[命令失败,退出码为 127]文件操作
沙箱内的文件系统工具与其他后端相同:
typescript
ls("/workspace")
read_file("/workspace/package.json")
write_file("/workspace/app.js", "console.log('Hello')")
edit_file("/workspace/app.js", {
old_string: "Hello",
new_string: "Hello World"
})两种文件访问方式
理解这两种文件访问方式很重要:
1. 代理文件系统工具
代理在执行过程中使用的工具(read_file、write_file 等),通过沙箱内的 execute() 运行。
typescript
// 代理调用
read_file("/workspace/code.js")
write_file("/workspace/output.txt", "结果")2. 文件传输 API
你的应用代码调用的 uploadFiles() 和 downloadFiles() 方法,用于在宿主和沙箱之间传输文件。
typescript
// 你的应用代码
// 为沙箱播种
const encoder = new TextEncoder();
await sandbox.uploadFiles([
["src/index.js", encoder.encode("console.log('Hello')")],
["package.json", encoder.encode('{"name": "my-app"}')],
]);
// 运行代理...
// 取回产物
const results = await sandbox.downloadFiles([
"dist/bundle.js",
"report.html"
]);使用场景:
| 方法 | 使用者 | 用途 |
|---|---|---|
| read_file/write_file | 代理 | 执行任务时读写文件 |
| uploadFiles | 应用代码 | 在代理运行前准备文件 |
| downloadFiles | 应用代码 | 在代理结束后取回产物 |

生命周期管理
基础生命周期
typescript
// 1. 创建并初始化
const sandbox = await ModalSandbox.create(options);
// 2. 使用沙箱
const result = await sandbox.execute("echo hello");
// 或通过代理使用
const agent = createDeepAgent({ backend: sandbox });
await agent.invoke(messages);
// 3. 清理
await sandbox.close();按对话生命周期
在聊天应用中,通常每个 thread_id 使用一个独立的沙箱:
typescript
import { v4 as uuidv4 } from "uuid";
import { Daytona } from "@daytonaio/sdk";
import { DaytonaSandbox } from "@langchain/daytona";
import { createDeepAgent } from "deepagents";
const client = new Daytona();
const threadId = uuidv4();
let sandbox;
try {
sandbox = await client.findOne({ labels: { thread_id: threadId } });
} catch {
sandbox = await client.create({
labels: { thread_id: threadId },
autoDeleteInterval: 3600,
});
}
const backend = await DaytonaSandbox.fromId(sandbox.id);
const agent = createDeepAgent({
backend,
systemPrompt: "你是一个编码助手。",
});
try {
const result = await agent.invoke(
{ messages },
{ configurable: { thread_id: threadId } }
);
} catch (err) {
await client.delete(sandbox);
throw err;
}TTL 配置
为聊天应用配置存活时间(TTL),自动清理空闲沙箱:
typescript
const sandbox = await DaytonaSandbox.create({
autoDeleteInterval: 3600, // 1小时后自动删除
});
const sandbox = await DenoSandbox.create({
lifetime: "10m", // 10分钟后过期
});安全注意事项
隔离边界能做什么
✅ 沙箱能保护:
- 宿主文件系统
- 宿主环境变量
- 其他进程
隔离边界不能做什么
⚠️ 沙箱无法防御:
上下文注入攻击:如果攻击者能控制代理输入的一部分,就可以指示代理在沙箱内执行任意命令。
网络数据泄露:除非阻断网络访问,否则被注入的代理可以通过 HTTP 或 DNS 将数据发送出沙箱。
🚨 永远不要把密钥放进沙箱
┌────────────────────────────────────────────────────────────┐
│ 🔴 严重警告 │
│ │
│ 永远不要把以下内容放入沙箱: │
│ • API Key │
│ • 数据库凭据 │
│ • Token │
│ • 通过环境变量注入的密钥 │
│ • 通过文件挂载的凭据 │
│ │
│ 原因: │
│ 遭受上下文注入的代理可以读取并外泄这些密钥。 │
│ 即使是短生命周期的凭据也一样。 │
└────────────────────────────────────────────────────────────┘安全地处理密钥
方案一:将密钥保留在沙箱外的工具中(推荐)
typescript
const callAuthenticatedAPI = tool(
async ({ endpoint, params }) => {
const response = await fetch(endpoint, {
headers: {
Authorization: `Bearer ${process.env.API_KEY}`, // 在宿主环境
},
body: JSON.stringify(params),
});
return response.json();
},
{
name: "call_api",
description: "调用需要认证的 API",
schema: z.object({
endpoint: z.string(),
params: z.object({}),
}),
}
);
const agent = createDeepAgent({
backend: sandbox,
tools: [callAuthenticatedAPI], // 工具运行在宿主环境
});方案二:使用注入凭据的网络代理
某些提供商支持代理拦截 HTTP 请求,在转发前附加凭据。代理看不到密钥。
通用最佳实践
| 实践 | 说明 |
|---|---|
| 复核沙箱输出 | 在采取行动前验证输出 |
| 阻断不必要的网络 | 使用 blockNetwork: true 等选项 |
| 使用中间件过滤输出 | 脱敏敏感模式 |
| 视沙箱产出为不信任输入 | 不要直接使用未验证的输出 |

沙箱 vs LocalShellBackend
| 特性 | LocalShellBackend | 沙箱后端 |
|---|---|---|
| 执行环境 | 宿主机 | 隔离容器 |
| 安全性 | ⚠️ 危险 | ✅ 安全 |
| 访问宿主文件 | ✅ 可以 | ❌ 隔离 |
| 性能 | 快 | 稍慢(网络延迟) |
| 适用场景 | 本地开发(自己使用) | 生产环境 |
| 成本 | 无 | 按使用付费 |
选择建议:
- 本地开发、自己使用 → LocalShellBackend(配合 interrupt_on)
- 生产环境、处理用户输入 → 沙箱后端
小结
本文介绍了 DeepAgents 沙箱系统的核心概念:
| 概念 | 说明 |
|---|---|
| 沙箱 | 隔离的代码执行环境 |
| 隔离边界 | 保护宿主系统不受代理影响 |
| execute 工具 | 在沙箱中运行 Shell 命令 |
| 两种集成模式 | "沙箱内运行代理" vs "沙箱作为工具" |
| 提供商 | Modal、Daytona、Deno、Node VFS |
安全原则:
- ✅ 使用沙箱隔离代码执行
- ✅ 密钥保留在沙箱外
- ✅ 阻断不必要的网络访问
- ✅ 视沙箱输出为不信任
下一步
在下一篇文章中,我们将实战配置 Modal 和 Daytona 沙箱,构建完整的安全代码执行平台。
实践任务
- 使用 Deno 沙箱创建一个简单的代码执行代理
- 测试沙箱的隔离性:尝试在沙箱内读取宿主文件
- 实现文件上传/下载:为沙箱准备代码并取回产物