ComfyUI与AI图像生成工作流完全教程

教程简介

本教程全面讲解ComfyUI节点式AI图像生成工作流的核心技术,涵盖ComfyUI安装部署、节点系统与工作流设计、Stable Diffusion XL/Flux模型集成、ControlNet精确控制等核心内容。

ComfyUI与AI图像生成工作流完全教程

适用读者:对AI图像生成有基本了解,希望系统掌握ComfyUI节点式工作流的开发者与创作者。 最后更新:2025年6月


目录

  1. 概述与核心概念
  2. ComfyUI安装与部署
  3. 节点系统与工作流设计
  4. Stable Diffusion XL与Flux模型集成
  5. ControlNet精确控制
  6. LoRA微调模型加载
  7. 图像修复与扩展:Inpainting与Outpainting
  8. 批量生成与自动化
  9. 自定义节点开发
  10. 工作流模板与复用
  11. ComfyUI与A1111 WebUI对比
  12. 企业级图像生成流水线
  13. 常见问题与排错
  14. 总结与进阶资源

1. 概述与核心概念

1.1 什么是ComfyUI

ComfyUI是由开发者 comfyanonymous 创建的一个**节点式(Node-based)**AI图像生成界面。与传统的线性WebUI不同,ComfyUI将图像生成的每一个步骤抽象为独立的"节点",用户通过连接这些节点来构建完整的生成管线。这种设计理念源于图形编程语言(如Blender的Shader Editor、Unreal Engine的Blueprint),赋予用户极大的灵活性和透明度。

核心优势

  • 可视化管线:每个处理步骤都以节点形式呈现,数据流一目了然
  • 高度可定制:节点可自由组合,支持复杂分支与并行处理
  • 低内存占用:相比A1111 WebUI,ComfyUI的显存管理更为高效
  • 工作流可复用:工作流以JSON格式保存,便于分享和版本控制
  • 原生支持最新模型:社区更新速度快,通常率先支持新发布的模型架构

1.2 节点式工作流的核心概念

在ComfyUI中,理解以下核心概念至关重要:

节点(Node):每个节点代表一个独立的处理单元,拥有特定的输入端口(Input)和输出端口(Output)。常见节点类型包括:

  • 加载器节点:负责加载模型、VAE、LoRA等资源
  • 编码器节点:将文本或图像编码为潜空间(Latent Space)表示
  • 采样器节点:执行扩散模型的去噪过程
  • 解码器节点:将潜空间表示解码为最终图像
  • 后处理节点:超分辨率、锐化等图像增强操作

连接(Link/Edge):通过拖拽输出端口到输入端口,建立节点间的数据流。ComfyUI具有类型安全机制——只有兼容的数据类型才能连接。

数据类型:ComfyUI定义了多种数据类型,包括:

数据类型 说明
MODEL 模型数据(UNet/DiT)
CLIP 文本编码器
VAE 变分自编码器
CONDITIONING 条件信号(正面/负面提示词编码结果)
LATENT 潜空间张量
IMAGE 像素空间图像
MASK 蒙版数据
INT / FLOAT / STRING / BOOLEAN 基础标量类型

1.3 数据流工作原理

ComfyUI采用**惰性求值(Lazy Evaluation)**策略——只有当"Queue Prompt"被执行时,系统才会根据节点图的拓扑顺序计算数据流。这意味着:

  1. 修改某个节点的参数不会立即触发重新计算
  2. 已缓存的中间结果会被复用(除非上游节点发生变化)
  3. 未连接的分支不会消耗计算资源

这种策略使得ComfyUI在迭代调试工作流时非常高效。


2. ComfyUI安装与部署

2.1 环境准备

系统要求

  • 操作系统:Windows 10/11、Linux(推荐Ubuntu 20.04+)、macOS(Apple Silicon)
  • GPU:NVIDIA GPU(推荐8GB+ VRAM),支持CUDA 11.8+;AMD GPU通过ROCm支持;Apple Silicon通过MPS支持
  • Python:3.10 或 3.11(推荐3.10)
  • Git:最新版本

2.2 标准安装流程

# 1. 克隆仓库
git clone https://github.com/comfyanonymous/ComfyUI.git
cd ComfyUI

# 2. 创建虚拟环境(推荐)
python -m venv venv
source venv/bin/activate  # Linux/macOS
# venv\Scripts\activate   # Windows

# 3. 安装PyTorch(根据GPU选择)
# NVIDIA GPU(CUDA 11.8)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
# NVIDIA GPU(CUDA 12.1)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

# 4. 安装ComfyUI依赖
pip install -r requirements.txt

# 5. 下载模型
# 将SD模型放入 models/checkpoints/
# 将VAE放入 models/vae/
# 将LoRA放入 models/loras/
# 将ControlNet模型放入 models/controlnet/

# 6. 启动ComfyUI
python main.py
# 默认访问地址: http://127.0.0.1:8188

2.3 Docker部署

对于生产环境,推荐使用Docker部署:

# docker-compose.yml
version: '3.8'
services:
  comfyui:
    build: .
    ports:
      - "8188:8188"
    volumes:
      - ./models:/app/models
      - ./output:/app/output
      - ./custom_nodes:/app/custom_nodes
      - ./input:/app/input
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: unless-stopped
# Dockerfile
FROM python:3.10-slim

RUN apt-get update && apt-get install -y git && rm -rf /var/lib/apt/lists/*

WORKDIR /app
RUN git clone https://github.com/comfyanonymous/ComfyUI.git .

RUN pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
RUN pip install -r requirements.txt

EXPOSE 8188
CMD ["python", "main.py", "--listen", "0.0.0.0", "--port", "8188"]

2.4 常用启动参数

# 基础启动
python main.py

# 指定监听地址和端口(局域网访问)
python main.py --listen 0.0.0.0 --port 8188

# 使用CPU模式(无GPU环境)
python main.py --cpu

# 启用TLS/HTTPS
python main.py --tls-cert cert.pem --tls-key key.pem

# 指定模型目录
python main.py --base-path /data/comfyui-models

# 启用详细日志
python main.py --verbose

# 启动时自动加载工作流
python main.py --workflow /path/to/workflow.json

3. 节点系统与工作流设计

3.1 基础文本生图工作流

最基础的ComfyUI工作流包含以下节点链路:

[CheckpointLoader] → MODEL / CLIP / VAE
        ↓                ↓
   [KSampler]      [CLIPTextEncode] × 2(正面+负面)
        ↓                ↓
   [VAEDecode] ← LATENT(来自KSampler)
        ↓
   [SaveImage]

构建步骤

  1. 添加CheckpointLoaderSimple节点:右键空白区域 → Add Node → Loaders → CheckpointLoaderSimple。选择你的SD模型(如 sd_xl_base_1.0.safetensors

  2. 添加两个CLIPTextEncode节点:分别输入正面提示词和负面提示词

    # 正面提示词示例
    a beautiful landscape painting, mountains, lake, sunset, 
    golden hour lighting, masterpiece, best quality, 8k uhd
    
    # 负面提示词示例
    lowres, bad anatomy, bad hands, text, error, missing fingers, 
    extra digit, fewer digits, cropped, worst quality, low quality
    
  3. 添加EmptyLatentImage节点:设置生成图像的宽高(SDXL推荐1024×1024)

  4. 添加KSampler节点:配置采样参数

    • seed:随机种子(-1为随机)
    • steps:采样步数(推荐20-30)
    • cfg:CFG Scale(推荐7.0-8.0)
    • sampler_name:采样器(推荐 euler_ancestraldpmpp_2m
    • scheduler:调度器(推荐 normalkarras
    • denoise:去噪强度(1.0为完全去噪)
  5. 连接VAEDecode和SaveImage:完成管线

3.2 关键节点详解

KSampler节点

KSampler是ComfyUI中最核心的节点,负责执行扩散模型的去噪采样过程:

输入:
  - model (MODEL): 加载的扩散模型
  - seed (INT): 随机种子,控制生成的可重复性
  - steps (INT): 采样步数,步数越多质量越高但速度越慢
  - cfg (FLOAT): Classifier-Free Guidance强度,控制提示词的遵循程度
  - sampler_name (枚举): 采样算法选择
  - scheduler (枚举): 噪声调度策略
  - positive (CONDITIONING): 正面条件
  - negative (CONDITIONING): 负面条件
  - latent_image (LATENT): 初始潜空间图像
输出:
  - LATENT: 采样后的潜空间图像

常用采样器特性对比

采样器 速度 质量 特点
euler_ancestral 中等 有随机性,每步结果不同
euler 中等 确定性采样
dpmpp_2m 二阶方法,质量稳定
dpmpp_sde SDE变体,细节丰富
dpmpp_2m_sde 综合最优选择
uni_pc 快速收敛

CLIPTextEncode节点

将自然语言提示词编码为模型可理解的条件向量。对于SDXL模型,有两个文本编码器(CLIP ViT-L/14 和 OpenCLIP ViT-bigG),ComfyUI的 CLIPTextEncodeSDXL 节点允许分别设置两个编码器的输入。

ConditioningCombine节点

当你需要混合多个条件时(如同时使用提示词和ControlNet条件),可以使用此节点:

[CLIPTextEncode] → conditioning_a ─┐
                                    ├→ [ConditioningCombine] → combined_conditioning
[ControlNetApply] → conditioning_b ─┘

3.3 工作流设计最佳实践

模块化设计:将复杂工作流分解为可复用的子模块。例如,将"加载模型+加载LoRA+配置采样器"封装为一个功能组(Group),方便在不同工作流中复用。

颜色编码:为不同类型的节点组设置颜色——模型加载为蓝色,采样为绿色,后处理为橙色,保存为红色。这大幅提升工作流的可读性。

注释与标注:使用 Reroute 节点和 Note 节点为工作流添加说明文字,方便团队协作。

参数命名规范:使用 Primitive 节点为关键参数提供有意义的名称,并暴露在工作流的顶层,便于非技术人员调整。


4. Stable Diffusion XL与Flux模型集成

4.1 SDXL模型架构

Stable Diffusion XL(SDXL)相比SD 1.5有重大架构升级:

  • 双文本编码器:CLIP ViT-L/14 + OpenCLIP ViT-bigG/14,提供更丰富的文本理解能力
  • 更大的UNet:约3.5B参数,包含更多的Attention层
  • Refiner模型:可选的专用细化模型,在高噪声步骤后接管生成
  • 微条件:支持输入图像尺寸、裁剪参数等额外条件

ComfyUI中的SDXL工作流

[CheckpointLoaderSimple] → model / clip / vae
        │
        ├── [CLIPTextEncodeSDXL] (正面) ── positive_conditioning
        │     ├── clip_l: "a cyberpunk city at night"
        │     └── clip_g: "a cyberpunk city at night"
        │     ├── width: 1024
        │     ├── height: 1024
        │     ├── target_width: 1024
        │     └── target_height: 1024
        │
        ├── [CLIPTextEncodeSDXL] (负面) ── negative_conditioning
        │     ├── clip_l: "blurry, low quality"
        │     └── clip_g: "blurry, low quality"
        │
        └── [EmptyLatentImage] 1024×1024
                │
                ↓
           [KSampler] steps=25, cfg=7.0, sampler=dpmpp_2m_sde
                │
                ↓
           [VAEDecode]
                │
                ↓
           [SaveImage]

SDXL推荐分辨率

比例 分辨率
1:1 1024×1024
4:3 1152×896
3:4 896×1152
16:9 1344×768
9:16 768×1344

4.2 SDXL Base + Refiner两阶段工作流

SDXL的Refiner模型专门在高噪声阶段优化细节,ComfyUI中实现两阶段生成:

阶段1(Base模型,高噪声阶段):
[SDXLBaseLoader] → base_model
[EmptyLatentImage] → latent
[KSampler] steps=25, end_step=20, denoise=1.0 → partially_denoised_latent

阶段2(Refiner模型,低噪声阶段):
[SDXLRefinerLoader] → refiner_model
[KSampler] steps=25, start_step=20, denoise=1.0, 
           model=refiner_model, latent_image=partially_denoised_latent → final_latent

[VAEDecode] → [SaveImage]

在ComfyUI中,关键参数是KSampler的 start_at_stepend_at_step,它们控制每个模型参与的噪声范围。

4.3 Flux模型集成

Flux是由Black Forest Labs(Stable Diffusion原始团队)推出的新一代文生图模型,采用DiT(Diffusion Transformer)架构。

Flux模型特点

  • Transformer架构:摒弃传统UNet,使用Transformer作为去噪骨干网络
  • Flow Matching:使用流匹配(Flow Matching)而非传统DDPM采样
  • 单阶段:不需要Refiner,单一模型即可生成高质量图像
  • 支持更大分辨率:原生支持2048×2048等高分辨率

ComfyUI中加载Flux模型

Flux模型在ComfyUI中通过 UNETLoader 节点加载(而非CheckpointLoaderSimple),因为其架构与传统SD不同:

[UNETLoader] → model(选择 flux1-dev.safetensors 或 flux1-schnell.safetensors)
[CLIPLoader] → clip(加载 clip_l)
[DualCLIPLoader] → clip(同时加载 clip_l 和 t5xxl)
[VAELoader] → vae(加载 flux vae)

Flux基础工作流

[UNETLoader] flux1-dev.safetensors, weight_dtype=fp16
        │
[DualCLIPLoader] clip_name1=clip_l.safetensors, 
                 clip_name2=t5xxl_fp16.safetensors, 
                 type=flux
        │
[VAELoader] ae.safetensors
        │
[CLIPTextEncode] "a serene Japanese garden with cherry blossoms"
        │
[EmptySD3LatentImage] 1024×1024  ← Flux使用SD3的潜空间格式
        │
[KSampler] steps=20, cfg=1.0, sampler=euler, scheduler=simple, denoise=1.0
        │
[VAEDecode]
        │
[SaveImage]

Flux关键注意事项

  • Flux的 cfg 通常设置为 1.0(因为Flow Matching不依赖CFG引导)
  • 使用 euler 采样器和 simple 调度器
  • Flux-dev适合高质量生成(20-50步),Flux-schnell适合快速生成(4-8步)
  • T5文本编码器需要约10GB额外显存,低显存环境可使用量化版本

5. ControlNet精确控制

5.1 ControlNet原理

ControlNet是一种条件控制方法,允许用户通过提供额外的控制信号(如边缘图、深度图、姿势骨架等)来精确控制生成图像的构图、姿势和结构。其核心思想是在预训练扩散模型的基础上注入可训练的控制分支,同时冻结原始模型权重。

5.2 ComfyUI中使用ControlNet

基础ControlNet工作流

[LoadControlNetModel] → control_net
        │
[CheckpointLoaderSimple] → model / clip / vae
        │
[CLIPTextEncode] × 2 → positive / negative conditioning
        │
[LoadImage] → 输入控制图像(如Canny边缘图)
        │
[ControlNetApplyAdvanced] 
  - strength: 0.8          ← ControlNet影响强度
  - start_percent: 0.0     ← 从第0%步开始应用
  - end_percent: 1.0       ← 到第100%步结束应用
        │
[KSampler] → [VAEDecode] → [SaveImage]

5.3 常用ControlNet类型详解

Canny边缘检测

适用于保留图像的边缘结构:

[LoadImage] → 原始图像
        │
[CannyEdgePreprocessor]
  - low_threshold: 100
  - high_threshold: 200
        │
  canny边缘图 → [ControlNetApplyAdvanced]
        │
[CheckpointLoaderSimple] → model
[CLIPTextEncode] → "a watercolor painting of a cat" (正面)
        │
[KSampler] steps=25, cfg=7.0 → [VAEDecode] → [SaveImage]

Depth深度图控制

使用MiDaS或DepthAnything生成深度图,控制图像的空间关系:

[LoadImage] → 原始图像
        │
[DepthAnythingPreprocessor] 或 [MiDaS-DepthMapPreprocessor]
        │
  深度图 → [ControlNetApplyAdvanced]
        │
[KSampler] → [VAEDecode] → [SaveImage]

OpenPose姿势控制

适用于人物姿态控制,支持身体、手部、面部关键点:

[LoadImage] → 姿势参考图
        │
[DWPreprocessor] 或 [OpenPosePreprocessor]
  - detect_hand: true
  - detect_body: true
  - detect_face: true
        │
  姿势骨架图 → [ControlNetApplyAdvanced]
        │
[KSampler] → [VAEDecode] → [SaveImage]

Scribble涂鸦控制

用户手绘涂鸦作为构图引导:

[LoadImage] → 涂鸦图像
        │
[ScribblePreprocessor] 或直接使用涂鸦图像
        │
[ControlNetApplyAdvanced] strength=0.9
        │
[KSampler] → [VAEDecode] → [SaveImage]

5.4 多ControlNet组合使用

ComfyUI支持同时应用多个ControlNet,通过串联 ControlNetApplyAdvanced 节点实现:

[CLIPTextEncode] → base_conditioning
        │
[ControlNetApplyAdvanced] #1 (Canny, strength=0.6) → cond_after_canny
        │
[ControlNetApplyAdvanced] #2 (Depth, strength=0.4) → cond_after_depth
        │
[ControlNetApplyAdvanced] #3 (OpenPose, strength=0.5) → final_conditioning
        │
[KSampler] → [VAEDecode] → [SaveImage]

组合策略建议

  • Canny + Depth:适合建筑、室内设计,同时控制结构和空间
  • OpenPose + Depth:适合人物场景,控制姿态和景深
  • Scribble + Canny:适合概念设计,涂鸦控制大构图,Canny补充细节
  • 多ControlNet时,适当降低每个的strength(建议0.3-0.6),避免过度约束导致画面僵硬

5.5 ControlNet预处理器管理

ComfyUI通过 comfyui_controlnet_aux 自定义节点包提供丰富的预处理器。安装方式:

cd ComfyUI/custom_nodes
git clone https://github.com/Fannovel16/comfyui_controlnet_aux.git
cd comfyui_controlnet_aux
pip install -r requirements.txt

重启ComfyUI后,即可在节点列表中找到各种预处理器节点。


6. LoRA微调模型加载

6.1 LoRA技术原理

LoRA(Low-Rank Adaptation)是一种参数高效的微调方法,通过在预训练模型的权重矩阵中注入低秩分解矩阵来实现风格或内容的定制化,而无需修改原始模型权重。一个LoRA文件通常只有几MB到几百MB,远小于完整的模型文件。

6.2 ComfyUI中加载LoRA

ComfyUI提供专门的 LoraLoader 节点:

[CheckpointLoaderSimple] → model / clip / vae
        │
[LoraLoader]
  - lora_name: "my_style_lora.safetensors"
  - strength_model: 0.8    ← 对UNet的影响强度(0.0-2.0)
  - strength_clip: 0.8     ← 对CLIP的影响强度(0.0-2.0)
        │
  lora_model / lora_clip → 后续节点

6.3 多LoRA叠加

ComfyUI支持串联多个LoRA,构建复合风格:

[CheckpointLoaderSimple] → model / clip
        │
[LoraLoader] #1 (油画风格, strength=0.7) → model1 / clip1
        │
[LoraLoader] #2 (光影增强, strength=0.5) → model2 / clip2
        │
[LoraLoader] #3 (特定角色, strength=0.9) → model3 / clip3
        │
[KSampler] 使用 model3

叠加注意事项

  • 多LoRA叠加时总strength不宜超过2.0,否则容易出现伪影
  • 某些LoRA之间可能存在冲突(如两种不兼容的风格),需要实验调整
  • 建议先单独测试每个LoRA的效果,再进行组合

6.4 LoRA触发词与权重语法

许多LoRA训练时使用了特定的触发词(Trigger Words),需要在提示词中包含才能激活效果:

# 假设LoRA的触发词是 "mystyle_art"
正面提示词: mystyle_art, a portrait of a woman, detailed background

在ComfyUI中,还可以在提示词中使用权重语法微调效果:

# 增加某个词的权重
(a beautiful sunset:1.3), mountains, reflection in water

# 减少某个词的权重
(ugly:0.5), (deformed:0.5)

# LoRA在提示词中的嵌入式调用(部分节点支持)
<lora:my_lora:0.7> a beautiful landscape

6.5 LoRA模型推荐来源

  • Civitai(civitai.com):最大的社区模型分享平台
  • Hugging Face:开源模型仓库,包含大量学术和社区LoRA
  • Tensor.Art:在线生成和模型分享平台

7. 图像修复与扩展:Inpainting与Outpainting

7.1 Inpainting(图像修复)

Inpainting用于修复图像的特定区域或替换其中的内容。ComfyUI提供了灵活的Inpainting工作流。

基础Inpainting工作流

[LoadImage] → 原始图像
        │
[LoadImageMask] 或 [ImageToMask] → 修复蒙版(白色区域=需要修复)
        │
[CheckpointLoaderSimple] → model / vae
        │
[VAEEncodeForInpaint]
  - 原始图像
  - 蒙版
  - grow_mask_by: 6  ← 蒙版向外扩展像素数,避免接缝
        │
  inpaint_latent → [KSampler]
        │
[CLIPTextEncode] "a beautiful garden" (正面)
[CLIPTextEncode] "blurry, low quality" (负面)
        │
[KSampler] denoise=0.8 ← 修复时通常不需要1.0,保留周围上下文
        │
[VAEDecode] → [SaveImage]

Inpainting专用模型:使用专门的Inpainting模型(如 sd-v1-5-inpainting.safetensors)可以获得更好的效果,这些模型在潜空间通道中额外输入了蒙版和原始图像信息。

7.2 Outpainting(图像扩展)

Outpainting用于扩展图像的边界,向外生成新的内容:

[LoadImage] → 原始图像
        │
[PadImageForOutpainting] 或手动创建扩展蒙版
  - left: 256, right: 256, top: 128, bottom: 128
        │
  扩展后的图像 + 蒙版 → [VAEEncodeForInpaint]
        │
[KSampler] denoise=1.0 ← Outpainting区域需要完全去噪
        │
[VAEDecode] → [SaveImage]

Outpainting技巧

  • 每次只扩展一个方向,避免多方向同时扩展导致内容不连贯
  • 使用较低的CFG(5.0-6.0)让生成内容更自然
  • 可以配合ControlNet(如Depth)保持扩展区域的空间一致性

7.3 局部重绘(Regional Inpainting)

对于需要对图像不同区域应用不同提示词的场景,可以使用 ConditioningSetMask 节点:

# 区域1:天空
[CLIPTextEncode] "a dramatic sunset sky" → cond1
[MaskToRegion] mask1 → [ConditioningSetMask] cond1, strength=1.0

# 区域2:地面
[CLIPTextEncode] "lush green meadow with wildflowers" → cond2
[MaskToRegion] mask2 → [ConditioningSetMask] cond2, strength=1.0

# 合并条件
[ConditioningCombine] cond1 + cond2 → combined
        │
[KSampler] → [VAEDecode] → [SaveImage]

8. 批量生成与自动化

8.1 批量生成基础

ComfyUI支持多种批量生成方式:

方式一:Batch Size参数

EmptyLatentImage 节点的 batch_size 参数可以一次生成多张图像:

[EmptyLatentImage] width=1024, height=1024, batch_size=4
        │
[KSampler] → [VAEDecode] → 4张图像

方式二:使用Prompt Queue

通过ComfyUI的Queue机制,可以排队执行多个不同的提示词:

# 通过API提交多个任务
import requests
import json

workflow = json.load(open("workflow.json"))

prompts = [
    "a red dragon on a mountain",
    "a blue dragon in the ocean",
    "a green dragon in a forest",
    "a golden dragon in the clouds"
]

for i, prompt in enumerate(prompts):
    workflow["6"]["inputs"]["text"] = prompt  # 修改正面提示词节点
    workflow["3"]["inputs"]["seed"] = i * 1000  # 不同种子
    
    response = requests.post(
        "http://127.0.0.1:8188/prompt",
        json={"prompt": workflow}
    )
    print(f"Queued: {prompt} - Status: {response.status_code}")

8.2 工作流API调用

ComfyUI暴露了RESTful API,支持程序化控制:

import requests
import websocket
import json
import uuid

class ComfyUIClient:
    def __init__(self, server="127.0.0.1:8188"):
        self.server = server
        self.client_id = str(uuid.uuid4())
    
    def queue_prompt(self, prompt):
        """提交工作流到队列"""
        data = {
            "prompt": prompt,
            "client_id": self.client_id
        }
        response = requests.post(
            f"http://{self.server}/prompt",
            json=data
        )
        return response.json()
    
    def get_history(self, prompt_id):
        """获取任务历史"""
        response = requests.get(
            f"http://{self.server}/history/{prompt_id}"
        )
        return response.json()
    
    def get_image(self, filename, subfolder, folder_type):
        """下载生成的图像"""
        params = {
            "filename": filename,
            "subfolder": subfolder,
            "type": folder_type
        }
        response = requests.get(
            f"http://{self.server}/view",
            params=params
        )
        return response.content
    
    def wait_for_completion(self, prompt_id):
        """通过WebSocket等待任务完成"""
        ws = websocket.WebSocket()
        ws.connect(f"ws://{self.server}/ws?clientId={self.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:
                    break  # 执行完成
        ws.close()


# 使用示例
client = ComfyUIClient()

# 加载工作流模板
with open("workflow.json", "r") as f:
    workflow = json.load(f)

# 修改参数
workflow["6"]["inputs"]["text"] = "a futuristic cityscape at night"
workflow["3"]["inputs"]["seed"] = 42

# 提交并等待
result = client.queue_prompt(workflow)
prompt_id = result["prompt_id"]
client.wait_for_completion(prompt_id)

# 获取结果
history = client.get_history(prompt_id)
outputs = history[prompt_id]["outputs"]

8.3 使用ComfyUI Manager管理节点

ComfyUI Manager是一个必装的管理工具,提供图形化的节点安装、更新和工作流管理功能:

cd ComfyUI/custom_nodes
git clone https://github.com/ltdrdata/ComfyUI-Manager.git

安装后重启ComfyUI,界面中会出现"Manager"按钮,支持:

  • 一键安装/更新自定义节点
  • 缺失节点自动检测和安装
  • 模型下载管理
  • 工作流快照和恢复

9. 自定义节点开发

9.1 自定义节点结构

ComfyUI的自定义节点本质上是一个Python模块,放在 custom_nodes/ 目录下。一个最小的自定义节点包含:

custom_nodes/
└── my_custom_node/
    ├── __init__.py      ← 节点注册入口
    ├── nodes.py         ← 节点实现
    └── requirements.txt ← 依赖声明

9.2 节点开发示例

以下是一个完整的自定义节点示例——实现一个图像色调调整节点:

# custom_nodes/my_color_adjust/__init__.py
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS

__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# custom_nodes/my_color_adjust/nodes.py
import torch
import numpy as np

class ColorAdjustNode:
    """调整图像色调、饱和度和亮度的节点"""
    
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "image": ("IMAGE",),
                "hue_shift": ("FLOAT", {
                    "default": 0.0,
                    "min": -180.0,
                    "max": 180.0,
                    "step": 1.0,
                    "display": "slider"
                }),
                "saturation": ("FLOAT", {
                    "default": 1.0,
                    "min": 0.0,
                    "max": 3.0,
                    "step": 0.01,
                    "display": "slider"
                }),
                "brightness": ("FLOAT", {
                    "default": 1.0,
                    "min": 0.0,
                    "max": 3.0,
                    "step": 0.01,
                    "display": "slider"
                }),
            },
            "optional": {
                "mask": ("MASK",),
            }
        }
    
    RETURN_TYPES = ("IMAGE",)
    RETURN_NAMES = ("adjusted_image",)
    FUNCTION = "adjust_color"
    CATEGORY = "image/adjustment"
    OUTPUT_NODE = False
    
    def adjust_color(self, image, hue_shift, saturation, brightness, mask=None):
        # image shape: [batch, height, width, channels], range [0, 1]
        batch_size, height, width, channels = image.shape
        result = image.clone()
        
        # RGB转HSV
        for b in range(batch_size):
            img_np = result[b].cpu().numpy()
            
            # 简化的HSV转换
            r, g, bl = img_np[:,:,0], img_np[:,:,1], img_np[:,:,2]
            cmax = np.maximum(np.maximum(r, g), bl)
            cmin = np.minimum(np.minimum(r, g), bl)
            delta = cmax - cmin
            
            # 亮度调整
            img_np = img_np * brightness
            
            # 饱和度调整
            gray = np.mean(img_np, axis=2, keepdims=True)
            img_np = gray + (img_np - gray) * saturation
            
            # 裁剪到有效范围
            img_np = np.clip(img_np, 0.0, 1.0)
            
            # 如果有蒙版,只对蒙版区域应用
            if mask is not None:
                mask_np = mask[b].cpu().numpy()
                if len(mask_np.shape) == 2:
                    mask_np = mask_np[:, :, np.newaxis]
                original = image[b].cpu().numpy()
                img_np = original * (1 - mask_np) + img_np * mask_np
            
            result[b] = torch.from_numpy(img_np).to(result.device)
        
        return (result,)


# 节点注册映射
NODE_CLASS_MAPPINGS = {
    "ColorAdjust": ColorAdjustNode
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "ColorAdjust": "🎨 Color Adjust"
}

9.3 高级节点开发技巧

动态输入类型:通过 INPUT_TYPES 返回 "COMBO" 类型,可以创建动态下拉菜单:

@classmethod
def INPUT_TYPES(cls):
    # 从文件系统动态获取模型列表
    models = [f for f in os.listdir("models/checkpoints") if f.endswith(".safetensors")]
    return {
        "required": {
            "model_name": (models, {"default": models[0] if models else ""}),
        }
    }

节点间通信:通过 IS_CHANGED 类方法实现缓存控制——当输入参数未变化时,ComfyUI会跳过重新计算:

@classmethod
def IS_CHANGED(cls, seed, **kwargs):
    return seed  # 种子变化时才重新计算

输出多个图像:设置 OUTPUT_NODE = True 并返回字典格式,可以将图像直接显示在UI中:

OUTPUT_NODE = True

def save_and_display(self, images):
    # 保存到文件
    for i, img in enumerate(images):
        # ... 保存逻辑
        pass
    
    # 返回用于UI预览
    return {"ui": {"images": results}, "result": (images,)}

10. 工作流模板与复用

10.1 工作流JSON格式

ComfyUI的工作流以JSON格式存储,结构清晰:

{
  "last_node_id": 10,
  "last_link_id": 15,
  "nodes": [
    {
      "id": 1,
      "type": "CheckpointLoaderSimple",
      "pos": [50, 100],
      "size": [315, 98],
      "properties": {},
      "widgets_values": ["sd_xl_base_1.0.safetensors"]
    }
  ],
  "links": [
    [1, 1, 0, 3, 0, "MODEL"],
    [2, 1, 1, 2, 0, "CLIP"]
  ],
  "groups": [],
  "config": {},
  "extra": {
    "ds": {
      "scale": 1.0,
      "offset": [0, 0]
    }
  }
}

10.2 工作流模板系统

创建可配置模板:使用 Primitive 节点将关键参数暴露为顶层控件:

[Primitive] "生成数量" (INT, default=4)
[Primitive] "风格强度" (FLOAT, default=0.7)
[Primitive] "正面提示词" (STRING, default="...")
[Primitive] "负面提示词" (STRING, default="blurry, low quality")

子工作流(Subgraph):ComfyUI支持将一组节点封装为子工作流,在主工作流中作为单个节点使用。这类似于编程中的函数封装。

工作流版本控制:建议使用Git管理工作流文件:

mkdir comfyui-workflows
cd comfyui-workflows
git init

# 保存工作流时使用有意义的命名
# 例如:sdxl_portrait_v2.1_cn_depth.json

10.3 工作流分享平台

  • OpenArt(openart.ai):工作流分享社区
  • ComfyUI Workflow(comfyuiworkflow.com):精选工作流集合
  • Civitai:除了模型,也支持工作流分享
  • GitHub:许多开发者在GitHub上维护工作流仓库

11. ComfyUI与A1111 WebUI对比

11.1 核心差异对比

维度 ComfyUI A1111 WebUI
界面模式 节点式,可视化管线 表单式,参数面板
学习曲线 较陡峭,需要理解节点和数据流 较平缓,类似传统软件
灵活性 极高,可自由组合任意管线 中等,依赖扩展和脚本
显存效率 优秀,自动缓存和优化 一般,完整管线占用更多
调试能力 强,可查看每个中间节点的输出 弱,只能看最终结果
批量处理 原生支持,API友好 需要额外脚本或扩展
扩展生态 自定义节点丰富,更新快 扩展数量庞大,成熟稳定
工作流分享 JSON格式,天然可分享 需要额外工具
适合人群 高级用户、开发者、生产环境 初学者、快速出图、个人使用

11.2 迁移指南

从A1111迁移到ComfyUI的关键对应关系:

A1111功能 ComfyUI对应
txt2img 基础文生图工作流
img2img KSampler + LoadImage + VAEEncode
Inpainting VAEEncodeForInpaint + Mask
ControlNet扩展 ControlNetApplyAdvanced节点
LoRA LoraLoader节点
高清修复 KSampler(低denoise)+ Upscale节点
提示词权重 提示词语法 (word:weight)
批量生成 API批量提交或batch_size
脚本/扩展 自定义节点

12. 企业级图像生成流水线

12.1 生产环境架构

企业级部署需要考虑可扩展性、可靠性和安全性:

┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  任务队列    │────→│  ComfyUI     │────→│  对象存储    │
│  (Redis/     │     │  Worker Pool │     │  (S3/MinIO)  │
│   RabbitMQ)  │     │  (多个实例)   │     │              │
└─────────────┘     └──────────────┘     └─────────────┘
       ↑                    │                    │
       │                    ↓                    ↓
┌─────────────┐     ┌──────────────┐     ┌─────────────┐
│  API网关     │     │  模型缓存    │     │  CDN分发     │
│  (FastAPI)   │     │  (共享存储)  │     │              │
└─────────────┘     └──────────────┘     └─────────────┘

12.2 多实例负载均衡

# docker-compose.scale.yml
version: '3.8'
services:
  comfyui-worker:
    build: .
    deploy:
      replicas: 4
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    volumes:
      - shared-models:/app/models:ro
      - shared-output:/app/output
    environment:
      - COMFYUI_PORT=8188
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
  
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf

volumes:
  shared-models:
  shared-output:

12.3 任务调度与API封装

# api_server.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import redis
import json
import uuid
from typing import Optional

app = FastAPI(title="ComfyUI图像生成服务")
r = redis.Redis(host='localhost', port=6379, db=0)

class GenerationRequest(BaseModel):
    prompt: str
    negative_prompt: str = "low quality, blurry"
    width: int = 1024
    height: int = 1024
    steps: int = 25
    cfg_scale: float = 7.0
    seed: int = -1
    model: str = "sd_xl_base_1.0.safetensors"
    lora: Optional[str] = None
    lora_strength: float = 0.7

class GenerationResponse(BaseModel):
    task_id: str
    status: str
    message: str

@app.post("/generate", response_model=GenerationResponse)
async def create_generation(request: GenerationRequest):
    task_id = str(uuid.uuid4())
    
    # 构建工作流
    workflow = build_workflow(request)
    
    # 推送到任务队列
    task_data = {
        "task_id": task_id,
        "workflow": workflow,
        "params": request.dict()
    }
    r.lpush("comfyui_tasks", json.dumps(task_data))
    
    # 记录任务状态
    r.hset(f"task:{task_id}", mapping={
        "status": "queued",
        "created_at": str(time.time())
    })
    
    return GenerationResponse(
        task_id=task_id,
        status="queued",
        message="任务已提交到队列"
    )

@app.get("/task/{task_id}")
async def get_task_status(task_id: str):
    task = r.hgetall(f"task:{task_id}")
    if not task:
        raise HTTPException(status_code=404, detail="任务不存在")
    
    return {
        "task_id": task_id,
        "status": task.get(b"status", b"unknown").decode(),
        "result_url": task.get(b"result_url", b"").decode() or None
    }

def build_workflow(request: GenerationRequest) -> dict:
    """根据请求参数构建ComfyUI工作流"""
    workflow = {
        "1": {
            "class_type": "CheckpointLoaderSimple",
            "inputs": {"ckpt_name": request.model}
        },
        "2": {
            "class_type": "CLIPTextEncode",
            "inputs": {
                "text": request.prompt,
                "clip": ["1", 1]
            }
        },
        "3": {
            "class_type": "CLIPTextEncode",
            "inputs": {
                "text": request.negative_prompt,
                "clip": ["1", 1]
            }
        },
        "4": {
            "class_type": "EmptyLatentImage",
            "inputs": {
                "width": request.width,
                "height": request.height,
                "batch_size": 1
            }
        },
        "5": {
            "class_type": "KSampler",
            "inputs": {
                "model": ["1", 0],
                "positive": ["2", 0],
                "negative": ["3", 0],
                "latent_image": ["4", 0],
                "seed": request.seed,
                "steps": request.steps,
                "cfg": request.cfg_scale,
                "sampler_name": "dpmpp_2m_sde",
                "scheduler": "karras",
                "denoise": 1.0
            }
        },
        "6": {
            "class_type": "VAEDecode",
            "inputs": {
                "samples": ["5", 0],
                "vae": ["1", 2]
            }
        },
        "7": {
            "class_type": "SaveImage",
            "inputs": {
                "images": ["6", 0],
                "filename_prefix": f"gen_{request.prompt[:20]}"
            }
        }
    }
    
    # 如果指定了LoRA,插入LoraLoader节点
    if request.lora:
        workflow["1b"] = {
            "class_type": "LoraLoader",
            "inputs": {
                "lora_name": request.lora,
                "strength_model": request.lora_strength,
                "strength_clip": request.lora_strength,
                "model": ["1", 0],
                "clip": ["1", 1]
            }
        }
        # 更新后续节点引用
        workflow["5"]["inputs"]["model"] = ["1b", 0]
        workflow["2"]["inputs"]["clip"] = ["1b", 1]
        workflow["3"]["inputs"]["clip"] = ["1b", 1]
    
    return workflow

12.4 监控与告警

# monitor.py - ComfyUI实例健康监控
import requests
import time
import logging
from dataclasses import dataclass

@dataclass
class WorkerStatus:
    url: str
    healthy: bool
    queue_length: int
    gpu_memory_used: float
    gpu_memory_total: float
    last_check: float

class ComfyUIMonitor:
    def __init__(self, workers: list[str]):
        self.workers = workers
        self.status = {}
    
    def check_health(self) -> dict[str, WorkerStatus]:
        for url in self.workers:
            try:
                # 检查队列状态
                resp = requests.get(f"http://{url}/queue", timeout=5)
                queue_data = resp.json()
                
                # 获取系统统计
                stats_resp = requests.get(f"http://{url}/system_stats", timeout=5)
                stats = stats_resp.json()
                
                self.status[url] = WorkerStatus(
                    url=url,
                    healthy=True,
                    queue_length=len(queue_data.get("queue_running", [])) + 
                                len(queue_data.get("queue_pending", [])),
                    gpu_memory_used=stats.get("devices", [{}])[0].get("vram_used", 0),
                    gpu_memory_total=stats.get("devices", [{}])[0].get("vram_total", 1),
                    last_check=time.time()
                )
            except Exception as e:
                self.status[url] = WorkerStatus(
                    url=url, healthy=False, queue_length=-1,
                    gpu_memory_used=0, gpu_memory_total=0,
                    last_check=time.time()
                )
                logging.error(f"Worker {url} 健康检查失败: {e}")
        
        return self.status
    
    def get_best_worker(self) -> str:
        """选择队列最短的健康Worker"""
        healthy = [s for s in self.status.values() if s.healthy]
        if not healthy:
            raise RuntimeError("没有可用的Worker")
        return min(healthy, key=lambda x: x.queue_length).url

12.5 安全最佳实践

  • 网络隔离:ComfyUI实例不应直接暴露在公网,通过API网关层做认证和限流
  • 模型文件校验:对下载的模型文件进行SHA256校验,防止篡改
  • 输入验证:对用户输入的提示词做长度限制和内容过滤
  • 资源限制:设置单任务最大分辨率和步数上限,防止资源耗尽
  • 日志审计:记录所有生成任务的参数和结果,便于溯源

13. 常见问题与排错

13.1 显存不足(Out of Memory)

症状:CUDA out of memory错误

解决方案

# 启用低显存模式
python main.py --lowvram

# 或者使用极低显存模式(性能下降较多)
python main.py --novram

工作流层面的优化

  • 使用较小的分辨率,后期通过Upscale放大
  • 减少batch_size为1
  • 使用量化模型(如FP16或INT8)
  • 避免同时加载多个大模型(如Base+Refiner+ControlNet)

13.2 自定义节点安装失败

# 常见问题:依赖冲突
cd ComfyUI/custom_nodes/问题节点目录
pip install -r requirements.txt 2>&1 | tail -20  # 查看具体错误

# 权限问题
sudo pip install -r requirements.txt

# Python版本不匹配
python --version  # 确认是3.10或3.11

13.3 模型加载错误

# 确认模型路径
ComfyUI/
├── models/
│   ├── checkpoints/     ← SD模型
│   ├── loras/           ← LoRA模型
│   ├── controlnet/      ← ControlNet模型
│   ├── vae/             ← VAE模型
│   ├── clip/            ← CLIP模型
│   └── upscale_models/  ← 超分模型

# 常见错误:模型文件损坏
# 解决:重新下载,校验SHA256
sha256sum models/checkpoints/*.safetensors

13.4 生成速度慢

  • 确认GPU正在被使用(而非CPU):检查 nvidia-smi 输出
  • 更新显卡驱动到最新版本
  • 对于SDXL,使用FP16精度:--force-fp16
  • 减少采样步数(20步通常已足够)
  • 使用更快的采样器(如 euler_ancestral

13.5 工作流无法加载

  • 确认ComfyUI版本与工作流兼容
  • 使用ComfyUI Manager的"Install Missing Custom Nodes"功能
  • 检查JSON格式是否正确(可用JSON验证工具检查)
  • 尝试重置视图:菜单 → Reset View

14. 总结与进阶资源

14.1 核心要点回顾

  1. ComfyUI是节点式AI图像生成工具,通过可视化连接节点构建生成管线,具有极高的灵活性和透明度
  2. 基础工作流由模型加载器、文本编码器、采样器、解码器和保存器组成
  3. SDXL和Flux代表了当前最先进的开源文生图模型,ComfyUI原生支持两者
  4. ControlNet提供了精确的结构控制能力,支持边缘、深度、姿势等多种控制信号
  5. LoRA是轻量级微调方案,可在不更换基础模型的情况下定制风格和内容
  6. Inpainting/Outpainting实现了图像的局部修复和扩展
  7. API和批量处理使得ComfyUI可以集成到企业级生产流水线中
  8. 自定义节点开发让ComfyUI的能力可以无限扩展

14.2 进阶学习资源

官方资源

  • ComfyUI GitHub仓库:https://github.com/comfyanonymous/ComfyUI
  • ComfyUI官方示例工作流:仓库中的 workflow/ 目录

社区资源

  • ComfyUI社区Wiki和教程
  • Reddit r/comfyui 社区
  • Discord ComfyUI社区

推荐学习路径

  1. 入门阶段(1-2周):掌握基础文生图工作流,理解节点和数据流概念
  2. 进阶阶段(2-4周):学习ControlNet、LoRA、Inpainting等高级功能
  3. 开发阶段(4-8周):学习自定义节点开发,理解ComfyUI内部架构
  4. 生产阶段(持续):构建自动化流水线,优化性能和可靠性

14.3 模型资源推荐

模型 类型 推荐场景
SDXL Base 1.0 通用 基础高质量文生图
SDXL Turbo 快速 实时预览和快速迭代
Flux.1 Dev 高质量 最新一代文生图
Flux.1 Schnell 快速 快速生成,4步出图
DreamShaper XL 风格化 艺术风格图像
Realistic Vision 写实 照片级写实图像
ControlNet 1.1 控制 各类精确控制

附录A:常用快捷键

快捷键 功能
Ctrl + Enter 执行当前工作流
Ctrl + Shift + Enter 将当前工作流加入队列
Ctrl + Z 撤销
Ctrl + Y 重做
Ctrl + S 保存工作流
Ctrl + O 加载工作流
Ctrl + A 全选节点
Ctrl + M 静音/取消静音节点
Ctrl + B 绕过节点(Bypass)
Delete 删除选中节点
Space + 拖拽 平移画布
右键 打开节点菜单
双击空白 搜索节点

附录B:工作流调试技巧

  1. 使用PreviewImage节点:在管线中间插入PreviewImage节点,查看中间结果
  2. Bypass节点:选中节点后按 Ctrl+B 可以临时跳过该节点,快速定位问题
  3. 修改参数后Queue:ComfyUI的缓存机制会复用未变化的节点结果,加速迭代
  4. 保存中间结果:使用SaveImage节点保存关键中间步骤的输出,便于对比
  5. 使用Note节点:在工作流中添加注释说明,便于后续维护

版权声明:本教程为原创内容,仅供学习和参考使用。ComfyUI及相关工具的使用请遵守各自的开源协议。模型的使用请遵守相应的许可协议。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录