news 2026/10/21 2:30:09

给Agent接代码沙箱:六个容易踩的坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
给Agent接代码沙箱:六个容易踩的坑

给Agent接代码沙箱:六个容易踩的坑

原文:GitHub - 《SPACE:给智能体跑代码的沙箱平台(仓库与架构文档)》(https://github.com/perplexityai/space)

前言

让 Agent 写代码不难,难的是让它把代码跑起来。直接在宿主上执行,等于把 shell 交给了模型;随便用个容器,又挡不住恶意代码往外连。真正要在生产里跑,绕不开沙箱这一层。

Perplexity 开源了支撑 Perplexity Computer 全部沙箱能力的平台 SPACE,全称是 Sandboxed Platform for Agentic Code Execution,把 Agent 和用户的代码放进 Firecracker microVM 里隔离执行,对外只暴露 HTTP API。仓库里有一份写得相当克制的文档,把对象模型、生命周期、输出流都讲清楚了。

正是这份文档让我发现,接沙箱时的坑几乎都不在"能不能跑",而在"你以为的状态"和"它真实的状态"之间。下面六个,是我读完架构文档后认为最容易写错的地方。

一、先记住五个对象

文档把整个平台拆成五个概念,后面所有坑都跟它们的边界有关:

对象含义
template起始文件系统与运行配置,多个沙箱可以共用同一个模板
sandbox一个独立的 microVM,有自己的可写文件系统和生命周期
process沙箱内运行的程序,同沙箱内的进程共享环境与文件系统
command session一条命令的连接信息与授权,创建时分配进程 ID,attach 之后进程才启动
operation异步请求的记录,跟踪 create / pause / suspend / wake / delete

请求里的 template 既可以写seed:latest这种发布引用,也可以写 digest。沙箱的响应会回报解析后的 template digest,方便你记录这次到底跑的是什么内容。

二、坑一:接口返回成功,不等于沙箱能跑

这是最容易写错的一处。文档说得很直:API 只是把期望状态写进 PostgreSQL,控制器和 worker 去把它对齐,所以请求被接受不是就绪信号。

正确的等待顺序是三段,缺一不可:

  1. 轮询 operation,直到 state 表示成功;
  2. 查沙箱,直到 state 是 running;
  3. 确认进程端点可达,然后才创建 command session。

文档还特别提醒:operation 的 state 描述的是这次生命周期请求的状态,沙箱的 state 描述的是环境状态,两者都不是命令的退出码。把这三个状态混成一个"成功"来判断,是后面一连串诡异问题的源头。

三、坑二:pause 不是快照

暂停有两种,语义完全不同:

  • pause:执行暂停,但 VM 内存仍然驻留,文件系统还在 worker 上,文档明确写了这不是持久快照;
  • suspend:把 VM 内存、运行时状态和根文件系统做成快照,释放活跃的 VM 资源;
  • wake:把暂停或挂起的沙箱恢复到 running,恢复后要重新等就绪再发命令。

如果你的代码把 pause 当成"存档",指望节点重启后还能捞回来,就会丢状态。

顺带一个容易踩的语义错位:仓库里的 E2B 兼容层(ECW)用的是另一套名字,它的 pause 映射到 SPACE 的 suspend,resume 映射到 restore。同一个词在两套 API 里指的不是一件事,迁移时务必按语义而不是按名字对齐。

四、坑三:命令输出是流,不是日志

文档里有一句话很值得抄下来贴在屏幕上:命令输出是实时流,不是持久日志存储。

具体表现是:客户端用Last-Event-ID重连,得到的是resync_required,而不是补发漏掉的输出。也就是说断了就是断了,没有回放。所以应用侧必须自己把事件流落成 transcript,流失败后保留记录;重跑命令只在"重复执行副作用是安全的"前提下才允许。

下面是一段读取流的骨架,按文档描述的事件语义写成,不是官方 SDK:

# 读取命令输出(自拟示意,基于 SPACE 文档的事件语义)importbase64importjsondefdrain(stream,process_id,timeout=45):out,err,exit_code=[],[],Noneforlineinstream.iter_lines():# SSE:每行形如 "data: {...}"ifnotline.startswith("data: "):continueev=json.loads(line[6:])ifev["type"]=="stdout":# 输出是 base64,必须解码,否则中文和二进制都会乱out.append(base64.b64decode(ev["data"]).decode("utf-8","replace"))elifev["type"]=="stderr":err.append(base64.b64decode(ev["data"]).decode("utf-8","replace"))elifev["type"]=="process_exited":exit_code=ev["exit_code"]break# 只有收到 process_exited 且退出码为 0,才算这条命令成功return"".join(out),"".join(err),exit_code

几个要点:输出内容是 base64,不解码会把中文和二进制一起搞坏;process_exited是唯一可靠的结束标志,流关闭本身不代表命令成功;示例里给流设了 45 秒上限,所以命令自身的 timeout 要设得比它小,否则你会先被自己掐断。

五、坑四:命令是参数数组,不是 shell 字符串

请求里的命令是一个可执行文件加参数数组,不是隐式交给 shell 解释的字符串。想要管道、重定向、变量展开,得自己显式写sh -c。

# 想用管道就必须显式走 sh -cCOMMAND={"command":["sh","-c","printf '%s\n' \"$GREETING\"; pwd"],"cwd":"/tmp","env":{"GREETING":"hello"},"timeout":"30s",}

还有一个常被忽略的前提:可执行文件和工作目录必须存在于沙箱内,而不是宿主上。你在本机 ls 出来的路径,在 guest 里可能根本不存在。

六、坑五:两套 token 不能混用

管理面用 user session 或 service bearer token,命令流用创建 session 时返回的、作用域限定在该 session 的 attachment token。文档写明两者不可互换。

示例代码还额外做了两件事,值得照做:一是只接受配置好的 data plane origin 加上精确的 session path 作为 attachment 地址,防止 token 被引到别处;二是除回环地址外一律要求 HTTPS。

如果这两条你不做,等于把一次性的命令执行凭据交给了任何一个能改 URL 的地方。

七、坑六:超时不等于失败,别自动重试

分布式系统里这条老规矩在沙箱上格外要命。文档的建议是:每次生命周期变更都带Idempotency-Key,超时后先 inspect,按保存下来的原始请求和幂等键复用,而不是重新发起一个新请求。

还有两个相关的默认行为值得记住:

  • 命令执行完成不会自动删除沙箱,删不删是你自己的决定;
  • 进程查询接口只返回运行中的进程,命令退出后就查不到了,别指望用它拿历史结果。

顺带几个工程默认值

  • 通过 API 创建的沙箱,默认在连续六小时无访问、无命令活动后自动 suspend。这是闲置上限,不是最长生命周期,两者别搞混;
  • 根文件系统用 btrfs 的写时复制来创建,模板可以复用而互不影响;
  • 仓库提供 warm pool,在 Kubernetes 上预热已启动的沙箱,让创建跳过冷启动;
  • Firecracker 隔离的是 guest 执行,宿主侧管理 VM、存储、网络的服务仍然需要特权,安全边界不在这一层;
  • 项目使用 Apache License 2.0。

小结

接代码沙箱这件事,真正的成本不在"调通一个执行接口",而在把异步生命周期、流式输出、凭据隔离这三件事的边界认清楚。上面六个坑,归根到底是一句话:把"请求成功"和"执行成功"分开,把"暂停"和"存档"分开,把"实时流"和"日志"分开。

文中对象模型、接口路径与默认值均来自该仓库文档,具体字段约束以其 OpenAPI 描述与官方文档为准;沙箱平台仍在演进,版本细节请以仓库最新说明为准。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 2:30:09

率能半导体 SS6323H三相无刷直流电机驱动芯片:18V/2.3A,覆盖三相BLDC电机、风扇、泵、机器人等应用场景

在三相无刷直流电机(BLDC)驱动设计中,工程师通常需要构建三个半桥来驱动电机的三相绕组。传统方案需要三颗半桥栅极驱动器加六颗外置功率MOSFET,不仅占用大量PCB面积,还增加了设计复杂度和BOM成本。如果有一颗芯片能够…

作者头像 李华
网站建设 2026/10/21 2:30:05

HiL测试工程师的Simulink实战指南:从看懂模型到独立定位问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 2:30:03

Docker配置hosts导致服务无法启动的排查与正确实践

1. 问题现象与排查思路拆解1.1 这个报错到底在说什么先把场景还原一下。你在一台 Linux 机器上,为了让容器内部能通过某个自定义域名访问到指定服务,动手改了 Docker 的配置,比如往daemon.json里塞了hosts字段,或者直接改了/etc/h…

作者头像 李华
网站建设 2026/10/21 2:29:25

DynaHarness 方法总结

论文“DYNAHARNESS: A DYNAMIC PHYSICAL HARNESS FOR SELF-EVOLVING ROBOT AGENTS”,26年9月来自南洋理工和南京大学。一、动机用 VLA(视觉-语言-动作模型,比如 π0.5)给机器人提供动作先验,在长时程操作任务上仍然不够…

作者头像 李华
网站建设 2026/10/21 2:29:14

字符串相等判断:大小写敏感必知技巧

在编程的语言环境当中, 字符串是一种非常普遍使用的数据类型。在很多具体的业务实际场景之下, 我们需要对两个字符串是否相等进行判断操作, 这是一个特别基础的内容。那么究竟需要如何去实现这种判断功能呢, 以下是详细的具体操作流程和说明内容:在中间进行判断, 想要看两个字符…

作者头像 李华
网站建设 2026/10/21 2:28:56

2026 AI 论文辅助工具排行榜|实测打分,毕设党必看

前言 每到开题、论文撰写、盲审季,大量本科生、研究生都会在网上寻找好用的 AI 论文辅助工具。市面上工具五花八门,有的擅长单项写作,有的主打文献检索,还有的只是简单文本润色。很多同学踩坑:多个工具来回切换&#…

作者头像 李华