@delta-infra/cli 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/checksums.txt +5 -5
- package/package.json +1 -1
- package/scripts/install-wizard.js +701 -701
- package/scripts/install.js +442 -442
- package/scripts/run.js +65 -65
- package/skills/delta-sandbox/SKILL.md +383 -383
- package/skills/delta-sandbox/agents/openai.yaml +4 -4
- package/skills/delta-sandbox/references/commands.md +58 -58
- package/skills/delta-sandbox/references/lifecycle.md +100 -100
- package/skills/delta-sandbox/references/recipes.md +138 -138
- package/skills/delta-science/AGENTS.md +47 -47
- package/skills/delta-science/SKILL.md +174 -151
- package/skills/delta-science/agents/openai.yaml +4 -4
- package/skills/delta-science/references/antbo.md +100 -100
- package/skills/delta-science/references/delta-bo.md +89 -89
- package/skills/delta-science/references/gsasii.md +116 -116
- package/skills/delta-science/references/lammps.md +66 -66
- package/skills/delta-science/references/ldm-bo.md +129 -129
- package/skills/delta-science/references/materials-design.md +118 -109
- package/skills/delta-science/references/pubchem.md +126 -126
- package/skills/delta-science/references/pymatgen.md +68 -68
- package/skills/delta-science/references/qe.md +125 -123
- package/skills/delta-science/references/rdkit.md +138 -138
- package/skills/delta-science/references/strbo.md +87 -87
- package/skills/delta-science/references/synbo.md +198 -198
- package/skills/delta-science/scripts/catalog.py +92 -92
- package/skills/delta-science/scripts/generated_catalog.py +69 -69
- package/skills/delta-science/scripts/invoke.py +346 -346
- package/skills/delta-shared/SKILL.md +97 -97
- package/skills/delta-shared/agents/openai.yaml +4 -4
- package/skills/manifest.json +19 -19
|
@@ -1,383 +1,383 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: delta-sandbox
|
|
3
|
-
description: "在 Delta Sandbox Linux 容器中运行任意命令或脚本。适用于训练、推理、编译、数据处理等任意需要 sandbox 的计算任务,支持 Python / Node.js / Go / Java / Rust 等语言。所有任务通用同一套输出约定:命令在 stdout 末尾输出结构化 JSON;SSE 路径下 CLI 检测 stdout 末尾 JSON 并直接在 complete 帧带出 `result_summary`,缺失时才需 skill 从 log_file 反向扫描提取(旧服务器回退路径由 CLI `--summary` 自动提取为 `data.result_summary`)。Planner 调用本 skill 时,请在 plan step 的 required_outputs 中声明 [{kind: 'file', extensions: ['.json']}]。请求中用中性动词(运行/执行)描述命令,只有真的会落盘文件时,才使用创建/写入/保存等动词并带上扩展名。认证/配置/权限错误转 delta-shared。"
|
|
4
|
-
metadata:
|
|
5
|
-
requires:
|
|
6
|
-
bins: ["delta-cli"]
|
|
7
|
-
cliHelp: "delta-cli sandbox --help"
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../delta-shared/SKILL.md`](../delta-shared/SKILL.md),其中包含认证、配置和通用错误处理**
|
|
11
|
-
|
|
12
|
-
# delta-sandbox
|
|
13
|
-
|
|
14
|
-
## 何时使用
|
|
15
|
-
|
|
16
|
-
- 用户提到 sandbox、容器、GPU、CUDA、训练、推理、计算任务。
|
|
17
|
-
- 用户需要创建/管理 sandbox 实例、在 sandbox 中运行命令、读写文件。
|
|
18
|
-
- **不要**用于:认证、配置初始化、权限恢复、更新检查 → 转 `delta-shared`。
|
|
19
|
-
|
|
20
|
-
## 使用边界
|
|
21
|
-
|
|
22
|
-
- 所有 sandbox 操作使用 `delta-cli sandbox <subcommand>`。
|
|
23
|
-
- delta-cli 不内嵌默认凭证,运行前必须按 `delta-shared` 完成认证配置。
|
|
24
|
-
- sandbox_id 必须来自真实返回,不要凭用户口述猜测。
|
|
25
|
-
|
|
26
|
-
## 沙箱环境(镜像源)
|
|
27
|
-
|
|
28
|
-
- **国内部署(domestic)**:沙箱创建时服务端已自动注入国内镜像配置。`pip install` / `npm install` / `git clone` / HuggingFace 下载**无需额外配置**,直接可用。覆盖:pip index-url(pypi.tuna.tsinghua.edu.cn)、npm registry(npmmirror)、HF_ENDPOINT(hf-mirror.com)、git insteadOf(github 代理)。
|
|
29
|
-
- **海外部署(overseas)**:不注入镜像,使用官方源。
|
|
30
|
-
- **第三方算力沙箱**:默认不注入镜像(默认关闭),按官方源使用。
|
|
31
|
-
- 若镜像源异常,优先检查沙箱所在区域的网络,而不是假设沙箱未配置。
|
|
32
|
-
|
|
33
|
-
## 强制规则(违规则任务视为失败)
|
|
34
|
-
|
|
35
|
-
1. **必须使用 delta-cli**:所有 sandbox 操作必须通过 `delta-cli sandbox <subcommand>` 执行。**禁止**用 Python(urllib/httpx/requests)或任何 SDK 直接调用 HTTP API。delta-cli 提供类型化错误输出(`error.type`),是 AI agent 正确解析错误的唯一途径。
|
|
36
|
-
2. **必须销毁**:每次 `sandbox create` 成功后,在当前任务的最终 `step_finish` 之前 **必须** 执行 `sandbox kill <id>` 或 `sandbox finish <id>` 销毁 sandbox。未销毁等价于任务失败。
|
|
37
|
-
3. **异常时也必须销毁**:即使 sandbox 内命令失败、超时或报错,仍然 **必须** 执行 `sandbox kill <id>`。异常不是跳过销毁的理由。
|
|
38
|
-
4. **不得依赖外部清理**:server 端 **没有** sandbox 的自动过期或 TTL 机制。不 kill → sandbox 永久占用 GPU 资源。
|
|
39
|
-
5. **同步任务一条命令内完成,后台任务可跨轮次**:
|
|
40
|
-
- 同步模式(`sandbox run`):从 `create` 到 `kill` 的完整生命周期必须在同一次执行中完成。不允许跨任务保留。
|
|
41
|
-
- 后台模式(`sandbox run-bg`):允许在当前执行中将 `sandbox_id` 和 `execution_id` 报告给用户,后续轮次通过 `sandbox status` + `sandbox logs` 获取结果。任务完成后仍需 `sandbox kill`。
|
|
42
|
-
6. **禁止重复创建**:同一次任务中只允许存在一个活跃 sandbox。若命令失败需要重试,优先复用已创建的 `sandbox_id`;若确实需要重新创建,必须先 `sandbox kill <旧_sandbox_id>`,确认旧实例销毁后再执行新的 `sandbox create`。可用 `delta-cli sandbox list` 查询当前用户的活跃 sandbox 来**清理残留**(上轮任务异常中断遗留的 sandbox),但 **禁止** 用 list 来绕过“同一次任务只保留一个 sandbox”的规则。
|
|
43
|
-
|
|
44
|
-
7. **长任务必须后台执行**:预计执行时间超过 60 秒的任务(例如下载模型、训练、微调、长推理、大规模数据处理),**必须**使用 `delta-cli sandbox run-bg <id> --command "..." --timeout <秒>` 提交为后台任务,禁止使用同步的 `sandbox run`。
|
|
45
|
-
|
|
46
|
-
**推荐方式(1 次 tool call 等完成)**:
|
|
47
|
-
```bash
|
|
48
|
-
delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> --wait
|
|
49
|
-
```
|
|
50
|
-
CLI 通过 SSE 实时流(`/logs/stream`)跟随输出,把原始 SSE 帧(`data: {...}`)**逐帧透传到 stdout**(curl -N 风格),帧类型见 JSON 的 `type` 字段:`init`(含 `execution_id`)/ `stdout` / `stderr` / `error` / `complete`。收到 `complete` 即命令结束。**CLI 会在 complete 帧里补全字段**:服务端给 `exit_code`/`execution_id`/`error`,CLI 注入流内累计的 `stdout`/`stderr`、兜底恢复的 `log_file`、以及检测到的末尾 JSON `result_summary`(stdout 末尾 50 行内有合法 JSON 对象时)。命令成败看 `complete` 帧里的 `exit_code`;**命令非零退出或报错时,CLI 追加一个 `error.type: command_failed` 信封并以非零码退出(进程退出码 8)**,信封 `message` 已附 stderr 尾部,便于 `&&` 链与按 `error.type` 分支、快速扫读失败原因;`type:"error"` 帧的 `message` 也会 best-effort 附上已流出的 stderr 尾部;成功则不追加信封。若在 `--timeout`(默认 300s,同时充当服务端命令超时和客户端等待 deadline)内未收到 `complete`,CLI 会在已透传的帧后**追加一个 `finished=false` 的快照信封**(`CommandLogsResult`),表示命令仍运行。完整 stdout 在 `finished` 后被写入沙箱内的 `log_file`(默认 `/workspace/{user_id}/sandbox_logs/delta-result-{sandbox_id}-{execution_id}.json`,以服务端返回的 `log_file` 字段为准),需要读全文时用 `sandbox read <id> --path <log_file>`(末尾 JSON 摘要已在 complete 帧里,通常无需再读)。**`--summary`/`--artifacts` 富化(含 hints)仅在旧服务器回退路径生效**(服务端无 `/logs/stream` 端点时 CLI 自动回退到 5s 轮询并输出 `CommandResult` 信封)。`execution_id` 可用于后续 `sandbox logs` / `cancel`。
|
|
51
|
-
|
|
52
|
-
**实时跟随 / 手动轮询(需要查看中间进度时)**:
|
|
53
|
-
`sandbox logs <id> --execution-id <exec_id> --stream` 实时跟随(SSE):原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件,无末尾信封(`--stream` 必须传 `--execution-id`)。`sandbox logs <id> --execution-id <exec_id>`(不加 `--stream`)返回快照信封;`--execution-id` 可省略——省略时仅按 sandbox 查询,服务端因 sandbox_id / execution_id 未同时传递而自动走 ES(Elasticsearch)查询(此时返回按 execution_id 聚合的**数组**,而非单个快照对象):
|
|
54
|
-
- `finished=true` + `exit_code=0` → 完成
|
|
55
|
-
- `finished=true` + `exit_code!=0` → 失败
|
|
56
|
-
- `running=true` → 仍运行
|
|
57
|
-
|
|
58
|
-
**故障诊断**:连续 3 次 `running=true` 且 `stderr_tail`/`stdout_tail`/`cursor` 无变化且距离提交超过 60 秒 → 用 `sandbox status <id>` 检查 sandbox 是否存活。注意:`cursor` 字段无 `omitempty`,永远会出现在响应中(即使为 0);`stderr_tail` 默认只保留 **最后 200 字节** stderr 片段,`stdout_tail` 默认保留 **最后 800 字节** stdout 片段(CLI 硬编码 `tailString(s, n)`,按字节截取;ASCII 文本等同于 n 字符,多字节 UTF-8 可能被截断),`cursor=0` 通常意味着服务端从 provider 还没读到任何 stderr 行。
|
|
59
|
-
|
|
60
|
-
- 后台任务成功后仍需按规则 2/3 销毁 sandbox。
|
|
61
|
-
|
|
62
|
-
8. **写入文件默认落到 sandbox 的 working-directory,禁止使用 Shell 变量路径**:
|
|
63
|
-
- ✅ **推荐**:`delta-cli sandbox write <id> --source train.py`(不传 `--path`,默认写到 `<working-directory>/train.py`)
|
|
64
|
-
- ✅ **显式指定**:`delta-cli sandbox write <id> --path <working-directory>/train.py --source train.py`(`--path` 传相对路径也会自动以 working-directory 为前缀)
|
|
65
|
-
- ❌ **禁止**:`--source "$WORKSPACE_ROOT/train.py"`、`--source "$(pwd)/train.py"`(Shell 展开路径 → 空文件或失败)
|
|
66
|
-
- ❌ **禁止**:`--data "大量代码..."`(Shell 转义问题)
|
|
67
|
-
- ❌ **不建议**:`--path /workspace/train.py` 这类直接写工作目录根、落在 `{user_id}` 之外的路径——kill 后 `rclone sync` 不会写回 OSS,文件不持久化。
|
|
68
|
-
- 查询 sandbox 的 working-directory:`delta-cli sandbox working-directory <id>`(返回 `data.path`)。`run`/`run-bg` 引用脚本时用 `python <working-directory>/train.py` 的完整路径。
|
|
69
|
-
- 少量配置(< 20 行)可用 `--data "..."`,此时必须带 `--path`(无法从文件名推断)。`--mode 755` 可设置文件权限。
|
|
70
|
-
|
|
71
|
-
9. **每个 `run-bg` 生成独立 `execution_id`**:每次调用 `sandbox run-bg` 都会返回一个唯一的 `execution_id`,多个后台任务之间通过它区分。务必保存每次返回的 `execution_id` 并与任务对应,后续通过 `sandbox logs <id> --execution-id <exec_id>` 分别查询各任务的结果。同步 `sandbox run` 无需 `execution_id`,结果直接返回。
|
|
72
|
-
|
|
73
|
-
10. **执行 delta-cli 命令必须使用 `bash` 工具,禁止用 `python_repl` 包装 `subprocess`**
|
|
74
|
-
- `delta-cli` 返回的是结构化 JSON,用 `bash` 直接执行最清晰,也便于 `log_file` 提取。
|
|
75
|
-
- ❌ **禁止**:在 `python_repl` 里写 `subprocess.run(['delta-cli', ...])`。这会把输出混在 Python 代码块中,既难读又多走一步解析。
|
|
76
|
-
- ✅ **正确**:`bash` 工具直接执行 `delta-cli sandbox ...`。
|
|
77
|
-
- 只有在**解析 JSON**(例如从 `sandbox read` 的信封中提取 `summary` 并写入 `result.json`)时,才使用 `python3`/`python_repl`。
|
|
78
|
-
|
|
79
|
-
11. **代码/数据必须经由 `delta-cli sandbox write` 进入 sandbox**
|
|
80
|
-
- 所有要送进 sandbox 执行的脚本、配置、数据文件,**必须**通过 `delta-cli sandbox write <id> --source <本地文件名>` 写入,默认落到该 sandbox 的 working-directory(`/workspace/{user_id}/{sandbox_id}/`,可用 `delta-cli sandbox working-directory <id>` 查询),位于 OSS 同步范围内、kill 后可持久化。
|
|
81
|
-
- 不要把脚本先写到宿主 workspace 再依赖同步;宿主 workspace 的文件对 sandbox 内部不可见。
|
|
82
|
-
- 本规则关注的是“入口统一为 `sandbox write`”,不针对任何特定宿主工具。无论宿主提供何种文件操作接口,最终都要把内容送到 sandbox 内部。
|
|
83
|
-
|
|
84
|
-
12. **任务完成标准是执行结果,而不是脚本文件存在**
|
|
85
|
-
- 本 skill 的交付物是**命令在 sandbox 中执行后的结果摘要**(本地 `result.json` 及其 `result_summary`),而不是 `.py`、`.sh` 等脚本文件本身。
|
|
86
|
-
- 仅把脚本写入 disk 不代表任务完成;必须完成 `write → run → read log_file → create result.json → kill sandbox` 整个生命周期,才能调用 `step_finish`。
|
|
87
|
-
- 因此,`skill_request` 中不要用“写出/保存/创建某某文件并执行”这类把文件落盘当成里程碑的措辞,而是直接要求“运行某某命令并返回结果摘要”。
|
|
88
|
-
|
|
89
|
-
13. **`result.json` 是完成前的必要条件**
|
|
90
|
-
- 在调用 `step_finish` 之前,必须确认:
|
|
91
|
-
1. `delta-cli sandbox run` / `run-bg --wait` 已返回且 `finished=true`;
|
|
92
|
-
2. (默认)直接复用 `sandbox run` / `run-bg --wait` 的 SSE `complete` 帧里的 `result_summary` 写入 `result.json`;缺失时(fallback)从 `log_file` 用 `sandbox read` + Python 解析
|
|
93
|
-
3. 已执行 `sandbox kill <id>` 销毁 sandbox;
|
|
94
|
-
4. `final_response` 只有一行 `RESULT: <result_summary>`。
|
|
95
|
-
|
|
96
|
-
14. **避免冗余检查与验证**
|
|
97
|
-
- 如果 `skill_request` 或用户请求已经明确指定了镜像与资源,**不要**在 `sandbox create` 前先调用 `sandbox images` 或 `sandbox recommend`。
|
|
98
|
-
- 文件写入 sandbox 后,只需要 `sandbox stat`(可选)确认 size 正常即可。**禁止**用 `sandbox ls` + `sandbox read` 把刚写入的文件读回宿主再验证内容;这会产生一次无意义的往返。
|
|
99
|
-
- 不要在 `sandbox create` 成功后反复调用 `sandbox status` 轮询;create 返回时 sandbox 已就绪。
|
|
100
|
-
|
|
101
|
-
## 请求措辞约定
|
|
102
|
-
|
|
103
|
-
为降低被 host 的 Phase-10B 文件扩展名校验误判的概率,构造 `skill_request` 时遵守:
|
|
104
|
-
|
|
105
|
-
1. **运行/执行命令时,用中性动词**
|
|
106
|
-
优先使用 `运行`、`执行`、`调用`。
|
|
107
|
-
- 正确:`在 GPU sandbox 中运行 PyTorch CUDA 自检脚本`
|
|
108
|
-
- 不推荐在请求里直接写代码并带“输出”动词,例如
|
|
109
|
-
`脚本需输出:1) torch.cuda.is_available();...`
|
|
110
|
-
其中的 `torch.cuda` 会被正则误判为承诺输出 `.cuda` 文件。
|
|
111
|
-
|
|
112
|
-
2. **只有真正会创建文件时,才用承诺动词 + 扩展名**
|
|
113
|
-
`创建`、`写入`、`保存`、`输出`、`生成` 这类词只在你确实会让 skill 落盘文件时使用,并显式写出扩展名。
|
|
114
|
-
- 正确:`创建 check_cuda.py 并运行`
|
|
115
|
-
- 正确:`训练完成后将指标写入 result.json`
|
|
116
|
-
|
|
117
|
-
3. **不要把完整代码块写进 `skill_request`**
|
|
118
|
-
用自然语言描述任务目标,具体代码由 skill 内部写入 sandbox。
|
|
119
|
-
- 正确:`运行标准 PyTorch CUDA 可用性检查`
|
|
120
|
-
- 不推荐:在 `skill_request` 里粘贴 `import torch; print(torch.cuda.is_available())`。
|
|
121
|
-
|
|
122
|
-
### required_outputs 声明
|
|
123
|
-
|
|
124
|
-
构造调用本 skill 的 plan step 时,**必须**在 `required_outputs` 中声明会落盘一个 `.json` 文件:
|
|
125
|
-
|
|
126
|
-
```yaml
|
|
127
|
-
required_outputs:
|
|
128
|
-
- kind: file
|
|
129
|
-
extensions: [".json"]
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
原因:本 skill 运行命令后会从 sandbox 读取 `log_file` 并写入本地 `result.json`。host 的 Phase-10B deliverable 校验是“或”语义(任意一个 required 扩展名被真实文件满足即可);显式声明 `.json` 可以避免 `torch.cuda` 等代码 token 被误判为 `.cuda` 文件承诺,从而防止 `VERIFICATION_FAILED` 导致的重复执行。
|
|
133
|
-
|
|
134
|
-
## 快速路由
|
|
135
|
-
|
|
136
|
-
> ⚠️ **`list` 与 `ls` 区分**:`sandbox list` 列出用户的 **沙箱实例**(与 `docker ps` 类似),`sandbox ls <id>` 列出 **沙箱内目录**(与 `ls` 命令类似)。Tab 补全时注意区分。
|
|
137
|
-
|
|
138
|
-
| 用户目标 | 命令 |
|
|
139
|
-
|---------|------|
|
|
140
|
-
| **发现** | |
|
|
141
|
-
| 查看可用镜像(镜像名) | `sandbox images` |
|
|
142
|
-
| 查看剩余可申请资源 | `sandbox resources` — 按算力分组返回 GPU/显存/核心可用量:`compute_sources[].source` 为 `private`(私有算力)或 `third_party`(第三方算力);`gpu_types` 结构随分组不同(private 含 `vgpu`/`core`/`memory_mib`,third_party 含 `gpu_name`/`total_gpu_num`/`idle_gpu_num`)。算力后端为内部概念,自动选择,无需指定 |
|
|
143
|
-
| 获取资源推荐 | `sandbox recommend --cpu N --memory XGi [--gpu N] [--gpu-mem N]` |
|
|
144
|
-
| 列出当前用户的 sandbox | `sandbox list [--status <running/finished/killed/error>] [--start-time <ISO8601>] [--end-time <ISO8601>] [--sandbox-id <id>] [--days N]` |
|
|
145
|
-
| **生命周期** | |
|
|
146
|
-
| 创建 sandbox | `sandbox create --image-name <镜像名> [--cpu N --memory XGi --gpu N --gpu-mem N --gpu-type <型号> --max-life M]`;`--image-name` 用镜像名(`sandbox images` 返回的 `image_name` 字段,如 `"PyTorch CUDA13 (GPU)"`),底层镜像标识对用户隐藏;`--gpu-type` 指定 GPU 型号(如 `"RTX 4090"`、`H100`),服务端映射为 HAMi use-gputype 注解按型号调度,可选型号查 `sandbox resources`(`third_party` 分组 `gpu_types[].gpu_name`),不传则自动调度;不希望被自动清理时加 `--no-auto-cleanup`(仅显式 kill/finish 可销毁);**单沙箱资源上限** cpu ≤ 512 / memory ≤ 1024Gi / gpu ≤ 64 / gpuMem ≤ 1024Gi,超出快速报错不挂起 |
|
|
147
|
-
| 连接 sandbox | `sandbox connect <id>` |
|
|
148
|
-
| 查看状态 | `sandbox status <id>` |
|
|
149
|
-
| 完成 sandbox | `sandbox finish <id> [--results '{...}']` |
|
|
150
|
-
| 销毁 sandbox | `sandbox kill <id>` |
|
|
151
|
-
| **命令执行** | |
|
|
152
|
-
| 同步运行命令(≤60s) | `sandbox run <id> --command "..." [--timeout <秒>] [--summary/--no-summary] [--artifacts]` |
|
|
153
|
-
| 后台运行命令(>60s) | `sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--summary/--no-summary] [--artifacts]` |
|
|
154
|
-
| 查看后台日志 | `sandbox logs <id> [--execution-id <exec_id>] [--tail N --grep <pattern> --context N] [--stream]`(`--execution-id` 可选;省略时按 sandbox 从 ES 查询,仅 `--stream` 必须传)|
|
|
155
|
-
| 中断后台命令 | `sandbox cancel <id> --execution-id <exec_id>` |
|
|
156
|
-
| **内网穿透(端口暴露)** | |
|
|
157
|
-
| 暴露端口到公网 | `sandbox expose <id> [--port N]` — 把沙箱内服务端口暴露到公网,返回 `public_url`(`http://<host>:<remote_port>`,remote_port 由服务端从 20000-29999 随机分配);**必须先启服务再暴露**(服务端探活失败报「服务未就绪」);`--port` 默认 8700(平台约定) |
|
|
158
|
-
| 查询活跃隧道 | `sandbox exposes <id>` — 列出该 sandbox 当前的公网隧道 |
|
|
159
|
-
| 回收隧道 | `sandbox dispose <id> --port N` — 显式回收单条隧道(杀 frpc + 释放公网端口);kill sandbox 也会自动回收全部隧道 |
|
|
160
|
-
| **文件操作** | |
|
|
161
|
-
| 读取文件 | `sandbox read <id> --path <path> [--output <本地路径> --tail N --grep <pattern> --offset N --limit N --context N --max-bytes N --parse-json]` |
|
|
162
|
-
| 拉取文件/目录 | `sandbox pull <id> --source <沙箱路径> --target <本地路径> [--recursive] [--pattern <glob>]` — 单文件或目录递归,含 sha1 校验(mirror of upload,flag 方向与 upload 相反:source=远程,target=本地) |
|
|
163
|
-
| 查询工作目录 | `sandbox working-directory <id>`(返回 `data.path`,形如 `/workspace/{user_id}/{sandbox_id}`)|
|
|
164
|
-
| 写入文件 | `sandbox write <id> --source <文件名>`(不传 `--path` 默认写到 working-directory);`--path <路径>` 传相对路径自动加 working-directory 前缀,绝对路径原样使用 |
|
|
165
|
-
| 批量写入 | `sandbox write-multiple <id> --entry <远程路径>=<本地路径> [--entry ...]` |
|
|
166
|
-
| 上传目录 | `sandbox upload <id> --source <本地目录> --target <沙箱路径>` |
|
|
167
|
-
| 列出目录 | `sandbox ls <id> --path <路径>`(默认 `.`)|
|
|
168
|
-
| 文件元数据 | `sandbox stat <id> --path <path>` |
|
|
169
|
-
| 移动/重命名 | `sandbox mv <id> --entry <src=dest> [--entry ...]` |
|
|
170
|
-
| 替换内容 | `sandbox replace <id> --path <path> --old <文本> --new <文本>` |
|
|
171
|
-
| 修改权限 | `sandbox chmod <id> --path <path> --mode <八进制>` |
|
|
172
|
-
| 删除文件 | `sandbox rm <id> --path <path> [--path ...]` |
|
|
173
|
-
| 删除目录 | `sandbox rmdir <id> --path <路径> [--path ...]` |
|
|
174
|
-
| 创建目录 | `sandbox mkdir <id> --path <路径> [--path ...]` |
|
|
175
|
-
| 搜索文件 | `sandbox search <id> --path <根目录> --pattern <glob>` |
|
|
176
|
-
| 上传目录 | `sandbox upload <id> --source <本地目录> --target <沙箱路径>` |
|
|
177
|
-
|
|
178
|
-
## 完整生命周期
|
|
179
|
-
|
|
180
|
-
1. **选择镜像(按需)**:
|
|
181
|
-
- 算力后端(provider)是内部概念,由系统自动选择,**不需要也不支持用户指定**。
|
|
182
|
-
- 如果请求里已经明确指定了镜像(例如 `--image-name "PyTorch CUDA13 (GPU)"`,镜像名)和资源(cpu/memory/gpu,以及可选的 `--gpu-type`),**直接跳到步骤 2 创建 sandbox**,不要再调 `sandbox images` / `sandbox recommend`。
|
|
183
|
-
- 只有在用户要求“推荐一个配置”或需要查询可用镜像时,才调用:
|
|
184
|
-
- `delta-cli sandbox images` — 服务端支持的镜像列表(返回镜像名,隐藏底层镜像标识)
|
|
185
|
-
- `delta-cli sandbox recommend --cpu N --memory XGi [--gpu N]` — 资源配置推荐
|
|
186
|
-
2. **创建**:`delta-cli sandbox create --image-name <镜像名> --cpu 4 --memory 16Gi --gpu 1 --gpu-mem 8000 --max-life 120`。**返回的 JSON 信封中是 `data.sandbox_id`,不是 `data.id`;后续所有命令必须使用这个真实的 `sandbox_id`。**(创建后 sandbox 立即可用,无需额外连接)。**响应会回显请求的镜像名/resource(服务端未返回的字段由 CLI 用请求值补齐,服务端返回值优先),可直接核对一遍资源配置,无需额外调 status。**(镜像回显为显示名;`provider` 为内部概念,不再回显)。**同一次任务若已有 `sandbox_id`,禁止再次 create,必须优先复用。**
|
|
187
|
-
- --max-life 指定 sandbox 最大存活时间(分钟),默认 30。长任务请调高,确保 sandbox 在命令执行期间不被回收。
|
|
188
|
-
- **`--gpu-mem`(单位 MiB)要匹配实际运行的模型**:模型参数量/精度越大所需显存越大,大模型塞进过小显存会直接 OOM。示例中的 8000(8GiB)只适配小模型,跑 7B/9B/13B 前先评估显存需求并适当调大。
|
|
189
|
-
- **`--gpu-type <型号>`(可选)指定 GPU 型号**:如 `"RTX 4090"`、`H100`,服务端映射为 HAMi use-gputype 注解,将 pod 调度到匹配型号的 GPU 上。可用 `sandbox resources` 查看可选型号(`third_party` 分组的 `gpu_types[].gpu_name`)。仅在用户明确要求特定型号时使用;不传则由系统自动调度。
|
|
190
|
-
- `--no-auto-cleanup`:加此 flag 后该 sandbox **不会被自动清理**(服务端超时回收 + 本地周期 cleanup_stale 均跳过),只能通过显式 `sandbox kill`/`finish` 销毁。仅当任务确实需要跨越周期清理长期存活时才使用,任务结束后必须主动销毁,避免资源泄漏。
|
|
191
|
-
- **禁止在 create 成功后反复调用 `sandbox status` 轮询**。`sandbox create` 返回时 sandbox 已经就绪,直接用它返回的 `data.sandbox_id` 执行 `write`/`run` 即可。多余的轮询会增加工具调用次数且没有任何收益。
|
|
192
|
-
3. **写入代码/数据**:
|
|
193
|
-
- **单个文件**:`delta-cli sandbox write <id> --source <文件名>` — 不传 `--path`,默认写到该 sandbox 的 working-directory(`/workspace/{user_id}/{sandbox_id}/`,可用 `delta-cli sandbox working-directory <id>` 查询)。`--source` 只传本地文件名(如 `--source train.py`),**禁止**使用 `"$WORKSPACE_ROOT/train.py"` 或 `$(pwd)/train.py` 等 Shell 变量路径。`--source` 让 CLI 自行读取本地文件,不会经过 Shell 展开,是最安全的方式。若需指定路径,`--path <相对路径>` 会自动加 working-directory 前缀,绝对路径原样使用(**不建议**写 `/workspace/<filename>` 这种落在 `{user_id}` 之外的位置,kill 后不持久化)。写入后返回的 `size` 字段是实际磁盘字节数(来自 stat 验证),可对比确认写入完整性。少量配置(<20 行)可用 `--data "..."`,但此时必须带 `--path`。文件路径和扩展名由镜像中的运行时决定。
|
|
194
|
-
- **批量写入**:`sandbox write-multiple <id> --entry <远程路径>=<本地路径> [--entry ...]`(远程路径在 `=` 左边,本地路径在右边,`--data` 批量写入不可用)
|
|
195
|
-
- **上传目录**:`sandbox upload <id> --source <本地目录> --target <沙箱路径>` — CLI 将本地目录打包为 tar.gz,通过 multipart/form-data 上传,服务端自动解压到 target 目录。返回每个文件的路径、大小、模式。上传后 CLI 自动对比本地和远程文件清单做完整性校验(大小不匹配、多余文件等会告警)。
|
|
196
|
-
- **注意**:`--source` 是**本地目录路径**,`--target` 是**沙箱内的目标目录**,target 目录不存在会自动创建。
|
|
197
|
-
- 支持嵌套目录,空目录也会被创建。
|
|
198
|
-
- **写后验证(可选)**:`sandbox stat <id> --path <path>` 确认文件存在且 size 符合预期即可。**不要**用 `sandbox ls` + `sandbox read` 把刚写入的文件读回宿主再逐字对比;无异常时不需要读回。
|
|
199
|
-
4. **运行命令**:
|
|
200
|
-
- **短任务(预计 ≤ 60 秒)**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,命令的 stdout/stderr 通过 SSE **以原始 `data:` 帧逐帧透传到 stdout**(无末尾信封),`exit_code` / `log_file` / `execution_id` 从 `complete` 帧读取。完整 `stdout` 在完成后写入 run envelope 日志文件(`log_file` 字段),需要读全文或提取末尾 JSON 时用 `sandbox read`,**不要**再调用 `sandbox logs`。可通过 `--log-file <路径>` 自定义 run envelope 日志文件路径;默认值为 `/workspace/{user_id}/sandbox_logs/delta-result-{sandbox_id}-{execution_id}.json`(以服务端返回的 `log_file` 字段为准)。脚本经 `sandbox write` 落盘后位于 working-directory,运行命令需用完整路径 `<working-directory>/<文件名>`(先 `sandbox working-directory <id>` 查询)。根据镜像中的运行时构造命令,常见示例(`<wd>` 为 working-directory):
|
|
201
|
-
- Python:`python <wd>/train.py`
|
|
202
|
-
- Node.js:`node <wd>/app.js`
|
|
203
|
-
- Go:`go run <wd>/main.go`
|
|
204
|
-
- Shell:`bash <wd>/run.sh`
|
|
205
|
-
- **长任务(预计 > 60 秒,如下载模型、训练、编译、大规模数据处理)**:`delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> [--log-file <路径>]` 提交后台任务,获得 `execution_id` 后通过以下命令查询:
|
|
206
|
-
- `delta-cli sandbox logs <id> --execution-id <execution_id> --stream` — SSE 实时跟随,原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件(含 `exit_code`/`log_file`),无末尾信封(`--stream` 必须传 `--execution-id`)。
|
|
207
|
-
- `delta-cli sandbox logs <id> --execution-id <execution_id>` — 快照信封,返回 `stdout_tail`(末尾 800 字节)、`stderr_tail`(末尾 200 字节)、`stdout_size`、`stderr_size`、`cursor`、`running`、`finished`、`exit_code`、`log_file`(完成后)。当 `finished=true` 时认为完成,完整输出需读取 `log_file`。`--execution-id` 可省略:省略时仅按 sandbox 查询,服务端因 sandbox_id / execution_id 未同时传递而自动走 ES 查询,返回按 execution_id 聚合的**数组**。
|
|
208
|
-
禁止对长任务使用同步 `sandbox run`
|
|
209
|
-
|
|
210
|
-
**推荐实践**:让 sandbox 脚本在 `stdout` 末尾打印一个独立的结构化 JSON 对象(例如 `{"status":"ok", ...}`)。SSE 路径下 CLI 会检测该末尾 JSON 并直接在 `complete` 帧里带出 `result_summary`,**任务结束后直接复用 complete 帧的 `result_summary` 写入 result.json**,无需再 `sandbox read` log_file 反向扫描。仅当 complete 帧缺失 `result_summary`(末尾无合法 JSON)时,才用 `sandbox read <id> --path <log_file>` 读取并按步骤 5 反向扫描提取,避免让大模型“读整段日志再摘要”。
|
|
211
|
-
5. **生成 result.json**(SSE 首选:直接复用 complete 帧的 `result_summary`;缺失时才从 `log_file` 读取解析):
|
|
212
|
-
- **获取 `log_file` 路径**:`run` / `run-bg --wait` 的 SSE `complete` 帧含 `log_file` 字段(CLI 已兜底补全);`sandbox logs <id> --execution-id <eid>` 快照在 `finished=true` 后同样返回 `log_file`。需要 read 时再按下面方式解析:
|
|
213
|
-
- `sandbox read` 返回的是 CLI 信封 `{"ok":true,"data":{"content":"..."}}`,真实结果 JSON 在 `data.content` 字段里。
|
|
214
|
-
- **不要把 CLI 信封或完整长 `stdout` 原样写入本地 `result.json`**;应该解析后生成一份精简的结构化摘要。
|
|
215
|
-
- **推荐做法(避免 heredoc 引号失败)**:先用 `bash` 把信封写入临时文件,再用 `python3`(或 `python_repl`)读取该文件并生成 `result.json`。**不要**把 `delta-cli sandbox read` 的输出通过管道直接喂给内联 heredoc,管道+heredoc 的组合极易因 Shell 转义/引号问题失败。
|
|
216
|
-
1. `bash` 保存信封:
|
|
217
|
-
```bash
|
|
218
|
-
delta-cli sandbox read <sandbox_id> --path <log_file> > /tmp/_delta_result_envelope.json
|
|
219
|
-
```
|
|
220
|
-
2. `python3` / `python_repl` 生成 `result.json`:
|
|
221
|
-
```python
|
|
222
|
-
import json, os
|
|
223
|
-
|
|
224
|
-
env = json.load(open("/tmp/_delta_result_envelope.json", encoding="utf-8"))
|
|
225
|
-
raw = json.loads(env["data"]["content"])
|
|
226
|
-
|
|
227
|
-
# 精简到用户关心的极简字段,避免 host read_file 预览截断
|
|
228
|
-
concise = {
|
|
229
|
-
"exit_code": raw.get("exit_code"),
|
|
230
|
-
"finished": raw.get("finished"),
|
|
231
|
-
"summary": {},
|
|
232
|
-
"error": raw.get("error"),
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
# 优先提取 stdout 末尾的独立 JSON 对象作为 summary
|
|
236
|
-
stdout = raw.get("stdout", "")
|
|
237
|
-
for line in reversed(stdout.strip().splitlines()):
|
|
238
|
-
s = line.strip()
|
|
239
|
-
if s.startswith("{") and s.endswith("}"):
|
|
240
|
-
try:
|
|
241
|
-
concise["summary"] = json.loads(s)
|
|
242
|
-
break
|
|
243
|
-
except Exception:
|
|
244
|
-
pass
|
|
245
|
-
|
|
246
|
-
# 构造 result_summary:一行字符串,最终回答的 RESULT: 行直接用它
|
|
247
|
-
parts = [f"exit_code={concise['exit_code']}", f"finished={concise['finished']}"]
|
|
248
|
-
for key, value in concise["summary"].items():
|
|
249
|
-
# 避免把长文本塞进 result_summary
|
|
250
|
-
if isinstance(value, (str, int, float, bool)) and len(str(value)) < 200:
|
|
251
|
-
parts.append(f"{key}={value}")
|
|
252
|
-
concise["result_summary"] = ", ".join(parts)
|
|
253
|
-
|
|
254
|
-
ws = os.environ.get("WORKSPACE_ROOT", ".")
|
|
255
|
-
with open(os.path.join(ws, "result.json"), "w", encoding="utf-8") as f:
|
|
256
|
-
json.dump(concise, f, indent=2, ensure_ascii=False)
|
|
257
|
-
print(concise["result_summary"])
|
|
258
|
-
```
|
|
259
|
-
3. 如果当前环境没有 `python3`,用 `jq`、`node` 或任何可解析 JSON 的工具实现**相同逻辑**:1)读取 `/tmp/_delta_result_envelope.json`;2)解开 CLI 信封取 `data.content`;3)解析内部 JSON;4)从 `stdout` 末尾提取 summary JSON;5)生成 `result_summary` 并写入 `result.json`。
|
|
260
|
-
- 如果 `stdout` 非常大(训练日志、下载日志等),不要把整段 `stdout` 写进 `result.json`;只保留关键指标和 preview。
|
|
261
|
-
- 完整原始输出仍保留在 sandbox 的 `log_file` 中,必要时可再次 `sandbox read` 获取。
|
|
262
|
-
- 写入本地副本的目的是让 host 的 deliverable 校验识别到真实的 `.json` 文件;由于校验是“或”语义,`.json` 文件会盖过 `torch.cuda` 等 token 被误判出的 `.cuda` 文件承诺。
|
|
263
|
-
6. **销毁**:`delta-cli sandbox kill <id>`(如需保存结果,用 `sandbox finish --results '{...}'` 替代 kill,finish 会自动销毁 sandbox)
|
|
264
|
-
|
|
265
|
-
详细步骤见 [lifecycle.md](references/lifecycle.md)。
|
|
266
|
-
|
|
267
|
-
## 内网穿透(端口暴露)
|
|
268
|
-
|
|
269
|
-
把沙箱内服务端口暴露到公网,让用户通过 `public_url` 直接访问。原理:沙箱内
|
|
270
|
-
frpc 向外拨号到公网 frps,frps 把公网端口(20000-29999 随机分配)的 TCP 流量
|
|
271
|
-
转发回沙箱内服务;**无需在宿主机为每个 sandbox 配置端口**(反向连接)。
|
|
272
|
-
|
|
273
|
-
可用命令:
|
|
274
|
-
|
|
275
|
-
| 命令 | 作用 |
|
|
276
|
-
|------|------|
|
|
277
|
-
| `sandbox expose <id> [--port N]` | 暴露端口,返回 `public_url`;`--port` 默认 **8700**(平台约定),不传即可 |
|
|
278
|
-
| `sandbox exposes <id>` | 查询该 sandbox 当前活跃隧道(service_port / remote_port / public_url) |
|
|
279
|
-
| `sandbox dispose <id> --port N` | 显式回收单条隧道(杀 frpc + 释放公网端口) |
|
|
280
|
-
|
|
281
|
-
必须遵守的顺序与约定:
|
|
282
|
-
|
|
283
|
-
1. **先启服务,再暴露**。expose 前服务必须在沙箱内监听(绑定 `0.0.0.0`,不是
|
|
284
|
-
仅 `127.0.0.1`),且服务端会对 `127.0.0.1:{port}` 探活——**任何 HTTP 响应
|
|
285
|
-
(含 4xx/5xx)都算存活**,只有连接失败/超时才报「服务未就绪」。
|
|
286
|
-
2. **长驻服务必须前台常驻**:用 `run-bg` 时让服务进程成为任务的前台进程,
|
|
287
|
-
**禁止** `nohup ... &` 后台化(任务结束时后台进程会被回收,服务随之消失):
|
|
288
|
-
|
|
289
|
-
```bash
|
|
290
|
-
delta-cli sandbox run-bg <id> --command "cd <wd> && exec python3 -m http.server 8700 --bind 0.0.0.0" --timeout 3600
|
|
291
|
-
delta-cli sandbox expose <id> # --port 默认 8700,无需传
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
3. **生命周期自动回收**:`kill` sandbox 时服务端钩子自动把隧道记录置 closed,
|
|
295
|
-
frps 侧公网端口随 frpc 连接断开自动释放;`dispose` 用于不销毁 sandbox 的
|
|
296
|
-
显式回收。**暴露的端口对公网全开放,业务服务自身必须带鉴权**,不要裸奔端口。
|
|
297
|
-
4. 公网连不上排查顺序:① 沙箱内服务是否监听(`curl 127.0.0.1:{port}`,可先
|
|
298
|
-
`sandbox run` 验证)→ ② frpc 进程是否存活(`ps aux | grep frpc`)→ ③ frps
|
|
299
|
-
公网主机连通性 → ④ 安全组是否放行业务端口段 → ⑤ frpc/frps 两端 token 一致。
|
|
300
|
-
|
|
301
|
-
## 通用结构化输出约定
|
|
302
|
-
|
|
303
|
-
本约定适用于**所有** `delta-sandbox` 任务(GPU/CPU、训练/推理/编译/数据处理等),目的是让 Runner 能直接从 `result.json` 读到结论,无需再次解析长日志。
|
|
304
|
-
|
|
305
|
-
### sandbox 脚本/命令应该输出的内容
|
|
306
|
-
|
|
307
|
-
命令执行完毕后,推荐在 `stdout` 末尾打印一行**独立的结构化 JSON**。例如:
|
|
308
|
-
|
|
309
|
-
- CUDA 检查:`{"status":"ok","torch_version":"2.5.1+cu121","cuda_available":true,"device_name":"NVIDIA H100 80GB HBM3"}`
|
|
310
|
-
- 训练任务:`{"status":"ok","epochs":10,"final_loss":0.12,"final_accuracy":0.94,"model_file":"<wd>/model.pt"}`
|
|
311
|
-
- 数据处理:`{"status":"ok","input_rows":10000,"output_rows":9876,"output_file":"<wd>/output.csv"}`
|
|
312
|
-
|
|
313
|
-
这样 SKILL 可以用同一套代码把它提取到 `result.json` 的 `summary` 字段,再生成 `RESULT:` 行。
|
|
314
|
-
|
|
315
|
-
- **禁止**在 stdout 中打印 `FINAL_ANSWER: ...` 这类冗余标记行;多余的标记会让 Runner 把普通文本误认为结果字段。
|
|
316
|
-
- `summary` 中**不要**使用 `final_answer` 这类与 schema 重复的关键字。统一用语义字段表示结果,例如推理任务用 `generated_text` / `output` / `result`,训练任务用 `final_loss` / `final_accuracy`,数据处理用 `output_file` / `output_rows`。
|
|
317
|
-
|
|
318
|
-
### 本地 `result.json` 通用 Schema
|
|
319
|
-
|
|
320
|
-
```json
|
|
321
|
-
{
|
|
322
|
-
"exit_code": 0,
|
|
323
|
-
"finished": true,
|
|
324
|
-
"summary": {},
|
|
325
|
-
"result_summary": "exit_code=0, status=ok, torch_version=..., ...",
|
|
326
|
-
"error": null
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
关键字段:
|
|
331
|
-
- `summary`: 命令输出的结构化结果对象。
|
|
332
|
-
- `result_summary`: **一行字符串**,与最终回答的 `RESULT:` 行内容一致,方便 Runner 直接读取。
|
|
333
|
-
- `error`: 命令失败时的错误信息;成功时一般为 `null`。
|
|
334
|
-
- **不要把完整 `stdout` 塞进来**。完整日志仍保留在 sandbox 的 `log_file` 中。
|
|
335
|
-
|
|
336
|
-
## 输出阅读与最终回答格式
|
|
337
|
-
|
|
338
|
-
1. **SSE 路径下 `sandbox run` / `run-bg --wait` 的 complete 帧直接带 `result_summary`**(CLI 检测 stdout 末尾 JSON 注入)以及 `stdout` / `stderr` / `log_file`(CLI 兜底补全)。`exit_code` / `log_file` / `execution_id` 从 complete 帧读取;命令失败时 complete 帧后追加 `error.type: command_failed` 信封(进程退出码 8)。`log_file` 路径也可从 `sandbox logs <id> --execution-id <eid>` 快照(`finished=true` 后)获得。
|
|
339
|
-
2. **提取摘要**:首选直接复用 complete 帧的 `result_summary`;缺失时用 `sandbox read <id> --path <log_file>` 读取 run envelope 日志文件并解析,反向扫描 stdout 末尾 JSON 生成 `result_summary`(见“完整生命周期”步骤 5)。
|
|
340
|
-
- run envelope 日志文件包含完整 `stdout`、`stderr`、`exit_code`、`finished`、`command`、`error`。
|
|
341
|
-
3. **生成本地精简 `result.json`**(见“完整生命周期”步骤 5 和“通用结构化输出约定”)。本地副本只应包含关键字段和摘要,**不应包含完整长 `stdout`**。
|
|
342
|
-
4. **最终回答必须且只能是 `RESULT:` 开头的一行。**
|
|
343
|
-
- 优先直接复用 `result.json` 里的 `result_summary` 字段:
|
|
344
|
-
```text
|
|
345
|
-
RESULT: <result.json 中的 result_summary>
|
|
346
|
-
```
|
|
347
|
-
- 如果 `result_summary` 缺失,则从 `summary` 对象构造一行,例如:
|
|
348
|
-
```text
|
|
349
|
-
RESULT: exit_code=0, status=ok, torch_version=2.7.0+cu128, cuda_available=True, device_name=NVIDIA GeForce RTX 3090.
|
|
350
|
-
```
|
|
351
|
-
5. **`final_response` 必须以 `RESULT:` 开头**
|
|
352
|
-
- **首位字符必须是 `R`,紧接 `ESULT:` 前缀**,整串只有 `RESULT: <result_summary>` 一行,不允许任何前置自然语言、空行、过渡句或过程汇报。
|
|
353
|
-
- 原因:host Runner 与下游 LLM 默认只识别 `RESULT:` 开头的 `final_response` 并把其后内容作为任务结论转述给用户。任何前置文字——包括"沙箱已销毁"、"任务完成"、"完整的生命周期已完成"等中性过程汇报——都会让 Runner 把 `final_response` 当作过程摘录而非最终结论,转而向用户输出"未包含具体内容"等失败回复,让真实结论丢失。
|
|
354
|
-
- 正确:`RESULT: exit_code=0, cuda_available=True, torch_version=2.5.1+cu121, device_name=NVIDIA H100 80GB HBM3`
|
|
355
|
-
- 错误(前缀污染,真实案例):`沙箱已成功销毁。完整的生命周期已完成:创建 → 写入 → 运行 → 生成 result.json → 销毁。\n\nRESULT: torch_version=2.5.1+cu121, cuda_available=True, ...` —— Runner 读到"沙箱已成功销毁..."前缀后已放弃读取后段 `RESULT:`,用户最终看到"未包含 result.json 的具体内容"的失败回复。
|
|
356
|
-
- 错误:`Sandbox killed successfully. Final answer with result summary. RESULT: ...`
|
|
357
|
-
- 错误:`Sandbox killed. Task done. Need final answer exactly RESULT: line from result_summary. RESULT: ...`
|
|
358
|
-
- 错误:`The user wants me to verify CUDA... RESULT: ...`
|
|
359
|
-
- 错误:`Summary: ... RESULT: ...`
|
|
360
|
-
- 错误:在 `RESULT:` 后追加 `Let me know if you need anything else.` 等多余句子。
|
|
361
|
-
6. **不要依赖或被截断的 `stdout`/`stderr` 字段**,关键结论必须来自 `result.json` 的 `summary` / `result_summary`。
|
|
362
|
-
7. **`sandbox kill` 成功后不再调用任何工具**。kill 命令返回 `{ok:true}` 即表示任务已收尾,**下一回合的 `final_response` 必须严格遵守 RULE 5 的 `RESULT:` 开头格式**。禁止在 `RESULT:` 之前插入"沙箱已成功销毁"、"完整的生命周期已完成"、"任务完成"、"Sandbox killed" 等任何过渡句或过程汇报——这些信息 Runner 不需要,且会把 `RESULT:` 挤到 `final_response` 后段,触发 RULE 5 所述的结论丢失问题。也禁止再调 `bash`/`read_file`/`ls` 等工具,禁止以 "Need final answer exactly..." 等内心独白开头。
|
|
363
|
-
|
|
364
|
-
## 宿主 / Planner 调用提示
|
|
365
|
-
|
|
366
|
-
本 skill 设计成“结论自包含”:执行结束后,**`result.json` 的 `result_summary` 与 `final_response` 已经包含最终面向用户的结论**。因此:
|
|
367
|
-
|
|
368
|
-
- **Planner** 调用本 skill 的 plan step 时,`required_outputs` 必须声明 `.json`(见上方“请求措辞约定”)。
|
|
369
|
-
- **宿主(host / Runner / finalize 阶段)** 在收到 skill 返回后,应直接把 `final_response` 或 `result.json` 里的 `result_summary` 作为最终答案,**不要再让大模型去读取 result.json、验证文件内容或重新推理**。
|
|
370
|
-
- 如果宿主担心内容不可信,可以读取 `result.json` 核对,但**最终输出仍必须是 `RESULT:` 开头的一行**,不允许把“我需要先读取文件验证”、“Wait, I don't have...”等思考过程暴露给用户。
|
|
371
|
-
|
|
372
|
-
## 常见恢复
|
|
373
|
-
|
|
374
|
-
| 错误 / 现象 | 恢复动作 |
|
|
375
|
-
|-------------|----------|
|
|
376
|
-
| `error.type: not_found` | 检查 sandbox_id 是否正确,是否已被 kill |
|
|
377
|
-
| `error.type: network` | 检查 base_url 和网络连通性 |
|
|
378
|
-
| `error.type: validation` | 检查参数格式和必填 flag |
|
|
379
|
-
| `error.type: auth / permission` | 参考 delta-shared 的权限不足处理 |
|
|
380
|
-
| `error.type: api`(5xx) | 服务端错误,检查 API 服务状态 |
|
|
381
|
-
| 命令执行失败(exit_code ≠ 0) | 查看 `run` 返回的 stdout/stderr |
|
|
382
|
-
| 命令超时 | 增加 `--timeout` 或使用 `run-bg` |
|
|
383
|
-
| `error.type: internal` | 客户端内部错误,检查 delta-cli 版本或联系维护者 |
|
|
1
|
+
---
|
|
2
|
+
name: delta-sandbox
|
|
3
|
+
description: "在 Delta Sandbox Linux 容器中运行任意命令或脚本。适用于训练、推理、编译、数据处理等任意需要 sandbox 的计算任务,支持 Python / Node.js / Go / Java / Rust 等语言。所有任务通用同一套输出约定:命令在 stdout 末尾输出结构化 JSON;SSE 路径下 CLI 检测 stdout 末尾 JSON 并直接在 complete 帧带出 `result_summary`,缺失时才需 skill 从 log_file 反向扫描提取(旧服务器回退路径由 CLI `--summary` 自动提取为 `data.result_summary`)。Planner 调用本 skill 时,请在 plan step 的 required_outputs 中声明 [{kind: 'file', extensions: ['.json']}]。请求中用中性动词(运行/执行)描述命令,只有真的会落盘文件时,才使用创建/写入/保存等动词并带上扩展名。认证/配置/权限错误转 delta-shared。"
|
|
4
|
+
metadata:
|
|
5
|
+
requires:
|
|
6
|
+
bins: ["delta-cli"]
|
|
7
|
+
cliHelp: "delta-cli sandbox --help"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**CRITICAL — 开始前 MUST 先用 Read 工具读取 [`../delta-shared/SKILL.md`](../delta-shared/SKILL.md),其中包含认证、配置和通用错误处理**
|
|
11
|
+
|
|
12
|
+
# delta-sandbox
|
|
13
|
+
|
|
14
|
+
## 何时使用
|
|
15
|
+
|
|
16
|
+
- 用户提到 sandbox、容器、GPU、CUDA、训练、推理、计算任务。
|
|
17
|
+
- 用户需要创建/管理 sandbox 实例、在 sandbox 中运行命令、读写文件。
|
|
18
|
+
- **不要**用于:认证、配置初始化、权限恢复、更新检查 → 转 `delta-shared`。
|
|
19
|
+
|
|
20
|
+
## 使用边界
|
|
21
|
+
|
|
22
|
+
- 所有 sandbox 操作使用 `delta-cli sandbox <subcommand>`。
|
|
23
|
+
- delta-cli 不内嵌默认凭证,运行前必须按 `delta-shared` 完成认证配置。
|
|
24
|
+
- sandbox_id 必须来自真实返回,不要凭用户口述猜测。
|
|
25
|
+
|
|
26
|
+
## 沙箱环境(镜像源)
|
|
27
|
+
|
|
28
|
+
- **国内部署(domestic)**:沙箱创建时服务端已自动注入国内镜像配置。`pip install` / `npm install` / `git clone` / HuggingFace 下载**无需额外配置**,直接可用。覆盖:pip index-url(pypi.tuna.tsinghua.edu.cn)、npm registry(npmmirror)、HF_ENDPOINT(hf-mirror.com)、git insteadOf(github 代理)。
|
|
29
|
+
- **海外部署(overseas)**:不注入镜像,使用官方源。
|
|
30
|
+
- **第三方算力沙箱**:默认不注入镜像(默认关闭),按官方源使用。
|
|
31
|
+
- 若镜像源异常,优先检查沙箱所在区域的网络,而不是假设沙箱未配置。
|
|
32
|
+
|
|
33
|
+
## 强制规则(违规则任务视为失败)
|
|
34
|
+
|
|
35
|
+
1. **必须使用 delta-cli**:所有 sandbox 操作必须通过 `delta-cli sandbox <subcommand>` 执行。**禁止**用 Python(urllib/httpx/requests)或任何 SDK 直接调用 HTTP API。delta-cli 提供类型化错误输出(`error.type`),是 AI agent 正确解析错误的唯一途径。
|
|
36
|
+
2. **必须销毁**:每次 `sandbox create` 成功后,在当前任务的最终 `step_finish` 之前 **必须** 执行 `sandbox kill <id>` 或 `sandbox finish <id>` 销毁 sandbox。未销毁等价于任务失败。
|
|
37
|
+
3. **异常时也必须销毁**:即使 sandbox 内命令失败、超时或报错,仍然 **必须** 执行 `sandbox kill <id>`。异常不是跳过销毁的理由。
|
|
38
|
+
4. **不得依赖外部清理**:server 端 **没有** sandbox 的自动过期或 TTL 机制。不 kill → sandbox 永久占用 GPU 资源。
|
|
39
|
+
5. **同步任务一条命令内完成,后台任务可跨轮次**:
|
|
40
|
+
- 同步模式(`sandbox run`):从 `create` 到 `kill` 的完整生命周期必须在同一次执行中完成。不允许跨任务保留。
|
|
41
|
+
- 后台模式(`sandbox run-bg`):允许在当前执行中将 `sandbox_id` 和 `execution_id` 报告给用户,后续轮次通过 `sandbox status` + `sandbox logs` 获取结果。任务完成后仍需 `sandbox kill`。
|
|
42
|
+
6. **禁止重复创建**:同一次任务中只允许存在一个活跃 sandbox。若命令失败需要重试,优先复用已创建的 `sandbox_id`;若确实需要重新创建,必须先 `sandbox kill <旧_sandbox_id>`,确认旧实例销毁后再执行新的 `sandbox create`。可用 `delta-cli sandbox list` 查询当前用户的活跃 sandbox 来**清理残留**(上轮任务异常中断遗留的 sandbox),但 **禁止** 用 list 来绕过“同一次任务只保留一个 sandbox”的规则。
|
|
43
|
+
|
|
44
|
+
7. **长任务必须后台执行**:预计执行时间超过 60 秒的任务(例如下载模型、训练、微调、长推理、大规模数据处理),**必须**使用 `delta-cli sandbox run-bg <id> --command "..." --timeout <秒>` 提交为后台任务,禁止使用同步的 `sandbox run`。
|
|
45
|
+
|
|
46
|
+
**推荐方式(1 次 tool call 等完成)**:
|
|
47
|
+
```bash
|
|
48
|
+
delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> --wait
|
|
49
|
+
```
|
|
50
|
+
CLI 通过 SSE 实时流(`/logs/stream`)跟随输出,把原始 SSE 帧(`data: {...}`)**逐帧透传到 stdout**(curl -N 风格),帧类型见 JSON 的 `type` 字段:`init`(含 `execution_id`)/ `stdout` / `stderr` / `error` / `complete`。收到 `complete` 即命令结束。**CLI 会在 complete 帧里补全字段**:服务端给 `exit_code`/`execution_id`/`error`,CLI 注入流内累计的 `stdout`/`stderr`、兜底恢复的 `log_file`、以及检测到的末尾 JSON `result_summary`(stdout 末尾 50 行内有合法 JSON 对象时)。命令成败看 `complete` 帧里的 `exit_code`;**命令非零退出或报错时,CLI 追加一个 `error.type: command_failed` 信封并以非零码退出(进程退出码 8)**,信封 `message` 已附 stderr 尾部,便于 `&&` 链与按 `error.type` 分支、快速扫读失败原因;`type:"error"` 帧的 `message` 也会 best-effort 附上已流出的 stderr 尾部;成功则不追加信封。若在 `--timeout`(默认 300s,同时充当服务端命令超时和客户端等待 deadline)内未收到 `complete`,CLI 会在已透传的帧后**追加一个 `finished=false` 的快照信封**(`CommandLogsResult`),表示命令仍运行。完整 stdout 在 `finished` 后被写入沙箱内的 `log_file`(默认 `/workspace/{user_id}/sandbox_logs/delta-result-{sandbox_id}-{execution_id}.json`,以服务端返回的 `log_file` 字段为准),需要读全文时用 `sandbox read <id> --path <log_file>`(末尾 JSON 摘要已在 complete 帧里,通常无需再读)。**`--summary`/`--artifacts` 富化(含 hints)仅在旧服务器回退路径生效**(服务端无 `/logs/stream` 端点时 CLI 自动回退到 5s 轮询并输出 `CommandResult` 信封)。`execution_id` 可用于后续 `sandbox logs` / `cancel`。
|
|
51
|
+
|
|
52
|
+
**实时跟随 / 手动轮询(需要查看中间进度时)**:
|
|
53
|
+
`sandbox logs <id> --execution-id <exec_id> --stream` 实时跟随(SSE):原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件,无末尾信封(`--stream` 必须传 `--execution-id`)。`sandbox logs <id> --execution-id <exec_id>`(不加 `--stream`)返回快照信封;`--execution-id` 可省略——省略时仅按 sandbox 查询,服务端因 sandbox_id / execution_id 未同时传递而自动走 ES(Elasticsearch)查询(此时返回按 execution_id 聚合的**数组**,而非单个快照对象):
|
|
54
|
+
- `finished=true` + `exit_code=0` → 完成
|
|
55
|
+
- `finished=true` + `exit_code!=0` → 失败
|
|
56
|
+
- `running=true` → 仍运行
|
|
57
|
+
|
|
58
|
+
**故障诊断**:连续 3 次 `running=true` 且 `stderr_tail`/`stdout_tail`/`cursor` 无变化且距离提交超过 60 秒 → 用 `sandbox status <id>` 检查 sandbox 是否存活。注意:`cursor` 字段无 `omitempty`,永远会出现在响应中(即使为 0);`stderr_tail` 默认只保留 **最后 200 字节** stderr 片段,`stdout_tail` 默认保留 **最后 800 字节** stdout 片段(CLI 硬编码 `tailString(s, n)`,按字节截取;ASCII 文本等同于 n 字符,多字节 UTF-8 可能被截断),`cursor=0` 通常意味着服务端从 provider 还没读到任何 stderr 行。
|
|
59
|
+
|
|
60
|
+
- 后台任务成功后仍需按规则 2/3 销毁 sandbox。
|
|
61
|
+
|
|
62
|
+
8. **写入文件默认落到 sandbox 的 working-directory,禁止使用 Shell 变量路径**:
|
|
63
|
+
- ✅ **推荐**:`delta-cli sandbox write <id> --source train.py`(不传 `--path`,默认写到 `<working-directory>/train.py`)
|
|
64
|
+
- ✅ **显式指定**:`delta-cli sandbox write <id> --path <working-directory>/train.py --source train.py`(`--path` 传相对路径也会自动以 working-directory 为前缀)
|
|
65
|
+
- ❌ **禁止**:`--source "$WORKSPACE_ROOT/train.py"`、`--source "$(pwd)/train.py"`(Shell 展开路径 → 空文件或失败)
|
|
66
|
+
- ❌ **禁止**:`--data "大量代码..."`(Shell 转义问题)
|
|
67
|
+
- ❌ **不建议**:`--path /workspace/train.py` 这类直接写工作目录根、落在 `{user_id}` 之外的路径——kill 后 `rclone sync` 不会写回 OSS,文件不持久化。
|
|
68
|
+
- 查询 sandbox 的 working-directory:`delta-cli sandbox working-directory <id>`(返回 `data.path`)。`run`/`run-bg` 引用脚本时用 `python <working-directory>/train.py` 的完整路径。
|
|
69
|
+
- 少量配置(< 20 行)可用 `--data "..."`,此时必须带 `--path`(无法从文件名推断)。`--mode 755` 可设置文件权限。
|
|
70
|
+
|
|
71
|
+
9. **每个 `run-bg` 生成独立 `execution_id`**:每次调用 `sandbox run-bg` 都会返回一个唯一的 `execution_id`,多个后台任务之间通过它区分。务必保存每次返回的 `execution_id` 并与任务对应,后续通过 `sandbox logs <id> --execution-id <exec_id>` 分别查询各任务的结果。同步 `sandbox run` 无需 `execution_id`,结果直接返回。
|
|
72
|
+
|
|
73
|
+
10. **执行 delta-cli 命令必须使用 `bash` 工具,禁止用 `python_repl` 包装 `subprocess`**
|
|
74
|
+
- `delta-cli` 返回的是结构化 JSON,用 `bash` 直接执行最清晰,也便于 `log_file` 提取。
|
|
75
|
+
- ❌ **禁止**:在 `python_repl` 里写 `subprocess.run(['delta-cli', ...])`。这会把输出混在 Python 代码块中,既难读又多走一步解析。
|
|
76
|
+
- ✅ **正确**:`bash` 工具直接执行 `delta-cli sandbox ...`。
|
|
77
|
+
- 只有在**解析 JSON**(例如从 `sandbox read` 的信封中提取 `summary` 并写入 `result.json`)时,才使用 `python3`/`python_repl`。
|
|
78
|
+
|
|
79
|
+
11. **代码/数据必须经由 `delta-cli sandbox write` 进入 sandbox**
|
|
80
|
+
- 所有要送进 sandbox 执行的脚本、配置、数据文件,**必须**通过 `delta-cli sandbox write <id> --source <本地文件名>` 写入,默认落到该 sandbox 的 working-directory(`/workspace/{user_id}/{sandbox_id}/`,可用 `delta-cli sandbox working-directory <id>` 查询),位于 OSS 同步范围内、kill 后可持久化。
|
|
81
|
+
- 不要把脚本先写到宿主 workspace 再依赖同步;宿主 workspace 的文件对 sandbox 内部不可见。
|
|
82
|
+
- 本规则关注的是“入口统一为 `sandbox write`”,不针对任何特定宿主工具。无论宿主提供何种文件操作接口,最终都要把内容送到 sandbox 内部。
|
|
83
|
+
|
|
84
|
+
12. **任务完成标准是执行结果,而不是脚本文件存在**
|
|
85
|
+
- 本 skill 的交付物是**命令在 sandbox 中执行后的结果摘要**(本地 `result.json` 及其 `result_summary`),而不是 `.py`、`.sh` 等脚本文件本身。
|
|
86
|
+
- 仅把脚本写入 disk 不代表任务完成;必须完成 `write → run → read log_file → create result.json → kill sandbox` 整个生命周期,才能调用 `step_finish`。
|
|
87
|
+
- 因此,`skill_request` 中不要用“写出/保存/创建某某文件并执行”这类把文件落盘当成里程碑的措辞,而是直接要求“运行某某命令并返回结果摘要”。
|
|
88
|
+
|
|
89
|
+
13. **`result.json` 是完成前的必要条件**
|
|
90
|
+
- 在调用 `step_finish` 之前,必须确认:
|
|
91
|
+
1. `delta-cli sandbox run` / `run-bg --wait` 已返回且 `finished=true`;
|
|
92
|
+
2. (默认)直接复用 `sandbox run` / `run-bg --wait` 的 SSE `complete` 帧里的 `result_summary` 写入 `result.json`;缺失时(fallback)从 `log_file` 用 `sandbox read` + Python 解析
|
|
93
|
+
3. 已执行 `sandbox kill <id>` 销毁 sandbox;
|
|
94
|
+
4. `final_response` 只有一行 `RESULT: <result_summary>`。
|
|
95
|
+
|
|
96
|
+
14. **避免冗余检查与验证**
|
|
97
|
+
- 如果 `skill_request` 或用户请求已经明确指定了镜像与资源,**不要**在 `sandbox create` 前先调用 `sandbox images` 或 `sandbox recommend`。
|
|
98
|
+
- 文件写入 sandbox 后,只需要 `sandbox stat`(可选)确认 size 正常即可。**禁止**用 `sandbox ls` + `sandbox read` 把刚写入的文件读回宿主再验证内容;这会产生一次无意义的往返。
|
|
99
|
+
- 不要在 `sandbox create` 成功后反复调用 `sandbox status` 轮询;create 返回时 sandbox 已就绪。
|
|
100
|
+
|
|
101
|
+
## 请求措辞约定
|
|
102
|
+
|
|
103
|
+
为降低被 host 的 Phase-10B 文件扩展名校验误判的概率,构造 `skill_request` 时遵守:
|
|
104
|
+
|
|
105
|
+
1. **运行/执行命令时,用中性动词**
|
|
106
|
+
优先使用 `运行`、`执行`、`调用`。
|
|
107
|
+
- 正确:`在 GPU sandbox 中运行 PyTorch CUDA 自检脚本`
|
|
108
|
+
- 不推荐在请求里直接写代码并带“输出”动词,例如
|
|
109
|
+
`脚本需输出:1) torch.cuda.is_available();...`
|
|
110
|
+
其中的 `torch.cuda` 会被正则误判为承诺输出 `.cuda` 文件。
|
|
111
|
+
|
|
112
|
+
2. **只有真正会创建文件时,才用承诺动词 + 扩展名**
|
|
113
|
+
`创建`、`写入`、`保存`、`输出`、`生成` 这类词只在你确实会让 skill 落盘文件时使用,并显式写出扩展名。
|
|
114
|
+
- 正确:`创建 check_cuda.py 并运行`
|
|
115
|
+
- 正确:`训练完成后将指标写入 result.json`
|
|
116
|
+
|
|
117
|
+
3. **不要把完整代码块写进 `skill_request`**
|
|
118
|
+
用自然语言描述任务目标,具体代码由 skill 内部写入 sandbox。
|
|
119
|
+
- 正确:`运行标准 PyTorch CUDA 可用性检查`
|
|
120
|
+
- 不推荐:在 `skill_request` 里粘贴 `import torch; print(torch.cuda.is_available())`。
|
|
121
|
+
|
|
122
|
+
### required_outputs 声明
|
|
123
|
+
|
|
124
|
+
构造调用本 skill 的 plan step 时,**必须**在 `required_outputs` 中声明会落盘一个 `.json` 文件:
|
|
125
|
+
|
|
126
|
+
```yaml
|
|
127
|
+
required_outputs:
|
|
128
|
+
- kind: file
|
|
129
|
+
extensions: [".json"]
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
原因:本 skill 运行命令后会从 sandbox 读取 `log_file` 并写入本地 `result.json`。host 的 Phase-10B deliverable 校验是“或”语义(任意一个 required 扩展名被真实文件满足即可);显式声明 `.json` 可以避免 `torch.cuda` 等代码 token 被误判为 `.cuda` 文件承诺,从而防止 `VERIFICATION_FAILED` 导致的重复执行。
|
|
133
|
+
|
|
134
|
+
## 快速路由
|
|
135
|
+
|
|
136
|
+
> ⚠️ **`list` 与 `ls` 区分**:`sandbox list` 列出用户的 **沙箱实例**(与 `docker ps` 类似),`sandbox ls <id>` 列出 **沙箱内目录**(与 `ls` 命令类似)。Tab 补全时注意区分。
|
|
137
|
+
|
|
138
|
+
| 用户目标 | 命令 |
|
|
139
|
+
|---------|------|
|
|
140
|
+
| **发现** | |
|
|
141
|
+
| 查看可用镜像(镜像名) | `sandbox images` |
|
|
142
|
+
| 查看剩余可申请资源 | `sandbox resources` — 按算力分组返回 GPU/显存/核心可用量:`compute_sources[].source` 为 `private`(私有算力)或 `third_party`(第三方算力);`gpu_types` 结构随分组不同(private 含 `vgpu`/`core`/`memory_mib`,third_party 含 `gpu_name`/`total_gpu_num`/`idle_gpu_num`)。算力后端为内部概念,自动选择,无需指定 |
|
|
143
|
+
| 获取资源推荐 | `sandbox recommend --cpu N --memory XGi [--gpu N] [--gpu-mem N]` |
|
|
144
|
+
| 列出当前用户的 sandbox | `sandbox list [--status <running/finished/killed/error>] [--start-time <ISO8601>] [--end-time <ISO8601>] [--sandbox-id <id>] [--days N]` |
|
|
145
|
+
| **生命周期** | |
|
|
146
|
+
| 创建 sandbox | `sandbox create --image-name <镜像名> [--cpu N --memory XGi --gpu N --gpu-mem N --gpu-type <型号> --max-life M]`;`--image-name` 用镜像名(`sandbox images` 返回的 `image_name` 字段,如 `"PyTorch CUDA13 (GPU)"`),底层镜像标识对用户隐藏;`--gpu-type` 指定 GPU 型号(如 `"RTX 4090"`、`H100`),服务端映射为 HAMi use-gputype 注解按型号调度,可选型号查 `sandbox resources`(`third_party` 分组 `gpu_types[].gpu_name`),不传则自动调度;不希望被自动清理时加 `--no-auto-cleanup`(仅显式 kill/finish 可销毁);**单沙箱资源上限** cpu ≤ 512 / memory ≤ 1024Gi / gpu ≤ 64 / gpuMem ≤ 1024Gi,超出快速报错不挂起 |
|
|
147
|
+
| 连接 sandbox | `sandbox connect <id>` |
|
|
148
|
+
| 查看状态 | `sandbox status <id>` |
|
|
149
|
+
| 完成 sandbox | `sandbox finish <id> [--results '{...}']` |
|
|
150
|
+
| 销毁 sandbox | `sandbox kill <id>` |
|
|
151
|
+
| **命令执行** | |
|
|
152
|
+
| 同步运行命令(≤60s) | `sandbox run <id> --command "..." [--timeout <秒>] [--summary/--no-summary] [--artifacts]` |
|
|
153
|
+
| 后台运行命令(>60s) | `sandbox run-bg <id> --command "..." [--timeout <秒>] [--wait] [--summary/--no-summary] [--artifacts]` |
|
|
154
|
+
| 查看后台日志 | `sandbox logs <id> [--execution-id <exec_id>] [--tail N --grep <pattern> --context N] [--stream]`(`--execution-id` 可选;省略时按 sandbox 从 ES 查询,仅 `--stream` 必须传)|
|
|
155
|
+
| 中断后台命令 | `sandbox cancel <id> --execution-id <exec_id>` |
|
|
156
|
+
| **内网穿透(端口暴露)** | |
|
|
157
|
+
| 暴露端口到公网 | `sandbox expose <id> [--port N]` — 把沙箱内服务端口暴露到公网,返回 `public_url`(`http://<host>:<remote_port>`,remote_port 由服务端从 20000-29999 随机分配);**必须先启服务再暴露**(服务端探活失败报「服务未就绪」);`--port` 默认 8700(平台约定) |
|
|
158
|
+
| 查询活跃隧道 | `sandbox exposes <id>` — 列出该 sandbox 当前的公网隧道 |
|
|
159
|
+
| 回收隧道 | `sandbox dispose <id> --port N` — 显式回收单条隧道(杀 frpc + 释放公网端口);kill sandbox 也会自动回收全部隧道 |
|
|
160
|
+
| **文件操作** | |
|
|
161
|
+
| 读取文件 | `sandbox read <id> --path <path> [--output <本地路径> --tail N --grep <pattern> --offset N --limit N --context N --max-bytes N --parse-json]` |
|
|
162
|
+
| 拉取文件/目录 | `sandbox pull <id> --source <沙箱路径> --target <本地路径> [--recursive] [--pattern <glob>]` — 单文件或目录递归,含 sha1 校验(mirror of upload,flag 方向与 upload 相反:source=远程,target=本地) |
|
|
163
|
+
| 查询工作目录 | `sandbox working-directory <id>`(返回 `data.path`,形如 `/workspace/{user_id}/{sandbox_id}`)|
|
|
164
|
+
| 写入文件 | `sandbox write <id> --source <文件名>`(不传 `--path` 默认写到 working-directory);`--path <路径>` 传相对路径自动加 working-directory 前缀,绝对路径原样使用 |
|
|
165
|
+
| 批量写入 | `sandbox write-multiple <id> --entry <远程路径>=<本地路径> [--entry ...]` |
|
|
166
|
+
| 上传目录 | `sandbox upload <id> --source <本地目录> --target <沙箱路径>` |
|
|
167
|
+
| 列出目录 | `sandbox ls <id> --path <路径>`(默认 `.`)|
|
|
168
|
+
| 文件元数据 | `sandbox stat <id> --path <path>` |
|
|
169
|
+
| 移动/重命名 | `sandbox mv <id> --entry <src=dest> [--entry ...]` |
|
|
170
|
+
| 替换内容 | `sandbox replace <id> --path <path> --old <文本> --new <文本>` |
|
|
171
|
+
| 修改权限 | `sandbox chmod <id> --path <path> --mode <八进制>` |
|
|
172
|
+
| 删除文件 | `sandbox rm <id> --path <path> [--path ...]` |
|
|
173
|
+
| 删除目录 | `sandbox rmdir <id> --path <路径> [--path ...]` |
|
|
174
|
+
| 创建目录 | `sandbox mkdir <id> --path <路径> [--path ...]` |
|
|
175
|
+
| 搜索文件 | `sandbox search <id> --path <根目录> --pattern <glob>` |
|
|
176
|
+
| 上传目录 | `sandbox upload <id> --source <本地目录> --target <沙箱路径>` |
|
|
177
|
+
|
|
178
|
+
## 完整生命周期
|
|
179
|
+
|
|
180
|
+
1. **选择镜像(按需)**:
|
|
181
|
+
- 算力后端(provider)是内部概念,由系统自动选择,**不需要也不支持用户指定**。
|
|
182
|
+
- 如果请求里已经明确指定了镜像(例如 `--image-name "PyTorch CUDA13 (GPU)"`,镜像名)和资源(cpu/memory/gpu,以及可选的 `--gpu-type`),**直接跳到步骤 2 创建 sandbox**,不要再调 `sandbox images` / `sandbox recommend`。
|
|
183
|
+
- 只有在用户要求“推荐一个配置”或需要查询可用镜像时,才调用:
|
|
184
|
+
- `delta-cli sandbox images` — 服务端支持的镜像列表(返回镜像名,隐藏底层镜像标识)
|
|
185
|
+
- `delta-cli sandbox recommend --cpu N --memory XGi [--gpu N]` — 资源配置推荐
|
|
186
|
+
2. **创建**:`delta-cli sandbox create --image-name <镜像名> --cpu 4 --memory 16Gi --gpu 1 --gpu-mem 8000 --max-life 120`。**返回的 JSON 信封中是 `data.sandbox_id`,不是 `data.id`;后续所有命令必须使用这个真实的 `sandbox_id`。**(创建后 sandbox 立即可用,无需额外连接)。**响应会回显请求的镜像名/resource(服务端未返回的字段由 CLI 用请求值补齐,服务端返回值优先),可直接核对一遍资源配置,无需额外调 status。**(镜像回显为显示名;`provider` 为内部概念,不再回显)。**同一次任务若已有 `sandbox_id`,禁止再次 create,必须优先复用。**
|
|
187
|
+
- --max-life 指定 sandbox 最大存活时间(分钟),默认 30。长任务请调高,确保 sandbox 在命令执行期间不被回收。
|
|
188
|
+
- **`--gpu-mem`(单位 MiB)要匹配实际运行的模型**:模型参数量/精度越大所需显存越大,大模型塞进过小显存会直接 OOM。示例中的 8000(8GiB)只适配小模型,跑 7B/9B/13B 前先评估显存需求并适当调大。
|
|
189
|
+
- **`--gpu-type <型号>`(可选)指定 GPU 型号**:如 `"RTX 4090"`、`H100`,服务端映射为 HAMi use-gputype 注解,将 pod 调度到匹配型号的 GPU 上。可用 `sandbox resources` 查看可选型号(`third_party` 分组的 `gpu_types[].gpu_name`)。仅在用户明确要求特定型号时使用;不传则由系统自动调度。
|
|
190
|
+
- `--no-auto-cleanup`:加此 flag 后该 sandbox **不会被自动清理**(服务端超时回收 + 本地周期 cleanup_stale 均跳过),只能通过显式 `sandbox kill`/`finish` 销毁。仅当任务确实需要跨越周期清理长期存活时才使用,任务结束后必须主动销毁,避免资源泄漏。
|
|
191
|
+
- **禁止在 create 成功后反复调用 `sandbox status` 轮询**。`sandbox create` 返回时 sandbox 已经就绪,直接用它返回的 `data.sandbox_id` 执行 `write`/`run` 即可。多余的轮询会增加工具调用次数且没有任何收益。
|
|
192
|
+
3. **写入代码/数据**:
|
|
193
|
+
- **单个文件**:`delta-cli sandbox write <id> --source <文件名>` — 不传 `--path`,默认写到该 sandbox 的 working-directory(`/workspace/{user_id}/{sandbox_id}/`,可用 `delta-cli sandbox working-directory <id>` 查询)。`--source` 只传本地文件名(如 `--source train.py`),**禁止**使用 `"$WORKSPACE_ROOT/train.py"` 或 `$(pwd)/train.py` 等 Shell 变量路径。`--source` 让 CLI 自行读取本地文件,不会经过 Shell 展开,是最安全的方式。若需指定路径,`--path <相对路径>` 会自动加 working-directory 前缀,绝对路径原样使用(**不建议**写 `/workspace/<filename>` 这种落在 `{user_id}` 之外的位置,kill 后不持久化)。写入后返回的 `size` 字段是实际磁盘字节数(来自 stat 验证),可对比确认写入完整性。少量配置(<20 行)可用 `--data "..."`,但此时必须带 `--path`。文件路径和扩展名由镜像中的运行时决定。
|
|
194
|
+
- **批量写入**:`sandbox write-multiple <id> --entry <远程路径>=<本地路径> [--entry ...]`(远程路径在 `=` 左边,本地路径在右边,`--data` 批量写入不可用)
|
|
195
|
+
- **上传目录**:`sandbox upload <id> --source <本地目录> --target <沙箱路径>` — CLI 将本地目录打包为 tar.gz,通过 multipart/form-data 上传,服务端自动解压到 target 目录。返回每个文件的路径、大小、模式。上传后 CLI 自动对比本地和远程文件清单做完整性校验(大小不匹配、多余文件等会告警)。
|
|
196
|
+
- **注意**:`--source` 是**本地目录路径**,`--target` 是**沙箱内的目标目录**,target 目录不存在会自动创建。
|
|
197
|
+
- 支持嵌套目录,空目录也会被创建。
|
|
198
|
+
- **写后验证(可选)**:`sandbox stat <id> --path <path>` 确认文件存在且 size 符合预期即可。**不要**用 `sandbox ls` + `sandbox read` 把刚写入的文件读回宿主再逐字对比;无异常时不需要读回。
|
|
199
|
+
4. **运行命令**:
|
|
200
|
+
- **短任务(预计 ≤ 60 秒)**:`delta-cli sandbox run <id> --command "<命令>" --timeout <秒>` 同步执行,命令的 stdout/stderr 通过 SSE **以原始 `data:` 帧逐帧透传到 stdout**(无末尾信封),`exit_code` / `log_file` / `execution_id` 从 `complete` 帧读取。完整 `stdout` 在完成后写入 run envelope 日志文件(`log_file` 字段),需要读全文或提取末尾 JSON 时用 `sandbox read`,**不要**再调用 `sandbox logs`。可通过 `--log-file <路径>` 自定义 run envelope 日志文件路径;默认值为 `/workspace/{user_id}/sandbox_logs/delta-result-{sandbox_id}-{execution_id}.json`(以服务端返回的 `log_file` 字段为准)。脚本经 `sandbox write` 落盘后位于 working-directory,运行命令需用完整路径 `<working-directory>/<文件名>`(先 `sandbox working-directory <id>` 查询)。根据镜像中的运行时构造命令,常见示例(`<wd>` 为 working-directory):
|
|
201
|
+
- Python:`python <wd>/train.py`
|
|
202
|
+
- Node.js:`node <wd>/app.js`
|
|
203
|
+
- Go:`go run <wd>/main.go`
|
|
204
|
+
- Shell:`bash <wd>/run.sh`
|
|
205
|
+
- **长任务(预计 > 60 秒,如下载模型、训练、编译、大规模数据处理)**:`delta-cli sandbox run-bg <id> --command "<命令>" --timeout <秒> [--log-file <路径>]` 提交后台任务,获得 `execution_id` 后通过以下命令查询:
|
|
206
|
+
- `delta-cli sandbox logs <id> --execution-id <execution_id> --stream` — SSE 实时跟随,原始 `data:` 帧逐帧透传到 stdout,直到 `complete` 事件(含 `exit_code`/`log_file`),无末尾信封(`--stream` 必须传 `--execution-id`)。
|
|
207
|
+
- `delta-cli sandbox logs <id> --execution-id <execution_id>` — 快照信封,返回 `stdout_tail`(末尾 800 字节)、`stderr_tail`(末尾 200 字节)、`stdout_size`、`stderr_size`、`cursor`、`running`、`finished`、`exit_code`、`log_file`(完成后)。当 `finished=true` 时认为完成,完整输出需读取 `log_file`。`--execution-id` 可省略:省略时仅按 sandbox 查询,服务端因 sandbox_id / execution_id 未同时传递而自动走 ES 查询,返回按 execution_id 聚合的**数组**。
|
|
208
|
+
禁止对长任务使用同步 `sandbox run`
|
|
209
|
+
|
|
210
|
+
**推荐实践**:让 sandbox 脚本在 `stdout` 末尾打印一个独立的结构化 JSON 对象(例如 `{"status":"ok", ...}`)。SSE 路径下 CLI 会检测该末尾 JSON 并直接在 `complete` 帧里带出 `result_summary`,**任务结束后直接复用 complete 帧的 `result_summary` 写入 result.json**,无需再 `sandbox read` log_file 反向扫描。仅当 complete 帧缺失 `result_summary`(末尾无合法 JSON)时,才用 `sandbox read <id> --path <log_file>` 读取并按步骤 5 反向扫描提取,避免让大模型“读整段日志再摘要”。
|
|
211
|
+
5. **生成 result.json**(SSE 首选:直接复用 complete 帧的 `result_summary`;缺失时才从 `log_file` 读取解析):
|
|
212
|
+
- **获取 `log_file` 路径**:`run` / `run-bg --wait` 的 SSE `complete` 帧含 `log_file` 字段(CLI 已兜底补全);`sandbox logs <id> --execution-id <eid>` 快照在 `finished=true` 后同样返回 `log_file`。需要 read 时再按下面方式解析:
|
|
213
|
+
- `sandbox read` 返回的是 CLI 信封 `{"ok":true,"data":{"content":"..."}}`,真实结果 JSON 在 `data.content` 字段里。
|
|
214
|
+
- **不要把 CLI 信封或完整长 `stdout` 原样写入本地 `result.json`**;应该解析后生成一份精简的结构化摘要。
|
|
215
|
+
- **推荐做法(避免 heredoc 引号失败)**:先用 `bash` 把信封写入临时文件,再用 `python3`(或 `python_repl`)读取该文件并生成 `result.json`。**不要**把 `delta-cli sandbox read` 的输出通过管道直接喂给内联 heredoc,管道+heredoc 的组合极易因 Shell 转义/引号问题失败。
|
|
216
|
+
1. `bash` 保存信封:
|
|
217
|
+
```bash
|
|
218
|
+
delta-cli sandbox read <sandbox_id> --path <log_file> > /tmp/_delta_result_envelope.json
|
|
219
|
+
```
|
|
220
|
+
2. `python3` / `python_repl` 生成 `result.json`:
|
|
221
|
+
```python
|
|
222
|
+
import json, os
|
|
223
|
+
|
|
224
|
+
env = json.load(open("/tmp/_delta_result_envelope.json", encoding="utf-8"))
|
|
225
|
+
raw = json.loads(env["data"]["content"])
|
|
226
|
+
|
|
227
|
+
# 精简到用户关心的极简字段,避免 host read_file 预览截断
|
|
228
|
+
concise = {
|
|
229
|
+
"exit_code": raw.get("exit_code"),
|
|
230
|
+
"finished": raw.get("finished"),
|
|
231
|
+
"summary": {},
|
|
232
|
+
"error": raw.get("error"),
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
# 优先提取 stdout 末尾的独立 JSON 对象作为 summary
|
|
236
|
+
stdout = raw.get("stdout", "")
|
|
237
|
+
for line in reversed(stdout.strip().splitlines()):
|
|
238
|
+
s = line.strip()
|
|
239
|
+
if s.startswith("{") and s.endswith("}"):
|
|
240
|
+
try:
|
|
241
|
+
concise["summary"] = json.loads(s)
|
|
242
|
+
break
|
|
243
|
+
except Exception:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
# 构造 result_summary:一行字符串,最终回答的 RESULT: 行直接用它
|
|
247
|
+
parts = [f"exit_code={concise['exit_code']}", f"finished={concise['finished']}"]
|
|
248
|
+
for key, value in concise["summary"].items():
|
|
249
|
+
# 避免把长文本塞进 result_summary
|
|
250
|
+
if isinstance(value, (str, int, float, bool)) and len(str(value)) < 200:
|
|
251
|
+
parts.append(f"{key}={value}")
|
|
252
|
+
concise["result_summary"] = ", ".join(parts)
|
|
253
|
+
|
|
254
|
+
ws = os.environ.get("WORKSPACE_ROOT", ".")
|
|
255
|
+
with open(os.path.join(ws, "result.json"), "w", encoding="utf-8") as f:
|
|
256
|
+
json.dump(concise, f, indent=2, ensure_ascii=False)
|
|
257
|
+
print(concise["result_summary"])
|
|
258
|
+
```
|
|
259
|
+
3. 如果当前环境没有 `python3`,用 `jq`、`node` 或任何可解析 JSON 的工具实现**相同逻辑**:1)读取 `/tmp/_delta_result_envelope.json`;2)解开 CLI 信封取 `data.content`;3)解析内部 JSON;4)从 `stdout` 末尾提取 summary JSON;5)生成 `result_summary` 并写入 `result.json`。
|
|
260
|
+
- 如果 `stdout` 非常大(训练日志、下载日志等),不要把整段 `stdout` 写进 `result.json`;只保留关键指标和 preview。
|
|
261
|
+
- 完整原始输出仍保留在 sandbox 的 `log_file` 中,必要时可再次 `sandbox read` 获取。
|
|
262
|
+
- 写入本地副本的目的是让 host 的 deliverable 校验识别到真实的 `.json` 文件;由于校验是“或”语义,`.json` 文件会盖过 `torch.cuda` 等 token 被误判出的 `.cuda` 文件承诺。
|
|
263
|
+
6. **销毁**:`delta-cli sandbox kill <id>`(如需保存结果,用 `sandbox finish --results '{...}'` 替代 kill,finish 会自动销毁 sandbox)
|
|
264
|
+
|
|
265
|
+
详细步骤见 [lifecycle.md](references/lifecycle.md)。
|
|
266
|
+
|
|
267
|
+
## 内网穿透(端口暴露)
|
|
268
|
+
|
|
269
|
+
把沙箱内服务端口暴露到公网,让用户通过 `public_url` 直接访问。原理:沙箱内
|
|
270
|
+
frpc 向外拨号到公网 frps,frps 把公网端口(20000-29999 随机分配)的 TCP 流量
|
|
271
|
+
转发回沙箱内服务;**无需在宿主机为每个 sandbox 配置端口**(反向连接)。
|
|
272
|
+
|
|
273
|
+
可用命令:
|
|
274
|
+
|
|
275
|
+
| 命令 | 作用 |
|
|
276
|
+
|------|------|
|
|
277
|
+
| `sandbox expose <id> [--port N]` | 暴露端口,返回 `public_url`;`--port` 默认 **8700**(平台约定),不传即可 |
|
|
278
|
+
| `sandbox exposes <id>` | 查询该 sandbox 当前活跃隧道(service_port / remote_port / public_url) |
|
|
279
|
+
| `sandbox dispose <id> --port N` | 显式回收单条隧道(杀 frpc + 释放公网端口) |
|
|
280
|
+
|
|
281
|
+
必须遵守的顺序与约定:
|
|
282
|
+
|
|
283
|
+
1. **先启服务,再暴露**。expose 前服务必须在沙箱内监听(绑定 `0.0.0.0`,不是
|
|
284
|
+
仅 `127.0.0.1`),且服务端会对 `127.0.0.1:{port}` 探活——**任何 HTTP 响应
|
|
285
|
+
(含 4xx/5xx)都算存活**,只有连接失败/超时才报「服务未就绪」。
|
|
286
|
+
2. **长驻服务必须前台常驻**:用 `run-bg` 时让服务进程成为任务的前台进程,
|
|
287
|
+
**禁止** `nohup ... &` 后台化(任务结束时后台进程会被回收,服务随之消失):
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
delta-cli sandbox run-bg <id> --command "cd <wd> && exec python3 -m http.server 8700 --bind 0.0.0.0" --timeout 3600
|
|
291
|
+
delta-cli sandbox expose <id> # --port 默认 8700,无需传
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
3. **生命周期自动回收**:`kill` sandbox 时服务端钩子自动把隧道记录置 closed,
|
|
295
|
+
frps 侧公网端口随 frpc 连接断开自动释放;`dispose` 用于不销毁 sandbox 的
|
|
296
|
+
显式回收。**暴露的端口对公网全开放,业务服务自身必须带鉴权**,不要裸奔端口。
|
|
297
|
+
4. 公网连不上排查顺序:① 沙箱内服务是否监听(`curl 127.0.0.1:{port}`,可先
|
|
298
|
+
`sandbox run` 验证)→ ② frpc 进程是否存活(`ps aux | grep frpc`)→ ③ frps
|
|
299
|
+
公网主机连通性 → ④ 安全组是否放行业务端口段 → ⑤ frpc/frps 两端 token 一致。
|
|
300
|
+
|
|
301
|
+
## 通用结构化输出约定
|
|
302
|
+
|
|
303
|
+
本约定适用于**所有** `delta-sandbox` 任务(GPU/CPU、训练/推理/编译/数据处理等),目的是让 Runner 能直接从 `result.json` 读到结论,无需再次解析长日志。
|
|
304
|
+
|
|
305
|
+
### sandbox 脚本/命令应该输出的内容
|
|
306
|
+
|
|
307
|
+
命令执行完毕后,推荐在 `stdout` 末尾打印一行**独立的结构化 JSON**。例如:
|
|
308
|
+
|
|
309
|
+
- CUDA 检查:`{"status":"ok","torch_version":"2.5.1+cu121","cuda_available":true,"device_name":"NVIDIA H100 80GB HBM3"}`
|
|
310
|
+
- 训练任务:`{"status":"ok","epochs":10,"final_loss":0.12,"final_accuracy":0.94,"model_file":"<wd>/model.pt"}`
|
|
311
|
+
- 数据处理:`{"status":"ok","input_rows":10000,"output_rows":9876,"output_file":"<wd>/output.csv"}`
|
|
312
|
+
|
|
313
|
+
这样 SKILL 可以用同一套代码把它提取到 `result.json` 的 `summary` 字段,再生成 `RESULT:` 行。
|
|
314
|
+
|
|
315
|
+
- **禁止**在 stdout 中打印 `FINAL_ANSWER: ...` 这类冗余标记行;多余的标记会让 Runner 把普通文本误认为结果字段。
|
|
316
|
+
- `summary` 中**不要**使用 `final_answer` 这类与 schema 重复的关键字。统一用语义字段表示结果,例如推理任务用 `generated_text` / `output` / `result`,训练任务用 `final_loss` / `final_accuracy`,数据处理用 `output_file` / `output_rows`。
|
|
317
|
+
|
|
318
|
+
### 本地 `result.json` 通用 Schema
|
|
319
|
+
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"exit_code": 0,
|
|
323
|
+
"finished": true,
|
|
324
|
+
"summary": {},
|
|
325
|
+
"result_summary": "exit_code=0, status=ok, torch_version=..., ...",
|
|
326
|
+
"error": null
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
关键字段:
|
|
331
|
+
- `summary`: 命令输出的结构化结果对象。
|
|
332
|
+
- `result_summary`: **一行字符串**,与最终回答的 `RESULT:` 行内容一致,方便 Runner 直接读取。
|
|
333
|
+
- `error`: 命令失败时的错误信息;成功时一般为 `null`。
|
|
334
|
+
- **不要把完整 `stdout` 塞进来**。完整日志仍保留在 sandbox 的 `log_file` 中。
|
|
335
|
+
|
|
336
|
+
## 输出阅读与最终回答格式
|
|
337
|
+
|
|
338
|
+
1. **SSE 路径下 `sandbox run` / `run-bg --wait` 的 complete 帧直接带 `result_summary`**(CLI 检测 stdout 末尾 JSON 注入)以及 `stdout` / `stderr` / `log_file`(CLI 兜底补全)。`exit_code` / `log_file` / `execution_id` 从 complete 帧读取;命令失败时 complete 帧后追加 `error.type: command_failed` 信封(进程退出码 8)。`log_file` 路径也可从 `sandbox logs <id> --execution-id <eid>` 快照(`finished=true` 后)获得。
|
|
339
|
+
2. **提取摘要**:首选直接复用 complete 帧的 `result_summary`;缺失时用 `sandbox read <id> --path <log_file>` 读取 run envelope 日志文件并解析,反向扫描 stdout 末尾 JSON 生成 `result_summary`(见“完整生命周期”步骤 5)。
|
|
340
|
+
- run envelope 日志文件包含完整 `stdout`、`stderr`、`exit_code`、`finished`、`command`、`error`。
|
|
341
|
+
3. **生成本地精简 `result.json`**(见“完整生命周期”步骤 5 和“通用结构化输出约定”)。本地副本只应包含关键字段和摘要,**不应包含完整长 `stdout`**。
|
|
342
|
+
4. **最终回答必须且只能是 `RESULT:` 开头的一行。**
|
|
343
|
+
- 优先直接复用 `result.json` 里的 `result_summary` 字段:
|
|
344
|
+
```text
|
|
345
|
+
RESULT: <result.json 中的 result_summary>
|
|
346
|
+
```
|
|
347
|
+
- 如果 `result_summary` 缺失,则从 `summary` 对象构造一行,例如:
|
|
348
|
+
```text
|
|
349
|
+
RESULT: exit_code=0, status=ok, torch_version=2.7.0+cu128, cuda_available=True, device_name=NVIDIA GeForce RTX 3090.
|
|
350
|
+
```
|
|
351
|
+
5. **`final_response` 必须以 `RESULT:` 开头**
|
|
352
|
+
- **首位字符必须是 `R`,紧接 `ESULT:` 前缀**,整串只有 `RESULT: <result_summary>` 一行,不允许任何前置自然语言、空行、过渡句或过程汇报。
|
|
353
|
+
- 原因:host Runner 与下游 LLM 默认只识别 `RESULT:` 开头的 `final_response` 并把其后内容作为任务结论转述给用户。任何前置文字——包括"沙箱已销毁"、"任务完成"、"完整的生命周期已完成"等中性过程汇报——都会让 Runner 把 `final_response` 当作过程摘录而非最终结论,转而向用户输出"未包含具体内容"等失败回复,让真实结论丢失。
|
|
354
|
+
- 正确:`RESULT: exit_code=0, cuda_available=True, torch_version=2.5.1+cu121, device_name=NVIDIA H100 80GB HBM3`
|
|
355
|
+
- 错误(前缀污染,真实案例):`沙箱已成功销毁。完整的生命周期已完成:创建 → 写入 → 运行 → 生成 result.json → 销毁。\n\nRESULT: torch_version=2.5.1+cu121, cuda_available=True, ...` —— Runner 读到"沙箱已成功销毁..."前缀后已放弃读取后段 `RESULT:`,用户最终看到"未包含 result.json 的具体内容"的失败回复。
|
|
356
|
+
- 错误:`Sandbox killed successfully. Final answer with result summary. RESULT: ...`
|
|
357
|
+
- 错误:`Sandbox killed. Task done. Need final answer exactly RESULT: line from result_summary. RESULT: ...`
|
|
358
|
+
- 错误:`The user wants me to verify CUDA... RESULT: ...`
|
|
359
|
+
- 错误:`Summary: ... RESULT: ...`
|
|
360
|
+
- 错误:在 `RESULT:` 后追加 `Let me know if you need anything else.` 等多余句子。
|
|
361
|
+
6. **不要依赖或被截断的 `stdout`/`stderr` 字段**,关键结论必须来自 `result.json` 的 `summary` / `result_summary`。
|
|
362
|
+
7. **`sandbox kill` 成功后不再调用任何工具**。kill 命令返回 `{ok:true}` 即表示任务已收尾,**下一回合的 `final_response` 必须严格遵守 RULE 5 的 `RESULT:` 开头格式**。禁止在 `RESULT:` 之前插入"沙箱已成功销毁"、"完整的生命周期已完成"、"任务完成"、"Sandbox killed" 等任何过渡句或过程汇报——这些信息 Runner 不需要,且会把 `RESULT:` 挤到 `final_response` 后段,触发 RULE 5 所述的结论丢失问题。也禁止再调 `bash`/`read_file`/`ls` 等工具,禁止以 "Need final answer exactly..." 等内心独白开头。
|
|
363
|
+
|
|
364
|
+
## 宿主 / Planner 调用提示
|
|
365
|
+
|
|
366
|
+
本 skill 设计成“结论自包含”:执行结束后,**`result.json` 的 `result_summary` 与 `final_response` 已经包含最终面向用户的结论**。因此:
|
|
367
|
+
|
|
368
|
+
- **Planner** 调用本 skill 的 plan step 时,`required_outputs` 必须声明 `.json`(见上方“请求措辞约定”)。
|
|
369
|
+
- **宿主(host / Runner / finalize 阶段)** 在收到 skill 返回后,应直接把 `final_response` 或 `result.json` 里的 `result_summary` 作为最终答案,**不要再让大模型去读取 result.json、验证文件内容或重新推理**。
|
|
370
|
+
- 如果宿主担心内容不可信,可以读取 `result.json` 核对,但**最终输出仍必须是 `RESULT:` 开头的一行**,不允许把“我需要先读取文件验证”、“Wait, I don't have...”等思考过程暴露给用户。
|
|
371
|
+
|
|
372
|
+
## 常见恢复
|
|
373
|
+
|
|
374
|
+
| 错误 / 现象 | 恢复动作 |
|
|
375
|
+
|-------------|----------|
|
|
376
|
+
| `error.type: not_found` | 检查 sandbox_id 是否正确,是否已被 kill |
|
|
377
|
+
| `error.type: network` | 检查 base_url 和网络连通性 |
|
|
378
|
+
| `error.type: validation` | 检查参数格式和必填 flag |
|
|
379
|
+
| `error.type: auth / permission` | 参考 delta-shared 的权限不足处理 |
|
|
380
|
+
| `error.type: api`(5xx) | 服务端错误,检查 API 服务状态 |
|
|
381
|
+
| 命令执行失败(exit_code ≠ 0) | 查看 `run` 返回的 stdout/stderr |
|
|
382
|
+
| 命令超时 | 增加 `--timeout` 或使用 `run-bg` |
|
|
383
|
+
| `error.type: internal` | 客户端内部错误,检查 delta-cli 版本或联系维护者 |
|