: "${ARK_API_KEY:?Set ARK_API_KEY before running this script}"
: "${AGENT_MODEL_ID:?Set AGENT_MODEL_ID to a model shown for Visual Creation Agent in the console}"
: "${IMAGE_MODEL_ID:?Set IMAGE_MODEL_ID to an image model shown for Visual Creation Agent in the console}"
: "${VIDEO_MODEL_ID:?Set VIDEO_MODEL_ID to a video model shown for Visual Creation Agent in the console}"
ARK_BASE_URL="${ARK_BASE_URL:-https://ark.cn-beijing.volces.com/api/v3}"
OUTPUT_DIR="${OUTPUT_DIR:-./visual-creation-agent-output}"
RUN_SUFFIX="${RUN_SUFFIX:-$(date +%Y%m%d%H%M%S)}"
SYSTEM_PROMPT='You are Seed Creation Agent, an interactive creation agent with a sandbox environment. You help users turn ideas, requirements, and source materials into finished creative deliverables.
Supported work includes, but is not limited to, images, videos, audio, scripts, copywriting, storyboards, prompts, presentations, documents, structured content, creative plans, and multi-file projects. Use the instructions below and the available tools to assist the user.'
curl -sS -o "$response_file" -w "%{http_code}" \
-H "Authorization: Bearer $ARK_API_KEY" \
if [[ "$status" == 2[0-9][0-9] ]]; then
printf 'HTTP %s from %s\n' "$status" "$url" >&2
printf 'Request failed before receiving an HTTP response from %s.\n' \
if [[ -s "$response_file" ]]; then
cat "$response_file" >&2
if [[ -n "$STREAM_PID" ]]; then
kill "$STREAM_PID" 2>/dev/null || true
if [[ -n "$SESSION_ID" ]]; then
if ! request_json DELETE "$ARK_BASE_URL/sessions/$SESSION_ID" >/dev/null; then
if [[ -n "$AGENT_ID" ]]; then
if ! request_json DELETE "$ARK_BASE_URL/agents/$AGENT_ID" >/dev/null; then
if [[ -n "$ENVIRONMENT_ID" ]]; then
if ! request_json DELETE "$ARK_BASE_URL/environments/$ENVIRONMENT_ID" >/dev/null; then
return "$cleanup_status"
if [[ "$exit_code" -ne 0 ]]; then
if [[ -n "$SESSION_ID" || -n "$AGENT_ID" || -n "$ENVIRONMENT_ID" ]]; then
echo "Run failed; cleaning up resources created by this script." >&2
cleanup_resources || true
elif [[ "${CLEANUP_RESOURCES:-0}" == "1" ]]; then
if ! cleanup_resources; then
--arg name "visual-creation-agent-$RUN_SUFFIX" \
--arg agent_model "$AGENT_MODEL_ID" \
--arg image_model "$IMAGE_MODEL_ID" \
--arg video_model "$VIDEO_MODEL_ID" \
--arg system_prompt "$SYSTEM_PROMPT" \
description: "Generate campaign images and videos in one Session.",
# The visual enhancement Agent requires high reasoning effort.
reasoning_effort: "high"
# Select the visual enhancement Agent type.
base_agent: "ultra_vision_agent_preview",
# Mount the base tools, but disable web_search because general_search is enabled below.
type: "agent_toolset_20260916",
{name: "web_search", enabled: false}
# Disable multimodal defaults and explicitly enable the tools used by this tutorial.
type: "agent_toolset_multimodal_20260825",
# A single configured model is also the default image model.
{id: $image_model, default: true}
# A single configured model is also the default video model.
{id: $video_model, default: true}
{name: "visual_search", enabled: true},
{name: "general_search", enabled: true},
{name: "compose_video", enabled: true},
{name: "todo_write", enabled: true}
request_json POST "$ARK_BASE_URL/agents" \
-H "Content-Type: application/json" \
AGENT_ID=$(jq -er '.id' <<<"$agent")
echo "Agent ID: $AGENT_ID"
environment_payload=$(jq -n \
--arg name "visual-creation-env-$RUN_SUFFIX" \
description: "Cloud environment for visual creation tasks.",
networking: {type: "unrestricted"}
request_json POST "$ARK_BASE_URL/environments" \
-H "Content-Type: application/json" \
-d "$environment_payload"
ENVIRONMENT_ID=$(jq -er '.id' <<<"$environment")
echo "Environment ID: $ENVIRONMENT_ID"
session_payload=$(jq -n \
--arg agent "$AGENT_ID" \
--arg environment "$ENVIRONMENT_ID" \
environment_id: $environment,
title: "Coffee campaign visual creation"
request_json POST "$ARK_BASE_URL/sessions" \
-H "Content-Type: application/json" \
SESSION_ID=$(jq -er '.id' <<<"$session")
echo "Session ID: $SESSION_ID"
stream_error_file=$(mktemp)
while IFS= read -r line; do
[[ "$line" == data:* ]] || continue
event_type=$(jq -r '.type // empty' <<<"$json" 2>/dev/null || true)
echo "[Session] running"
printf '[Tool] %s\n' "$(jq -r '.name // "unknown"' <<<"$json")"
jq -c '{type, tool_use_id, is_error}' <<<"$json"
jq -r '.content[]? | select(.type == "text") | .text' <<<"$json"
if [[ "$seen_running" -eq 1 ]]; then
curl -sS -N --fail-with-body --max-time 1800 \
"$ARK_BASE_URL/sessions/$SESSION_ID/events/stream" \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Accept: text/event-stream" \
if [[ "$stream_result" == "idle" ]]; then
rm -f "$stream_error_file"
if [[ "$stream_result" == "error" ]]; then
rm -f "$stream_error_file"
if [[ -s "$stream_error_file" ]]; then
cat "$stream_error_file" >&2
rm -f "$stream_error_file"
echo "SSE stream ended before the Session returned to idle." >&2
task_payload=$(cat <<'EOF'
"text": "为一家城市精品咖啡品牌制作一组新品宣传素材。先生成一张 16:9 的电影感主视觉:清晨城市街角,手冲咖啡放在窗边木桌上,暖色自然光,画面中不要出现文字,并保存为 /mnt/session/outputs/campaign-key-visual.jpg。再以这张图为首帧,生成一段 4 秒、16:9 的宣传视频,镜头缓慢推进,咖啡蒸汽自然上升,并保存为 /mnt/session/outputs/campaign-teaser.mp4。完成后总结两个文件的名称和生成结果。"
"$ARK_BASE_URL/sessions/$SESSION_ID/events" \
-H "Content-Type: application/json" \
-d "$task_payload" >/dev/null
for _ in $(seq 1 60); do
request_json GET "$ARK_BASE_URL/files?scope_id=$SESSION_ID"
image_count=$(jq '[.data[]? | select((.mime_type // "") | startswith("image/"))] | length' <<<"$files")
video_count=$(jq '[.data[]? | select((.mime_type // "") | startswith("video/"))] | length' <<<"$files")
if [[ "$image_count" -ge 1 && "$video_count" -ge 1 ]]; then
if [[ "$image_count" -lt 1 || "$video_count" -lt 1 ]]; then
echo "Expected image and video outputs were not registered in time." >&2
| select((.mime_type // "") | test("^(image|video)/"))
| select(.download_url != null and .download_url != "")
| [.filename, .download_url]
while IFS=$'\t' read -r filename download_url; do
safe_name=$(basename "$filename")
curl -sS --fail-with-body "$download_url" -o "$OUTPUT_DIR/$safe_name"
echo "Downloaded: $OUTPUT_DIR/$safe_name"
echo " AGENT_ID=$AGENT_ID"
echo " ENVIRONMENT_ID=$ENVIRONMENT_ID"
echo " SESSION_ID=$SESSION_ID"