Files
pikasTech-unidesk/.agents/skills/unidesk-trans/references/remote-experiment-smoke.md
T
2026-07-18 17:52:32 +02:00

195 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 远程临时实验 Smoke
## 适用范围
本模板用于一次性远程 build、Docker GPU、模型下载或硬件 smoke。目标是让执行者在一个实验目录内完成:
- 一次 preflight
- 一个或多个串行后台 job
- 一次原入口 smoke
- 一份有界 evidence
- 一次幂等 cleanup。
正式 CI/CD、YAML-first apply、常驻服务、Secret 下发和公网暴露继续使用对应专项 CLI 或 skill,不能套用本模板。
## 必填参数
开始写入目标前必须明确以下参数,不得根据宿主名、历史目录或上一任务猜测:
| 参数 | 约束 |
| --- | --- |
| `route` | 任务上下文提供的 `Target:absoluteWorkspace` 或对应 k3s route。 |
| `work_dir` | 目标允许范围内的绝对临时目录,默认位于 `/tmp`。 |
| `resource_prefix` | 容器、镜像、网络、PID 和证据文件共享的任务前缀。 |
| `job_id` | 当前长步骤的稳定短标识,例如 `model-download``image-build`。 |
| `command` | 写入 job 脚本的单一长命令,不在同步 `trans` 中等待完成。 |
| `artifact_allowlist` | 允许保留的模型缓存、WAV、JSON 或摘要文件。 |
| `stop_condition` | OOM、ABI、许可证、上游 ref 或硬件能力等首次确定停止条件。 |
参数缺失时先返回可操作错误:
- 不要创建第二条执行路径;
- 不要把 Secret、token 或完整环境变量写入参数、脚本、日志和 evidence。
## 单次 Smoke 流程
### 1. Preflight
只做一次有界探测,确认目录、资源前缀、依赖入口和停止条件:
```bash
trans <Target>:/tmp sh <<'SH'
set -eu
work_dir=<absolute-work-dir>
resource_prefix=<resource-prefix>
test "${work_dir#/tmp/}" != "$work_dir"
case "$resource_prefix" in
''|*[!a-zA-Z0-9_.-]*) echo 'invalid resource_prefix' >&2; exit 2 ;;
esac
mkdir -p "$work_dir/jobs" "$work_dir/artifacts"
printf 'host='; hostname
df -h "$work_dir" | tail -1
docker version --format 'docker={{.Server.Version}}' 2>/dev/null || true
nvidia-smi --query-gpu=name,driver_version,memory.total,memory.free --format=csv,noheader 2>/dev/null || true
docker ps -a --filter "label=unidesk.task=$resource_prefix" --format '{{.Names}} {{.Status}} {{.Image}}' 2>/dev/null || true
SH
```
若目标范围不是 `/tmp`,必须由 owning issue 或任务上下文明确授权,再相应替换目录断言。
### 2. 写入 Job 脚本
远端文本修改只使用 `apply-patch`。每个长步骤的脚本必须写最终 exit code 和 done 标记:
```bash
trans <Target>:<absolute-work-dir> apply-patch <<'PATCH'
*** Begin Patch
*** Add File: jobs/<job-id>.sh
+#!/bin/sh
+set +e
+
+<one-long-command>
+code=$?
+printf '%s\n' "$code" > /absolute/work-dir/jobs/<job-id>.exit
+touch /absolute/work-dir/jobs/<job-id>.done
+exit "$code"
*** End Patch
PATCH
```
job 脚本只能引用任务已确认的绝对目录、资源前缀和公开依赖身份。需要 Secret 的任务不使用本模板自行读取运行面值。
### 3. Submit
submit 只负责清理旧状态、后台启动和返回 PID,不等待长命令:
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
job_id=<job-id>
job_dir=/absolute/work-dir/jobs
rm -f "$job_dir/$job_id.done" "$job_dir/$job_id.exit" "$job_dir/$job_id.stdout" "$job_dir/$job_id.stderr"
chmod +x "$job_dir/$job_id.sh"
nohup sh "$job_dir/$job_id.sh" \
> "$job_dir/$job_id.stdout" \
2> "$job_dir/$job_id.stderr" \
< /dev/null &
printf '%s\n' "$!" > "$job_dir/$job_id.pid"
printf 'job=%s state=submitted pid=%s\n' "$job_id" "$(cat "$job_dir/$job_id.pid")"
SH
```
### 4. Poll
每次 poll 只输出状态和有界日志尾部。不得重复 submit,也不得恢复完整 stdout dump
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
job_id=<job-id>
job_dir=/absolute/work-dir/jobs
pid=$(cat "$job_dir/$job_id.pid" 2>/dev/null || true)
if [ -f "$job_dir/$job_id.done" ]; then
state=finished
exit_code=$(cat "$job_dir/$job_id.exit")
elif [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
state=running
exit_code=-
else
state=lost
exit_code=-
fi
printf 'job=%s state=%s exit=%s pid=%s\n' "$job_id" "$state" "$exit_code" "${pid:--}"
printf '%s\n' '--- stdout tail ---'
tail -n 20 "$job_dir/$job_id.stdout" 2>/dev/null || true
printf '%s\n' '--- stderr tail ---'
tail -n 20 "$job_dir/$job_id.stderr" 2>/dev/null || true
SH
```
`state=lost` 表示 PID 已消失但没有 done 标记:
- 先保留状态文件和日志;
- 不要自动重提;
- 根据首个确定错误决定停止或创建新的 `job_id`
### 5. 原入口 Smoke 与 Evidence
只执行 owning issue 要求的一个 smoke。通用 evidence 至少包含:
- `sourceRef`、模型或依赖 ref、许可证;
- 容器基础镜像 digest、构建镜像 ID、runtime 和 GPU request
- 精确 API 或命令参数;
- 首次调用和热调用耗时;
- GPU 基线、峰值显存和 OOM 状态;
- 生成物大小、哈希和原生解码/检查摘要;
- 首个失败点和是否触发 `stop_condition`
- cleanup 结果和允许保留的 artifact 路径。
证据文件只写摘要、presence、fingerprint、ID、digest 和有界日志尾部,不复制 Secret、完整环境、模型正文或无界 stdout。
### 6. Cleanup
cleanup 必须幂等,并按资源前缀或 label 精确删除:
```bash
trans <Target>:<absolute-work-dir> sh <<'SH'
set -eu
work_dir=/absolute/work-dir
resource_prefix=<resource-prefix>
for container in $(docker ps -aq --filter "label=unidesk.task=$resource_prefix"); do
docker rm -f "$container"
done
for image in $(docker images -q --filter "label=unidesk.task=$resource_prefix"); do
docker image rm "$image" || true
done
for network in $(docker network ls -q --filter "label=unidesk.task=$resource_prefix"); do
docker network rm "$network" || true
done
find "$work_dir/jobs" -type f \( -name '*.pid' -o -name '*.done' -o -name '*.exit' \) -delete
printf '%s\n' 'containers:'
docker ps -a --filter "label=unidesk.task=$resource_prefix" --format '{{.Names}} {{.Status}} {{.Image}}'
printf '%s\n' 'images:'
docker images --filter "label=unidesk.task=$resource_prefix" --format '{{.Repository}}:{{.Tag}} {{.ID}} {{.Size}}'
printf '%s\n' 'networks:'
docker network ls --filter "label=unidesk.task=$resource_prefix" --format '{{.Name}}'
SH
```
只保留 `artifact_allowlist` 明确列出的缓存和证据。临时容器、镜像、网络、PID 和非复用 scratch 不得伪装成正式运行面。
## 停止判定
- 首个确定的 OOM、ABI、许可证、上游 ref 或硬件能力错误触发 `stop_condition` 后立即停止同族试配。
- 依赖下载失败与模型失败必须分层记录,不能把网络、registry 或包 ABI 问题误判为模型不可用。
- smoke 未进入原入口时,首次调用、热调用、峰值显存和生成物必须明确写 `未执行`,不能填推测值。
- 后续候选模型或依赖组合使用新的 issue、TaskTree Task、资源前缀和实验目录,不能在当前 smoke 中无界扩展;遗留 MDTODO 按 `$unidesk-tasktree` 迁移。