Skip to main content
Sandbox agent 会在隔离环境中启动 coding-agent CLI,给它 workspace 和任务,让它自由修改文件、运行命令,然后收集 transcript、diff 和测试结果。

内置 sandbox agents

claude-code

运行 Anthropic Claude Code CLI,需要 ANTHROPIC_API_KEY

codex

运行 OpenAI Codex CLI,需要 CODEX_API_KEY

bub

运行 bub coding agent,鉴权遵循 bub CLI 自身约定。

运行内置 agent

在 Eval 或 Experiment 上设置 sandbox,选择 NiceEval 在哪里创建隔离环境:
没有对应的 CLI flag,也没有项目级默认 provider。Eval 与 Experiment 都没贡献 template-bearing layer 时,link planning 会在创建任何 Sandbox 前失败。
云端跑 coding agent 时,可以直接使用 NiceEval 已发布的 E2B 公共模板,避免每个 Attempt 安装 CLI:
Claude Code 用 NICEEVAL_CLAUDE_CODE_E2B_TEMPLATE,Bub 用 NICEEVAL_BUB_E2B_TEMPLATE。常量的值是完整、版本固定的引用,版本跟着模板里那个 Agent 的版本走,你不用跟踪它。添加系统包、二进制或模型缓存的步骤见 Sandbox Provider · 从官方基线继续构建以提速 内置 agent 从 niceeval/adapter 导出的是工厂函数。需要配置鉴权、代理、MCP 或 GitHub skill 时,把这些写进工厂参数。模型仍然写在 experiment 的 model 字段,sandbox provider 仍然写在 sandbox 字段:

agent 环境变量

工作流程

起始文件和验证命令都写在 test(t) 中。agent 执行阶段只能看到你已经写进 sandbox 的文件。 t.sandbox.fileChanged() 等归因断言只评 agent 在 t.send() 期间改动的文件:NiceEval 在每次 t.send() 前后记录一次工作区状态,把中间的变化记在 agent 名下。你上传的起始文件、t.send() 之后写入的验证材料都不会混进来,所以 fileChanged("src/app.ts") 只在 agent 真的动过这个文件时通过。

文件改动的采集条件

NiceEval 在 Sandbox 里另开一本私有 Git 仓库,每次 t.send() 前后各记一次工作区状态,中间的变化就是 agent 改的。要让这份记录采得到,下面几条要成立:
  • Sandbox 里有 git 和 POSIX shell。 内置 provider 的默认镜像都带,自定义镜像要自己确认。被测项目自己是不是 Git 仓库不影响采集:这本账放在 workdir 之外的私有路径,不碰你的 .git,agent 也看不到它。
  • 文件写在 workdir 里。 采集范围就是 sandbox.workdir。写文件时省略 targetDir / cwd,需要绝对路径就读 sandbox.workdir,不要硬编码 /workspace——写到 workdir 之外的文件 agent 看不见,也采不到。
  • workdir 根下没有第二个 Git 仓库。 被测 checkout 直接放在 workdir 根。workdir 里出现 submodule 或另一个 clone 进来的仓库时,这一步直接报执行错误并列出路径,因为那个仓库里的普通文件改动会从记录里静默消失。确实不参与评分的仓库用 diff.ignore 整体排除。
  • 改动发生在 t.send() 期间。 最后一次 t.send() 之后写入的文件算你的验证材料,不进这份记录。
.gitnode_modules__pycache__、Python 虚拟环境、常见构建产物和包管理器缓存默认排除。否则,环境准备中的一次 npm install 就可能产生几万个文件。要调整就写评估用例的 diff 字段:
pattern 是 gitignore 风格,以 workdir 根为基准:不带 / 的匹配任意深度的同名项,带 / 的从 workdir 根匹配,末尾 / 表示目录。项目自己的 .gitignore 不参与判断,被项目忽略的文件照常记录。清单在第一次记账时冻结,agent 事后改 .gitignore 影响不了它。 单个 t.send() 窗口最多采 10,000 个路径、64 MiB 内容。超了这一步直接报执行错误,不会伪装成「一个文件都没改」让文件断言假通过。 生命周期(.setup() / .teardown())挂在 experiment sandbox 字段的 spec 上,用来做”按实验变化的环境准备”——装某个实验专属的二进制、预热、跨 attempt 载入和回存状态。它写下的文件属于环境,不会被算进 agent 产出的 diff。写法和规则见 Sandbox provider · 生命周期

自定义 sandbox agent

createNpmCliInstaller 会在宿主机准备 npm 包,再把它送入 Sandbox。Sandbox 里不需要安装 Node.js 或 npm。完整步骤见为自定义 Sandbox Agent 安装 CLI setup、每次 sendteardown 都拿到各自作用域的反馈方法:
  • ctx.progress({ message, current?, total? }) 更新当前短期状态,适合安装 CLI、运行 Turn、读取 transcript。不要逐 token 或逐 JSONL frame 调用。
  • ctx.diagnostic({ code, level, message, data?, dedupeKey? }) 保存协议退化、transcript 缺失和清理问题。它会进入终端永久事件,并作为 channel event 随 Attempt 提交进 Record。
  • 无法继续运行时抛出异常。runner 会把发生阶段、错误码、message、cause 和 stack 写入诊断通道,并在 niceeval.verdict 通道形成 errored Verdict。
不要从 Adapter 直接调用 console.log/error 或写 process.stdout/stderr。它们会打散 Human dashboard,也会破坏 CI 日志顺序。 运行中看到 Sandbox 或 Adapter 错误时,终端会给出 Attempt locator:
运行 niceeval show --run <runId> --page attempt-5TB8167MXJ30SYZCNAVRHPQ4D2 可查看结构化错误、diagnostics 和已完成的生命周期阶段。该页面读取 timing 通道,包含排队、Sandbox 启动、setup/teardown hook 里的 shell、Agent CLI 安装与启动命令、每轮 send、可关联的 OTel model/tool 与收尾。它可直接看出错误或超时发生在哪一层,以及之前的时间花在哪里。 树超过 80 个细节节点时会保留失败、慢点和首尾样本,并提示省略数量。需要逐节点审计时,从同一 Sample 的页面索引选择完整 timing route;execution route 以事件为骨架查看 agent 做了什么,有 OTel 时只把时间贴到能唯一关联的事件旁。Sandbox 创建失败可能发生在 telemetry 建立前,所以错误回顾不依赖 trace。

ctx.modelctx.flags

Experiment 声明的 model 和 flags 会出现在 Adapter context 中。Adapter 可以把它们转换成 CLI 参数或 HTTP payload。

在 experiment 中使用自定义 agent

不要在 runner 里写 agent-specific 分支。差异行为应该放进 adapter。