Agent 与沙箱:架构与关系解析

Agent 与沙箱:架构与关系解析

采集环境:主机 2663239c2aaa · Ubuntu 24.04.3 LTS · 内核 6.6.117-45.11.6.tl4.x86_64

采集时间:2026-10-15 10:18 CST

证据来源:/proc/<pid>/environ、/proc/<pid>/ns、supervisord 配置、workspace-init 启动脚本、ss -tnp socket 连接、cgroup 文件、mount 表


0. 一句话结论

Agent 不是沙箱之外的东西,而是沙箱内部一个由 sandbox-proxy 派生出来的普通进程。

控制面不直接连接 Agent:它先把配置注入 envd,再由沙箱内的 sandbox-proxy 拉起 Agent 进程;Agent 通过沙箱共享的文件系统、网络和命令执行能力完成工作,出网统一经由宿主网关 172.24.0.1:80。

换句话说:沙箱是"机器",Agent 是跑在这台机器里的"操作员"------两者不是并列关系,而是宿主与租户、容器与进程的关系。


1. 环境身份:这是一个什么沙箱

从 Agent 进程(PID 1416)和 sandbox-proxy(PID 125)的环境变量中可以看到完整身份:

类别 关键变量 值
平台 X_IDE_IS_CLOUDSTUDIO / X_IDE_SPACE_HOST TRUE / sandbox.cloudstudio.club
编排 WORKSPACE_NAMESPACE cs-spacelet-v2-headless.coding-workspace.svc.cluster.local.
区域 X_IDE_SPACE_REGION sh4(上海)
环境 IDE_ENV prod
运行时镜像 IDE_USER_RUNTIME_IMAGE cloudstudio-sh.tencentcloudcr.com/saas.container/full-1.0.0:202505140048
宿主名 HOSTNAME VM-32-6-tencentos
IDE IDE_EDITOR_TYPE VSCODE
工作区 IDE_WORK_DIR /workspace
基础目录 IDE_EDITOR_SERVER_DIR /.PlnPyKFp4CRfFtgC1

结论:这是腾讯云 CloudStudio 的 spacelet 沙箱 ,运行在一个名为 cs-spacelet-v2-headless 的 K8s 工作负载中(命名空间 coding-workspace),采用 headless(无界面)形态,区域 sh4。

资源配额(envd 下发与 cgroup 实测一致)

项目 envd 声明 cgroup 实测 一致性
内存 X_IDE_MEMORY_LIMIT=8G memory.max = 8589934592(8 GiB) ✅ 一致
CPU X_IDE_CPU_LIMIT=4 cpu.max = 400000/100000(4 核) ✅ 一致
磁盘 X_IDE_DISK_QUOTA=250G 根 overlay 256 G ✅ 基本一致
PID 上限 --- pids.max = 151533 ---

注意:在容器内直接执行 free 会看到宿主的 123 GiB 内存(未挂载 lxcfs),判断配额必须读 cgroup。这一点在排障时极易误判。


2. 三层结构总览

复制代码
┌─ 外部(控制面与网关)────────────────────────────────────┐
│  CloudStudio 控制面(SaaS)        宿主网关 172.24.0.1    │
│  沙箱调度 · 配置下发 · 会话路由     出网 :80 · 日志 :18100 │
└───────────────────────┬──────────────────────────────────┘
                        │ 配置下发(HTTP)
┌─ 沙箱边界(K8s Pod / 容器)───────────────────────────────┐
│                                                          │
│  【沙箱基础服务层】由 supervisord (PID 61) 守护            │
│   envd:49983  sync_server  editor_sdk  node api          │
│   terminal:65310  log-collector  dockerd/containerd      │
│                                                          │
│  【Agent 子系统(cbc.d)】                                │
│   sandbox-proxy:9090/65225  ← Agent 启动器 + 出网代理      │
│     └─ codebuddy ×3 (prewarm 池)                          │
│          └─ mcp-stdio / MCP 服务 / zsh 命令 shell         │
│                                                          │
│  【共享资源】/workspace · cgroup 8GiB/4vCPU · cwd 共享     │
└──────────────────────────────────────────────────────────┘

关键点:服务层与 Agent 层位于同一个容器内,共享 PID / 网络 / 挂载命名空间,之间没有硬隔离。


3. 沙箱侧:基础服务与守护体系

3.1 守护骨架

复制代码
PID 1   docker-init(容器入口,执行 exec supervisord)
 └ PID 61  supervisord -c supervisord-conf/supervisord.conf
     ├ [program:proxy]      → sandbox-proxy  ← 经由 start-cbc.d 拉起
     ├ [program:e2b-envd]   → envd
     ├ [program:api]        → node api
     ├ [program:terminal]   → terminal/app, local-proxy
     ├ [program:docker]     → dockerd → containerd
     └ 其他一次性/常驻:init-workspace、sandbox-log-collector、editor_sdk、sync_server

supervisord 配置中 autostart=true / autorestart=true,意味着这些服务被杀死后会自动重启------这是沙箱自愈能力的来源。

3.2 服务清单与端口

服务 PID 监听 职责
envd 1284 *:49983 配置权威源 + 控制面入口 (/envs 接口)
sync_server 42496 127.0.0.1:8089、*:18601 工作区文件双向同步(本地 ↔ 沙箱)
editor_sdk 42465 127.0.0.1:39099 编辑器 SDK(仅回环,--cors_origin=*)
node api 107 *:65213 沙箱 API 服务
terminal / app 95 *:65310 终端服务
local-proxy 93 *:65216 本地代理
sandbox-proxy 125 *:9090、*:65225 出网代理 + Agent 启动器
sandbox-log-collector 118 出向 :18100 日志汇聚转发
dockerd / containerd 29424 / 29488 unix socket 容器引擎(供沙箱内嵌套构建)

3.3 envd ------ 最容易被忽略但最关键的一环

envd 不只是"环境变量服务",它是沙箱与控制面之间的唯一配置通道:

  1. 对外做配置分发 :GET http://localhost:49983/envs 返回所有环境变量,启动脚本靠它决定功能开关。
  2. 对内接收控制面连接 :ss 显示 envd 的 49983 端口上有来自 172.24.0.1(宿主侧) 的已建立连接(fd 6、fd 9),说明控制面是主动连进沙箱的。
bash 复制代码
# start-cbc.d 和 agent-init 的标准模式
ENVD_URL="http://localhost:49983/envs"
ENVD_RESPONSE=$(curl -s "${ENVD_URL}")
ENABLE_CBC_D_VALUE=$(echo "$ENVD_RESPONSE" | grep -o '"ENABLE_CBC_D":"[^"]*"' | cut -d'"' -f4)
if [ "$ENABLE_CBC_D_VALUE" != "true" ]; then exit 0; fi

设计含义 :功能开关(ENABLE_CBC_D、ENABLE_AGENT_INIT)不在镜像里写死,而是由控制面通过 envd 动态下发。这使得平台可以在不改镜像的前提下灰度开关 Agent 能力。


4. Agent 侧:形态、位置与派生关系

4.1 Agent 是谁

复制代码
node /root/.nvm/versions/node/v22.13.1/lib/node_modules/@tencent-ai/codebuddy-code/bin/codebuddy --prewarm --prewarm-id sbx-125-N
  • 安装包:@tencent-ai/codebuddy-code
  • 运行时:Node.js v22.13.1(nvm 管理)
  • 当前沙箱内共 3 个实例,构成预热池:
PID 启动时间 RSS 参数 角色
1416 10:04:43 ~335 MB --prewarm-id sbx-125-1 当前活跃会话
1417 10:04:43 162 MB --prewarm-id sbx-125-2 预热待命
42241 10:18:03 162 MB --prewarm-id sbx-125-3 预热待命

三者的父进程都是 sandbox-proxy(PID 125)------这是判定"Agent 属于沙箱"最直接的证据。

4.2 Agent 的两种形态

形态 启动方式 监听 用途
prewarm 模式 sandbox-proxy 派生 无对外端口 预热待命,收到会话请求即接管(当前形态)
serve 模式 start-codebuddy-serve 脚本 0.0.0.0:65222 作为常驻 HTTP 服务运行

start-codebuddy-serve 脚本内容:

bash 复制代码
cd "/workspace"
# 使用 setsid 完全脱离控制终端,避免 codebuddy 因 tty 问题被挂起
exec setsid codebuddy --serve --host 0.0.0.0 --port 65222 </dev/null

4.3 Agent 调用外部能力的方式

在 /proc/1416/fd 与进程树中可以看到 Agent 的两类子进程:

子进程 PID 作用
sandbox-proxy mcp-stdio 42268 MCP 协议的 stdio 传输通道
node .../workbuddy-sheetagent/mcp/start.mjs 42512 表格处理 MCP 服务
zsh 53271+ 由 Agent 派发的命令执行 shell(临时生命周期)

MCP 插件清单(/root/.codebuddy/.mcp.json)包含:agent-browser、docx、pdf、pptx、xlsx、playwright-cli、workbuddy-docx、workbuddy-pptx、workbuddy-sheetagent、hot-skills。

重要机制 :Agent 的"工具执行"在本机表现为 fork 子 shell / 起 MCP 子进程。每一次工具调用都是沙箱内的一次真实进程创建,而不是远程 RPC。


5. 两者的接口契约:四条通道

Agent 与沙箱/控制面之间只有四条交互通道,边界非常清晰:

# 通道 方向 载体 证据
① 配置下发 控制面 → 沙箱 envd:49983 的 /envs 接口 启动脚本 curl localhost:49983/envs;envd 上有来自 172.24.0.1 的入站连接
② 进程派生 沙箱 → Agent supervisord → start-cbc.d → sandbox-proxy → codebuddy ps --ppid 125 显示 3 个 codebuddy
③ 出网访问 Agent/沙箱 → 外网 宿主网关 172.24.0.1:80 1416 的 fd 20/22、125 的多个 fd 均指向 172.24.0.1:80
④ 日志上报 沙箱 → 宿主 log-forward-tee → nc → 172.24.0.1:18100 SANDBOX_LOG_FORWARD=1;多个 nc 172.24.0.1 18100 进程

注意 ③ 的细节 :Agent 进程 自身也直连 172.24.0.1:80,并非所有流量都绕经 sandbox-proxy。说明 sandbox-proxy 的"代理"职责与"启动器"职责是两件独立的事。


6. 完整启动链路(从容器到 Agent)

复制代码
1. 容器启动
   PID 1  docker-init -- bash -c "...exec supervisord"

2. 进程管理
   PID 61 supervisord(按 supervisord.conf 拉起各 program)

3. 读取平台配置
   start-cbc.d → curl http://localhost:49983/envs(envd)
               → 解析 ENABLE_CBC_D,为 true 才继续

4. 获取 Agent 运行时(box)
   优先:/root/proxy/box-current(sandbox-componentd 版本管理软链)
   兜底:curl https://cs-res-1324618448.cos.ap-shanghai.myqcloud.com/sandbox/tools/box.tar.gz
        → 解压到 /root/proxy/box

5. 识别产物形态并运行
   Go 形态:   /root/proxy/box/sandbox-proxy       → exec 前台运行
   Node 形态: ecosystem.config.js → pm2 start     → pm2 logs 保持前台

6. Agent 派生
   sandbox-proxy(125) → codebuddy --prewarm --prewarm-id sbx-125-{1,2,3}

7. 会话接入
   当前会话落在 codebuddy 1416;工具调用 → fork zsh / 启 MCP 子进程

sandbox-proxy 二进制实测为 46 MB 的 Go 静态链接 ELF (statically linked、带 debug_info),说明它是外部分发、独立升级的组件------这正好解释了 start-cbc.d 中"从 COS 下载 + 支持 box-current 软链切换版本"的设计。

启动脚本的一个工程细节

agent-init 脚本中有一段明确注释,记录了一个已修复的线上问题,很能反映这套架构的可靠性设计:

历史上这里有 download-if-absent 兜底(文件不存在就从固定 COS URL 拉取)。该兜底有两个问题已移除:

  1. COS 那份长期未更新(停在 2026-02 旧版),一旦触发就把系统盘自带的新版覆盖成旧版(生产实测 sh3:base 盘 1.0.22 被覆盖成 1.0.10);
  2. 触发条件是竞态------warm 空壳的系统盘是 base 盘的 COW fork,脚本在 NBD 盘就绪前的窗口内 [ -f ] 判为不存在 → 误触下载。

含义 :沙箱使用 warm 池 + NBD 远程克隆盘做秒级启动,因此启动早期存在"文件尚未可见"的时间窗,任何"文件缺失即下载"的逻辑都会误判。


7. 边界与共享:Agent 到底能看到什么

7.1 命名空间:无隔离

/proc/1416/ns 显示 Agent 与沙箱服务处于同一组命名空间:

复制代码
net:[4026534002]   pid:[4026534001]   mnt:[4026533985]   ipc:[4026534000]

因此:

  • Agent 能直接访问 127.0.0.1:49983(envd)、127.0.0.1:8089(sync_server)、127.0.0.1:39099(editor_sdk)
  • Agent 与沙箱服务共享同一张网络栈、同一个 PID 空间、同一套挂载

7.2 文件系统:完全共享

观察项 值
/proc/1416/cwd /workspace
/proc/1416/root /
根挂载 overlay,upperdir=/mnt/user-data/46e3d0d99e364ee9b2f628933b04a755/diff
warm 池来源 /var/run/cloudstudio/warm-b492449d-8153-4eb5-8096-dcd22815b96d...
嵌套容器盘 /dev/nbd503(btrfs)→ /var/lib/docker、/var/lib/buildkit

Agent 对沙箱文件系统的读写就是直接的磁盘读写 ,/workspace 同时被 sync_server 同步到用户本地。

7.3 存储分层

复制代码
NBD 远程盘(/dev/vdb、/dev/nbd503,btrfs)
   └─ overlay upperdir(/mnt/user-data/<uuid>/diff)
        └─ 容器根 /(rw) → /workspace(用户工作区)

启动方式为 NBD 克隆盘 + overlay,配合 warm 池实现"空壳预热、按需接管"。


8. 通信关系总表

源 目标 协议/端口 用途
控制面 envd 172.24.0.1 → 172.24.0.11:49983 配置/指令下发
codebuddy 1416 宿主网关 172.24.0.11 → 172.24.0.1:80 Agent 出网(云端模型调用等)
sandbox-proxy 125 宿主网关 → 172.24.0.1:80(多条) 代理出网
sandbox-proxy 125 外网 → 43.141.132.124:443 HTTPS 出网
sandbox-proxy 125 envd 127.0.0.1:58324 → :49983 本地配置读取
sandbox-proxy 125 sync_server 127.0.0.1:54252 → :8089 同步服务调用
codebuddy 1416 自身 127.0.0.1:35729 内部 RPC / 扩展通道
nc / log-collector 宿主 → 172.24.0.1:18100 日志转发
sync_server 宿主 → 172.24.0.1:80 同步数据回传

9. 关键洞察与排障提示

洞察 1:职责分离做得比想象中干净

  • envd 管配置 ,sandbox-proxy 管进程与出网 ,sync_server 管文件 ,log-collector 管日志
  • 控制面从不直连 Agent 进程,只通过 envd 注入配置,Agent 由沙箱内部自行派生

洞察 2:Agent 是"可替换的租户",沙箱是"稳定的宿主"

  • 三个 codebuddy 实例由 sandbox-proxy 统一派生,会话切换只是换一个 prewarm 实例
  • Agent 崩溃不影响沙箱存活:sandbox-proxy 在,随时能再拉一个

洞察 3:prewarm 池 = 首字延迟优化

  • CODEBUDDY_STREAM_TIMEOUT_MS=300000、CODEBUDDY_FIRST_TOKEN_TIMEOUT_MS=300000(均 5 分钟)
  • 两份待命实例让新会话无需冷启动

洞察 4:热升级路径清晰

  • sandbox-proxy 从 COS 分发,支持 box-current 软链切版本
  • Agent 包在 codebuddy-code npm 目录(独立 overlay 挂载),可单独更新

排障提示

现象 正确排查方式
内存/CPU 显示与预期不符 读 cat /sys/fs/cgroup/memory.max、cpu.max,不要用 free
判断某能力是否开启 `curl -s localhost:49983/envs
确认 Agent 归属 ps --ppid <sandbox-proxy-pid> 看是否为其子进程
出网异常 检查到 172.24.0.1:80 的连接,而非公网直连
日志丢失 检查 nc 172.24.0.1 18100 链路与 SANDBOX_LOG_FORWARD=1

附录:一句话对照表

概念 具体实体 定位
沙箱 CloudStudio spacelet(K8s Pod / 容器) 宿主,提供机器、文件、网络、命令
控制面 CloudStudio SaaS(codingcorp.cloudstudio.net/api) 调度与配置来源
配置通道 envd:49983 控制面进入沙箱的唯一入口
Agent codebuddy(@tencent-ai/codebuddy-code) 沙箱内的租户进程,执行任务
Agent 启动器 sandbox-proxy:9090/65225 派生 Agent + 出网代理
边界出口 宿主网关 172.24.0.1(:80 出网 / :18100 日志) 沙箱与外界的唯一出入口
相关推荐
淡海水2 小时前
03-03-架构篇-SGen GC架构解析
unity·架构·c#·gc·sgen
java_nnnn2 小时前
Redis 初识:从单体架构到分布式架构的演进
redis·分布式·架构
硅基手札3 小时前
【Linux 内核专栏 18】进程间通信 IPC(下)— Socket·Netlink·fd 三件套·futex·线程同步
linux·运维·驱动开发·单片机·嵌入式硬件·mcu·架构
安易算力12 小时前
昇腾CANN算子开发与MindSpore部署调优实践:从Ascend C到模型推理全链路
网络·架构
省钱兄--zs15 小时前
深圳24小时自助健身房软硬件解决方案实战指南与开发流程详解
java·数据仓库·spring boot·小程序·架构
用户03906147071916 小时前
PMSM 矢量控制(FOC)环路公式推导
架构
HaipengYu16 小时前
从 Config Hook 到 Server/TUI:OpenCode 插件 V1 到 V2 的架构演进
人工智能·架构
罗丹丹Luna16 小时前
移动端显示“已提交”以后:SAP 与低代码集成该怎样暴露真实状态
后端·架构
吴凡wudianmei16 小时前
每调一次模型都花钱?如何用工程手段把调用压到最少?
前端·后端·架构