BatchJob Async Job Skill
Use this skill when the user wants to run or manage batch jobs through the BatchJob service.
Required Environment
BATCHJOB_BASE_URLBATCHJOB_BEARER_TOKEN
All HTTP requests must include:
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}"
-H "Content-Type: application/json"
API Endpoints
POST /v1/batch/files:uploadPOST /v1/batch/jobs:precheckPOST /v1/batch/jobsGET /v1/batch/jobs/{job_id}GET /v1/batch/jobs?page=1&page_size=10&status=...POST /v1/batch/jobs/{job_id}:cancel
Automation Policy (Default)
- Always run in full-auto mode.
- Do not ask user for
file_idfirst. - Resolve file source from current message/context, then upload automatically when needed.
- Ask follow-up questions only when no readable file source can be obtained.
- Accepted input file formats for upload:
jsonl,csv,xlsx,xls(BatchJob normalizes to internal JSONL). - For
jsonl, each line must be either:- canonical Vertex format (
contents+ optionalgenerationConfig) - simple prompt format (
prompt+ optionalaspect_ratio/image_urls, whereimage_urlsmust be publicly reachable URLs)
- canonical Vertex format (
- If user gives only
model modeafter a file message, treat it as confirmation and continue automatically.
Guardrails (Must Follow)
- Do not auto-retry by creating a second job unless user explicitly asks.
- Do not auto-rewrite dataset format after a terminal failure unless user explicitly asks.
- Before upload, auto-normalization is allowed only once for known safe mappings (e.g. simple prompt JSONL -> Vertex JSONL).
- Upload failure is terminal for this run: do not continue to precheck/submit when upload fails.
- Never submit when
row_count <= 0. - Do not fetch or parse
output_summary_urlautomatically; only do it when user asks for detailed failure reason. - After reaching terminal status (
completed,failed,partially_failed), stop execution and return summary immediately.
JSONL Compatibility Rule (Important)
BatchJob internal execution expects each JSONL line to be a VertexGeminiImageRequest shape.
- Canonical line format:
contents[0].parts[0].textcontains prompt textgenerationConfig.imageConfig.aspectRatiois optionalgenerationConfig.responseModalitiesshould includeIMAGEandTEXT
- Non-canonical simple JSONL like
{"prompt":"...","aspect_ratio":"1:1"}is acceptable; server will normalize it to Vertex format. - If JSONL has neither
contentsnorprompt, stop and ask user to provide valid data. - Explicitly unsupported (must reject before submit):
- OpenAI Batch style lines containing
method+url+body(for example/v1/chat/completionspayload). - This schema will fail with Vertex error:
at least one contents field is required.
- OpenAI Batch style lines containing
Dataset Output Rule (Important)
When user asks you to generate a template/demo file for BatchJob image tasks:
- Prefer CSV headers:
prompt,aspect_ratio,image_urls - Or JSONL line format with
prompt/aspect_ratio/image_urls - If
image_urlsis provided, it must be publicly accessible (http/https). - Do NOT generate OpenAI batch envelope (
custom_id+method+url+body) as final upload file
User Template Response (Important)
When user asks for format/template, return:
- A short explanation (
promptrequired,aspect_ratio/image_urlsoptional, andimage_urlsmust be public URLs). - One copyable CSV snippet (default).
- Optionally one JSONL snippet (simple prompt schema).
- Local template file paths (if available):
/home/node/.openclaw/workspace/templates/batchjob-input-template.csv/home/node/.openclaw/workspace/templates/batchjob-input-template.jsonl/home/node/.openclaw/workspace/templates/batchjob-format-guide.md
Do not output OpenAI batch envelope examples in template replies.
File Source Resolver (Strict Order)
- Existing
file_id:- if provided, skip upload.
- Public
file_url(http://orhttps://):- download to temp local file, then upload.
- Explicit local
file_path:- if readable, upload.
- Inbound attachment local path from channel/runtime context:
- examples:
/tmp/...,MEDIA:<path>,/tmp/openclaw-media/.... - if readable, upload.
- examples:
- Channel private file token/object (no local path, no public URL):
- if runtime has a channel adapter that can download attachment bytes, use it and upload.
- if not available, enter fallback interaction.
Resolver output must be normalized to one of:
file_idfile_path(readable local file)
Execution Flow
- Confirm
modelandmode; if missing, use safe defaults (model=google/gemini-2.5-flash-image,mode=fast) and tell user. - Resolve file source using resolver above.
- If resolver gives
file_path, upload viaPOST /v1/batch/files:uploadto getfile_id.- For
.jsonl, inspect a few non-empty lines first:- if
contentsexists, upload as-is. - if only
prompt/aspect_ratio/image_urlsexists, upload as-is (server will normalize). - if
method+url+bodyexists, stop and ask user to switch to BatchJob schema. - if structure is unknown, stop and ask user for valid schema.
- if
- Backward compatibility fallback:
- if upload fails with
unsupported file typeforcsv/xlsx/xls, convert once to JSONL and retry upload once. - do not retry more than once.
- If upload returns validation error (
unsupported schema,no valid data rows), stop immediately and return fix guidance.
- For
- Run precheck with
record_count(prefer uploaded filerow_count). - Submit job with
file_id. - Poll job status until terminal (
completed,failed,partially_failed) with bounded timeout:- interval: 5 seconds
- max polls: 12 (about 60 seconds)
- if still non-terminal after max polls: return current status and
job_id, then stop.
- Return concise summary with
job_id, status, progress, andoutput_summary_url.
When user only asks for estimate, stop at precheck and do not submit.
Fallback Interaction (Only When Needed)
Use this when resolver cannot read file bytes from current channel/context:
我拿到了“文件引用”,但当前运行环境无法直接读取该附件内容。请任选其一:
1) 直接发一个可公网下载的 URL
2) 提供本机可读路径(如 /tmp/xxx.csv)
3) 先把文件上传到 BatchJob,给我 file_id
If the current channel supports resending as direct attachment path in context, also ask user to resend once.
File Source Playbook
A) Public URL -> Local Temp File
FILE_URL="https://example.com/input.jsonl"
EXT="${FILE_URL##*.}"
FILE_PATH="$(mktemp "/tmp/batchjob-input.XXXXXX.${EXT:-jsonl}")"
curl -fL --retry 3 --connect-timeout 10 "$FILE_URL" -o "$FILE_PATH"
A2) JSONL Schema Sanity Check
Use this before upload to avoid wrong schema submission.
SRC_JSONL="/tmp/input.jsonl"
FIRST_LINE="$(grep -m1 -v '^[[:space:]]*$' "$SRC_JSONL")"
echo "$FIRST_LINE" | jq -e '
(has("contents")) or
(has("prompt")) and ( (has("method")|not) and (has("url")|not) and (has("body")|not) )
' >/dev/null || {
echo "JSONL schema invalid for BatchJob: requires contents or prompt-only schema"
exit 1
}
B) Feishu/Channel Attachment Path
If message context already includes local attachment path, treat it as FILE_PATH directly.
FILE_PATH="/tmp/openclaw-media/your-uploaded-file.jsonl"
If only a channel token/link is provided but no downloadable URL and no local path, try channel adapter download first. If adapter is unavailable, use fallback interaction.
Curl Templates
FILE_PATH="/path/to/input.jsonl"
FILE_NAME="$(basename "${FILE_PATH}")"
test -f "${FILE_PATH}" || { echo "文件 ${FILE_PATH} 不存在"; exit 1; }
FILE_CONTENT_B64="$(base64 < "${FILE_PATH}" | tr -d '\n')"
curl -sS "${BATCHJOB_BASE_URL}/v1/batch/files:upload" \
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"filename\":\"${FILE_NAME}\",\"mode\":\"fast\",\"content\":\"${FILE_CONTENT_B64}\"}"
curl -sS "${BATCHJOB_BASE_URL}/v1/batch/jobs:precheck" \
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"record_count": 100, "model": "google/gemini-2.5-flash-image", "mode": "fast"}'
curl -sS "${BATCHJOB_BASE_URL}/v1/batch/jobs" \
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"file_id": "your-file-id", "model": "google/gemini-2.5-flash-image", "mode": "fast"}'
curl -sS "${BATCHJOB_BASE_URL}/v1/batch/jobs/${JOB_ID}" \
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}"
curl -sS "${BATCHJOB_BASE_URL}/v1/batch/jobs/${JOB_ID}:cancel" \
-H "Authorization: Bearer ${BATCHJOB_BEARER_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"reason":"user requested cancellation"}'