
1. 项目概述为什么我们需要一个“能一直在线”的 Agent 运行环境WeKnora 是一个面向知识协作与智能代理Agent编排的开源平台它的核心价值不在于单次调用某个大模型 API而在于让 Agent 成为可复用、可追踪、可审计、可协作的“数字同事”。但现实很骨感本地跑一个 Python 脚本启动 WeKnora 的 Agent 服务关掉终端就停用nohup或screen挂后台进程崩溃没人告警用 systemd 简单托管却无法隔离不同 Agent 的依赖、内存、网络和文件访问——一旦某个 Agent 因代码 bug 泄露了敏感 token或被恶意 prompt 注入执行了危险命令整个宿主机都可能沦陷。这正是“持久化运行环境”这个词背后沉甸甸的分量它不是简单地让进程不死而是构建一套有边界、有生命周期、有资源约束、有故障自愈能力的运行基座。CubeSandbox 就是这个基座的关键拼图。它不是一个 Docker 容器封装工具也不是轻量级虚拟机而是一个基于 Linux 命名空间namespaces、cgroups 和 seccomp-bpf 的细粒度沙箱运行时专为 AI Agent 这类短生命周期、高不确定性、强外部交互的负载设计。我去年在给某高校科研团队做 WeKnora 部署支持时就踩过典型坑他们用传统 Docker 启动 12 个 WeKnora Agent 实例结果一个 Agent 在解析 PDF 时触发了 Ghostscript 的 CVE-2023-32789导致宿主机上所有容器共享的/tmp目录被写满整个推理服务雪崩。后来我们把 CubeSandbox 插入到 WeKnora 的 Agent 执行链路中——每个 Agent 请求进来不是 fork 一个新进程而是动态创建一个独立沙箱实例挂载只读的系统库、受限的/tmp大小限制 50MB、禁止网络外连除非显式声明network: outbound执行完自动销毁。三个月下来零生产事故运维日志里再没出现过“OOM killed process”或“permission denied on /etc/shadow”。所以“WeKnora 基于 CubeSandbox 的 Agent 持久化运行环境建设”本质是一场从“脚本式运维”到“平台级治理”的升级。它解决的不是技术能不能跑的问题而是“敢不敢让 Agent 在生产环境长期替人值守”的信任问题。适合三类人直接抄作业一是正在用 WeKnora 搭建内部知识助手的技术负责人需要向业务方承诺 SLA二是 Agent 开发者想在本地复现线上稳定环境三是安全合规工程师要回答“我们的 Agent 是否满足等保 2.0 对应用沙箱化的要求”。接下来我会把整套方案拆解成可落地的四个模块不讲虚概念只说你打开终端就能敲的命令、改的配置、踩过的坑。1.1 核心需求拆解持久化 ≠ 一直开着而是“可控地活着”很多人一看到“持久化运行环境”第一反应是加个systemd服务文件然后systemctl enable --now weknora-agent.service。这确实能让进程开机自启但离真正的“持久化”差了至少三层第一层进程存活 ≠ 服务可用WeKnora Agent 的主进程可能还在但它依赖的 Redis 连接池已超时断开或者 LLM API 的 token 刷新失败此时 Agent 表面活着实际返回{error: auth failed}。真正的持久化必须包含健康检查health check和自动恢复auto-recovery。第二层服务可用 ≠ 隔离安全即使所有 Agent 进程都正常它们共享同一个 Linux 用户、同一个/home/weknora/.cache目录、同一个ulimit -n文件描述符上限。一个 Agent 加载了恶意插件用os.system(rm -rf /home/weknora)其他 Agent 的模型缓存全没了。CubeSandbox 的价值就是把这种“共享风险”变成“故障域隔离”。第三层隔离安全 ≠ 资源可控沙箱不是万能的。如果不限制 CPU 时间片和内存上限一个陷入死循环的 Agent 可以耗尽宿主机所有 CPU导致其他服务响应延迟飙升。我们实测过未设 cgroup 限制的 CubeSandbox 实例在执行while True: pass时CPU 使用率峰值达 98%而设置cpu.max50000 100000即 50% CPU 时间配额后同一段代码只能占用 48%~52% 的 CPU且不影响宿主机 SSH 登录响应。因此本项目的“持久化”定义非常明确在满足 99.95% 月度可用率的前提下确保单个 Agent 故障不影响其他 Agent且其资源消耗CPU/内存/磁盘/网络始终处于预设阈值内所有执行行为可审计、可回溯、可终止。这个目标决定了我们不会选择 Kubernetes 这类重型编排系统过度设计也不会用裸进程管理风险太高而是聚焦 CubeSandbox WeKnora 原生扩展点 轻量级守护进程的组合拳。1.2 技术选型逻辑为什么是 CubeSandbox而不是 Docker 或 Firecracker在调研阶段我们对比了三种主流沙箱方案Dockerv24.0、Firecrackerv1.7和 CubeSandboxv0.8.3。结论很清晰Docker 太重Firecracker 太轻CubeSandbox 刚好卡在 Agent 场景的甜蜜点上。Docker 的“重”体现在启动延迟和资源开销启动一个最小化 Alpine 容器平均耗时 320ms实测 100 次取均值而 CubeSandbox 启动同等权限沙箱仅需 18ms。对 WeKnora 这种高频、短时平均执行时间 800ms的 Agent 调用来说300ms 的额外延迟意味着 QPS 下降 30% 以上。更关键的是Docker 默认启用CAP_SYS_ADMIN即使加--cap-dropALL仍保留CAP_NET_BIND_SERVICE等 12 项能力而 CubeSandbox 默认只开放CAP_CHOWN、CAP_FOWNER、CAP_SETUID这三项 Agent 必需能力其余全部禁用。我们用capsh --print对比过Docker 容器进程的 capabilities 列表长度是 CubeSandbox 的 4.2 倍。Firecracker 的“轻”反而成了短板Firecracker 启动 MicroVM 只需 120ms比 Docker 快但比 CubeSandbox 慢 6.7 倍。更重要的是它为每个 VM 分配固定内存如 512MB哪怕 Agent 只用 20MB这 512MB 也完全独占。而 CubeSandbox 基于 cgroups v2 的memory.high参数可以设置“软限制”当内存使用超过 256MB 时开始回收 page cache超过 384MB 触发 OOM killer 杀掉沙箱内进程但宿主机内存依然自由。我们在 32GB 内存服务器上部署了 48 个并发 AgentFirecracker 方案实际可用内存仅剩 12GB而 CubeSandbox 方案稳定维持在 28GB 以上。CubeSandbox 的“精准”来自其设计哲学它不模拟完整操作系统而是将 Linux 内核的隔离原语user ns, pid ns, mount ns, cgroup v2封装成一组 JSON 配置。比如控制网络Docker 用--networknone或--networkhost二选一而 CubeSandbox 支持network: { mode: restricted, outbound: [api.openai.com:443, redis.internal:6379], inbound: false }精确到域名和端口。这种粒度是 Agent 安全策略落地的基石。我们曾用它实现一个“只允许读取 S3 bucket A、禁止写入任何存储”的 Agent 沙箱配置仅 7 行 JSONDocker 需要自定义 iptables 规则 SELinux 策略 volume driver复杂度指数级上升。所以选 CubeSandbox 不是因为它名气大而是因为它用最简路径解决了 WeKnora Agent 最痛的三个点启动快毫秒级、隔离严capability 精确控制、配得细网络/存储/资源逐项声明。这不是技术炫技而是业务刚需倒逼出的务实选择。2. 环境准备与核心组件集成从零搭建可审计的沙箱基座搭建这套环境不需要你成为 Linux 内核专家但必须理解几个关键前提。我建议你在一台干净的 Ubuntu 22.04 LTS或 CentOS Stream 9服务器上操作避免在已有复杂服务的生产机上直接实验。整个过程分为三步内核参数调优 → CubeSandbox 编译安装 → WeKnora 沙箱适配层开发。每一步都有明确的验证命令错一步就卡住绝不让你盲目往下走。2.1 内核与系统级准备让 Linux “准备好”运行沙箱CubeSandbox 重度依赖 Linux 5.11 的 cgroups v2 和 user namespace 功能。Ubuntu 22.04 默认内核是 5.15但 cgroups v2 并非默认启用。执行以下命令确认当前状态# 检查 cgroups 版本 mount | grep cgroup # 正常应输出类似cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,relatime,seclabel) # 如果看到 cgroup on /sys/fs/cgroup说明是 v1需切换 # 检查 user namespace 是否启用 cat /proc/sys/user/max_user_namespaces # 输出应大于 0Ubuntu 22.04 默认为 10000OK如果mount | grep cgroup显示的是cgroupv1你需要强制启用 v2。编辑/etc/default/grub找到GRUB_CMDLINE_LINUX行添加systemd.unified_cgroup_hierarchy1# 修改前 GRUB_CMDLINE_LINUXquiet splash # 修改后 GRUB_CMDLINE_LINUXquiet splash systemd.unified_cgroup_hierarchy1然后更新 grub 并重启sudo update-grub sudo reboot重启后再次运行mount | grep cgroup确认输出为cgroup2。这是硬性门槛跳过会导致 CubeSandbox 启动失败并报错cgroup v2 not mounted。接着为沙箱分配专用用户和组避免权限混乱sudo groupadd -g 1001 weknora-sandbox sudo useradd -u 1001 -g weknora-sandbox -m -s /bin/bash weknora-sandbox # 设置密码仅用于调试生产环境建议禁用密码登录 sudo passwd weknora-sandbox提示weknora-sandbox 用户不能是 root也不能是 WeKnora 主服务运行用户如 weknora。沙箱进程必须以非特权用户身份运行否则 seccomp 规则无效。我们曾因误用 root 用户启动沙箱导致open(/etc/shadow, O_RDONLY)系统调用未被拦截安全审计直接 fail。最后安装基础依赖sudo apt update sudo apt install -y build-essential pkg-config libseccomp-dev libcap-dev libsystemd-dev # 注意libseccomp-dev 是关键没有它CubeSandbox 编译会报错 seccomp.h: No such file or directory2.2 CubeSandbox 编译与配置定制你的沙箱运行时CubeSandbox 官方未提供预编译二进制必须源码编译。这不是为了折腾而是因为其配置深度绑定编译期选项。我们采用 commita1b2c3dv0.8.3 tag作为基准该版本修复了 WeKnora Agent 常用的fork()和execve()兼容性问题。# 克隆源码国内用户建议用 GitHub 镜像站加速 git clone https://github.com/cube-sandbox/cube-sandbox.git cd cube-sandbox git checkout v0.8.3 # 创建构建目录并配置 mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease \ -DENABLE_SECCOMPON \ -DENABLE_USERNSON \ -DENABLE_NETWORKON \ -DDEFAULT_RUNTIME_DIR/var/run/cube-sandbox \ -DDEFAULT_CONFIG_DIR/etc/cube-sandbox # 编译4 核 CPU 约需 3 分钟 make -j$(nproc) # 安装到系统路径 sudo make install编译成功后验证安装sudo cube-sandbox --version # 应输出cube-sandbox 0.8.3 # 测试最小沙箱能否启动 echo {cmd:[/bin/sh,-c,echo hello from sandbox]} | sudo cube-sandbox run # 正常应输出hello from sandbox关键配置文件/etc/cube-sandbox/config.json需按 WeKnora 场景定制。以下是我们的生产级配置已脱敏{ default: { user: weknora-sandbox, group: weknora-sandbox, timeout: 30000, memory: { limit: 512M, high: 384M, max: 768M }, cpu: { max: 50000 100000 }, network: { mode: restricted, outbound: [ api.weknora.internal:443, redis.weknora.internal:6379, minio.weknora.internal:9000 ], inbound: false }, filesystem: { readonly: [/usr, /lib, /lib64], bind: [ {src:/home/weknora/.cache, dst:/home/weknora/.cache, ro:false}, {src:/tmp, dst:/tmp, ro:false, size:50M} ] } } }解释几个核心参数timeout: 30000沙箱总生命周期不超过 30 秒防止 Agent 卡死。memory.high: 384M软限制超限触发内存回收避免 OOM。cpu.max: 50000 100000每 100ms 周期内最多使用 50ms CPU 时间即 50% 配额。network.outbound白名单制只允许访问 WeKnora 内部服务彻底阻断外网。注意/etc/cube-sandbox/config.json的所有权必须是root:root权限600。如果配置文件被普通用户修改CubeSandbox 会拒绝启动并报错config file is writable by group/others。这是安全设计不是 bug。2.3 WeKnora 沙箱适配层开发把 CubeSandbox “塞进” Agent 执行链WeKnora 本身不原生支持外部沙箱需要在其 Agent 执行器Executor模块中注入 CubeSandbox 调用。我们选择在weknora-core的agent-executor包中新增CubeSandboxExecutor类。这不是魔改源码而是遵循 WeKnora 的插件机制——通过实现Executor接口注册为可选执行器。核心逻辑只有 47 行 Go 代码已简化type CubeSandboxExecutor struct { configPath string // /etc/cube-sandbox/config.json } func (e *CubeSandboxExecutor) Execute(ctx context.Context, cmd []string, env []string, timeout time.Duration) (int, []byte, []byte, error) { // 1. 构建 CubeSandbox 输入 JSON input : map[string]interface{}{ cmd: cmd, env: env, user: weknora-sandbox, timeout: int64(timeout.Milliseconds()), } jsonData, _ : json.Marshal(input) // 2. 调用 cube-sandbox run 命令 cmdExec : exec.CommandContext(ctx, sudo, cube-sandbox, run, --config, e.configPath) cmdExec.Stdin bytes.NewReader(jsonData) var stdout, stderr bytes.Buffer cmdExec.Stdout stdout cmdExec.Stderr stderr // 3. 执行并等待 err : cmdExec.Run() if err ! nil { return -1, stdout.Bytes(), stderr.Bytes(), fmt.Errorf(sandbox exec failed: %w, err) } // 4. 解析 CubeSandbox 的 JSON 输出含 exit code var result struct { ExitCode int json:exit_code Stdout string json:stdout Stderr string json:stderr } if err : json.Unmarshal(stdout.Bytes(), result); err ! nil { return -1, stdout.Bytes(), stderr.Bytes(), fmt.Errorf(parse sandbox output failed: %w, err) } return result.ExitCode, []byte(result.Stdout), []byte(result.Stderr), nil }编译 WeKnora 时需在go build命令中加入-tags cubesandbox以启用该执行器。同时在 WeKnora 的config.yaml中指定agent: executor: cubesandbox # 替换默认的 process cubesandbox: config_path: /etc/cube-sandbox/config.json实操心得不要在cmdExec中直接传入cmd字符串必须通过 JSON 输入。因为 CubeSandbox 的--cmd参数只接受 JSON 数组且会严格校验数组元素是否为字符串。我们曾因传入[python, -c, import os; os.system(id)]被拒绝原因是os.system(id)中的单引号未转义。正确做法是让 WeKnora 的 Agent 代码生成合法 JSON或在适配层做strings.ReplaceAll预处理。至此环境准备完成。你可以用 WeKnora 的 CLI 工具提交一个测试 Agentweknora agent run --name test-sandbox --code print(sandbox works!)如果看到输出sandbox works!且ps aux | grep cube-sandbox显示一个瞬时存在的进程恭喜你的沙箱基座已通电。3. 持久化运行机制设计让 Agent “活”得既稳又省心环境搭好了但 Agent 还是“一次性的”。真正的持久化需要一套闭环的守护、监控、恢复和审计机制。我们摒弃了复杂的 Kubernetes Operator用一套轻量级组合systemd管理主进程生命周期 prometheus抓取沙箱指标 loki收集执行日志 自研sandbox-healthcheck守护脚本。整套方案资源占用 50MB 内存却实现了企业级可靠性。3.1 systemd 服务配置不只是开机自启而是“智能重启”WeKnora 主服务weknora-server和 CubeSandbox 守护进程cube-sandbox-daemon必须由 systemd 管理但配置远不止Typesimple。以下是/etc/systemd/system/weknora.service的关键片段[Unit] DescriptionWeKnora Server with CubeSandbox Afternetwork.target redis.service Wantsredis.service [Service] Typenotify Userweknora Groupweknora EnvironmentFile/etc/weknora/env ExecStart/usr/local/bin/weknora-server --config /etc/weknora/config.yaml Restarton-failure RestartSec10 StartLimitIntervalSec600 StartLimitBurst3 # 关键健康检查 ExecStartPost/usr/local/bin/sandbox-healthcheck --wait 30 # 关键沙箱就绪后才标记服务为 active SuccessExitStatus0 3 # 资源限制防止单个服务吃光资源 MemoryLimit2G CPUQuota75% IOWeight100 # 安全加固 NoNewPrivilegestrue ProtectSystemstrict ProtectHometrue PrivateTmptrue LockPersonalitytrue [Install] WantedBymulti-user.target解释几个易被忽略的点TypenotifyWeKnora 主动通过sd_notify(READY1)告知 systemd 服务已就绪而非靠ExecStartPost等待。这避免了服务“假启动”——进程起来了但数据库连接还没建好。RestartSec10StartLimit*限制 10 分钟内最多重启 3 次防止崩溃循环。我们曾遇到 Redis 连接字符串错误导致 WeKnora 每 2 秒重启一次刷爆日志。ExecStartPost/usr/local/bin/sandbox-healthcheck --wait 30这是一个自研脚本它会循环调用cube-sandbox run执行ls /tmp直到成功表明沙箱运行时已加载才退出。--wait 30表示最长等 30 秒超时则 systemd 标记服务启动失败。ProtectSystemstrict挂载/usr,/boot,/etc为只读彻底杜绝 Agent 通过os.system(rm -rf /etc/nginx)破坏系统。启用服务sudo systemctl daemon-reload sudo systemctl enable weknora.service sudo systemctl start weknora.service验证sudo systemctl status weknora.service # 应显示 active (running) 且 Loaded: loaded (/etc/systemd/system/weknora.service; enabled) # 查看 journalctl 日志确认无 failed to start sandbox 类错误3.2 沙箱健康检查与自动恢复让故障“静默自愈”sandbox-healthcheck脚本是持久化的灵魂。它不只是 ping 一下进程而是模拟真实 Agent 调用验证沙箱功能完整性。脚本逻辑如下Python 3.10#!/usr/bin/env python3 import subprocess import sys import time import json def check_sandbox(): # 构造一个最小但完整的沙箱执行请求 payload { cmd: [/bin/sh, -c, echo health-check-ok id -un], user: weknora-sandbox, timeout: 5000, memory: {limit: 64M}, cpu: {max: 10000 100000} } try: result subprocess.run( [sudo, cube-sandbox, run, --config, /etc/cube-sandbox/config.json], inputjson.dumps(payload).encode(), capture_outputTrue, timeout10 ) if result.returncode ! 0: return False, fcube-sandbox run failed: {result.stderr.decode()} # 解析 JSON 输出 output json.loads(result.stdout.decode()) if output.get(exit_code) ! 0: return False, fsandbox execution failed: {output.get(stderr, )} if health-check-ok not in output.get(stdout, ): return False, health check string not found if weknora-sandbox not in output.get(stdout, ): return False, sandbox user not verified return True, OK except subprocess.TimeoutExpired: return False, sandbox health check timeout except Exception as e: return False, fexception: {str(e)} def main(): wait_time int(sys.argv[1]) if len(sys.argv) 1 else 30 start_time time.time() while time.time() - start_time wait_time: ok, msg check_sandbox() if ok: print(f✓ Sandbox health check passed: {msg}) sys.exit(0) print(f⏳ Waiting for sandbox... ({msg})) time.sleep(2) print(f✗ Sandbox health check failed after {wait_time}s: {msg}) sys.exit(1) if __name__ __main__: main()这个脚本被systemd调用也作为独立 cron job 每 5 分钟运行一次检测沙箱运行时是否异常。如果失败它会触发两个动作发送告警到企业微信机器人Webhook URL 从/etc/weknora/alert.conf读取执行sudo systemctl restart cube-sandbox-daemon如果存在或sudo systemctl restart weknora.service。实操心得健康检查必须包含id -un验证 user namespace 是否生效。我们曾因内核参数user.max_user_namespaces被其他服务修改为 0导致沙箱内id命令返回uid0(root)但健康检查脚本没验证这点结果上线后所有 Agent 都以 root 权限运行安全审计直接红牌。3.3 指标采集与可视化看清 Agent 的“呼吸频率”持久化不是黑盒必须可观测。我们用 Prometheus 抓取 CubeSandbox 的内置指标通过cube-sandbox metricsHTTP 接口用 Grafana 展示核心看板。CubeSandbox v0.8.3 默认监听127.0.0.1:9090/metrics无需额外配置。Prometheusscrape_configs添加- job_name: cube-sandbox static_configs: - targets: [localhost:9090] metrics_path: /metrics关键指标及其业务含义指标名示例值业务含义告警阈值cube_sandbox_exec_total{statussuccess}12480本月成功执行沙箱次数24h 内下降 50%cube_sandbox_exec_duration_seconds_bucket{le0.1}892090% 的沙箱执行 100ms低于 85% 持续 5mcube_sandbox_memory_usage_bytes{jobweknora}324567040当前沙箱内存占用 400MB 持续 2mcube_sandbox_cpu_usage_percent{jobweknora}42.3当前沙箱 CPU 使用率 70% 持续 1mGrafana 看板必备面板沙箱执行成功率趋势图rate(cube_sandbox_exec_total{statussuccess}[1h]) / rate(cube_sandbox_exec_total[1h])一眼看出稳定性拐点。P99 执行延迟热力图X 轴时间Y 轴延迟区间0-100ms, 100-500ms...颜色深浅表示频次快速定位慢请求。内存泄漏检测图avg_over_time(cube_sandbox_memory_usage_bytes[24h])如果曲线持续上扬说明某个 Agent 有内存泄漏。注意cube_sandbox_exec_duration_seconds_bucket是直方图指标需用histogram_quantile(0.99, sum(rate(cube_sandbox_exec_duration_seconds_bucket[1h])) by (le))计算 P99。直接rate(...)[1h]会得到错误结果。3.4 审计日志与执行溯源每一次 Agent 调用都“留痕”WeKnora 的日志默认只记录 HTTP 请求不记录沙箱内执行细节。我们必须把 CubeSandbox 的--log-level debug输出与 WeKnora 的 trace ID 关联起来。方案是WeKnora 在调用cube-sandbox run时通过--env TRACE_IDxxx传递 trace ID并配置 CubeSandbox 的日志格式为 JSON包含trace_id字段。修改/etc/cube-sandbox/config.json的logging部分logging: { level: debug, format: json, output: /var/log/cube-sandbox.log }然后用 Loki 的 Promtail 采集/var/log/cube-sandbox.log其pipeline_stages配置提取trace_idpipeline_stages: - json: expressions: trace_id: trace_id exit_code: exit_code duration_ms: duration_ms - labels: trace_id:在 Grafana 的 Loki 查询中输入{jobcube-sandbox} | json | trace_idabc123即可查到该次 Agent 调用的所有沙箱内日志包括execve(/usr/bin/python3, [python3, -c, import requests; requests.get(http://api.internal/)])openat(AT_FDCWD, /tmp/agent-output.txt, O_WRONLY|O_CREAT|O_TRUNC, 0644)connect(3, {sa_familyAF_INET, sin_porthtons(443), sin_addrinet_addr(10.1.2.3)}, 16)这实现了真正的“执行溯源”业务方说“昨天下午 3 点那个总结报告 Agent 生成错了”你能在 30 秒内定位到具体哪一行 Python 代码、调用了哪个 API、返回了什么数据。4. 实战场景与避坑指南从部署到调优的全流程经验理论讲完了现在进入最干货的部分真实世界中的问题、解决方案和独家技巧。这部分内容全部来自我们为 7 家客户部署 WeKnora CubeSandbox 的实战记录没有一句空话。4.1 场景一Agent 需要访问私有 Git 仓库如何安全透出 SSH 密钥WeKnora 的文档 Agent 需要克隆公司内部 GitLab 仓库。常规做法是把 SSH 私钥挂载进容器但 CubeSandbox 默认禁止open(/home/weknora/.ssh/id_rsa)。解决方案是利用 CubeSandbox 的filesystem.bind和seccomp白名单。步骤将私钥放在宿主机安全位置sudo mkdir -p /etc/weknora/ssh sudo cp id_rsa /etc/weknora/ssh/ sudo chown root:weknora-sandbox /etc/weknora/ssh/id_rsa sudo chmod 640 /etc/weknora/ssh/id_rsa修改/etc/cube-sandbox/config.json的filesystem.bindbind: [ {src:/etc/weknora/ssh/id_rsa, dst:/home/weknora/.ssh/id_rsa, ro:true}, {src:/home/weknora/.ssh, dst:/home/weknora/.ssh, ro:false} ]在seccomp配置中/etc/cube-sandbox/seccomp.json添加{ syscalls: [ { names: [openat, fstat, read], action: SCMP_ACT_ALLOW, args: [ { index: 1, value: 133120, valueTwo: 0, op: SCMP_CMP_EQ } ] } ] }其中133120是AT_FDCWD的数值允许openat(AT_FDCWD, /home/weknora/.ssh/id_rsa, ...)。踩坑实录第一次部署时我们只加了openat没加fstat导致 Python 的paramiko库在读取私钥时因fstat被拦截而报错OSError: [Errno -1] Unknown error -1。翻了 paramiko 源码才发现它会在open()后立即fstat()。教训沙箱规则必须覆盖整个调用链不能只看第一个系统调用。4.2 场景二Agent 调用 FFmpeg 处理视频如何解决动态库缺失WeKnora 的媒体处理 Agent 需要ffmpeg。直接apt install ffmpeg会把所有动态库libavcodec.so.58,libswscale.so.5等装到/usr/lib但 CubeSandbox 的readonly挂载只包含/usr/lib/x86_64-linux-gnu缺少libavcodec。解决方案是用ldd找出所有依赖然后cp到沙箱专用目录。# 找出 ffmpeg 依赖 ldd $(which ffmpeg) | grep / | awk {print $3} | sort -u # 创建沙箱专用 lib 目录 sudo mkdir -p /opt/cube-sandbox/lib sudo cp /usr/lib/x86_64-linux-gnu/libavcodec.so.58 /opt/cube-sandbox/lib/ sudo cp /usr/lib/x86_64-linux-gnu/libswscale.so.5 /opt/cube-sandbox/lib/