AI视频生成-ComfyUI部署

ComfyUI通过配置节点来构建复杂的AIGC流程,帮助您完成社交平台短视频内容生成、动画制作等任务。EAS提供了场景化部署方式,您只需配置几个参数,即可一键部署基于ComfyUIStable Video Diffusion模型的AI视频生成服务。本文为您介绍如何基于ComfyUI镜像部署服务和几种常用的调用方式。

部署方式选择

场景化部署提供了4个版本:标准版、API版、集群版WebUIServerless版。不同版本主要区别如下:

适用场景

支持的调用方式

计费

标准版

仅适用于单用户使用WebUI或使用一个实例调用API。建议只部署1个实例。

  • WebUI

  • 在线调试

  • API调用(同步)

根据您的部署配置进行计费,详情请参见模型在线服务(EAS)计费说明

API

系统将自动转换服务为异步模式,适用于高并发场景。

API调用(异步)

集群版WebUI

适用于设计组或教学场景,适合多用户同时在WebUI页面进行操作。

WebUI

Serverless

需求波动较大的场景,可根据您的服务请求量自动弹性伸缩。

WebUI

服务部署完全免费,只根据使用出图的时长进行计费。

  • 集群版WebUI:每个用户有独立的后端环境和工作目录,实现高效的GPU共享和文件管理。请参见集群版服务原理介绍了解更多。

  • Serverless:目前,仅华东2(上海)、华东1(杭州)地域支持部署Serverless版服务。

如果场景化部署不能满足您的服务需求,标准版、集群版、API版可以使用对应的官方镜像进行自定义模型部署,支持配置更多的服务功能,详情请参见自定义部署参数说明

部署EAS服务

方式一:场景化模型部署(推荐)

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 模型在线服务(EAS)页面,单击部署服务,在场景化模型部署区域,单击AI视频生成-ComfyUI部署

  3. AI视频生成-ComfyUI部署页面,配置以下关键参数。

    参数

    描述

    基本信息

    服务名称

    自定义模型服务名称。

    版本选择

    请参见部署方式选择

    模型配置

    以下两种情形,您需要进行模型配置:

    支持以下几种配置类型:

    • 对象存储(OSS):单击image选择已创建的OSS存储目录。

    • 文件存储(NAS):配置NAS挂载点和NAS源路径。

    资源配置

    实例数

    当版本选择标准版时,建议将实例数配置为1。

    资源配置选择

    资源规格推荐使用GU30、A10T4卡型。系统默认选择GPU > ml.gu7i.c16m60.1-gu30,性价比高。

    说明

    ComfyUI仅支持单卡(单机单卡或多机单卡)运行,不支持多卡并发操作。

  4. 单击部署。服务部署时间约为5分钟,当服务状态运行中时,表明服务已成功部署。

方式二:自定义模型部署

  1. 登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS

  2. 单击部署服务,然后在自定义模型部署区域,单击自定义部署

  3. 自定义部署页面,配置以下关键参数。

    参数

    描述

    基本信息

    服务名称

    自定义服务名称。本案例使用的示例值为:comfyui_svd_demo

    环境信息

    部署方式

    选择镜像部署,并选中开启Web应用复选框。

    镜像配置

    官方镜像列表中选择comfyui>comfyui:1.7,其中:

    • x.x:表示标准版。

    • x.x-api:表示API版。

    • x.x-cluster:表示集群版。

    说明
    • 由于版本迭代迅速,部署时镜像版本选择最高版本即可。

    • 更多关于每个版本的使用场景说明,请参见部署方式选择

    模型配置

    以下两种情形,您需要进行模型配置:

    支持以下几种配置类型:

    • OSS

      • OSS:单击image选择已创建的OSS存储目录。例如oss://bucket-test/data-oss/

      • 挂载路径:配置为/code/data-oss,表示将您配置的OSS文件目录挂载到镜像的/code/data-oss路径下。

    • 通用型NAS

      • 选择文件系统:选择NAS文件系统。

      • 文件系统挂载点:选择NAS挂载点,EAS服务通过挂载点来访问NAS文件系统。

      • 文件系统路径:需要挂载的NAS中的源路径,即NAS实例内部的文件系统路径。例如/data-oss

      • 挂载路径:配置为/code/data-oss,表示将您配置的NAS源路径挂载到镜像的/code/data-oss路径下。

    运行命令

    • 配置镜像版本后,系统自动配置运行命令python main.py --listen --port 8000

    • 端口号为:8000。

    当您进行模型配置后,您需要在运行命令中增加--data-dir挂载目录,其中挂载目录需要与模型配置中的挂载路径一致。例如python main.py --listen --port 8000 --data-dir /code/data-oss

    资源部署

    资源类型

    选择公共资源

    实例数

    当镜像版本标准版时,建议将实例数配置为1。

    部署资源

    资源规格必须选择GPU类型,推荐使用ml.gu7i.c16m60.1-gu30(性价比最高)。如库存不足可选择ecs.gn6i-c16g1.4xlarge

    说明

    ComfyUI仅支持单卡(单机单卡或多机单卡)运行,不支持多卡并发操作。

  4. 单击部署。服务部署时间约为5分钟,当服务状态运行中时,表明服务已成功部署。

调用EAS服务

WebUI调用

标准版、集群版和Serverless版的EAS服务都支持通过WebUI使用。具体操作步骤如下:

  1. 单击目标服务的服务方式列下的查看Web应用

    说明

    访问WebUI时,大约需要1分钟的加载时间,之后您将能看到完整的初始工作流界面。

  2. WebUI页面的默认语言切换为中文

  3. WebUI页面进行模型推理验证。

    选择文生图的模型和图生视频的模型,本方案使用默认配置。然后在CLIP文本编码器中输入Prompts,例如:Rocket takes off from the ground, fire, sky, airplane,单击添加提示词队列, 等待工作流运行完成即可获得AI生成的视频。85453c9fcadd222fbb087c5acddb6e90.png

  4. 右键单击生成的视频,选择保存图像,即可将生成的视频保存到本地。image.png

    生成的视频示例如下所示:

API调用

标准版服务仅支持同步调用,并提供在线调试。API版服务仅支持异步调用。

说明

EAS中同步调用和异步调用的通用定义:

  • 同步调用是指直接请求推理实例,不使用EAS的队列服务;

  • 异步调用是指使用EAS的队列服务,向输入队列发送请求,以订阅的方式获得结果推送。

由于ComfyUI本身具有异步队列系统,即使发起同步调用,实质上也是异步进行的。用户发送请求后,系统会返回一个Prompt ID,然后需要使用Prompt ID轮询以获取推理结果。

1. 生成请求体

ComfyUIAPI请求体取决于工作流配置。您需要先在WebUI页面设置工作流,然后通过保存API格式获取工作流对应的JSON文件。

  • 同步调用的请求体需要将JSON文件内容包装在"prompt"键值下面。

  • 异步调用的请求体就是JSON文件内容。

单击查看API请求体获取步骤

  1. WebUI页面,单击image按钮,并在Settings对话框中选中启用开发模式选项复选框。image

  2. WebUI页面,根据您的业务需求配置工作流。

    您可以在Checkpoint加载器区域选择模型,在CLIP文本编码器中输入正向和反向提示词、调整采样器配置等。完成这些操作后,单击添加提示词队列以获取AI生成的视频,并确保所有配置都符合您的需求。

  3. 确定工作流符合预期后,请单击保存(API格式),下载该工作流对应的JSON文件。image

    单击查看上述工作流的同步调用请求体示例

    {
        "prompt": {
            "3": {
                "inputs": {
                    "seed": 367490676387803,
                    "steps": 40,
                    "cfg": 7,
                    "sampler_name": "dpmpp_sde_gpu",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "4",
                        0
                    ],
                    "positive": [
                        "6",
                        0
                    ],
                    "negative": [
                        "7",
                        0
                    ],
                    "latent_image": [
                        "5",
                        0
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "4": {
                "inputs": {
                    "ckpt_name": "LandscapeBING_v10.safetensors"
                },
                "class_type": "CheckpointLoaderSimple",
                "_meta": {
                    "title": "Checkpoint加载器(简易)"
                }
            },
            "5": {
                "inputs": {
                    "width": 720,
                    "height": 1280,
                    "batch_size": 1
                },
                "class_type": "EmptyLatentImage",
                "_meta": {
                    "title": "空Latent"
                }
            },
            "6": {
                "inputs": {
                    "text": "Rocket takes off from the ground, fire,sky, airplane",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "7": {
                "inputs": {
                    "text": "",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "8": {
                "inputs": {
                    "samples": [
                        "3",
                        0
                    ],
                    "vae": [
                        "4",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "9": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "images": [
                        "8",
                        0
                    ]
                },
                "class_type": "SaveImage",
                "_meta": {
                    "title": "保存图像"
                }
            },
            "13": {
                "inputs": {
                    "seed": 510424455529432,
                    "steps": 40,
                    "cfg": 2.5,
                    "sampler_name": "euler_ancestral",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "17",
                        0
                    ],
                    "positive": [
                        "16",
                        0
                    ],
                    "negative": [
                        "16",
                        1
                    ],
                    "latent_image": [
                        "16",
                        2
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "14": {
                "inputs": {
                    "samples": [
                        "13",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "15": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "fps": 10,
                    "lossless": false,
                    "quality": 85,
                    "method": "default",
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "SaveAnimatedWEBP",
                "_meta": {
                    "title": "保存WEBP"
                }
            },
            "16": {
                "inputs": {
                    "width": 512,
                    "height": 768,
                    "video_frames": 35,
                    "motion_bucket_id": 140,
                    "fps": 15,
                    "augmentation_level": 0.15,
                    "clip_vision": [
                        "18",
                        1
                    ],
                    "init_image": [
                        "8",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "SVD_img2vid_Conditioning",
                "_meta": {
                    "title": "SVD_图像到视频_条件"
                }
            },
            "17": {
                "inputs": {
                    "min_cfg": 1,
                    "model": [
                        "18",
                        0
                    ]
                },
                "class_type": "VideoLinearCFGGuidance",
                "_meta": {
                    "title": "线性CFG引导"
                }
            },
            "18": {
                "inputs": {
                    "ckpt_name": "svd_xt_image_decoder.safetensors"
                },
                "class_type": "ImageOnlyCheckpointLoader",
                "_meta": {
                    "title": "Checkpoint加载器(仅图像)"
                }
            },
            "19": {
                "inputs": {
                    "frame_rate": 10,
                    "loop_count": 0,
                    "filename_prefix": "comfyUI",
                    "format": "video/h264-mp4",
                    "pix_fmt": "yuv420p",
                    "crf": 20,
                    "save_metadata": true,
                    "pingpong": false,
                    "save_output": true,
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "VHS_VideoCombine",
                "_meta": {
                    "title": "合并为视频"
                }
            }
        }
    }

2. 发起调用

在线调试

仅标准版服务支持在线调试。

  1. 模型在线服务(EAS)页面,单击目标服务操作列下的在线调试,进入在线调试页面。

  2. 发送POST请求,获取Prompt ID。

    1. 在调试页面的在线调试请求参数区域的Body处填写已准备好的请求体。并在请求URL文本编辑框中添加/promptimage

    2. 单击发送请求,即可在调试信息区域查看返回结果,示例如下。image

  3. 发送GET请求,根据Prompt ID获取推理结果。

    1. 在线调试请求参数区域中,将请求方法修改为GET,并在文本框中配置/history/<prompt id>,示例如下。image

      其中<prompt id>需要替换为步骤2获取的Prompt ID。

    2. 单击发送请求,即可获取推理结果。

      您可以在挂载存储的output目录中,查看生成的推理结果。

同步调用

仅标准版服务支持同步调用。

  1. 查看调用信息。

    1. 在服务列表中,单击标准版服务名称,然后在基本信息区域,单击查看调用信息

    2. 调用信息对话框的公网地址调用页签,获取服务访问地址和Token。image

  2. 发送POST请求,获取Prompt ID。

    Curl

    • HTTP请求方式:POST

    • 请求URL:<service_url>/prompt

    • 请求头部:

      头部

      描述

      Authorization

      <token>

      授权密钥

      Content-Type

      application/json

      指定请求体格式

    • 代码示例

      curl --location --request POST '<service_url>/prompt' \
      --header 'Authorization: <token>' \
      --header 'Content-Type: application/json' \
      --data-raw '{
          "prompt":
          ...省略
      }'

      其中关键配置项如下:

      配置项

      描述

      <service_url>

      替换为步骤1中获取的服务访问地址。您需要将访问地址末尾的/删除。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

      <token>

      替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

      data-raw

      配置为请求体,例如:

      重要

      请求体中的布尔值(truefalse)首字母需要小写。

      单击此处查看请求体示例

      {
          "prompt": {
              "3": {
                  "inputs": {
                      "seed": 367490676387803,
                      "steps": 40,
                      "cfg": 7,
                      "sampler_name": "dpmpp_sde_gpu",
                      "scheduler": "karras",
                      "denoise": 1,
                      "model": [
                          "4",
                          0
                      ],
                      "positive": [
                          "6",
                          0
                      ],
                      "negative": [
                          "7",
                          0
                      ],
                      "latent_image": [
                          "5",
                          0
                      ]
                  },
                  "class_type": "KSampler",
                  "_meta": {
                      "title": "K采样器"
                  }
              },
              "4": {
                  "inputs": {
                      "ckpt_name": "LandscapeBING_v10.safetensors"
                  },
                  "class_type": "CheckpointLoaderSimple",
                  "_meta": {
                      "title": "Checkpoint加载器(简易)"
                  }
              },
              "5": {
                  "inputs": {
                      "width": 720,
                      "height": 1280,
                      "batch_size": 1
                  },
                  "class_type": "EmptyLatentImage",
                  "_meta": {
                      "title": "空Latent"
                  }
              },
              "6": {
                  "inputs": {
                      "text": "Rocket takes off from the ground, fire,sky, airplane",
                      "clip": [
                          "4",
                          1
                      ]
                  },
                  "class_type": "CLIPTextEncode",
                  "_meta": {
                      "title": "CLIP文本编码器"
                  }
              },
              "7": {
                  "inputs": {
                      "text": "",
                      "clip": [
                          "4",
                          1
                      ]
                  },
                  "class_type": "CLIPTextEncode",
                  "_meta": {
                      "title": "CLIP文本编码器"
                  }
              },
              "8": {
                  "inputs": {
                      "samples": [
                          "3",
                          0
                      ],
                      "vae": [
                          "4",
                          2
                      ]
                  },
                  "class_type": "VAEDecode",
                  "_meta": {
                      "title": "VAE解码"
                  }
              },
              "9": {
                  "inputs": {
                      "filename_prefix": "ComfyUI",
                      "images": [
                          "8",
                          0
                      ]
                  },
                  "class_type": "SaveImage",
                  "_meta": {
                      "title": "保存图像"
                  }
              },
              "13": {
                  "inputs": {
                      "seed": 510424455529432,
                      "steps": 40,
                      "cfg": 2.5,
                      "sampler_name": "euler_ancestral",
                      "scheduler": "karras",
                      "denoise": 1,
                      "model": [
                          "17",
                          0
                      ],
                      "positive": [
                          "16",
                          0
                      ],
                      "negative": [
                          "16",
                          1
                      ],
                      "latent_image": [
                          "16",
                          2
                      ]
                  },
                  "class_type": "KSampler",
                  "_meta": {
                      "title": "K采样器"
                  }
              },
              "14": {
                  "inputs": {
                      "samples": [
                          "13",
                          0
                      ],
                      "vae": [
                          "18",
                          2
                      ]
                  },
                  "class_type": "VAEDecode",
                  "_meta": {
                      "title": "VAE解码"
                  }
              },
              "15": {
                  "inputs": {
                      "filename_prefix": "ComfyUI",
                      "fps": 10,
                      "lossless": false,
                      "quality": 85,
                      "method": "default",
                      "images": [
                          "14",
                          0
                      ]
                  },
                  "class_type": "SaveAnimatedWEBP",
                  "_meta": {
                      "title": "保存WEBP"
                  }
              },
              "16": {
                  "inputs": {
                      "width": 512,
                      "height": 768,
                      "video_frames": 35,
                      "motion_bucket_id": 140,
                      "fps": 15,
                      "augmentation_level": 0.15,
                      "clip_vision": [
                          "18",
                          1
                      ],
                      "init_image": [
                          "8",
                          0
                      ],
                      "vae": [
                          "18",
                          2
                      ]
                  },
                  "class_type": "SVD_img2vid_Conditioning",
                  "_meta": {
                      "title": "SVD_图像到视频_条件"
                  }
              },
              "17": {
                  "inputs": {
                      "min_cfg": 1,
                      "model": [
                          "18",
                          0
                      ]
                  },
                  "class_type": "VideoLinearCFGGuidance",
                  "_meta": {
                      "title": "线性CFG引导"
                  }
              },
              "18": {
                  "inputs": {
                      "ckpt_name": "svd_xt_image_decoder.safetensors"
                  },
                  "class_type": "ImageOnlyCheckpointLoader",
                  "_meta": {
                      "title": "Checkpoint加载器(仅图像)"
                  }
              },
              "19": {
                  "inputs": {
                      "frame_rate": 10,
                      "loop_count": 0,
                      "filename_prefix": "comfyUI",
                      "format": "video/h264-mp4",
                      "pix_fmt": "yuv420p",
                      "crf": 20,
                      "save_metadata": true,
                      "pingpong": false,
                      "save_output": true,
                      "images": [
                          "14",
                          0
                      ]
                  },
                  "class_type": "VHS_VideoCombine",
                  "_meta": {
                      "title": "合并为视频"
                  }
              }
          }
      }

    Python

    代码示例如下:

    import requests
    
    url = "<service_url>/prompt"
    
    payload = {
        "prompt":
        ...省略
    }
    
    session = requests.session()
    session.headers.update({"Authorization":"<token>"})
    
    
    response = session.post(url=f'{url}', json=payload)
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    其中关键配置项如下:

    配置项

    描述

    <service_url>

    替换为步骤1中获取的服务访问地址。您需要将访问地址末尾的/删除,例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

    <token>

    替换为步骤1中获取的Token。ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

    payload

    配置为请求体,例如:

    重要

    请求体中的布尔值(TrueFalse)首字母需要大写。

    单击此处查看请求体示例

    {
        "prompt": {
            "3": {
                "inputs": {
                    "seed": 367490676387803,
                    "steps": 40,
                    "cfg": 7,
                    "sampler_name": "dpmpp_sde_gpu",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "4",
                        0
                    ],
                    "positive": [
                        "6",
                        0
                    ],
                    "negative": [
                        "7",
                        0
                    ],
                    "latent_image": [
                        "5",
                        0
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "4": {
                "inputs": {
                    "ckpt_name": "LandscapeBING_v10.safetensors"
                },
                "class_type": "CheckpointLoaderSimple",
                "_meta": {
                    "title": "Checkpoint加载器(简易)"
                }
            },
            "5": {
                "inputs": {
                    "width": 720,
                    "height": 1280,
                    "batch_size": 1
                },
                "class_type": "EmptyLatentImage",
                "_meta": {
                    "title": "空Latent"
                }
            },
            "6": {
                "inputs": {
                    "text": "Rocket takes off from the ground, fire,sky, airplane",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "7": {
                "inputs": {
                    "text": "",
                    "clip": [
                        "4",
                        1
                    ]
                },
                "class_type": "CLIPTextEncode",
                "_meta": {
                    "title": "CLIP文本编码器"
                }
            },
            "8": {
                "inputs": {
                    "samples": [
                        "3",
                        0
                    ],
                    "vae": [
                        "4",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "9": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "images": [
                        "8",
                        0
                    ]
                },
                "class_type": "SaveImage",
                "_meta": {
                    "title": "保存图像"
                }
            },
            "13": {
                "inputs": {
                    "seed": 510424455529432,
                    "steps": 40,
                    "cfg": 2.5,
                    "sampler_name": "euler_ancestral",
                    "scheduler": "karras",
                    "denoise": 1,
                    "model": [
                        "17",
                        0
                    ],
                    "positive": [
                        "16",
                        0
                    ],
                    "negative": [
                        "16",
                        1
                    ],
                    "latent_image": [
                        "16",
                        2
                    ]
                },
                "class_type": "KSampler",
                "_meta": {
                    "title": "K采样器"
                }
            },
            "14": {
                "inputs": {
                    "samples": [
                        "13",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "VAEDecode",
                "_meta": {
                    "title": "VAE解码"
                }
            },
            "15": {
                "inputs": {
                    "filename_prefix": "ComfyUI",
                    "fps": 10,
                    "lossless": False,
                    "quality": 85,
                    "method": "default",
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "SaveAnimatedWEBP",
                "_meta": {
                    "title": "保存WEBP"
                }
            },
            "16": {
                "inputs": {
                    "width": 512,
                    "height": 768,
                    "video_frames": 35,
                    "motion_bucket_id": 140,
                    "fps": 15,
                    "augmentation_level": 0.15,
                    "clip_vision": [
                        "18",
                        1
                    ],
                    "init_image": [
                        "8",
                        0
                    ],
                    "vae": [
                        "18",
                        2
                    ]
                },
                "class_type": "SVD_img2vid_Conditioning",
                "_meta": {
                    "title": "SVD_图像到视频_条件"
                }
            },
            "17": {
                "inputs": {
                    "min_cfg": 1,
                    "model": [
                        "18",
                        0
                    ]
                },
                "class_type": "VideoLinearCFGGuidance",
                "_meta": {
                    "title": "线性CFG引导"
                }
            },
            "18": {
                "inputs": {
                    "ckpt_name": "svd_xt_image_decoder.safetensors"
                },
                "class_type": "ImageOnlyCheckpointLoader",
                "_meta": {
                    "title": "Checkpoint加载器(仅图像)"
                }
            },
            "19": {
                "inputs": {
                    "frame_rate": 10,
                    "loop_count": 0,
                    "filename_prefix": "comfyUI",
                    "format": "video/h264-mp4",
                    "pix_fmt": "yuv420p",
                    "crf": 20,
                    "save_metadata": True,
                    "pingpong": False,
                    "save_output": True,
                    "images": [
                        "14",
                        0
                    ]
                },
                "class_type": "VHS_VideoCombine",
                "_meta": {
                    "title": "合并为视频"
                }
            }
        }
    }

    返回结果示例如下:

    {
        "prompt_id": "021ebc5b-e245-4e37-8bd3-00f7b949****",
        "number": 5,
        "node_errors": {}
    }

    您可以从返回结果中获取Prompt ID。

  3. 发送请求,获取推理结果。

    Curl

    • HTTP请求方式:GET

    • 请求URL:<service_url>/history/<prompt_id>

    • 请求头部:

    • 头部

      描述

      Authorization

      <token>

      授权密钥

    • 代码示例:

      curl --location --request GET '<service_url>/history/<prompt_id>' \
           --header 'Authorization: <token>'

      其中关键配置项如下:

      配置项

      描述

      <service_url>

      替换为步骤1中获取的服务访问地址。您需要将访问地址末尾的/删除。例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

      <token>

      替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

      <prompt_id>

      替换为步骤2中获取的prompt_id。

    Python

    代码示例如下:

    import requests
    
    # 构造请求URL。
    url = "<service_url>/history/<prompt_id>"
    
    session = requests.session()
    session.headers.update({"Authorization":"<token>"})
    
    response = session.get(url=f'{url}')
    
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    其中关键配置项如下:

    配置项

    描述

    <service_url>

    替换为步骤1中获取的服务访问地址。您需要将访问地址末尾的/删除,例如http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com

    <token>

    替换为步骤1中获取的Token。例如ZGJmNzcwYjczODE1MmVlNWY1NTNiNGYxNDkzODI****NzU2NTFiOA==

    <prompt_id>

    替换为步骤2中获取的prompt_id。

    返回结果示例如下:

    单击查看返回结果示例

    {
        "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
            "prompt": ...省略,
            "outputs": {
                "9": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645398_18dba34d-df87-4735-a577-c63d5506a6a1_.png",
                            "subfolder": "",
                            "type": "output"
                        }
                    ]
                },
                "15": {
                    "images": [
                        {
                            "filename": "ComfyUI_1712645867_.webp",
                            "subfolder": "",
                            "type": "output"
                        }
                    ],
                    "animated": [
                        true
                    ]
                },
                "19": {
                    "gifs": [
                        {
                            "filename": "comfyUI_00002.mp4",
                            "subfolder": "",
                            "type": "output",
                            "format": "video/h264-mp4"
                        }
                    ]
                }
            },
            "status": {
                "status_str": "success",
                "completed": true,
                "messages": ...省略,
            }
        }
    }
    

    在本示例返回的outputs中提供了prompt生成的图像、webp文件和mp4视频,您可以在挂载存储的output目录中,根据文件名称来查找这些文件。

异步调用

API版的服务支持异步调用,且仅支持api_prompt路径。

  1. 查看调用信息。

    单击API版服务的服务方式列下的调用信息,在调用信息对话框的异步调用页签,查看服务访问地址和Token。image

  2. 推送请求。

    代码示例如下:

    import requests,io,base64
    from PIL import Image, PngImagePlugin
    
    url = "<service_url>"
    session = requests.session()
    session.headers.update({"Authorization":"<token>"})
    
    work_flow = {
        '3': 
        ...省略
      }
    
    for i in range(5):
      payload = work_flow
      response = session.post(url=f'{url}/api_prompt?task_id=txt2img_{i}', json=payload)
      if response.status_code != 200:
        exit(f"send request error:{response.content}")
      else:
        print(f"send {i} success, index is {response.content}")

    其中关键配置项如下:

    配置项

    描述

    <service_url>

    替换为步骤1获取的服务访问地址。您需要将访问地址末尾的/删除,例如http://175805416243****.cn-beijing.pai-eas.aliyuncs.com/api/predict/comfyui_api

    <token>

    替换为步骤1获取的Token。例如ZTJhM****TBhMmJkYjM3M2U0NjM1NGE3OGNlZGEyZTdjYjlm****Nw==

    work_flow

    配置为请求体(工作流对应的JSON文件内容)。

    重要

    文件中的布尔值(TrueFalse)首字母需要大写。

    单击此处查看JSON文件示例

    {
      "3": {
        "inputs": {
          "seed": 1021224598837526,
          "steps": 40,
          "cfg": 7,
          "sampler_name": "dpmpp_sde_gpu",
          "scheduler": "karras",
          "denoise": 1,
          "model": [
            "4",
            0
          ],
          "positive": [
            "6",
            0
          ],
          "negative": [
            "7",
            0
          ],
          "latent_image": [
            "5",
            0
          ]
        },
        "class_type": "KSampler",
        "_meta": {
          "title": "K采样器"
        }
      },
      "4": {
        "inputs": {
          "ckpt_name": "LandscapeBING_v10.safetensors"
        },
        "class_type": "CheckpointLoaderSimple",
        "_meta": {
          "title": "Checkpoint加载器(简易)"
        }
      },
      "5": {
        "inputs": {
          "width": 720,
          "height": 1280,
          "batch_size": 1
        },
        "class_type": "EmptyLatentImage",
        "_meta": {
          "title": "空Latent"
        }
      },
      "6": {
        "inputs": {
          "text": "Rocket takes off from the ground, fire, sky, airplane",
          "clip": [
            "4",
            1
          ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
          "title": "CLIP文本编码器"
        }
      },
      "7": {
        "inputs": {
          "text": "",
          "clip": [
            "4",
            1
          ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
          "title": "CLIP文本编码器"
        }
      },
      "8": {
        "inputs": {
          "samples": [
            "3",
            0
          ],
          "vae": [
            "4",
            2
          ]
        },
        "class_type": "VAEDecode",
        "_meta": {
          "title": "VAE解码"
        }
      },
      "9": {
        "inputs": {
          "filename_prefix": "ComfyUI",
          "images": [
            "8",
            0
          ]
        },
        "class_type": "SaveImage",
        "_meta": {
          "title": "保存图像"
        }
      },
      "13": {
        "inputs": {
          "seed": 1072245043382649,
          "steps": 40,
          "cfg": 2.5,
          "sampler_name": "euler_ancestral",
          "scheduler": "karras",
          "denoise": 1,
          "model": [
            "17",
            0
          ],
          "positive": [
            "16",
            0
          ],
          "negative": [
            "16",
            1
          ],
          "latent_image": [
            "16",
            2
          ]
        },
        "class_type": "KSampler",
        "_meta": {
          "title": "K采样器"
        }
      },
      "14": {
        "inputs": {
          "samples": [
            "13",
            0
          ],
          "vae": [
            "18",
            2
          ]
        },
        "class_type": "VAEDecode",
        "_meta": {
          "title": "VAE解码"
        }
      },
      "15": {
        "inputs": {
          "filename_prefix": "ComfyUI",
          "fps": 10,
          "lossless": False,
          "quality": 85,
          "method": "default",
          "images": [
            "14",
            0
          ]
        },
        "class_type": "SaveAnimatedWEBP",
        "_meta": {
          "title": "保存WEBP"
        }
      },
      "16": {
        "inputs": {
          "width": 512,
          "height": 768,
          "video_frames": 35,
          "motion_bucket_id": 140,
          "fps": 15,
          "augmentation_level": 0.15,
          "clip_vision": [
            "18",
            1
          ],
          "init_image": [
            "8",
            0
          ],
          "vae": [
            "18",
            2
          ]
        },
        "class_type": "SVD_img2vid_Conditioning",
        "_meta": {
          "title": "SVD_图像到视频_条件"
        }
      },
      "17": {
        "inputs": {
          "min_cfg": 1,
          "model": [
            "18",
            0
          ]
        },
        "class_type": "VideoLinearCFGGuidance",
        "_meta": {
          "title": "线性CFG引导"
        }
      },
      "18": {
        "inputs": {
          "ckpt_name": "svd_xt_image_decoder.safetensors"
        },
        "class_type": "ImageOnlyCheckpointLoader",
        "_meta": {
          "title": "Checkpoint加载器(仅图像)"
        }
      },
      "19": {
        "inputs": {
          "frame_rate": 10,
          "loop_count": 0,
          "filename_prefix": "comfyUI",
          "format": "video/h264-mp4",
          "pix_fmt": "yuv420p",
          "crf": 20,
          "save_metadata": True,
          "pingpong": False,
          "save_output": True,
          "images": [
            "14",
            0
          ]
        },
        "class_type": "VHS_VideoCombine",
        "_meta": {
          "title": "合并为视频"
        }
      }
    }
  3. 订阅结果。

    1. 执行以下命令安装eas_prediction SDK。

      pip install eas_prediction  --user
    2. 执行以下代码,获取返回结果。

      from eas_prediction import QueueClient
      
      sink_queue = QueueClient('<service_domain>', '<service_name>/sink')
      sink_queue.set_token('<token>')
      sink_queue.init()
      
      watcher = sink_queue.watch(0, 5, auto_commit=False)
      for x in watcher.run():
          if 'task_id' in x.tags:
              print('index {} task_id is {}'.format(x.index, x.tags['task_id']))
          print(f'index {x.index} data is {x.data}')
          sink_queue.commit(x.index)
      

      其中关键配置说明如下:

      配置项

      描述

      <service_domain>

      请替换为步骤1查询的服务访问地址中的调用信息。例如139699392458****.cn-hangzhou.pai-eas.aliyuncs.com

      <service_name>

      请替换为EAS服务名称。

      <token>

      请替换为步骤1查询的Token。

      返回结果示例如下:

      index 42 task_id is txt2img_0
      index 42 data is b'[{"type": "executed", "data": {"node": "9", "output": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "15", "output": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"type": "executed", "data": {"node": "19", "output": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}, "prompt_id": "c3c983b6-f92b-4dd5-b4dc-442db4d1736f"}}, {"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 43 task_id is txt2img_1
      index 43 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 44 task_id is txt2img_2
      index 44 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 45 task_id is txt2img_3
      index 45 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'
      index 46 task_id is txt2img_4
      index 46 data is b'[{"9": {"images": [{"filename": "ComfyUI_1712647318_8e7f3c93-d2a8-4377-92d5-8eb552adc172_.png", "subfolder": "", "type": "output"}]}, "15": {"images": [{"filename": "ComfyUI_1712647895_.webp", "subfolder": "", "type": "output"}], "animated": [true]}, "19": {"gifs": [{"filename": "comfyUI_00001.mp4", "subfolder": "", "type": "output", "format": "video/h264-mp4"}]}}]'

      您可以在挂载存储的output目录中,查看推理结果文件。

更多使用

ComfyUI中使用自己的工作流

如下图可打开本地文件系统中的工作流进行使用。

comfyui使用自己的workflow

使用自定义模型和ComfyUI插件

如果想要使用自定义模型或者安装缺失节点(ComfyUI插件),您需要给服务挂载OSS或者NAS存储空间。服务部署后,系统会自动在已挂载的OSSNAS存储空间中创建以下目录结构:

image

其中:

  • custom_nodes:该目录用来存储ComfyUI插件。

  • models:该目录用来存放模型文件。

具体操作步骤如下:

  1. 上传模型文件或插件。如使用OSS,请参见步骤二:上传文件

模型文件上传

请将模型文件上传至挂载存储的models目录下的相应子目录中,请参考对应节点的开源项目库的使用说明,确定模型上传至哪个子目录。例如:

  • 对于Checkpoint加载器节点,对应的模型应上传至models/checkpoints

  • 对于风格模型加载器,对应的模型应上传至models/style_models

插件上传

推荐您将ComfyUI第三方插件上传至挂载存储的custom_nodes目录。

说明

使用ComfyUI管理器直接安装插件,由于需要从GitHub等平台拉取代码,有可能存在网络连接失败的问题。不建议使用。

  1. 重启服务

    在挂载的存储空间上传模型或者插件之后,需要重启服务才能生效。您可以在ComfyUI管理器中单击重启按钮。重启过程将持续大约5分钟,在此期间服务会自动重启并恢复正常运行。

    image

常见问题

模型加载器显示undefined

首先确认模型的目录位置是否正确,这依赖于模型加载器的要求。

如果是服务启动之后更新的模型文件,请重启服务。

ComfyUI无法出图

检查服务镜像和资源规格配置是否正确,资源推荐使用ml.gu7i.c16m60.1-gu30

xFormer对图片生成速度的加速效果

xFormers是基于Transformer的开源加速工具,能够有效缩短图片和视频生成时长,节省显存使用。ComfyUI镜像部署默认已开启xFormers加速。加速效果跟工作流的大小相关,针对GPU调用的内容尤其是使用NVIDIA显卡的提升比较明显。

EAS与函数计算在部署ComfyUI Serverless版的主要区别

  • EAS:适合有状态、长周期运行的服务,支持一键部署模型为在线推理服务或AI-Web应用,具备弹性扩缩容、蓝绿部署等功能。例如,您可以通过EAS的场景化模型部署或自定义模型部署方式来部署ComfyUI。

  • 函数计算:基于Serverless架构,提供按需付费、弹性伸缩等优势,适合需要高质量图像生成功能的场景,可自定义ComfyUI模型及安装插件。例如,您可以在函数计算3.0控制台创建应用、选择ComfyUI模板、设置配置项并创建应用。

如何查看当前可用的模型文件和ComfyUI插件列表

  • 对于ComfyUI默认工作流,您需要在相应节点查看该节点可用的模型文件,例如在Checkpoint加载器的下拉列表中查看当前可用的模型文件。

  • 右键单击WebUI页面,在快捷菜单中单击新建节点,查看所有已安装的ComfyUI插件。

如何将WebUI页面的默认语言切换为中文?

  1. 模型在线服务(EAS)页面,单击目标服务的服务方式列下的查看Web应用,进入WebUI页面。

    说明

    访问WebUI时,大约需要1分钟的加载时间,之后您将能看到完整的初始工作流界面。

  2. WebUI页面,待工作流加载成功后,单击image按钮。image

  3. Settings对话框中,将AGLTranslation-langualge修改为中文[Chinese Simplified]image

    参数设置完成后,系统将自动切换至中文界面,大约需要1分钟的加载时间,之后您将能看到完整的初始工作流界面。

相关文档

  • ComfyUIAPI版本启用了异步队列,关于异步调用的原理介绍,请参见部署异步推理服务

  • 通过EAS,您还可以完成以下场景化部署:

    • 部署支持WebUIAPI调用的LLM大语言模型,并在部署LLM应用后,利用LangChain框架集成企业知识库,实现智能问答和自动化功能。详情请参见5分钟使用EAS一键部署LLM大语言模型应用

    • 部署集成了大语言模型(LLM)和检索增强生成(RAG)技术的对话系统服务,适用于问答、摘要生成和依赖外部知识的自然语言处理任务。详情请参见大模型RAG对话系统

附录

集群版服务原理介绍

实现原理图如下:

image
  • 集群版服务主要针对多用户场景,实现了客户端和后端推理实例解耦,以便多用户可以分时复用后端推理实例,提升实例的利用率和降低推理成本。

  • Proxy代理主要负责客户端进程和推理实例的管理。用户的所有操作都在自己的进程中进行处理,相关的文件操作仅限于公共目录和个人目录,从而实现了用户间工作目录的有效隔离。当用户需要使用推理实例来处理请求时,Proxy代理会从后端推理实例中找到可用的空闲实例来处理该推理请求。