پرش به محتوا
← همهٔ یادداشت‌ها

ComfyUI API: ساخت هزار تصویر با اسکریپت پایتون

·۸ دقیقه مطالعه·تیم جی‌پی‌یو

ردیف‌های سربازان سفالی ارتش تراکوتا در گودال‌های خاکی محل کشف، همه ایستاده و رو به یک جهت.

اگر لازم دارید صدها یا هزار تصویر با یک استایل ثابت بسازید، کلیک کردن روی دکمهٔ Queue در ComfyUI جواب نمی‌دهد. راه درست این است که ورک‌فلو را به شکل API Format ذخیره کنید و با یک اسکریپت پایتون، پشت سر هم به ComfyUI بدهید. در این آموزش ComfyUI روی GPU ابری، همین کار را قدم‌به‌قدم انجام می‌دهیم: یک کارت گرافیک ساعتی اجاره می‌کنیم، ورک‌فلو را API می‌گیریم، اسکریپتی می‌نویسیم که فهرست پرامپت‌ها را بخواند و همه را تولید کند، و در پایان خروجی‌ها را یک‌جا دانلود می‌کنیم.

کل کار حدود یک ساعت وقت می‌برد و روی یک کارت ۲۴ گیگابایتی، تولید هزار تصویر SDXL معمولاً سه تا پنج ساعت طول می‌کشد. با نرخ‌های بازار در زمان نوشتن این مطلب یعنی چیزی حدود ۱۵۰ تا ۲۵۰ هزار تومان برای کل دسته. نیازی به برنامه‌نویس بودن ندارید؛ اسکریپت فقط از کتابخانه‌های استاندارد پایتون استفاده می‌کند و چیزی نصب نمی‌شود.

پیش‌نیازها

یک حساب در جی‌پی‌یو لازم دارید؛ اگر ندارید از صفحهٔ ثبت‌نام بسازید. پیش از اجاره باید یک کلید عمومی SSH در /account ثبت کرده باشید، چون ورود به ماشین فقط با کلید انجام می‌شود و رمز عبوری در کار نیست.

کیف پول هم باید دست‌کم به اندازهٔ یک ساعت از نرخ ماشین موجودی داشته باشد. شارژ با کارت به کارت انجام می‌شود و تأیید اپراتور معمولاً چند ساعت طول می‌کشد، پس یک روز قبل شارژ کنید. جزئیات در اجاره کارت گرافیک ساعتی: شارژ کیف پول و محاسبهٔ هزینه آمده است.

فرض ما این است که با خود ComfyUI آشنا هستید و یک ورک‌فلو دارید که در حالت تکی جواب می‌دهد. اگر هنوز آن‌جا نیستید، اول آموزش ComfyUI روی GPU ابری: اولین تصویر با SDXL را بخوانید.

انتخاب ماشین مناسب برای تولید دسته‌ای

در کار دسته‌ای، VRAM تعیین می‌کند که اصلاً مدل بالا می‌آید یا نه، و سرعت کارت تعیین می‌کند که هزینهٔ کل چقدر می‌شود. برای SDXL با رزولوشن ۱۰۲۴ پیکسل، ۱۲ گیگابایت کف کار است. برای FLUX.1 یا ورک‌فلوهایی که ControlNet و آپ‌اسکیلر هم دارند، ۲۴ گیگابایت بگیرید تا وسط دسته به خطای کمبود حافظه نخورید.

نکتهٔ مهم اقتصادی این است که در تولید دسته‌ای، کارت گران‌تر لزوماً گران‌تر تمام نمی‌شود. اگر کارتی دو برابر قیمت دارد ولی هر تصویر را در نصف زمان می‌سازد، هزینهٔ هر تصویر یکی است و شما چند ساعت زودتر کارتان تمام می‌شود. پس به جای «ارزان‌ترین ساعت»، به «ارزان‌ترین تصویر» فکر کنید.

قیمت‌های زنده در بازار GPU دیده می‌شود. در زمان نوشتن این مطلب، RTX 3060 با ۱۲ گیگابایت از حدود ۱۶ هزار تومان در ساعت، RTX A4000 با ۱۶ گیگابایت از حدود ۲۷ هزار تومان، و RTX 3090 با ۲۴ گیگابایت از حدود ۴۸ هزار تومان در ساعت شروع می‌شود. برای دسته‌های بزرگ، RTX 3090 معمولاً نقطهٔ تعادل خوبی است. قیمت ساعتی لحظهٔ اجاره قفل می‌شود و تا پایان کار همان می‌ماند.

اجاره ماشین و راه‌اندازی ComfyUI روی GPU ابری

در بازار، کارت مورد نظر را فیلتر کنید و موقع اجاره قالب ComfyUI را انتخاب کنید. ماشین با ComfyUI آمادهٔ اجرا بالا می‌آید و لازم نیست چیزی نصب کنید. اگر با فهرست قالب‌ها آشنا نیستید، سرور GPU آماده: قالب‌ها چیست و کدام را انتخاب کنیم؟ را ببینید.

چند دقیقه بعد ماشین «آماده» می‌شود. صفحهٔ ماشین دستور SSH آماده، دکمهٔ «پنل ماشین» برای باز کردن رابط ComfyUI، و دستور «تونل امن» را به شما می‌دهد؛ همه یک‌کلیک کپی می‌شوند. برای این آموزش هر دو را لازم دارید: رابط وب را برای ساختن ورک‌فلو، و SSH را برای اجرای اسکریپت.

مدل‌ها را روی خود ماشین دانلود کنید، نه از لپ‌تاپ‌تان. ماشین‌ها خارج از ایران هستند و دانلود از Hugging Face یا Civitai روی خودشان با سرعت گیگابیتی انجام می‌شود. روش کار در نصب مدل و LoRA از Civitai در ComfyUI روی GPU ابری توضیح داده شده.

گرفتن ورک‌فلو به شکل API Format

ورک‌فلویی که با Ctrl+S ذخیره می‌کنید برای باز کردن دوباره در مرورگر است و به درد API نمی‌خورد. چیزی که لازم دارید «API Format» است: همان گراف، ولی با شناسهٔ عددی گره‌ها و بدون اطلاعات ظاهری.

در رابط ComfyUI، از منوی Workflow در بالا سمت چپ گزینهٔ Export (API) را بزنید. در نسخه‌های قدیمی‌تر باید اول در تنظیمات، «Enable Dev mode Options» را روشن کنید تا دکمهٔ «Save (API Format)» ظاهر شود. فایل JSON روی کامپیوتر شما ذخیره می‌شود؛ آن را با نام workflow_api.json به ماشین بفرستید:

scp -P <PORT> workflow_api.json root@<HOST>:/workspace/

مقدار <HOST> و <PORT> را از دستور SSH در صفحهٔ ماشین بردارید. اگر با scp راحت نیستید، دانلود مدل هوش مصنوعی روی سرور GPU: scp و rsync را بخوانید.

حالا فایل را باز کنید و دو چیز را پیدا کنید: شناسهٔ گرهٔ CLIPTextEncode که پرامپت مثبت را نگه می‌دارد، و شناسهٔ گرهٔ KSampler که seed در آن است. در ورک‌فلوی پیش‌فرض این‌ها معمولاً "6" و "3" هستند، ولی حتماً در فایل خودتان چک کنید.

اولین درخواست با curl

قبل از نوشتن اسکریپت، مطمئن شوید API جواب می‌دهد. از طریق SSH وارد ماشین شوید و این را بزنید:

curl -s http://127.0.0.1:8188/prompt

خروجی باید یک JSON با کلید exec_info و queue_remaining باشد؛ یعنی ComfyUI روی پورت ۸۱۸۸ بالا است و صف خالی است. وضعیت کامل صف را هم می‌توانید ببینید:

curl -s http://127.0.0.1:8188/queue

این دو کلید queue_running و queue_pending برمی‌گرداند.

اسکریپت پایتون برای تولید دسته‌ای

اسکریپت زیر را روی ماشین در /workspace/batch.py بسازید. فقط از کتابخانه‌های استاندارد پایتون استفاده می‌کند، پس چیزی نصب نمی‌شود. فهرست پرامپت‌ها را از یک فایل متنی می‌خواند — هر خط یک پرامپت — و برای هر کدام یک درخواست به صف می‌فرستد:

import json, random, time, urllib.request, urllib.parse

SERVER = "http://127.0.0.1:8188"
PROMPT_NODE = "6"      # شناسهٔ گرهٔ CLIPTextEncode مثبت
SAMPLER_NODE = "3"     # شناسهٔ گرهٔ KSampler

workflow = json.load(open("/workspace/workflow_api.json"))
lines = [l.strip() for l in open("/workspace/prompts.txt") if l.strip()]

def queue_prompt(wf):
    data = json.dumps({"prompt": wf}).encode("utf-8")
    req = urllib.request.Request(
        SERVER + "/prompt", data=data,
        headers={"Content-Type": "application/json"})
    return json.loads(urllib.request.urlopen(req).read())

def history(prompt_id):
    with urllib.request.urlopen(SERVER + "/history/" + prompt_id) as r:
        return json.loads(r.read())

ids = []
for i, text in enumerate(lines):
    workflow[PROMPT_NODE]["inputs"]["text"] = text
    workflow[SAMPLER_NODE]["inputs"]["seed"] = random.randint(0, 2**32 - 1)
    res = queue_prompt(workflow)
    if "prompt_id" not in res:
        print("رد شد:", text[:40], res.get("error"))
        continue
    ids.append(res["prompt_id"])
    print(f"[{i+1}/{len(lines)}] در صف: {res['prompt_id']}")

print("همه در صف رفتند. منتظر پایان...")
done = set()
while len(done) < len(ids):
    for pid in ids:
        if pid in done:
            continue
        h = history(pid)
        if pid in h:
            done.add(pid)
    print(f"تمام‌شده: {len(done)}/{len(ids)}")
    time.sleep(15)
print("پایان دسته.")

نکتهٔ کلیدی random.randint در خط seed است. اگر seed را عوض نکنید، ComfyUI نتیجهٔ تکراری را از کش برمی‌گرداند و هزار تصویر یکسان می‌گیرید. فایل پرامپت‌ها را هم بسازید و اسکریپت را اجرا کنید:

cd /workspace
printf '%s\n' "a red fox in snow, cinematic" "a blue ceramic vase, studio light" > prompts.txt
python3 batch.py

خروجی هر چند ثانیه وضعیت را چاپ می‌کند. چون همهٔ کار داخل خود ماشین انجام می‌شود، قطع شدن اینترنت شما ارتباطی به ادامهٔ کار ندارد — به شرطی که اسکریپت را داخل tmux یا با nohup اجرا کرده باشید:

nohup python3 batch.py > batch.log 2>&1 &

دانلود خروجی‌ها

تصاویر در پوشهٔ output خود ComfyUI ذخیره می‌شوند. اول ببینید چند تا ساخته شده:

ls /workspace/ComfyUI/output | wc -l

اگر مسیر فرق داشت، با find / -name "ComfyUI" -maxdepth 4 -type d پیدایش کنید. بعد همه را فشرده کنید و روی لپ‌تاپ بیاورید:

cd /workspace/ComfyUI && zip -r /workspace/batch.zip output
scp -P <PORT> root@<HOST>:/workspace/batch.zip .

راه دیگر این است که تک‌تک تصاویر را از خود API بگیرید. نقطهٔ /view با سه پارامتر filename، subfolder و type فایل را برمی‌گرداند؛ نام فایل‌ها در پاسخ /history/<prompt_id> زیر outputs آمده است. برای دسته‌های بزرگ، همان zip ساده‌تر و سریع‌تر است.

مهم: دیسک ماشین با حذف ماشین پاک می‌شود و فضای ذخیره‌سازی دائمی وجود ندارد. قبل از حذف، حتماً خروجی را دانلود کنید یا به فضای ابری خودتان بفرستید.

اگر به مشکل خوردید

خطای CUDA out of memory وسط دسته. یعنی کارت برای این ورک‌فلو کوچک است. رزولوشن یا batch_size را کم کنید، یا ماشین را با کارت ۲۴ گیگابایتی عوض کنید. در تولید دسته‌ای حافظه به‌مرور پر می‌شود، پس حاشیهٔ امن بگذارید.

پاسخ /prompt کلید node_errors دارد. ورک‌فلو معتبر نیست؛ معمولاً یعنی نام مدل در فایل JSON با فایلی که روی ماشین هست فرق دارد. مقدار ckpt_name را با خروجی ls پوشهٔ مدل‌ها مقایسه کنید.

همهٔ تصاویر یکسان درآمدند. seed را عوض نکرده‌اید. مطمئن شوید SAMPLER_NODE شناسهٔ درست گرهٔ KSampler است.

اسکریپت با قطع SSH متوقف شد. از tmux یا nohup استفاده کنید تا فرایند به نشست SSH وابسته نباشد.

پورت ۸۱۸۸ باز نمی‌شود. اسکریپت باید روی خود ماشین اجرا شود و به 127.0.0.1 وصل شود. اگر رابط وب را از لپ‌تاپ می‌خواهید، از دستور «تونل امن» در صفحهٔ ماشین استفاده کنید.

هزینه و صرفه‌جویی

قبل از دستهٔ بزرگ، ده پرامپت را تست بگیرید و زمان هر تصویر را اندازه بگیرید. آن عدد ضرب در تعداد کل، تخمین نسبتاً دقیقی از ساعت‌های لازم می‌دهد و با نرخ ساعتی ماشین، هزینهٔ کل را می‌سازد.

هزینه دقیقه‌ای از کیف پول کم می‌شود و ماشین متوقف‌شده هزینه‌ای ندارد. پس وقتی اسکریپت تمام شد، خروجی را دانلود کنید و بلافاصله ماشین را متوقف یا حذف کنید. بزرگ‌ترین اتلاف پول در این کار، ماشینی است که دستهٔ آن ساعت‌ها پیش تمام شده و کسی یادش نیامده خاموشش کند.

قدم بعدی

اگر می‌خواهید هزار تصویر با یک استایل واحد باشند، به جای تکرار پرامپت از LoRA استفاده کنید؛ نتیجه خیلی یکدست‌تر می‌شود. و اگر لازم دارید ترکیب‌بندی خروجی‌ها کنترل‌شده باشد، ControlNet در ComfyUI: کنترل ژست و ترکیب‌بندی تصویر را ببینید. همین اسکریپت با یک گرهٔ ControlNet در ورک‌فلو، بدون تغییر کار می‌کند.

پرسش‌های متداول

آیا برای اجرای دسته‌ای ComfyUI به کارت گرافیک نیاز است؟

بله، ولی لازم نیست آن را بخرید. با اجاره GPU ساعتی، کارت را فقط برای همان چند ساعتی که دسته در حال اجراست می‌گیرید و بعد ماشین را متوقف می‌کنید.

ورک‌فلوی معمولی را می‌شود مستقیم به API داد؟

نه. فایلی که با Ctrl+S ذخیره می‌شود قالب دیگری دارد. باید از منوی Workflow گزینهٔ Export (API) را بزنید تا نسخهٔ API Format ساخته شود.

تولید هزار تصویر چقدر طول می‌کشد؟

بستگی به مدل و رزولوشن دارد. برای SDXL با ۱۰۲۴ پیکسل و ۲۵ مرحله روی یک کارت ۲۴ گیگابایتی، معمولاً هر تصویر چند ثانیه است و کل دسته سه تا پنج ساعت طول می‌کشد.

اگر وسط کار اینترنتم قطع شود چه می‌شود؟

اگر اسکریپت را با tmux یا nohup روی خود ماشین اجرا کرده باشید، کار ادامه پیدا می‌کند. ماشین خارج از ایران است و به اتصال شما وابسته نیست؛ بعداً دوباره SSH بزنید و batch.log را ببینید.

خروجی‌ها بعد از حذف ماشین باقی می‌مانند؟

نه. دیسک ماشین با حذف آن پاک می‌شود و فضای ذخیره‌سازی دائمی یا مشترک وجود ندارد. قبل از حذف، فایل zip خروجی را دانلود کنید.

منبع رسمی ابزار: مخزن ComfyUI در گیت‌هاب و مستندات مسیرهای سرور.

تصویر کاور: «Rows of terracota warriors are arranged in rows of pits» اثر shankar s.، از ویکی‌مدیا کامنز، لایسنس CC BY 2.0.