Skip to content

Agent Runtime & Sandbox —— 运行时与沙箱 / Runtime & Sandbox

本章观点

前几章讲的 Agent 都是"逻辑层"——LLM 决策、工具调用、context 管理。但 Agent 真正跑起来,还有一个物理层问题:

Agent 在哪里跑?怎么跑?失败怎么恢复?能不能闯祸?

这四个问题对应两块工程:

  • Agent Runtime(运行时):解决"在哪跑、怎么跑、失败怎么恢复"
  • Agent Sandbox(沙箱):解决"能不能闯祸、闯了多大祸能兜住"

这两块是 Agent 从 Demo 变成生产系统的关键分水岭。一个 Demo Agent 在你本地跑通了,搬到生产环境第一个崩的就是 runtime,第二个出事的就是 sandbox 没设。

这一章讲 Runtime 和 Sandbox 的设计原则、主流方案、踩坑经验。


9.1 Agent Runtime 是什么

Agent Runtime = Agent 执行任务的物理环境 + 生命周期管理。

具体包括:

能力解决什么例子
Execution EnvironmentAgent 在哪跑本地进程 / 容器 / VM / 云函数
ShellAgent 能执行什么命令bash / 受限 shell
FilesystemAgent 能读写哪工作目录 / 挂载卷
BrowserAgent 能上网吗Headless Chrome / Playwright
Queue任务怎么排队Redis / RabbitMQ
Cron定时任务怎么触发crontab / Cloudflare Cron
Retry失败怎么办指数退避 / 有限重试
Timeout卡住怎么办单步超时 / 总超时
Rollback改坏了怎么办git revert / 快照恢复
Checkpoint崩了能续吗序列化 state
Durable Execution长任务怎么不丢Temporal / Restate

没有 Runtime 的 Agent,本质就是个本地脚本——只能在你电脑上跑,崩了就崩了,跑长任务会断。有 Runtime 的 Agent 才是生产系统。


9.2 Execution Environment:Agent 在哪跑

四种典型环境

环境隔离性启动速度成本适合
本地进程即时0开发 / Demo
容器 (Docker)秒级单机部署
MicroVM (Firecracker)毫秒级多租户
云函数 (Lambda/Workers)秒级按调用事件驱动

Claude Code 的环境

Claude Code 在你本地终端跑,直接访问你的 shell 和文件系统。隔离性最低但灵活性最高——适合开发者自己用,不适合给多用户服务。

OpenHands 的环境

OpenHands 用 Docker 容器作为 runtime。每个任务起一个容器,容器里有:

  • 完整的 shell
  • Python / Node 环境
  • 工作目录挂载
  • 网络访问(可关)

任务结束容器销毁。这是"单机多租户"的标准方案。

E2B / Daytona 的环境

E2BDaytona 提供"沙箱即服务":你调 API 就能得到一个隔离的代码执行环境。适合不想自己管 Docker 的团队。

选型建议

  • 个人用 / 开发:本地进程(Claude Code 模式)
  • 单机生产:Docker 容器(OpenHands 模式)
  • 多租户 SaaS:MicroVM(Firecracker / gVisor)
  • 事件驱动 / 无状态:云函数(Lambda / Cloudflare Workers)
  • 不想管基建:E2B / Daytona

9.3 Shell 与 Filesystem:Agent 的手脚

Shell

Agent 调 Bash 工具时,本质是 runtime 起一个子进程执行命令。要决定:

决策选项建议
用什么 shellbash / sh / 受限 shell受限 shell(rbash)更安全
能跑什么命令白名单 / 黑名单 / 全放白名单 + 黑名单组合
命令时长单步超时30s 默认,可配
输出捕获stdout / stderr / 退出码三个都要
环境变量继承 / 隔离隔离,只显式注入

Filesystem

Agent 读写文件的范围要明确:

text
工作目录(Agent 可读写)
├── input/       ← 用户上传,只读
├── workspace/   ← Agent 工作区,可读写
├── output/      ← 结果输出,可写
└── system/      ← 系统文件,禁止访问

关键原则:Agent 的工作目录要和系统目录隔离。Claude Code 用项目根目录作为边界,OpenHands 用容器文件系统作为边界。

命令白名单示例

Claude Code 的 Permission 系统就是这么设计的:

json
{
  "permissions": {
    "Bash": {
      "allow": ["ls *", "cat *", "grep *", "git status", "git diff *"],
      "deny": ["rm -rf *", "sudo *", "curl * | sh", "git push --force *"]
    }
  }
}

白名单放行只读命令,黑名单拦截危险命令,灰色地带(如 git commit)询问用户。


9.4 Browser:Agent 上网

为什么 Agent 需要浏览器

很多任务离不开浏览器:

  • 看网页内容(搜索结果、文档)
  • 测试 Web 应用(E2E 测试)
  • 抓取动态渲染内容(SPA)
  • 自动化操作(填表、点击)

三种方案

方案实现适合
HTTP 抓取requests.get(url)静态页面
Headless ChromePlaywright / Puppeteer动态页面、SPA
Browser MCPmicrosoft/playwright-mcpAgent 友好的浏览器控制

Playwright MCP 的设计

Playwright MCP 把浏览器能力包装成 MCP tools:

  • browser_navigate(url) — 导航
  • browser_click(target) — 点击
  • browser_type(target, text) — 输入
  • browser_snapshot() — 获取页面快照
  • browser_evaluate(js) — 执行 JS

Agent 调这些工具就像调函数,不用解析 HTML。这是浏览器 Agent 的主流方案。

Browser Sandbox 注意点

  • 隔离用户会话:Agent 用的浏览器不能登录用户的账号(除非显式授权)
  • 限制域名:Agent 只能访问白名单域名
  • 下载隔离:下载文件放工作目录,不进系统下载文件夹
  • 网络监控:记录所有 HTTP 请求,便于审计

9.5 Queue & Cron:长任务和定时任务

Queue:任务排队

Agent 任务经常是长耗时(几分钟到几小时)。直接同步处理会超时,必须用队列:

text
用户提交任务 → 入队 (Redis / RabbitMQ / SQS)

            Worker 进程取出任务

            起 Agent runtime 执行

            结果写回数据库 + 通知用户

主流方案:

方案适合
Redis + BullMQ单机 / 小规模
RabbitMQ复杂路由
AWS SQS云原生
Cloudflare Queues边缘计算

Cron:定时触发

很多 Agent 是定时的(每周论文追踪、每日报告)。两种实现:

传统 cron

bash
# crontab
0 9 * * 1 python paper_tracker.py  # 每周一 9 点

云函数 cron

toml
# wrangler.toml (Cloudflare Workers)
[triggers]
crons = ["0 9 * * 1"]

云函数 cron 的好处是不用管服务器,坏处是执行时长受限(Lambda 15 分钟上限)。

长任务的工作流

text
Cron 触发
  → 入队
  → Worker 起容器
  → Agent 执行(可能跑几小时)
  → 写 Checkpoint(每 N 分钟)
  → 完成 → 通知用户
  → 失败 → 重试或人工介入

这套工作流是 OpenHands、Devin 这类产品的标准架构。


9.6 Retry / Timeout / Rollback:失败处理

Retry:重试

工具调用失败时重试。但要分情况:

失败类型是否重试重试策略
网络超时指数退避,最多 3 次
API 限流 (429)Retry-After header
临时错误 (5xx)指数退避
永久错误 (4xx)直接报错给 LLM
工具内部 bug报错给 LLM 让它换方法
python
import time
import random

def retry_with_backoff(fn, max_retries=3, base_delay=1.0):
    for attempt in range(max_retries):
        try:
            return fn()
        except TransientError as e:
            if attempt == max_retries - 1:
                raise
            delay = base_delay * (2 ** attempt) + random.random()
            time.sleep(delay)

不要无脑重试。重试 3 次还失败,就该把错误返回给 LLM 让它换方法,而不是继续磨。

Timeout:超时

两层超时:

python
# 单步超时:单个工具调用最多 30 秒
result = call_tool_with_timeout(tool, args, timeout=30)

# 总超时:整个 Agent 任务最多 10 分钟
agent_result = run_agent_with_timeout(task, total_timeout=600)

超时不是失败,是"该停了"。Agent 收到超时应该总结当前进度,而不是机械继续。

Rollback:回滚

Agent 改坏文件怎么办?三种回滚策略:

策略实现适合
Git revertgit checkout -- .代码项目
文件快照任务开始时复制工作目录临时文件操作
数据库事务BEGIN / ROLLBACKDB 操作

Claude Code 默认依赖 git,用户随时可以 git stashgit checkout 回退。OpenHands 在容器内做事,整个容器可以销毁重建——这是"硬回滚"。


9.7 Checkpoint & Durable Execution

Checkpoint:状态序列化

Checkpoint = 把 Agent 的 State(第 5 章 Context Engineering 讲过)序列化存盘。崩了能从 checkpoint 续跑,不用从头。

LangGraph 的 checkpoint 设计是范本:

python
from langgraph.checkpoint.memory import MemorySaver

workflow = StateGraph(State)
# ... add nodes / edges
app = workflow.compile(checkpointer=MemorySaver())

# 每次调用都会自动存 checkpoint
config = {"configurable": {"thread_id": "task-123"}}
result = app.invoke(initial_state, config=config)

# 崩了后续可以从 thread_id 恢复
resumed = app.invoke(None, config=config)  # 从最近 checkpoint 继续

Durable Execution:持久化执行

Durable Execution 比 Checkpoint 更进一步:整个执行流(包括等待外部事件)都持久化

典型场景:

  • Agent 发邮件后等用户回复(可能等几天)
  • Agent 提 PR 后等 CI 跑完(可能等几十分钟)
  • Agent 调外部 API,API 异步回调

传统做法是写轮询代码,丑且易错。Durable Execution 框架(TemporalRestateDBOS)让你写"看起来同步"的代码,框架自动处理持久化:

python
# Temporal 风格(伪代码)
@workflow
def agent_workflow(task):
    email_sent = send_email(task.user_email)
    # 这里 workflow 会"休眠",等用户回复
    # 即使 workflow 进程崩了,重启后能从这继续
    reply = wait_for_reply(email_sent.id, timeout=86400)
    
    if reply.approved:
        execute_task(task)
    else:
        notify_user(task, "rejected")

主流 Durable Execution 方案

方案特点适合
Temporal最成熟,支持复杂工作流企业级长流程
Restate轻量,Rust 写的中小项目
DBOS数据库原生数据密集型
InngestServerless 友好边缘计算
LangGraph Checkpoint简单 state 持久化Agent 状态机

Durable Execution 是长任务 Agent 的必选项。如果你的 Agent 任务可能跑超过 1 分钟,就要考虑 Durable Execution。


9.8 Agent Sandbox 是什么

Agent Sandbox = 限制 Agent 能力的隔离环境。

Sandbox 和 Runtime 的关系:

Runtime 解决"Agent 怎么跑",Sandbox 解决"Agent 不能干什么"。

Runtime 是"提供能力",Sandbox 是"限制能力"。两者通常在同一层实现(比如 Docker 容器既是 runtime 也是 sandbox)。


9.9 Sandbox 的十个维度

1. 文件权限

Agent 能读写哪些路径。

text
✅ 允许:/workspace/**
❌ 禁止:/etc/**, ~/.ssh/**, /root/**

实现:

  • Docker volume 挂载限制
  • Linux capabilities(CAP_DAC_OVERRIDE 去掉)
  • chroot / namespace

2. Shell 权限

Agent 能执行什么命令。第 9.3 节讲过:白名单 + 黑名单 + 询问。

3. 网络访问

Agent 能访问哪些域名 / 端口。

text
✅ 允许:api.anthropic.com, api.github.com
❌ 禁止:169.254.169.254 (云元数据), 内网 IP 段

实现:

  • DNS 黑洞
  • iptables 规则
  • Egress proxy

特别重要:云环境的元数据服务(如 AWS 169.254.169.254)能拿到 IAM 凭证。Agent 必须禁止访问。

4. 浏览器隔离

Agent 用的浏览器不能访问用户登录态。

python
# ✅ 用全新 profile
browser = playwright.chromium.launch_persistent_context(
    user_data_dir="/tmp/agent-profile",  # 独立 profile
    # 不复用用户 Chrome 的 cookies / 密码
)

5. 数据库权限

Agent 查数据库时,应该用只读账号

sql
-- 创建 Agent 专用只读账号
CREATE USER agent_ro WITH PASSWORD '...';
GRANT SELECT ON all_tables TO agent_ro;
-- 不给 INSERT / UPDATE / DELETE

写操作要明确白名单:哪些表能写,哪些字段不能动。

6. API Key / Secret 管理

Agent 不应该直接拿到原始 secret。两种方案:

方案 A:Secret 注入到环境变量

python
# Agent runtime 启动时注入
os.environ["ANTHROPIC_API_KEY"] = get_secret("anthropic")
# Agent 代码用 os.environ["ANTHROPIC_API_KEY"]

方案 B:通过 MCP server 代理

text
Agent → MCP server (持有 secret) → 外部 API

    Agent 拿不到原始 secret

方案 B 更安全,因为 Agent 代码永远看不到 secret。Claude Code 的 MCP server 就是这个模式——MCP server 持有 GitHub token,Agent 只能调 github.* 工具。

7. 危险操作人工确认

副作用类操作必须人工确认:

python
# Claude Code 的 Permission 系统
def execute_tool(tool_name, args):
    if tool_name in DANGEROUS_TOOLS:
        # 弹窗让用户确认
        approved = ask_user(f"执行 {tool_name}({args})?")
        if not approved:
            return {"error": "用户拒绝"}
    return run_tool(tool_name, args)

危险操作分级:

级别操作处理
安全read_file, ls, grep自动放行
中等write_file, git commit询问(默认允许)
危险rm -rf, git push --force, send_email必须显式确认
灾难drop table, sudo, `curlsh`

8. 日志记录

Agent 所有工具调用都要记录,便于审计:

json
{
  "timestamp": "2026-07-08T10:30:00Z",
  "agent_id": "agent-123",
  "tool": "Bash",
  "args": {"cmd": "rm /tmp/cache/*"},
  "approved_by": "user",
  "result": "success",
  "duration_ms": 120
}

日志要存到独立位置(Agent 不能改),保留期至少 90 天。

9. 超时控制

第 9.6 节讲过。Sandbox 层面还要加:

  • 单工具超时:30 秒
  • 单轮循环超时:5 分钟
  • 总任务超时:1 小时(可配)
  • 空闲超时:30 秒没动作自动停

10. 成本限制

Agent 烧钱无上限是个真问题。三层限制:

python
# Token 预算
MAX_TOKENS_PER_TASK = 1_000_000
MAX_TOKENS_PER_DAY = 10_000_000

# 调用次数
MAX_TOOL_CALLS_PER_TASK = 100
MAX_LLM_CALLS_PER_DAY = 1000

# 金钱
MAX_COST_PER_TASK = 5.0  # USD

超限要么停要么升级到人工确认。


9.10 Sandbox 实现方案对比

方案隔离强度启动速度复杂度适合
进程 + 权限检查即时个人开发
Docker 容器秒级单机生产
gVisor秒级多租户
Firecracker MicroVM极高毫秒级大规模 SaaS
WebAssembly (Wasm)中高毫秒级轻量沙箱
E2B / Daytona秒级不想自建

OpenHands 的沙箱设计

OpenHands 用 Docker 容器作为沙箱,关键设计:

  1. 每个任务一个容器:任务结束容器销毁
  2. 非 root 用户:容器内用 openhands 用户,不是 root
  3. 网络白名单:默认只放行几个 API 域名
  4. 资源限制:CPU / 内存 / 磁盘都有 cgroup 限制
  5. 挂载隔离:只挂载工作目录,不挂载宿主系统

这是单机生产级沙箱的范本。

Claude Code 的沙箱策略

Claude Code 不用容器(它在你的本地终端跑),但用权限系统作为软沙箱:

  • Read / Glob / Grep 自动放行
  • Edit / Write 在项目目录内放行
  • Bash 按白名单 + 询问
  • 危险操作(rm -rfgit push --force)必须确认

这种"软沙箱"适合开发者自己用(你信任自己),不适合给多用户服务。


9.11 一个完整的 Runtime + Sandbox 架构

把前面所有概念串起来,一个生产级 Agent 系统的架构:

text
┌──────────────────────────────────────────┐
│ User / API Client                        │
└──────────────┬───────────────────────────┘

┌──────────────────────────────────────────┐
│ API Gateway                              │
│ - 鉴权                                    │
│ - 限流                                    │
└──────────────┬───────────────────────────┘

┌──────────────────────────────────────────┐
│ Task Queue (Redis / SQS)                 │
│ - 任务排队                                │
│ - 优先级                                  │
└──────────────┬───────────────────────────┘

┌──────────────────────────────────────────┐
│ Worker Pool                              │
│ - 取任务                                  │
│ - 起 Sandbox                             │
│ - 监控执行                                │
└──────────────┬───────────────────────────┘

┌──────────────────────────────────────────┐
│ Sandbox (Docker / MicroVM)               │
│ ┌─────────────────────────────────────┐  │
│ │ Agent Runtime                       │  │
│ │ - LLM 调用 (Anthropic API)          │  │
│ │ - Tool 执行 (Bash / Read / Write)   │  │
│ │ - MCP server 连接                   │  │
│ │ - Context 管理                      │  │
│ │ - Checkpoint 存储                   │  │
│ └─────────────────────────────────────┘  │
│ 隔离层:                                   │
│ - 文件权限                                │
│ - Shell 白名单                            │
│ - 网络白名单                              │
│ - 资源限制 (CPU/MEM/DISK)                │
└──────────────┬───────────────────────────┘

┌──────────────────────────────────────────┐
│ Persistence Layer                        │
│ - PostgreSQL: 任务状态 / 历史              │
│ - S3 / B2: 文件 / Checkpoint             │
│ - Langfuse: Trace / Logs                 │
└──────────────────────────────────────────┘

这套架构是 Devin、OpenHands、Manus 这类产品的共同形态。具体实现各有差别,但骨架一样。


9.12 前人智慧 / Prior Art

OpenHands 的 Runtime 哲学

OpenHands 文档 里有一段关键说明:

"Each runtime is a sandboxed environment where the agent can execute code, browse the web, and manipulate files without affecting the host system."

翻译:每个 runtime 都是一个沙箱环境,Agent 可以执行代码、浏览网页、操作文件,不影响宿主系统。

OpenHands 的设计哲学:Runtime 即 Sandbox。两者不分层,直接用容器作为统一边界。好处是简单清晰,坏处是粒度粗(要么全开要么全关)。新一代系统(如 E2B)在探索更细粒度的沙箱——文件级、命令级、网络级的独立控制。

Temporal 的 Durable Execution 思想

Temporal 是 Durable Execution 的标杆。它的核心思想:

"Workflow code is deterministic. External interactions are through activities."

翻译:Workflow 代码是确定性的,外部交互通过 activity 进行。

这条原则解决了 Agent 长任务的几个根本问题:

  1. 确定性重放:workflow 崩了重启,能从最近 checkpoint 重放,结果一致
  2. 外部交互隔离:所有副作用(API 调用、DB 写)都在 activity 里,可追踪可回滚
  3. 定时器持久化workflow.sleep(86400) 能跨进程重启

这套思想迁移到 Agent 工程:Agent 的核心 loop 应该是确定性的(给定 messages 一定走同一路径),所有副作用都在工具调用里

Claude Code 的 Permission 分级

Claude Code 的 Permission 系统是软沙箱的范本。它的设计:

  1. 默认拒绝:未知操作默认询问
  2. 分级放行:读类自动,写类询问,删除类必须确认
  3. 可配置settings.json 里能改规则
  4. 可绕过(用户自己责任)--dangerously-skip-permissions 标志

这套设计的核心是把控制权交给用户。开发者自己用 Claude Code,可以接受"软沙箱 + 询问",因为开发者能判断每次询问。给多用户服务时,必须升级到容器沙箱。

Browser-use 的浏览器沙箱

Browser-use 是 LLM 友好的浏览器库。它的沙箱设计:

  1. 独立 profile:每个 Agent session 用独立浏览器 profile
  2. 域名白名单:默认只能访问白名单域名
  3. 下载隔离:下载文件到工作目录
  4. 截图审计:每步操作都截图存档

这套设计是 Browser Agent 的范本。Playwright MCP 在这基础上加了 MCP 协议层,让 Agent 调用更自然。

E2B 的"沙箱即服务"

E2B 把沙箱做成 API:

python
from e2b import Sandbox

sandbox = Sandbox()
sandbox.filesystem.write("/hello.py", "print('hello')")
sandbox.process.start("python /hello.py")
sandbox.close()

一行代码起一个隔离的代码执行环境。这种"沙箱即服务"让 Agent 开发者不用自己管 Docker / MicroVM,专注业务逻辑。是未来 Agent infra 的重要方向。

AWS Lambda 的隔离启示

AWS Lambda 用 Firecracker MicroVM 做隔离。每个 Lambda 函数跑在一个 MicroVM 里,启动时间 125ms,隔离强度接近硬件虚拟化。

这给 Agent sandbox 一个启示:MicroVM 是"容器速度 + VM 隔离"的折中。大规模 Agent SaaS(如 Devin)正在往 MicroVM 方向迁移。

Linux Capabilities 的细粒度权限

Linux capabilities 把 root 权限拆成几十个细粒度能力。Docker 默认丢掉大部分危险 capabilities(如 CAP_SYS_ADMIN)。

Agent runtime 用容器时,可以进一步收紧:

bash
docker run --cap-drop ALL \
  --cap-add CAP_NET_BIND_SERVICE \
  ...

把所有 capabilities 都丢掉,只加需要的。这是"最小权限原则"在容器层面的实现。


9.13 常见误区

误区 1:本地跑通就能上线。 错。本地没考虑:任务排队、崩溃恢复、成本限制、多租户隔离、审计日志。生产环境第一个崩的就是这些。

误区 2:Docker 等于安全沙箱。 不完全。Docker 默认的隔离强度不够,root 用户能逃逸。要加 --cap-drop ALL--user 非 root、--network 受限。

误区 3:Retry 总是好的。 错。重试只对临时错误有效。永久错误重试无用,工具 bug 重试会放大伤害。要分情况。

误区 4:Checkpoint 浪费性能。 错。Checkpoint 的开销远小于"长任务崩了从头跑"的开销。任何超过 1 分钟的任务都应该有 checkpoint。

误区 5:Sandbox 越严越好。 错。过度严格的 sandbox 会让 Agent 啥也干不了。Sandbox 要根据任务调整——查论文的 Agent 不需要写文件权限,但写代码的 Agent 需要。


9.14 课后练习

  1. 给你正在做的 Agent 设计一个 Runtime:选哪种 Execution Environment?要不要 Queue?要不要 Cron?写出理由。
  2. 设计一个 Sandbox 配置:列出 Agent 能访问的文件路径、能执行的命令白名单、能访问的域名白名单。
  3. 你的 Agent 任务平均跑 5 分钟,崩溃率 5%。设计一套 Checkpoint + Retry 策略,让任务成功率 ≥ 99%。
  4. 对比 Temporal 和 LangGraph Checkpoint:什么场景该用哪个?
  5. 解释为什么"Agent 直接拿原始 API key"是反模式。给出两种替代方案。

9.15 小结

  • Agent Runtime 解决"在哪跑、怎么跑、失败怎么恢复":Execution Environment / Shell / Filesystem / Browser / Queue / Cron / Retry / Timeout / Rollback / Checkpoint / Durable Execution。
  • Agent Sandbox 解决"能不能闯祸、闯了多大祸能兜住":文件 / Shell / 网络 / 浏览器 / DB / Secret / 危险操作确认 / 日志 / 超时 / 成本 十个维度。
  • Runtime 和 Sandbox 通常在同一层实现:Docker 容器既是 runtime 也是 sandbox。
  • Durable Execution 是长任务 Agent 的必选项:Temporal / Restate / LangGraph Checkpoint。
  • Permission 分级 是软沙箱的核心:读类放行、写类询问、删除类必须确认、灾难类禁止。
  • 核心原则:最小权限 + 可审计 + 可回滚 + 可恢复。

下一章我们详细讲 MCP——怎么让 Agent 的工具连接标准化,写一次到处用。

基于 CC BY-SA 4.0 发布