全部產品
Search
文件中心

Platform For AI:AI視頻產生-ComfyUI部署

更新時間:Dec 21, 2024

EAS提供了情境化部署方式,您只需配置幾個參數,即可一鍵部署基於ComfyUI和Stable Video Diffusion模型的AI視頻產生服務,協助您完成社交平台短視頻內容產生、動畫製作等任務。本文為您介紹如何基於ComfyUI鏡像部署服務和幾種常用的調用方式。

背景資訊

隨著AIGC的廣泛應用,AI視頻產生已成為當前熱門 App之一。目前市面上有許多開源視頻產生大模型可供選擇,它們在不同領域展現了各自獨特的效能。與此同時,AIGC開源工具ComfyUI也迅速在市場上嶄露頭角。作為一個基於節點流程式的AIGC產生工具WebUI,ComfyUI將AIGC流程拆分成工作節點,實現了精準的工作流程定製和可複現性。您可以一鍵部署基於ComfyUI的AI視頻產生服務,支援以下四種版本:

  • 標準版:僅適用於單使用者使用WebUI,或使用一個執行個體調用API情境。支援以下兩種調用方式:

    • WebUI:請求發送時,會繞過EAS介面,前端直接將請求傳遞給後端伺服器,所有請求均由同一個後端執行個體進行處理。建議您只部署1個執行個體,當您需要多台執行個體時,需選擇叢集版。

    • API:通過EAS介面發送請求給後端執行個體進行處理。建議您只部署1個執行個體,當您需要多台執行個體時,需選擇API版本。

  • API版:系統將自動轉換服務為非同步模式,適用於高並發情境。該模式僅支援API非同步呼叫。基於其非同步特性,系統會建立佇列服務執行個體,因此需要分配額外的CPU執行個體。

  • 叢集版:適合多使用者同時在WebUI頁面進行操作。僅支援通過WebUI進行調用,不提供API服務。主要適用於設計組或教學情境,通過分時複用的設計來提升推理叢集的利用率,降低成本。由於Proxy負責處理WebUI請求,因此需要分配額外的CPU執行個體。參考叢集版服務原理介紹,瞭解叢集版服務的實現原理。該版本優勢如下:

    • 系統為每個使用者提供獨立的後端環境。當開啟了多個執行個體時,單個使用者的任務會按順序執行,而多使用者環境下的任務則在不同執行個體之間分配,實現高效的GPU共用。

    • 系統為每個使用者指派獨立的工作目錄,便於管理和儲存模型、輸出映像或視頻等檔案。

  • Serverless版:目前,僅華東2(上海)、華東1(杭州)地區支援部署Serverless版服務。 Serverless版本服務部署完全免費,只根據使用出圖的時間長度進行計費,並會根據您的服務要求量自動Auto Scaling。

具體使用流程如下:

  1. 部署EAS服務

    根據您的具體使用情境,選擇部署標準版、API版、叢集版或Serverless版的服務。

  2. 調用EAS服務

    根據部署的服務版本,支援以下三種調用方式:

    • 通過WebUI調用EAS服務

      使用WebUI發送服務要求,僅標準版、叢集版和Serverless版服務支援使用該方式。

    • 線上調試EAS服務

      在EAS的線上調試頁面發送同步調用請求,只有標準版服務支援同步調用功能。

    • 通過API調用EAS服務

      只有標準版和API版服務支援通過API發送服務要求。其中,標準版服務僅支援同步調用,而API版服務僅支援非同步呼叫。

前提條件

在部署微調模型、安裝ComfyUI外掛程式、使用API調用服務時,您必須掛載NAS或OSS儲存,以便上傳微調模型、外掛程式和擷取推理結果。請提前準備NAS或OSS儲存空間:

  • 已建立OSS儲存空間和空目錄,例如:oss://bucket-test/data-oss/,其中:bucket-test為OSS儲存空間名稱;data-oss為該儲存空間下的空目錄。關於如何建立OSS儲存空間,請參見建立儲存空間;關於如何建立空目錄,請參見管理目錄

  • 已建立NAS檔案系統和空目錄。具體操作,請參見建立檔案系統

部署EAS服務

支援以下兩種部署方式:

方式一:情境化模型部署(推薦)

  1. 登入PAI控制台,在頁面上方選擇目標地區,並在右側選擇目標工作空間,然後單擊進入EAS

  2. 模型線上服務(EAS)頁面,單擊部署服務,在情境化模型部署地區,單擊AI視頻產生-ComfyUI部署

  3. AI視頻產生-ComfyUI部署頁面,配置以下關鍵參數。

    參數

    描述

    基本資料

    服務名稱

    自訂模型服務名稱。

    版本選擇

    支援選擇以下版本:

    • 標準版:適用於單使用者使用WebUI或使用一個執行個體調用API情境。支援通過WebUI產生視頻,也可通過API進行調用。

    • API版:系統將自動轉換服務為非同步模式,適用於高並發情境。僅支援通過API進行調用。

    • 叢集版WebUI:適合多使用者同時在WebUI頁面進行操作。僅支援通過WebUI進行調用,不提供API服務。關於該版本的實現原理介紹,請參見叢集版服務原理介紹

    • Serverless版:Serverless版本服務部署完全免費,只根據使用出圖的時間長度進行計費。

    更多關於每個版本的使用情境說明,請參見背景資訊

    模型配置

    當部署微調模型、安裝ComfyUI外掛程式,或選擇API版標準版並通過API進行調用時,您需要單擊添加按鈕,進行模型配置,以便上傳微調模型、外掛程式和擷取推理結果。支援以下幾種配置類型:

    • Object Storage Service:單擊image選擇已建立的OSS儲存目錄。

    • Apsara File Storage NAS:配置NAS掛載點和NAS源路徑。

    後續,您可以將自訂模型和ComfyUI外掛程式上傳至指定的OSS或NAS路徑,以便載入和使用這些資源。具體操作,請參見如何掛載自訂模型和ComfyUI外掛程式?

    資源配置

    執行個體數

    當版本選擇標準版時,建議將執行個體數配置為1。

    資源配置選擇

    資源規格推薦使用GU30、A10或T4卡型。系統預設選擇GPU > ml.gu7i.c16m60.1-gu30,性價比高。

    說明

    ComfyUI僅支援單卡(單機單卡或多機單卡)運行,不支援多卡並行作業。

  4. 單擊部署

方式二:自訂模型部署

  1. 登入PAI控制台,在頁面上方選擇目標地區,並在右側選擇目標工作空間,然後單擊進入EAS

  2. 單擊部署服務,然後在自訂模型部署地區,單擊自訂部署

  3. 自訂部署頁面,配置以下關鍵參數。

    參數

    描述

    基本資料

    服務名稱

    自訂服務名稱。本案例使用的樣本值為:comfyui_svd_demo

    環境資訊

    部署方式

    選擇鏡像部署,並選中開啟Web應用複選框。

    鏡像配置

    官方鏡像列表中選擇comfyui>comfyui:1.7,其中:

    • x.x:表示標準版。

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

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

    說明
    • 由於版本迭代迅速,部署時鏡像版本選擇最高版本即可。

    • 更多關於每個版本的使用情境說明,請參見背景資訊

    模型配置

    當部署微調模型、安裝ComfyUI外掛程式,或選擇API版標準版並通過API進行調用時,您需要進行模型配置,以便上傳微調模型、外掛程式和擷取推理結果。支援以下幾種配置類型:

    • 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路徑下。

    後續,您可以將自訂模型和ComfyUI外掛程式上傳至指定的OSS或NAS路徑,以便載入和使用這些資源。具體操作,請參見如何掛載自訂模型和ComfyUI外掛程式?

    運行命令

    • 配置鏡像版本後,系統自動設定運行命令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. 單擊部署

    服務狀態運行中時,表明服務已成功部署。

調用EAS服務

通過WebUI調用EAS服務

通過WebUI,您可以調用標準版、叢集版和Serverless版的EAS服務。在標準版服務中,所有請求都由同一個後端執行個體處理。而叢集版服務則適合多使用者同時操作,它能夠在多個執行個體間分配並處理各使用者的任務。具體操作步驟如下:

  1. 單擊目標服務的服務方式列下的查看Web應用

    說明

    訪問WebUI時,大約需要1分鐘的載入時間,之後您將能看到完整的初始工作流程介面。

  2. 在WebUI頁面進行模型推理驗證。

    根據您自己的業務需要,選擇文生圖的模型和圖生視頻的模型,本方案使用預設配置。然後在CLIP文本編碼器中輸入Prompts,例如:Rocket takes off from the ground, fire, sky, airplane,單擊添加提示詞隊列, 等待工作流程運行完成即可獲得AI產生的視頻。85453c9fcadd222fbb087c5acddb6e90.png

  3. 按右鍵產生的視頻,選擇儲存映像,即可將產生的視頻儲存到本地。image.png

    產生的視頻樣本如下所示:

線上調試EAS服務

僅標準版服務支援線上調試,具體操作步驟如下:

  1. 產生請求體。具體操作,請參見如何產生請求體

  2. 模型線上服務(EAS)頁面,單擊目標服務操作列下的線上調試,進入線上調試頁面。

  3. 發送POST請求,擷取Prompt ID。

    1. 在調試頁面的線上調試請求參數地區的Body處填寫已準備好的請求體。並在請求URL文本編輯框中添加/promptimage

    2. 單擊發送請求,即可在調試資訊地區查看預測結果,樣本如下。image

  4. 發送GET請求,根據Prompt ID擷取推理結果。

    1. 線上調試請求參數地區中,將要求方法修改為GET,並在文字框中配置/history/<prompt id>,樣本如下。image

      其中<prompt id>需要替換為步驟3擷取的Prompt ID。

    2. 單擊發送請求,即可擷取推理結果。

      您可以在掛載儲存的output目錄中,查看產生的推理結果。

通過API調用EAS服務

標準版和API版服務支援API調用。API調用支援同步調用和非同步呼叫兩種方式:

  • 同步調用

    標準版服務僅支援同步調用方式,即用戶端發送一個請求,同步等待結果返回。

  • 非同步呼叫

    API版服務僅支援非同步呼叫方式,即用戶端使用EAS的佇列服務向輸入隊列發送請求,並通過訂閱的方式從輸出隊列查詢結果。

由於ComfyUI本身具有非同步隊列系統,即使發起同步調用,實質上也是非同步進行的。即當使用者提交請求後,系統會返回一個Prompt ID,您需要使用該ID輪詢以擷取推理結果。

同步調用

  1. 查看調用資訊。

    1. 在服務列表中,單擊標準版服務名稱,然後在基本資料地區,單擊查看調用資訊

    2. 調用資訊對話方塊的公網地址調用頁簽,擷取服務訪問地址和Token。image

  2. 擷取Prompt ID。

    1. 產生請求體。具體操作,請參見如何產生請求體

    2. 發送請求,擷取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

        配置為請求體,例如:

        重要

        請求體中的布爾值(true和false)首字母需要小寫。

        單擊此處查看請求體樣本

        {
            "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

      配置為請求體,例如:

      重要

      請求體中的布爾值(True和False)首字母需要大寫。

      單擊此處查看請求體樣本

      {
          "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檔案內容,樣本如下。如何擷取工作流程JSON檔案,請參見如何產生請求體

    重要

    檔案中的布爾值(True和False)首字母需要大寫。

    單擊此處查看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的API版本啟用了非同步隊列,關於非同步呼叫的原理介紹,請參見部署非同步推理服務

  • 通過EAS,您還可以完成以下情境化部署:

    • 部署支援WebUI和API調用的LLM大語言模型,並在部署LLM應用後,利用LangChain框架組成企業知識庫,實現智能問答和自動化功能。詳情請參見5分鐘使用EAS一鍵部署LLM大語言模型應用

    • 部署整合了大語言模型(LLM)和檢索增強產生(RAG)技術的對話系統服務,適用於問答、摘要產生和依賴外部知識的自然語言處理任務。詳情請參見大模型RAG對話系統

附錄

如何產生請求體

您需要在WebUI版面設定滿足業務需求的工作流程,然後構建相應的請求體。具體操作步驟如下:

  1. 模型線上服務(EAS)頁面,單擊目標服務的服務方式列下的查看Web應用,進入WebUI頁面。

    說明

    訪問WebUI時,大約需要1分鐘的載入時間,之後您將能看到完整的初始工作流程介面。

  2. 在WebUI頁面,單擊image按鈕,並在Settings對話方塊中選中啟用開發模式選項複選框。image

  3. 在WebUI頁面,根據您的業務需求配置工作流程。

    您可以在Checkpoint載入器地區選取項目模型,在CLIP文本編碼器中輸入正向和反向提示詞、調整採樣器配置等。完成這些操作後,單擊添加提示詞隊列以擷取AI產生的視頻,並確保所有配置都符合您的需求。

  4. 確定工作流程符合預期後,請單擊儲存(API格式),下載該工作流程對應的JSON檔案。image

    其中:

    • 同步調用和線上調試的請求體需要將下載的JSON檔案內容置於prompt鍵下進行封裝。例如,上述工作流程對應的請求體為:

      {
          "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索引值。上述工作流程對應的請求體即為已下載的JSON檔案內容。

叢集版服務原理介紹

實現原理圖如下:

  • 叢集版服務主要針對多使用者情境,實現了用戶端和後端推理執行個體解耦,以便多使用者可以分時複用後端推理執行個體,提升執行個體的利用率和降低推理成本。

  • Proxy代理主要負責用戶端進程和推理執行個體的管理。使用者的所有操作都在自己的進程中進行處理,相關的檔案操作僅限於公用目錄和個人目錄,從而實現了使用者間工作目錄的有效隔離。當使用者需要使用推理執行個體來處理請求時,Proxy代理會從後端推理執行個體中找到可用的空閑執行個體來處理該推理請求。

加速圖片產生速度

xFormers是基於Transformer的開源加速工具,能夠有效縮短圖片和視頻產生時間長度,節省顯存使用。ComfyUI鏡像部署預設已開啟xFormers加速。

如何使用自己的工作流程

如下圖可開啟本地檔案系統中的工作流程進行使用。

comfyui使用自己的workflow

如何掛載自訂模型和ComfyUI外掛程式?

服務部署後,系統會自動在已掛載的OSS或NAS儲存空間中建立以下目錄結構:image

其中:

  • custom_nodes:該目錄用來儲存ComfyUI外掛程式。

  • models:該目錄用來存放模型檔案。

如果您從開源社區擷取了ComfyUI的第三方外掛程式,或自行訓練產生了自訂模型,您應將這些外掛程式或模型檔案存放於上述指定目錄中,以便載入使用新的模型和外掛程式。具體操作步驟如下:

  1. 服務部署成功後,單擊目標服務的服務方式列下的查看Web應用

  2. 在WebUI介面中,您可以瀏覽並查看當前可用的模型檔案和ComfyUI外掛程式列表。

    • 對於ComfyUI預設工作流程,您需要在相應節點查看該節點可用的模型檔案,例如在Checkpoint載入器的下拉式清單中查看當前可用的模型檔案。

    • 按右鍵WebUI頁面,在捷徑功能表中單擊建立節點,查看所有已安裝的ComfyUI外掛程式。image

  3. 載入模型檔案。

    1. 請將模型檔案上傳至掛載儲存的models目錄下的相應子目錄中,具體操作,請參見步驟二:上傳檔案。請參考對應節點的開源專案庫的使用說明,確定模型上傳至哪個子目錄。例如,對於Checkpoint載入器節點,對應的模型應上傳至models/checkpoints

    2. 在WebUI頁面中,單擊重新整理按鈕,然後在Checkpoint載入器的下拉式清單中查看模型檔案是否載入成功。image

    3. 如果未載入成功,您需要單擊Process Restart,以重新載入模型檔案。image

      此過程將持續大約5分鐘,在此期間服務會自動重啟並恢複正常運行。重啟完成後,您可以訪問WebUI頁面,以確認模型檔案是否已成功載入。

  4. 載入ComfyUI外掛程式,支援以下兩種方式:

    • 自行上傳並載入ComfyUI外掛程式(推薦)。

      1. 請將ComfyUI第三方外掛程式上傳至掛載儲存的custom_nodes目錄。

      2. 在WebUI頁面中,單擊Process Restart

        此過程將持續大約5分鐘,在此期間服務會自動重啟並恢複正常運行。重啟完成後,您可以訪問WebUI頁面,以確認外掛程式是否已成功載入。

    • 在管理器中直接安裝外掛程式。由於需要從GitHub等平台拉取代碼,有可能存在網路連接失敗的問題。

      在WebUI頁面,單擊管理器,然後在ComfyUI管理器對話方塊中安裝節點。image