Skip to content

sandbox

2 posts with the tag “sandbox”

DeerFlow 沙箱执行环境实现分析

DeerFlow的沙箱系统采用抽象接口+多实现的设计模式,提供两种执行模式:本地文件系统模式和Docker容器隔离模式,通过统一的接口对外提供服务。

Sandbox (接口) → 定义沙箱基本操作:execute_command、read_file、write_file、list_dir、update_file
SandboxProvider (接口) → 定义沙箱生命周期管理:acquire(获取)、get(查询)、release(释放)
  • 适用场景:开发环境、低安全要求场景
  • 实现原理
    • 单例模式,所有线程共享同一个沙箱实例
    • 通过路径映射机制实现逻辑隔离:
      • 虚拟路径:Agent 看到的统一路径 /mnt/user-data/{workspace,uploads,outputs}/mnt/skills
      • 实际路径:物理上存储在 backend/.deer-flow/threads/{thread_id}/user-data/... 和项目根目录的 skills/
    • 命令执行时自动转换路径:
      • 输入:将命令中的虚拟路径替换为实际本地路径,优先匹配最长前缀避免歧义
      • 输出:将执行结果中的本地路径替换回虚拟路径,对Agent完全透明
    • Shell自动检测:优先使用zsh,其次bash,最后sh,保证跨平台兼容性
  • 特点:轻量、无额外依赖、启动快,但隔离性较弱
  • 适用场景:生产环境、高安全要求场景
  • 核心特性
    • 两种后端
      • 本地Docker后端:直接管理本地Docker容器生命周期
      • 远程K8s后端:通过Provisioner服务动态创建K8s Pod作为沙箱
    • 暖池机制:释放的沙箱容器不会立即销毁,放入暖池等待复用,避免冷启动开销
    • 空闲超时管理:默认10分钟无活动自动销毁容器,节省资源
    • 并发控制:默认最大3个并发沙箱容器,超过时采用LRU策略销毁最旧的暖池容器;并发限制为软限制,活跃沙箱不会被强制销毁
    • 跨进程一致性:通过线程ID生成确定性沙箱ID,支持多进程/多Pod环境下的沙箱发现
    • 跨进程锁机制:通过文件锁防止多个进程同时为同一个线程创建沙箱导致的冲突
  • 安全特性
    • 每个线程独立容器,完全隔离,进程、文件系统、网络都互相隔离
    • 工作目录、上传目录、输出目录独立挂载,每个线程只能访问自己的数据
    • 技能目录只读挂载,防止Agent修改系统技能
    • ACP工作区只读挂载,仅允许Agent读取ACP代理的执行结果
    • 支持自定义环境变量和额外挂载配置

沙箱的生命周期由SandboxMiddleware中间件统一管理:

  1. 会话开始时调用provider.acquire(thread_id)获取沙箱
  2. 会话过程中通过provider.get(sandbox_id)获取沙箱实例执行操作
  3. 会话结束时调用provider.release(sandbox_id)释放沙箱(Docker模式下放入暖池)

沙箱功能被封装为统一的工具集提供给Agent使用:

  • bash:执行命令,自动处理路径转换和错误
  • ls:目录列表,返回tree格式
  • read_file:读取文件内容,支持行范围
  • write_file:写入/追加文件,自动创建目录
  • str_replace:字符串替换,支持批量替换

config.yaml中通过sandbox.use字段指定沙箱实现:

# 本地模式
sandbox:
use: deerflow.sandbox.local.local_sandbox_provider:LocalSandboxProvider
# Docker模式
sandbox:
use: deerflow.community.aio_sandbox:AioSandboxProvider
image: enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest
idle_timeout: 600
replicas: 3

这种设计使得沙箱实现可插拔,未来可以轻松扩展支持其他隔离技术(如Kata Containers、WASM等)而不影响上层业务逻辑。

DeerFlow Backend Sandbox 执行环境分析

本文档分析了在 deer-flow 后端体系中,代码沙箱执行环境(Sandbox)的架构设计与实现原理。沙箱主要用于让 AI 或用户安全、隔离地执行代码块(Shell 命令、读写文件等)。

后端的沙箱系统被设计为一个标准的面向对象接口体系,具有极高的解耦和可替换性:

定义在 packages/harness/deerflow/sandbox/sandbox.py。 它是一个标准的接口层,规定了所有沙箱必须提供以下能力:

  • execute_command(command):执行终端命令。
  • read_file(path) / write_file(path, content) / update_file:文本与二进制文件的 I/O 操作。
  • list_dir(path, max_depth):目录遍历。

1.2 SandboxProvider (生命周期、路由与缓存引擎)

Section titled “1.2 SandboxProvider (生命周期、路由与缓存引擎)”

定义在 sandbox_provider.py,但在 aio_sandbox_provider.py 中有极其高级的实现。 它是单例管理器,负责沙箱生命周期的调度:

  • acquire:获取或新建一个特定线程的沙箱空间。在 AIO Provider 中,它实现了一个极致的三层缓存架构
    • 第一层 (In-process cache):极速复用当前进程已挂载的活跃沙箱。
    • 第二层 (Warm pool):将释放的沙箱置于“保暖池”(容器不销毁,只解除绑定),以便同一个 Thread 再次申请时“零冷启动”秒速复用。
    • 第三层 (Cross-process Discovery):通过确定性哈希 ID(Deterministic ID)和跨进程文件锁(File Lock),让不同进程甚至不同 Pod 能够发现彼此启动的共享底层沙箱,解决多进程冲突。
  • get:根据 ID 提取已挂载的沙箱实例并在后台自动续签闲置过期时间 (Idle Timeout)。
  • release:将闲置沙箱退回到保暖池,只有超过系统限定副本数(Replicas)或超时时才会真正执行销毁任务。

2. 具体实现机制 (Concrete Implementations)

Section titled “2. 具体实现机制 (Concrete Implementations)”

目前系统中存在两种截然不同的沙箱实现策略:

2.1 本地宿主机伪沙箱 (LocalSandbox)

Section titled “2.1 本地宿主机伪沙箱 (LocalSandbox)”

定义在 sandbox/local/local_sandbox.py。 这是一种非容器化的轻量级实现形式,直接在后端进程宿主机通过原生进程执行命令。

  • 执行原理:底层使用了 Python 原生的 subprocess.run(command, shell=True) 来直接执行 Bash/Zsh 脚本,这代表着它并没有真正的物理隔离,执行的是宿主机的直接命令。
  • 智能路径映射机制(Path Mapping):为了让从外部或者习惯了容器化路径的工具能够平滑运行,它包含了一个精妙的正则拦截系统。
    • 它在内存里维护了一套映射表(如把虚拟容器路径 /mnt/skills 映射到真实的宿主物理路径 /Users/...)。
    • 在把用户的命令喂给 subprocess.run 执行之前,它会先通过正则,把命令里出现的虚拟路径全部**替换(Resolve)**成真实的绝对路径。
    • 当真实命令吐出 Output 日志时,它又会把这些真实的、可能泄漏隐私的绝对物理路径,全部**反向替换(Reverse Resolve)**回虚拟容器路径。实现了对大模型视角下的“路径伪装隔离”。

2.2 容器化云原生沙箱 (AioSandboxSandboxBackend)

Section titled “2.2 容器化云原生沙箱 (AioSandbox 与 SandboxBackend)”

定义在社区模块 community/aio_sandbox/ 下。这是一套真正的物理级别的容器化隔离方案,被拆分为执行者(AioSandbox)和驱动引擎(SandboxBackend)两个维度:

  • 执行器 (AioSandbox) 通信隔离:所有的 execute_command 并不是本地 Subprocess,而是被转化为长超时的 HTTP REST API,发送给内部运行了 agent-infra/sandbox 服务的 Docker 容器。
  • 底层驱动引擎 (SandboxBackend):负责产生并调度这些隔离容器。它支持两种模式:
    • RemoteSandboxBackend:连接远程 K8s / K3s 的 Provisioner,动态生成远端 Pod。
    • LocalContainerBackend:直接按需在物理机上启动新的 Docker 容器,并分配闲置端口。特别地,在 macOS 系统下,它会优先检测并采用苹果官方原生支持的轻量级虚拟化方案 (Apple Container),仅在找不到时降级回退给 Docker。此设计极大优化了 Mac 上的 I/O 与执行速度。
    • 请求将指令发往由 agent-infra/sandbox 提供的一个运行在大后方的 Docker 容器内部服务(默认为 HTTP API 监听)。
    • 由该远程 Docker 容器内部安全地执行这些指令代码,并将结果 JSON(通常包含 result.data.output)返回给后端的 AioSandbox 包装实例。

通过这套抽象实现,当 AI 大模型提出如 在命令行执行 python script.py 的需求时:

  • 系统不需重构大模型逻辑,即可通过配置自由决定是采用零成本的本地直连替换(LocalSandbox),还是高规格的容器 HTTP 远程直连(AioSandbox)。
  • 两套底层驱动均共享同一个上层执行接口,达到了业务代码的高度复用。