引言

当 AI Agent 从”回答问题”进化到”执行操作”,安全风险呈指数级增长。上午我们讨论了不确定性管理——如何让 Agent 在认知局限下做出可靠决策。下午,我们来探讨另一个关键命题:当 Agent 的决策需要落地执行时,如何确保这个过程是安全的?

答案就是安全沙箱(Sandbox)。

沙箱不是可选项,而是生产级 Agent 系统的必备基础设施。没有沙箱的 Agent 就像没有刹车的赛车——速度越快,风险越大。

本文将系统性地拆解 Agent 安全沙箱的设计原则、技术方案和实现细节。


一、Agent 执行的风险模型

1.1 执行风险的三个维度

风险维度 威胁示例 潜在后果
系统安全 Agent 执行 rm -rf / 或修改系统配置 数据丢失、服务中断
数据安全 Agent 访问敏感数据库或泄露 API Key 隐私泄露、合规风险
资源滥用 Agent 陷入无限循环或创建过多进程 资源耗尽、拒绝服务

1.2 为什么传统安全模型不够用

传统的应用安全模型假设:

  • 代码是开发者编写的,经过审查
  • 执行环境是可信的
  • 用户输入经过校验

但 AI Agent 打破了这些假设:

  • 代码是 AI 生成的,可能存在逻辑漏洞
  • 执行路径是动态的,无法预先审计所有分支
  • 工具调用是开放的,Agent 可能调用未预期功能

这意味着我们需要运行时隔离,而非仅靠静态审查。


二、沙箱的核心安全机制

2.1 隔离的三层模型

一个完整的安全沙箱需要实现三层隔离:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌─────────────────────────────────────────┐
│ 网络隔离层 │
│ ┌─────────────────────────────────┐ │
│ │ 文件系统隔离层 │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ 资源限制层 │ │ │
│ │ │ ┌─────────────────┐ │ │ │
│ │ │ │ Agent 进程 │ │ │ │
│ │ │ │ (被隔离) │ │ │ │
│ │ │ └─────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
└─────────────────────────────────────────┘

2.2 Linux 命名空间(Namespace)

命名空间是 Linux 内核提供的隔离机制,让进程拥有独立的系统视图:

命名空间 隔离内容 安全意义
PID 进程 ID Agent 看不到宿主机的其他进程
NET 网络接口 独立的网络栈,可限制外连
MNT 挂载点 独立的文件系统视图
UTS 主机名/域名 隐藏宿主机信息
IPC 进程间通信 防止与其他进程通信
USER 用户/组 ID 以非 root 身份运行

Docker 容器正是基于这些命名空间实现的隔离。

2.3 cgroups 资源限制

控制组(cgroups)用于限制进程的资源使用:

1
2
3
4
5
6
7
8
# 限制 CPU 使用率为 50%
echo 50000 > /sys/fs/cgroup/cpu/agent/cpu.cfs_quota_us

# 限制内存使用为 512MB
echo 536870912 > /sys/fs/cgroup/memory/agent/memory.limit_in_bytes

# 限制磁盘 I/O
echo "8:0 1048576" > /sys/fs/cgroup/blkio/agent/blkio.throttle.read_bps_device

cgroups v2 提供了更统一的接口,是现代容器运行时的基础。


三、E2B:专为 Agent 设计的云端沙箱

3.1 E2B 的核心价值

E2B 是一个专为 AI Agent 设计的云端沙箱服务,解决了自建沙箱的痛点:

  • 即时启动:毫秒级启动隔离环境
  • 预配置环境:包含 Python、Node.js 等常用运行时
  • 持久化存储:支持会话级文件持久化
  • 网络控制:精细的进出站网络策略

3.2 基础使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from e2b_code_interpreter import Sandbox

# 创建沙箱实例(默认 5 分钟超时)
sandbox = Sandbox()

# 在沙箱中执行代码
result = sandbox.run_code("""
import requests
import os

# 这个网络请求在隔离环境中执行
response = requests.get('https://api.example.com/data')
print(response.json())
""")

print(result.logs.stdout)

# 关闭沙箱(资源立即释放)
sandbox.kill()

3.3 E2B 的安全架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌────────────────────────────────────┐
│ 用户代码层 │
│ ┌──────────────────────────────┐ │
│ │ E2B SDK │ │
│ │ ┌────────────────────────┐ │ │
│ │ │ 轻量级 VM (Firecracker)│ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ 微型 Linux │ │ │ │
│ │ │ │ ┌────────────┐ │ │ │ │
│ │ │ │ │ 代码执行 │ │ │ │ │
│ │ │ │ │ 环境 │ │ │ │ │
│ │ │ │ └────────────┘ │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ └────────────────────────┘ │ │
│ └──────────────────────────────┘ │
└────────────────────────────────────┘

E2B 使用 AWS Firecracker 提供轻量级虚拟化,比 Docker 容器更隔离,比传统 VM 更轻量。

3.4 高级安全配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from e2b_code_interpreter import Sandbox

# 自定义超时和资源配置
sandbox = Sandbox(
timeout=300, # 5 分钟超时
envs={"OPENAI_API_KEY": "sk-xxx"}, # 注入环境变量
cwd="/home/user/project" # 设置工作目录
)

# 动态调整超时
sandbox.set_timeout(600) # 延长到 10 分钟

# 获取沙箱信息
info = sandbox.get_info()
print(f"沙箱 ID: {info.id}")
print(f"启动时间: {info.started_at}")
print(f"预计关闭: {info.end_at}")

3.5 文件系统操作

1
2
3
4
5
6
7
8
9
10
11
12
13
# 上传文件到沙箱
sandbox.files.write("/home/user/data.csv", file_content)

# 读取沙箱内文件
content = sandbox.files.read("/home/user/output.json")

# 列出目录
files = sandbox.files.list("/home/user")

# 监听文件变化
watch = sandbox.files.watch("/home/user/logs")
for change in watch:
print(f"文件变化: {change}")

四、Docker 容器化方案

4.1 自建沙箱的考量

对于需要完全控制或数据敏感的场景,自建 Docker 沙箱是可行方案。

优势:

  • 完全可控的基础设施
  • 无外部依赖
  • 可深度定制

劣势:

  • 需要维护基础设施
  • 启动速度较慢(秒级 vs 毫秒级)
  • 需要处理资源管理和调度

4.2 安全容器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Dockerfile.agent-sandbox
FROM python:3.11-slim

# 创建非 root 用户
RUN groupadd -r agent && useradd -r -g agent agent

# 安装必要的运行时
RUN pip install --no-cache-dir requests pandas numpy

# 设置工作目录
WORKDIR /workspace
RUN chown agent:agent /workspace

# 切换到非 root 用户
USER agent

# 预配置安全策略
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

CMD ["python"]

4.3 安全运行参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 运行安全沙箱容器
docker run -d \
--name agent-sandbox \
--read-only \ # 只读根文件系统
--tmpfs /tmp:noexec,nosuid,size=100m \ # 临时文件系统
--cap-drop ALL \ # 丢弃所有 capability
--cap-add SYS_PTRACE \ # 仅保留必要的 capability
--security-opt seccomp=agent-profile.json \ # seccomp 策略
--memory 512m \ # 内存限制
--memory-swap 512m \ # 禁用 swap
--cpus 1.0 \ # CPU 限制
--network none \ # 无网络(或自定义隔离网络)
--pids-limit 100 \ # 进程数限制
agent-sandbox:latest

4.4 seccomp 安全策略示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{
"names": [
"read", "write", "open", "close",
"stat", "fstat", "lstat", "mmap",
"mprotect", "munmap", "brk", "rt_sigaction"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["execve", "execveat"],
"action": "SCMP_ACT_ALLOW",
"args": []
}
]
}

此策略只允许基本的文件操作和进程执行,阻止危险的系统调用。


五、生产级实现:多层防御架构

5.1 架构概览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
┌─────────────────────────────────────────────────────────────┐
│ 用户请求层 │
│ (输入验证 & 意图识别) │
└───────────────────────┬─────────────────────────────────────┘

┌───────────────────────▼─────────────────────────────────────┐
│ Agent 决策层 │
│ (不确定性管理 + 置信度评估) │
│ [上午的主题] │
└───────────────────────┬─────────────────────────────────────┘
│ 低置信度 → 人工审核
│ 高置信度 → 继续执行
┌───────────────────────▼─────────────────────────────────────┐
│ 执行沙箱层 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 第一层:网络隔离 │ │
│ │ - 出站白名单 │ │
│ │ - DNS 过滤 │ │
│ └───────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌───────────────────▼─────────────────────────────────┐ │
│ │ 第二层:文件系统隔离 │ │
│ │ - 只读根目录 │ │
│ │ - tmpfs 临时空间 │ │
│ │ - 挂载点控制 │ │
│ └───────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌───────────────────▼─────────────────────────────────┐ │
│ │ 第三层:资源限制 │ │
│ │ - CPU / 内存 / 磁盘配额 │ │
│ │ - 进程数限制 │ │
│ │ - 执行超时控制 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

┌───────────────────────▼─────────────────────────────────────┐
│ 监控审计层 │
│ (日志记录 + 行为分析 + 异常检测) │
└─────────────────────────────────────────────────────────────┘

5.2 LangGraph 集成示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from langgraph.graph import StateGraph, END
from langgraph.types import Command
from typing import TypedDict, Annotated
from e2b_code_interpreter import Sandbox
import asyncio

class AgentState(TypedDict):
query: str
code: str
result: str
confidence: float
needs_review: bool

# 不确定性评估节点(复用上午的逻辑)
def assess_confidence(state: AgentState) -> AgentState:
"""评估执行置信度"""
# 基于代码复杂度、历史成功率等计算
confidence = calculate_confidence(state["code"])
state["confidence"] = confidence
state["needs_review"] = confidence < 0.8
return state

# 沙箱执行节点
def sandbox_execute(state: AgentState) -> AgentState:
"""在隔离沙箱中执行代码"""

# 创建沙箱(5分钟超时)
sandbox = Sandbox(timeout=300)

try:
# 执行代码
execution = sandbox.run_code(
state["code"],
timeout=60 # 单次执行最多60秒
)

# 收集结果
state["result"] = {
"stdout": execution.logs.stdout,
"stderr": execution.logs.stderr,
"exit_code": execution.error if execution.error else 0
}

except TimeoutError:
state["result"] = {"error": "Execution timeout"}
except Exception as e:
state["result"] = {"error": str(e)}
finally:
# 确保沙箱被清理
sandbox.kill()

return state

# 构建工作流
workflow = StateGraph(AgentState)

workflow.add_node("assess", assess_confidence)
workflow.add_node("review", human_review_node) # 人工审核
workflow.add_node("execute", sandbox_execute)

workflow.set_entry_point("assess")
workflow.add_conditional_edges(
"assess",
lambda s: "review" if s["needs_review"] else "execute"
)
workflow.add_edge("review", "execute")
workflow.add_edge("execute", END)

app = workflow.compile()

5.3 网络访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import httpx
from typing import List, Optional

class SecureHTTPClient:
"""带白名单控制的 HTTP 客户端"""

ALLOWED_DOMAINS = [
"api.openai.com",
"api.anthropic.com",
"api.github.com"
]

@classmethod
def is_allowed(cls, url: str) -> bool:
from urllib.parse import urlparse
domain = urlparse(url).netloc
return any(
domain == allowed or domain.endswith(f".{allowed}")
for allowed in cls.ALLOWED_DOMAINS
)

async def request(
self,
method: str,
url: str,
**kwargs
) -> httpx.Response:
if not self.is_allowed(url):
raise PermissionError(f"域名不在白名单中: {url}")

async with httpx.AsyncClient(timeout=30.0) as client:
return await client.request(method, url, **kwargs)

# 在沙箱中使用
client = SecureHTTPClient()

六、监控与审计

6.1 行为日志记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import json
import logging
from datetime import datetime
from dataclasses import dataclass

@dataclass
class SandboxExecutionLog:
timestamp: str
sandbox_id: str
code_hash: str
duration_ms: int
resource_usage: dict
network_requests: list
file_operations: list
exit_code: int

class SandboxAuditor:
def __init__(self, log_file: str = "sandbox_audit.log"):
self.logger = logging.getLogger("sandbox")
handler = logging.FileHandler(log_file)
handler.setFormatter(
logging.Formatter('%(message)s')
)
self.logger.addHandler(handler)
self.logger.setLevel(logging.INFO)

def log_execution(self, log_entry: SandboxExecutionLog):
"""记录执行日志"""
self.logger.info(json.dumps({
"type": "execution",
**log_entry.__dict__
}))

def log_anomaly(self, sandbox_id: str, reason: str):
"""记录异常行为"""
self.logger.warning(json.dumps({
"type": "anomaly",
"timestamp": datetime.utcnow().isoformat(),
"sandbox_id": sandbox_id,
"reason": reason
}))

6.2 异常行为检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class AnomalyDetector:
"""基于规则的异常检测"""

def __init__(self):
self.rules = [
self.check_resource_usage,
self.check_network_patterns,
self.check_file_access
]

def check_resource_usage(self, log: SandboxExecutionLog) -> Optional[str]:
"""检查资源使用异常"""
cpu_percent = log.resource_usage.get("cpu_percent", 0)
memory_mb = log.resource_usage.get("memory_mb", 0)

if cpu_percent > 95:
return f"CPU 使用率过高: {cpu_percent}%"
if memory_mb > 500:
return f"内存使用过高: {memory_mb}MB"
return None

def check_network_patterns(self, log: SandboxExecutionLog) -> Optional[str]:
"""检查网络行为异常"""
# 检测外连频率异常
if len(log.network_requests) > 100:
return f"网络请求过多: {len(log.network_requests)} 次"

# 检测可疑域名
suspicious_domains = ["pastebin.com", "transfer.sh"]
for req in log.network_requests:
if any(d in req.get("host", "") for d in suspicious_domains):
return f"访问可疑域名: {req['host']}"
return None

def analyze(self, log: SandboxExecutionLog) -> List[str]:
"""运行所有检测规则"""
anomalies = []
for rule in self.rules:
if result := rule(log):
anomalies.append(result)
return anomalies

七、最佳实践总结

7.1 安全策略 checklist

  • 最小权限原则:沙箱只拥有完成任务的最小权限
  • 超时控制:所有执行都有明确的超时限制
  • 资源配额:CPU、内存、磁盘、网络都有上限
  • 网络白名单:默认拒绝,显式允许
  • 只读根目录:防止系统文件被篡改
  • 非 root 运行:容器内进程以普通用户身份运行
  • 审计日志:所有操作都有完整记录
  • 快速销毁:执行完毕后立即清理沙箱

7.2 与不确定性管理的协同

安全沙箱与上午讨论的不确定性管理形成互补:

层面 不确定性管理 安全沙箱
关注点 决策质量 执行安全
机制 置信度评估、一致性检查 环境隔离、资源限制
失败处理 低置信度 → 人工审核 异常行为 → 强制终止
组合效果 先判断可信,再隔离执行

7.3 技术选型建议

场景 推荐方案 理由
快速原型 E2B 零运维、快速集成
企业部署 自建 Docker 完全可控、合规要求
高安全场景 Firecracker VM 更强的隔离级别
混合架构 E2B + 自建 弹性伸缩 + 敏感数据本地化

八、结语

安全沙箱不是 Agent 能力的束缚,而是让它能够放心发挥的保障。就像赛车需要安全带和护栏才能全速前进,Agent 需要沙箱才能在生产环境中可靠运行。

上午我们学会了如何让 Agent”聪明地决策”,下午我们学会了如何让 Agent”安全地执行”。两者结合,才能构建真正可信赖的 AI Agent 系统

记住:没有沙箱的 Agent 只是玩具,有了沙箱的 Agent 才能成为工具。


参考资源


本文是 LangGraph Agent 架构系列的一部分。上一篇:AI Agent 不确定性管理:构建可靠系统的核心能力