最近在AIGC圈子里,ComfyUI的热度越来越高,尤其是用它来跑那些顶级的生图、生视频大模型,效果和效率都让人惊喜。作为一个从零开始摸索过来的新手,我把自己搭建、调试ComfyUI工作流的经验整理了一下,希望能帮你少走点弯路。

ComfyUI工作流界面示例

1. 为什么是ComfyUI?聊聊当前的技术格局

现在AIGC工具很多,WebUI(如Automatic1111)对新手友好,Midjourney上手快但可控性弱。ComfyUI最大的不同在于它的“节点式”工作流。你可以把它想象成乐高积木,每个功能(加载模型、编码文本、采样图片)都是一个独立的节点,用线连起来就构成了完整的生成流水线。

这种设计带来了几个核心优势:

  • 极致可控与透明:你能清清楚楚看到数据(潜空间向量、条件信息)是怎么从文本一步步变成图片的,方便调试和定制。
  • 资源利用高效:相比一些一体化的WebUI,ComfyUI通常更节省显存,对复杂工作流和大模型的支持更好。
  • 易于复现与分享:工作流可以保存为JSON文件,别人导入就能完全复现你的生成过程,非常适合团队协作和技术分享。
  • 扩展性强:社区有大量自定义节点,可以轻松集成ControlNet、LoRA、IP-Adapter等高级控制功能。

对于想深入理解扩散模型原理,或者需要构建稳定、可定制化生产流程的开发者来说,ComfyUI是目前非常理想的选择。

2. 手把手搭建你的ComfyUI环境

别被节点界面吓到,安装其实很简单。我们目标是创建一个独立的Python环境,集成Stable Diffusion模型和ComfyUI。

  1. 安装Miniconda(如果还没装) 去Miniconda官网下载对应你操作系统的安装包,一路下一步安装好。Conda能帮你轻松管理不同的Python环境,避免包冲突。

  2. 创建并激活专属环境 打开终端(Windows用Anaconda Prompt或PowerShell),执行以下命令。这里我们用Python 3.10,这是一个比较稳定的版本。

    conda create -n comfyui python=3.10 -y
    conda activate comfyui
    
  3. 克隆ComfyUI官方仓库并安装依赖 ComfyUI的代码托管在GitHub上,我们把它克隆到本地。

    git clone https://github.com/comfyanonymous/ComfyUI.git
    cd ComfyUI
    pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118  # 根据你的CUDA版本调整,这里是CUDA 11.8
    pip install -r requirements.txt
    

    这一步会安装PyTorch和ComfyUI所需的所有Python包。

  4. 放置你的大模型 ComfyUI需要模型文件才能工作。在ComfyUI文件夹里,你会看到一个models目录,里面又有checkpointsloras等子文件夹。

    • 把下载好的Stable Diffusion基础模型(比如SDXL的 .safetensors 文件)放到 models/checkpoints 里。
    • 如果需要VAE模型,放到 models/vae 里。
    • 其他如ControlNet、LoRA模型也放到对应的文件夹。
  5. 启动ComfyUIComfyUI目录下,运行:

    python main.py
    

    看到终端输出一个本地地址(通常是 http://127.0.0.1:8188),用浏览器打开它,恭喜,你的ComfyUI就运行起来了!

3. 理解核心工作流:图像是怎么“流”出来的

第一次打开ComfyUI的空白画布可能会懵。别急,我们先理解一个最基础的文生图流程。你可以通过拖拽右侧节点列表里的模块来构建。

一个典型的简易工作流包含以下几个关键节点,它们按顺序连接:

[Load Checkpoint] -> [CLIP Text Encode (Prompt)] -> [CLIP Text Encode (Negative Prompt)] -> [KSampler] -> [VAE Decode] -> [Save Image]
                      |                                                            |
                      +------------------------------------------------------------+
                                     (条件信息输入到采样器)
  • Load Checkpoint: 这是起点,负责加载你放在 checkpoints 文件夹里的大模型。它会输出模型本身、CLIP文本编码器和VAE编码解码器。
  • CLIP Text Encode: 文本编码节点。你需要两个,一个输入正面提示词(Prompt),一个输入负面提示词(Negative Prompt)。它们将文字转换成模型能理解的“条件向量”。
  • KSampler核心中的核心,采样器节点。它接收模型、条件向量、随机种子、采样步数、CFG尺度等参数,在潜空间里进行多次去噪迭代,最终输出一个“干净的”潜空间特征。
  • VAE Decode: 解码器节点。它把KSampler产生的潜空间特征,解码成我们能看得见的RGB图像。
  • Save Image: 保存节点,将最终图像保存到指定目录。

理解这个数据流(模型 -> 文本编码 -> 采样去噪 -> 解码成图)是掌握ComfyUI的关键。之后所有复杂操作,比如图生图、局部重绘、添加ControlNet,都是在这个流水线上插入新的处理节点。

4. 代码实战:用Python驱动ComfyUI

ComfyUI不仅提供Web界面,还内置了强大的API,允许我们用Python脚本进行批量生成和集成。这在实际项目中非常有用。

首先,确保你的ComfyUI服务正在运行(python main.py)。API默认在8188端口。

下面是一个封装好的基础图像生成类:

import requests
import json
import io
import uuid
from PIL import Image

class ComfyUIAPIClient:
    def __init__(self, server_address="http://127.0.0.1:8188"):
        self.server_address = server_address

    def generate_image(self, prompt, negative_prompt="", seed=-1, steps=20, cfg=7.0, width=1024, height=1024, checkpoint_name="sd_xl_base_1.0.safetensors"):
        """
        调用ComfyUI API生成单张图片
        :param prompt: 正面提示词
        :param negative_prompt: 负面提示词
        :param seed: 随机种子,-1表示随机
        :param steps: 采样步数
        :param cfg: 分类器自由引导尺度,控制提示词相关性
        :param width: 图片宽度
        :param height: 图片高度
        :param checkpoint_name: 使用的模型文件名
        :return: PIL.Image对象
        """
        # 1. 构建工作流JSON数据
        # 这里需要根据你在Web界面搭建的实际工作流来定义prompt。
        # 以下是一个对应基础文生图流程的简化示例结构。
        workflow_prompt = {
            "3": {
                "class_type": "LoadCheckpoint",
                "inputs": {
                    "ckpt_name": checkpoint_name
                }
            },
            "6": {
                "class_type": "CLIPTextEncode",
                "inputs": {
                    "text": prompt,
                    "clip": ["3", 1]  # 连接到Load Checkpoint节点的CLIP输出
                }
            },
            "7": {
                "class_type": "CLIPTextEncode",
                "inputs": {
                    "text": negative_prompt,
                    "clip": ["3", 1]
                }
            },
            "10": {
                "class_type": "KSampler",
                "inputs": {
                    "seed": seed,
                    "steps": steps,
                    "cfg": cfg,
                    "sampler_name": "euler",  # 采样器类型
                    "scheduler": "normal",
                    "denoise": 1.0,
                    "model": ["3", 0],        # 连接到Load Checkpoint的MODEL输出
                    "positive": ["6", 0],     # 连接到正面提示词编码输出
                    "negative": ["7", 0],     # 连接到负面提示词编码输出
                    "latent_image": ["5", 0]  # 连接到空潜空间节点(图中未展示,需定义)
                }
            },
            # ... 还需要VAE Decode和Save Image节点
        }
        # 注意:实际使用时,你需要先用WebUI搭建一个工作流,然后通过“保存(JSON)”功能获取完整的、正确的prompt结构。
        # 这里仅为示意,直接运行会报错。

        # 2. 将工作流提交给ComfyUI服务器进行排队
        prompt_id = self._queue_prompt(workflow_prompt)

        # 3. 轮询获取生成结果
        image_data = self._get_result(prompt_id)

        # 4. 将二进制图像数据转换为PIL Image
        image = Image.open(io.BytesIO(image_data))
        return image

    def _queue_prompt(self, prompt):
        """ 提交生成任务到队列 """
        url = f"{self.server_address}/prompt"
        data = json.dumps({"prompt": prompt})
        response = requests.post(url, data=data)
        response.raise_for_status()
        return response.json()['prompt_id']

    def _get_result(self, prompt_id):
        """ 根据prompt_id获取生成完成的图片 """
        url = f"{self.server_address}/history"
        while True:
            response = requests.get(url)
            history = response.json()
            if prompt_id in history:
                # 找到生成记录,提取图片数据
                # 这里需要根据你工作流中Save Image节点的设置来定位图片
                # 例如,假设图片保存在节点“13”的输出
                image_data = history[prompt_id]['outputs']['13']['images'][0]
                image_url = f"{self.server_address}/view?filename={image_data['filename']}&subfolder={image_data['subfolder']}&type={image_data['type']}"
                img_response = requests.get(image_url)
                return img_response.content
            # 简单等待
            import time
            time.sleep(1)

# 使用示例
if __name__ == "__main__":
    client = ComfyUIAPIClient()
    try:
        img = client.generate_image(
            prompt="a beautiful sunset over mountains, digital art, detailed",
            negative_prompt="blurry, ugly, deformed",
            seed=42,
            steps=25,
            width=1024,
            height=1024
        )
        img.save("generated_sunset.png")
        print("图片生成并保存成功!")
    except requests.exceptions.ConnectionError:
        print("错误:无法连接到ComfyUI服务器,请确保已运行‘python main.py’。")
    except KeyError as e:
        print(f"错误:工作流节点配置可能不正确,请检查API调用代码。缺失键:{e}")
    except Exception as e:
        print(f"生成过程中发生未知错误:{e}")

批量处理与优化技巧:

  • 队列化请求:上述代码已经实现了基本的队列和轮询。对于大批量任务,可以维护一个任务列表,并发或顺序提交。
  • 复用连接:使用 requests.Session() 来保持HTTP连接,减少开销。
  • 异常处理:重点捕获 requests.exceptions.ConnectionError(服务未启动)和 torch.cuda.OutOfMemoryError(如果通过其他方式调用PyTorch)。对于API方式,OOM错误通常会体现在返回的JSON信息中,需要解析服务器响应。

5. 性能调优:让有限的GPU发挥最大效能

显卡显存是玩转大模型最宝贵的资源。不同的配置策略天差地别。

  1. 根据显存容量选择模型和参数

    • 8GB显存(如RTX 3070/4060Ti):可以流畅运行SD 1.5模型(512x512)。运行SDXL基础模型(1024x1024)比较吃力,需要启用--medvram--lowvram参数启动ComfyUI,并且使用较小的批处理大小(batch_size=1)。考虑使用FP16精度的模型。
    • 12GB显存(如RTX 3060/4070):这是运行SDXL的“甜点”级配置。可以较流畅地进行1024x1024的单图生成,可能支持轻量的图生图或ControlNet。开启xformers优化(在启动命令加--xformers)能有效提升速度并降低显存。
    • 24GB+显存(如RTX 3090/4090):可以任性很多。能运行更大的模型,支持更高的分辨率、更大的批处理(batch_size>1)进行并行生成,同时加载多个LoRA或ControlNet。
  2. FP16 vs FP32:精度与速度的权衡

    • FP32(单精度浮点):计算最精确,生成质量理论上最稳定,但显存占用大,速度慢。
    • FP16(半精度浮点):显存占用约为FP32的一半,计算速度更快,是现代AIGC的默认推荐。对于绝大多数生成任务,肉眼几乎看不出质量差异。
    • 实践建议:除非有极其严格的科研需求,否则一律使用FP16模型和推理。在ComfyUI的Load Checkpoint节点中,输出默认就是FP16的。使用--fp16启动参数也能确保整体推理在FP16下进行。
  3. 启用优化器 在启动命令中加入以下参数可以显著提升性能:

    python main.py --force-fp16 --xformers
    

    --force-fp16强制使用FP16,--xformers启用注意力优化,能降显存、提速度。

6. 新手避坑指南:五个常见错误及解决之道

  1. 错误:生成的图片全黑或全灰

    • 原因:最可能的是VAE模型不匹配或缺失。SDXL模型有时需要特定的VAE。
    • 解决:检查Load Checkpoint节点是否自动加载了VAE(通常是的)。如果不行,手动添加一个VAE Loader节点,加载一个与模型匹配的VAE文件(如sdxl_vae.safetensors),然后将其连接到VAE Decode节点。
  2. 错误:提示词感觉没起作用,图片内容随机

    • 原因CFG Scale值设置不当。这个参数控制模型听从提示词的程度。
    • 解决:在KSampler节点中,调整cfg参数。通常范围在5-15之间,7-9是常用值。太低则天马行空,太高则可能颜色饱和、构图僵硬。
  3. 错误:显存瞬间爆满(OOM)

    • 原因分辨率设置过高同时加载了过多大模型
    • 解决:首先降低生成图片的宽高。SD 1.5模型不要轻易超过768x768,SDXL不要轻易超过1024x1024。其次,确保工作流里没有同时加载多个不必要的检查点模型。使用--medvram参数启动ComfyUI。
  4. 错误:工作流加载失败,节点飘红

    • 原因节点版本不兼容缺失自定义节点
    • 解决:ComfyUI更新很快,社区节点也是。如果导入别人的工作流报错,首先尝试更新ComfyUI到最新版(git pull)。其次,根据缺失的节点名称(如ImpactPackComfyUI-Manager),通过管理器(如果有安装)或手动安装对应的自定义节点。
  5. 错误:生成速度异常缓慢

    • 原因使用了计算复杂的采样器CPU模式运行
    • 解决:在KSampler节点中,sampler_name选择eulereuler_ancestraldpmpp_2m等速度较快的采样器,避免ddim(较慢)或plms。同时确认终端启动信息中PyTorch是否识别到了CUDA(应显示类似Using device: cuda:0)。

生成效果对比示意

走完这一套流程,你应该已经能从零搭建起一个可用的ComfyUI生图环境,并且理解其核心的工作逻辑了。ComfyUI的魅力在于,一旦你熟悉了节点操作,就能组合出无限可能。

最后留几个进阶问题,供你继续探索:

  1. 如何实现多角色/多对象的一致性控制? 比如让同一个卡通形象出现在不同场景里。这涉及到LoRA训练、IP-Adapter图像提示,或者更底层的Attention Injection技术。
  2. 怎样构建一个“图生视频”的稳定工作流? 这需要集成AnimateDiff等动态模型,并考虑帧间连贯性、镜头运动控制等复杂因素。
  3. 如何优化工作流以实现“工业化”批量生产? 比如动态读取外部提示词CSV文件、自动分类保存结果、集成质量评分模型进行过滤等。

希望这篇笔记能成为你探索ComfyUI世界的一块有用的垫脚石。动手试起来,遇到问题多查查官方文档和社区讨论,乐趣和成长都在这个过程中。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐