ComfyUI engineering: workflow como código
- ⬜🎓 LoRA de imagem: treinar style/character em 30 min(Diffusion Models & Geração Multimodal)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Por que ComfyUI ganhou a guerra das UIs
Em 2022-2023 a comunidade Stable Diffusion vivia em torno do AUTOMATIC1111 — a "web UI" monolítica que botou SD na mão de todo mundo. Excelente para uso individual: prompts, sliders, extensões. Péssima para engenharia: workflow opaco, reprodução frágil, automação custosa.
ComfyUI (comfyanonymous, jan 2023) atacou o problema na raiz: o pipeline É um grafo. Cada operação (carregar checkpoint, encodar prompt, samplar, decodar VAE, salvar) vira um nó. Inputs/outputs tipados. Grafo serializa em JSON. Esse JSON é o artefato versionável — não mais "um conjunto de configurações na UI".
A virada vencedora: em vez de tentar esconder a complexidade do diffusion atrás de presets, ComfyUI expôs a complexidade. Para iniciantes assustou; para engineers virou o padrão profissional. Hoje (2026) é a UI dominante em produção, com fal.ai, Replicate, Runpod e ComfyUI Deploy hospedando-o como serviço.
O modelo mental: grafo direcionado de nós tipados
Tipos comuns: MODEL, CLIP, VAE, CONDITIONING, LATENT, IMAGE, MASK, CONTROL_NET. O engine valida que outputs/inputs casem — se você plugar IMAGE numa entrada esperando LATENT, erro de validação.
O JSON por trás do workflow
{
"1": {
"class_type": "CheckpointLoaderSimple",
"inputs": { "ckpt_name": "flux1-dev.safetensors" }
},
"2": {
"class_type": "CLIPTextEncode",
"inputs": {
"text": "a cyberpunk cat, neon lighting, 35mm film",
"clip": ["1", 1]
}
},
"3": {
"class_type": "CLIPTextEncode",
"inputs": { "text": "", "clip": ["1", 1] }
},
"4": {
"class_type": "EmptyLatentImage",
"inputs": { "width": 1024, "height": 1024, "batch_size": 1 }
},
"5": {
"class_type": "KSampler",
"inputs": {
"seed": 42, "steps": 28, "cfg": 3.5,
"sampler_name": "euler", "scheduler": "simple", "denoise": 1.0,
"model": ["1", 0], "positive": ["2", 0], "negative": ["3", 0],
"latent_image": ["4", 0]
}
},
"6": {
"class_type": "VAEDecode",
"inputs": { "samples": ["5", 0], "vae": ["1", 2] }
},
"7": {
"class_type": "SaveImage",
"inputs": { "images": ["6", 0], "filename_prefix": "out" }
}
}Note como cada input que vem de outro nó é uma tupla [node_id, output_index]. significa "primeiro output do nó 1" — o MODEL do CheckpointLoader. Output 1 é CLIP, output 2 é VAE.
Anatomia de um custom node
# Exemplo: um nó que aplica um filtro de luminância numa imagem
import torch
class LuminanceShift:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"shift": ("FLOAT", {"default": 0.1, "min": -1.0, "max": 1.0, "step": 0.01}),
},
"optional": {
"mask": ("MASK",),
},
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "shift_luminance"
CATEGORY = "image/postprocess"
def shift_luminance(self, image, shift, mask=None):
# image shape: [B, H, W, 3] em [0,1]
# Luminance: 0.299 R + 0.587 G + 0.114 B
lum = image[..., 0] * 0.299 + image[..., 1] * 0.587 + image[..., 2] * 0.114
delta = shift
if mask is not None:
delta = shift * mask.unsqueeze(-1)
out = (image + delta).clamp(0, 1)
return (out,)
NODE_CLASS_MAPPINGS = {"LuminanceShift": LuminanceShift}
NODE_DISPLAY_NAME_MAPPINGS = {"LuminanceShift": "Luminance Shift"}Custom nodes essenciais (2026)
| Node Pack | Autor | Função | Por que importa |
|---|---|---|---|
| ComfyUI-Manager | ltdrdata | Package manager | Instala/atualiza outros nodes sem mexer no git |
| ComfyUI-Impact-Pack | ltdrdata | Detalhamento facial, upscale, masking | Workflow profissional de retrato |
| ComfyUI_IPAdapter_plus | cubiq | IP-Adapter | Style/subject reference |
| comfyui_controlnet_aux | Fannovel16 | Pré-processadores de ControlNet | Canny/Depth/Pose tudo num pack |
| ComfyUI-AnimateDiff-Evolved | Kosinkadink | Vídeo curto (motion modules) | Antes de Sora/Veo, era o gerador de vídeo |
| ComfyUI-Crystools | crystian | Sysinfo overlay (VRAM, RAM) | Debug em production |
| rgthree-comfy | rgthree | Reroutes, Mute, Power Lora Loader | QoL — workflows complexos |
| was-node-suite | WASasquatch | Centenas de utilities | Image ops, math, color, latent |
| ComfyUI-Easy-Use | yolain | Workflows pré-prontos compactos | Reduz boilerplate |
| ComfyUI-Flux-Trainer | kijai | Treinar LoRA dentro do ComfyUI | Closing the loop |
REST API + WebSocket: ComfyUI como backend
# Cliente Python — manda workflow e baixa resultado
import json, time, uuid, requests, websocket
COMFY = "http://localhost:8188"
CLIENT_ID = str(uuid.uuid4())
def submit(workflow_json: dict) -> str:
r = requests.post(f"{COMFY}/prompt", json={
"prompt": workflow_json,
"client_id": CLIENT_ID,
})
return r.json()["prompt_id"]
def wait_via_ws(prompt_id: str) -> dict:
ws = websocket.WebSocket()
ws.connect(f"ws://localhost:8188/ws?clientId={CLIENT_ID}")
while True:
msg = json.loads(ws.recv())
if msg["type"] == "executing":
data = msg["data"]
if data["node"] is None and data["prompt_id"] == prompt_id:
ws.close()
break
# busca outputs no /history
return requests.get(f"{COMFY}/history/{prompt_id}").json()[prompt_id]
def download_image(filename: str, subfolder: str = "", folder_type: str = "output") -> bytes:
r = requests.get(f"{COMFY}/view", params={
"filename": filename, "subfolder": subfolder, "type": folder_type,
})
return r.content
# Uso
with open("workflow.json") as f:
wf = json.load(f)
wf["5"]["inputs"]["seed"] = int(time.time()) # parametrize
pid = submit(wf)
result = wait_via_ws(pid)
img = result["outputs"]["7"]["images"][0]
png_bytes = download_image(img["filename"], img["subfolder"], img["type"])
open("out.png", "wb").write(png_bytes)Essa API é estável desde 2023. Toda integração comercial (fal, Replicate, RunPod) usa exatamente esse pattern por trás dos panos.
O que a representação do pipeline como grafo serializável muda em relação a uma interface de configurações?
Produção: ComfyUI Deploy e alternativas
| Plataforma | Tipo | Quando usar |
|---|---|---|
| ComfyUI Deploy (BennyKok) | Self-hosted ou nuvem | Open-source, control total, autoscaling, multi-workflow management |
| Replicate | Hospedado | Sem ops; mais caro; ideal para protótipo/produto pequeno |
| fal.ai | Hospedado, low-latency | Real-time; melhor latência do mercado para diffusion |
| RunPod | GPU VM | Roda ComfyUI numa VM; barato em on-demand |
| Modal | Serverless GPU | Bom para batch jobs grandes; container Python |
| Salad | Edge GPU spot | GPU consumer barata; latência variável |
Decisão: ComfyUI vs A1111 vs SD.Next vs InvokeAI
📋 Você está montando um pipeline de produção que precisa ser versionável e automatizável
JSON serializável + custom nodes + REST API estável. Toda a infra cloud (fal, Replicate, RunPod) oferece como primeira classe. Comunidade enorme, suporte rápido a modelos novos (Flux, SD3 chegaram primeiro no ComfyUI).
Alt: AUTOMATIC1111
Alt: Workflow opaco, automação complexa, manutenção da extensions caótica. Ainda forte na comunidade casual
Alt: InvokeAI
Alt: UX mais polida, foco em fluxo profissional artístico — mas API menos madura
Alt: SD.Next
Alt: Fork de A1111 mais moderno, mas mesma limitação estrutural
Alt: Construir do zero com diffusers
Alt: Você reinventa workflow engine, queue, UI; ComfyUI já resolveu
Práticas de engenharia para workflows ComfyUI
Perguntas que sobram
❓ ComfyUI suporta vídeo (AnimateDiff, CogVideoX, Mochi)?
❓ Qual a diferença entre 'workflow JSON' e 'API JSON'?
❓ Como fazer A/B test de workflow em produção?
❓ Posso rodar ComfyUI em Mac (M-series)?
Recursos
Próximo: geração de vídeo. Sora, Runway Gen-4, Kling 2.0, Veo 3. Como DiT temporal funciona, VAE temporal, e quais são os limites práticos hoje (duração, fidelidade temporal, custo).
Perguntas frequentes
❓ Por que fluxo de nós virou padrão profissional?
❓ Fluxo de nós é código?
❓ Como colocar fluxo de geração em produção?
Fixando
Qual é a diferença entre o arquivo exportado pela interface e o formato voltado à automação?
Por que registrar as somas de verificação dos arquivos de modelo usados por um fluxo?
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…