Todos os produtos
Search
Central de documentação

Platform For AI:Implante um serviço do ComfyUI para geração de vídeo com IA

Última atualização: Sep 22, 2026

O ComfyUI é uma ferramenta AIGC baseada em nós, construída sobre o Stable Diffusion e projetada para a geração de vídeos curtos e produção de animações. O PAI-EAS oferece suporte à implantação de serviços do ComfyUI baseada em cenários.

Antes de começar

Principais limitações e considerações

Leia esta seção antes de implantar o serviço para evitar desperdício de recursos e erros comuns.

  • Recursos de implantação: Recomendamos os tipos de instância com GPU GU30, NVIDIA A10 ou NVIDIA T4. O tipo de instância ml.gu7i.c16m60.1-gu30 oferece a melhor relação custo-benefício.

    Importante

    Cada instância do PAI-EAS executa um único processo do ComfyUI e oferece suporte a apenas uma GPU (instância única com GPU única ou várias instâncias com GPU única). A inferência paralela com várias GPUs não é suportada. Não selecione tipos de instância com várias GPUs (como 2×A10). Tipos de instância com várias GPUs desperdiçam recursos e não melhoram o desempenho de tarefas únicas.

  • Dimensionamento horizontal: Para aumentar a simultaneidade, use a API Edition e aumente o número de réplicas em vez de selecionar um tipo de instância com várias GPUs.

Escolha uma edição de implantação

Selecione uma edição que melhor se adapta ao seu caso de uso:

Edição

Caso de uso

Método de acesso

Principais recursos

Método de faturamento

Standard Edition

Desenvolvimento e teste para um único usuário

  • WebUI

  • Chamadas de API (síncronas, com depuração online)

  • Simultaneidade limitada

  • Implante uma única instância

Quando implantada em recursos públicos, a cobrança começa assim que a instância entra em execução, mesmo sem chamadas de API.

API Edition

Cargas de trabalho de produção com alta simultaneidade

Chamadas de API (assíncronas)

  • Baseada no serviço de fila do PAI-EAS

  • Requer recursos adicionais de CPU

  • Suporta balanceamento de carga

Cluster Edition WebUI

Equipes com vários usuários e treinamento

WebUI

  • Ambientes de usuário isolados

  • Recursos de GPU compartilhados

  • Maior consumo de recursos

Para mais informações sobre como isso funciona, consulte Como a Cluster Edition funciona

Para mais informações sobre faturamento, consulte Faturamento do Elastic Algorithm Service (EAS).

Nota

As chamadas de API síncronas e assíncronas diferem com base no uso do serviço de fila do PAI-EAS:

  • Chamada síncrona: Envia uma solicitação diretamente para a instância de inferência sem usar o serviço de fila do PAI-EAS.

  • Chamada assíncrona: Usa o serviço de fila do PAI-EAS. As solicitações são enviadas para uma fila de entrada e os resultados são entregues por meio de um modelo de assinatura.

Como o ComfyUI possui seu próprio sistema de fila assíncrona integrado, até mesmo as chamadas síncronas são processadas de forma assíncrona nos bastidores. Após enviar uma solicitação, o sistema retorna um Prompt ID que você usa para consultar o resultado da inferência.

Implante o serviço

Standard Edition, Cluster Edition e API Edition: Use a implantação de modelo baseada em cenários para uma experiência simplificada ou a implantação de modelo personalizado para opções de configuração avançadas.

Método 1: Implantação de modelo baseada em cenários (recomendado)

  1. Acesse o PAI console. Selecione uma região na parte superior da página. Em seguida, selecione o espaço de trabalho desejado e clique em Enter Elastic Algorithm Service (EAS).

  2. Na página Elastic Algorithm Service (EAS), clique em Deploy Service. Na seção Scenario-based Model Deployment, clique em AI Video Generation: ComfyUI-based Deployment.

  3. Configure os seguintes parâmetros principais e defina os outros parâmetros conforme necessário:

    • Version: Consulte Escolha uma edição de implantação.

    • Model Settings: Por exemplo, usando o Object Storage Service (OSS), selecione um bucket e um diretório. Após a implantação bem-sucedida, o sistema cria automaticamente os diretórios necessários do ComfyUI neste local.

      Importante

      O PAI-EAS não oferece suporte à instalação de nós personalizados (plugins) diretamente da rede por meio do ComfyUI Manager ou Git Clone. Para usar seus próprios modelos, instalar nós personalizados ou fazer chamadas de API, configure esta montagem de armazenamento.

    • Resource Configuration: Selecione um tipo de instância com GPU. Recomendamos GU30, NVIDIA A10 ou NVIDIA T4. O ml.gu7i.c16m60.1-gu30 oferece a melhor relação custo-benefício. Se este tipo de instância não estiver disponível, selecione ecs.gn6i-c16g1.4xlarge.

  4. Clique em Deploy. Aguarde aproximadamente 5 minutos. Quando o Service Status mudar para Running, a implantação estará concluída.

Método 2: Implantação de modelo personalizado

  1. Acesse o PAI console. Selecione uma região na parte superior da página. Em seguida, selecione o espaço de trabalho desejado e clique em Enter Elastic Algorithm Service (EAS).

  2. Clique em Deploy Service. Na seção Inference Service, clique em Custom Deployment na seção Custom Model Deployment.

  3. Configure os seguintes parâmetros principais:

    • Deployment Method: Selecione Image-based Deployment e selecione a caixa de seleção Enable Web App.

    • Image Configuration: Na lista Alibaba Cloud Image, selecione comfyui:2.3. Convenção de nomenclatura da versão da imagem: x.x para a Standard Edition, x.x-api para a API Edition e x.x-cluster para a Cluster Edition.

      Nota
      • Como as versões são atualizadas frequentemente, selecione a versão de imagem mais recente disponível ao implantar.

      • Para os casos de uso de cada edição, consulte Escolha uma edição de implantação.

    • Mount storage: Usando o Object Storage Service (OSS) como exemplo, selecione um bucket e um diretório. Após a implantação bem-sucedida, o sistema cria automaticamente os diretórios necessários do ComfyUI neste local. Certifique-se de que o bucket esteja na mesma região do serviço do PAI-EAS.

      Importante

      O PAI-EAS não oferece suporte à instalação de nós personalizados (plugins) diretamente da rede por meio do ComfyUI Manager ou Git Clone. Para usar seus próprios modelos, instalar nós personalizados ou fazer chamadas de API, configure esta montagem de armazenamento.

      • URI: Clique em image para selecionar um diretório OSS existente. Por exemplo, oss://bucket-test/data-oss/.

      • Mount Path: Defina como /mnt/data para montar o diretório OSS no caminho do contêiner /mnt/data.

    • Command to Run:

      • Após selecionar uma versão da imagem, o sistema configura o comando de execução automaticamente.

      • Se você configurou uma montagem de armazenamento, adicione o parâmetro --data-dir ao Command e certifique-se de que seu valor corresponda exatamente ao Mount Path. Por exemplo, python main.py --listen --port 8000 --data-dir /mnt/data --cache-root /stable-diffusion-cache.

    • Resource Type: Selecione Public Resources.

    • Deployment: Selecione um tipo de instância com GPU. Recomendamos GU30, NVIDIA A10 ou NVIDIA T4. O ml.gu7i.c16m60.1-gu30 oferece a melhor relação custo-benefício. Se este tipo de instância não estiver disponível, selecione ecs.gn6i-c16g1.4xlarge.

  4. Clique em Deploy. A implantação leva aproximadamente 5 minutos. Quando o Service Status mudar para Running, o serviço estará implantado.

Use o serviço através do WebUI

Importante

A Standard Edition e a Cluster Edition suportam acesso por meio do WebUI.

Abra a interface web

Clique no nome do serviço de destino para abrir a página Overview e, em seguida, clique em Web Application no canto superior direito.

Se a página demorar muito para carregar, consulte A página congela ou demora muito para carregar .

Use um template de workflow

O ComfyUI fornece templates pré-configurados para tarefas comuns.

  1. Selecione um template (por exemplo, Wan VACE Text to Video).

    Nota

    Os templates variam de acordo com as versões da imagem do ComfyUI. Se o template de exemplo não estiver disponível, selecione um template diferente ou carregue um arquivo de workflow do sistema de arquivos local.

  2. Após o carregamento do workflow, você pode ver erros sobre modelos ausentes. Ignore esses erros (recomendamos selecionar a opção para não mostrar esta mensagem novamente).

  3. Como os caminhos dos modelos podem ter mudado, executar o workflow diretamente pode produzir o seguinte erro:

    Prompt outputs failed validation:
    UNETLoader:
    - Value not in list: unet_name: 'wan2.1_vace_14B_fp16.safetensors' not in (list of length 200)
    LoraLoader:
    - Value not in list: lora_name: 'Wan21_CausVid_14B_T2V_lora_rank32.safetensors' not in (list of length 364)

    Na seção Load models here, selecione novamente wan2.1_vace_14B_fp16.safetensors no carregador UNET e Wan21_CausVid_14B_T2V_lora_rank32.safetensors no carregador LoRA.

  4. Após a execução bem-sucedida do workflow, o vídeo gerado aparece na seção Save Video. Salve o vídeo se necessário. image

Use modelos e nós personalizados

  1. Certifique-se de que o serviço tenha uma montagem de armazenamento configurada. Após a implantação do serviço, o sistema cria automaticamente a seguinte estrutura de diretórios no armazenamento OSS ou NAS montado.

    data-oss/
    ├── custom_nodes/     # Custom node files (ComfyUI plugins)
    ├── models/           # Model files
    ├── input/
    ├── output/
    ├── unet/
    └── temp/
  2. Faça o upload de arquivos de modelo ou nó. Por exemplo, use o OSS e faça o upload de arquivos para o OSS pelo console. Para arquivos grandes, consulte Como faço o upload de objetos grandes para o OSS?.

    • Upload de arquivos de modelo: Verifique a documentação do repositório source para o nó que usa o modelo e faça o upload do modelo para o subdiretório correspondente em models. Por exemplo:

      • Carregador de Checkpoint: Faça o upload dos modelos para models/checkpoints.

      • Carregador de modelo de estilo: Faça o upload dos modelos para models/styles.

    • Upload de arquivos de nó: Faça o upload de nós personalizados para o diretório custom_nodes no armazenamento montado.

  3. Carregue o novo conteúdo.

    • Modelos: Na parte superior da página, clique em PAICustom > Load New Models. Se o modelo ainda não aparecer, clique em PAICustom > Restart Process. Após a reinicialização do processo, atualize a página do navegador.

    • Nós: Clique em Restart Process. Após a reinicialização do processo, atualize a página do navegador.

Chame o serviço através da API

Chame o serviço do ComfyUI por meio de sua API para integrá-lo aos seus aplicativos para geração automatizada de conteúdo. O workflow principal é: envie um JSON de workflow > obtenha um ID de tarefa > use o ID da tarefa para recuperar o resultado da geração.

Importante
  • Suporte de edição: A Standard Edition suporta apenas chamadas síncronas e fornece depuração online. A API Edition suporta apenas chamadas assíncronas e somente o endpoint /prompt.

  • Recuperação da saída gerada: Imagens e vídeos gerados são armazenados no diretório output montado. As respostas da API retornam nomes de arquivos e subdiretórios em vez dos próprios arquivos. Para o OSS, construa o caminho completo do arquivo para baixá-los. Para mais informações, consulte Baixe arquivos do OSS usando o Alibaba Cloud SDK.

Prepare o corpo da solicitação da API

O formato do corpo da solicitação depende do método de chamada:

  • Chamada síncrona: Envolva o conteúdo JSON do workflow dentro de um par chave-valor prompt.

  • Chamada assíncrona: O corpo da solicitação é o próprio conteúdo JSON do workflow.

Nota

Como baixar um workflow: No canto superior esquerdo da página do ComfyUI, clique em image > File > Export (API).

Como o workflow Wan VACE Text to Video acima consome muito tempo, fornecemos o seguinte workflow de teste para facilitar os testes (cada execução leva aproximadamente 3 minutos).

Clique para ver o exemplo de corpo de solicitação do workflow de teste

Chamada síncrona

{
    "prompt": {
        "3": {
            "inputs": {
                "seed": 423988542100860,
                "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": "KSampler"
            }
        },
        "4": {
            "inputs": {
                "ckpt_name": "LandscapeBING_v10.safetensors"
            },
            "class_type": "CheckpointLoaderSimple",
            "_meta": {
                "title": "Checkpoint Loader (Simple)"
            }
        },
        "5": {
            "inputs": {
                "width": 720,
                "height": 1280,
                "batch_size": 1
            },
            "class_type": "EmptyLatentImage",
            "_meta": {
                "title": "Empty Latent"
            }
        },
        "6": {
            "inputs": {
                "text": "Rocket takes off from the ground, fire,sky, airplane",
                "speak_and_recognation": {
                    "__value__": [
                        false,
                        true
                    ]
                },
                "clip": [
                    "4",
                    1
                ]
            },
            "class_type": "CLIPTextEncode",
            "_meta": {
                "title": "CLIP Text Encoder"
            }
        },
        "7": {
            "inputs": {
                "text": "",
                "speak_and_recognation": {
                    "__value__": [
                        false,
                        true
                    ]
                },
                "clip": [
                    "4",
                    1
                ]
            },
            "class_type": "CLIPTextEncode",
            "_meta": {
                "title": "CLIP Text Encoder"
            }
        },
        "8": {
            "inputs": {
                "samples": [
                    "3",
                    0
                ],
                "vae": [
                    "4",
                    2
                ]
            },
            "class_type": "VAEDecode",
            "_meta": {
                "title": "VAE Decode"
            }
        },
        "9": {
            "inputs": {
                "filename_prefix": "ComfyUI",
                "images": [
                    "8",
                    0
                ]
            },
            "class_type": "SaveImage",
            "_meta": {
                "title": "Save Image"
            }
        },
        "13": {
            "inputs": {
                "seed": 788620942678235,
                "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": "KSampler"
            }
        },
        "14": {
            "inputs": {
                "samples": [
                    "13",
                    0
                ],
                "vae": [
                    "18",
                    2
                ]
            },
            "class_type": "VAEDecode",
            "_meta": {
                "title": "VAE Decode"
            }
        },
        "15": {
            "inputs": {
                "filename_prefix": "ComfyUI",
                "fps": 10.000000000000002,
                "lossless": false,
                "quality": 85,
                "method": "default",
                "images": [
                    "14",
                    0
                ]
            },
            "class_type": "SaveAnimatedWEBP",
            "_meta": {
                "title": "Save WEBP"
            }
        },
        "16": {
            "inputs": {
                "width": 512,
                "height": 768,
                "video_frames": 35,
                "motion_bucket_id": 140,
                "fps": 15,
                "augmentation_level": 0.15000000000000002,
                "clip_vision": [
                    "18",
                    1
                ],
                "init_image": [
                    "8",
                    0
                ],
                "vae": [
                    "18",
                    2
                ]
            },
            "class_type": "SVD_img2vid_Conditioning",
            "_meta": {
                "title": "SVD_Image to Video_Conditioning"
            }
        },
        "17": {
            "inputs": {
                "min_cfg": 1,
                "model": [
                    "18",
                    0
                ]
            },
            "class_type": "VideoLinearCFGGuidance",
            "_meta": {
                "title": "Linear CFG Guidance"
            }
        },
        "18": {
            "inputs": {
                "ckpt_name": "svd_xt.safetensors"
            },
            "class_type": "ImageOnlyCheckpointLoader",
            "_meta": {
                "title": "Checkpoint Loader (Image Only)"
            }
        },
        "19": {
            "inputs": {
                "frame_rate": 10,
                "loop_count": 0,
                "filename_prefix": "comfyUI",
                "format": "video/h264-mp4",
                "pix_fmt": "yuv420p",
                "crf": 20,
                "save_metadata": true,
                "trim_to_audio": false,
                "pingpong": false,
                "save_output": true,
                "images": [
                    "14",
                    0
                ]
            },
            "class_type": "VHS_VideoCombine",
            "_meta": {
                "title": "Combine to Video"
            }
        }
    }
}

Chamada assíncrona

{
    "3": {
        "inputs": {
            "seed": 423988542100860,
            "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": "KSampler"
        }
    },
    "4": {
        "inputs": {
            "ckpt_name": "LandscapeBING_v10.safetensors"
        },
        "class_type": "CheckpointLoaderSimple",
        "_meta": {
            "title": "Checkpoint Loader (Simple)"
        }
    },
    "5": {
        "inputs": {
            "width": 720,
            "height": 1280,
            "batch_size": 1
        },
        "class_type": "EmptyLatentImage",
        "_meta": {
            "title": "Empty Latent"
        }
    },
    "6": {
        "inputs": {
            "text": "Rocket takes off from the ground, fire,sky, airplane",
            "speak_and_recognation": {
                "__value__": [
                    false,
                    true
                ]
            },
            "clip": [
                "4",
                1
            ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
            "title": "CLIP Text Encoder"
        }
    },
    "7": {
        "inputs": {
            "text": "",
            "speak_and_recognation": {
                "__value__": [
                    false,
                    true
                ]
            },
            "clip": [
                "4",
                1
            ]
        },
        "class_type": "CLIPTextEncode",
        "_meta": {
            "title": "CLIP Text Encoder"
        }
    },
    "8": {
        "inputs": {
            "samples": [
                "3",
                0
            ],
            "vae": [
                "4",
                2
            ]
        },
        "class_type": "VAEDecode",
        "_meta": {
            "title": "VAE Decode"
        }
    },
    "9": {
        "inputs": {
            "filename_prefix": "ComfyUI",
            "images": [
                "8",
                0
            ]
        },
        "class_type": "SaveImage",
        "_meta": {
            "title": "Save Image"
        }
    },
    "13": {
        "inputs": {
            "seed": 788620942678235,
            "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": "KSampler"
        }
    },
    "14": {
        "inputs": {
            "samples": [
                "13",
                0
            ],
            "vae": [
                "18",
                2
            ]
        },
        "class_type": "VAEDecode",
        "_meta": {
            "title": "VAE Decode"
        }
    },
    "15": {
        "inputs": {
            "filename_prefix": "ComfyUI",
            "fps": 10.000000000000002,
            "lossless": false,
            "quality": 85,
            "method": "default",
            "images": [
                "14",
                0
            ]
        },
        "class_type": "SaveAnimatedWEBP",
        "_meta": {
            "title": "Save WEBP"
        }
    },
    "16": {
        "inputs": {
            "width": 512,
            "height": 768,
            "video_frames": 35,
            "motion_bucket_id": 140,
            "fps": 15,
            "augmentation_level": 0.15000000000000002,
            "clip_vision": [
                "18",
                1
            ],
            "init_image": [
                "8",
                0
            ],
            "vae": [
                "18",
                2
            ]
        },
        "class_type": "SVD_img2vid_Conditioning",
        "_meta": {
            "title": "SVD_Image to Video_Conditioning"
        }
    },
    "17": {
        "inputs": {
            "min_cfg": 1,
            "model": [
                "18",
                0
            ]
        },
        "class_type": "VideoLinearCFGGuidance",
        "_meta": {
            "title": "Linear CFG Guidance"
        }
    },
    "18": {
        "inputs": {
            "ckpt_name": "svd_xt.safetensors"
        },
        "class_type": "ImageOnlyCheckpointLoader",
        "_meta": {
            "title": "Checkpoint Loader (Image Only)"
        }
    },
    "19": {
        "inputs": {
            "frame_rate": 10,
            "loop_count": 0,
            "filename_prefix": "comfyUI",
            "format": "video/h264-mp4",
            "pix_fmt": "yuv420p",
            "crf": 20,
            "save_metadata": true,
            "trim_to_audio": false,
            "pingpong": false,
            "save_output": true,
            "images": [
                "14",
                0
            ]
        },
        "class_type": "VHS_VideoCombine",
        "_meta": {
            "title": "Combine to Video"
        }
    }
}

Chamadas síncronas

As chamadas síncronas enviam solicitações diretamente para a instância do serviço do PAI-EAS. Este método é adequado para desenvolvimento, testes e cenários de baixa simultaneidade.

Depuração online

A depuração online permite testar chamadas de API diretamente no console sem escrever código. Isso é útil para validar rapidamente as configurações do workflow.

Na página Elastic Algorithm Service (EAS), clique em Online Debugging na coluna Actions do serviço de destino para abrir a página de depuração online.

  1. Envie uma solicitação POST para obter o Prompt ID.

    1. Na seção Request Parameter Online Tuning da página de depuração, insira o corpo da solicitação preparado no campo Body e anexe /prompt à URL da solicitação.

    2. Clique em Send Request para visualizar a resposta na seção Debugging Information.

      Após o envio da solicitação, o painel Debug Info à direita exibe a resposta: o Status Code é 200, e o Response Body contém os campos prompt_id (por exemplo, 130bc***c8c-3a1359a04e5d), number e node_errors. Anote o valor do prompt_id para usar na próxima etapa.

  2. Envie uma solicitação GET para recuperar o resultado da inferência usando o Prompt ID.

    1. Na seção Request Parameter Online Tuning, altere o método de solicitação para GET e insira /history/<prompt id> no campo URL.

      Substitua <prompt id> pelo Prompt ID obtido na Etapa 1.

    2. Clique em Send Request para recuperar o resultado da inferência.

      Encontre a saída gerada no diretório output do armazenamento montado.

Chamadas baseadas em código

Chame a API por meio de código para integrar o ComfyUI aos seus aplicativos. O workflow é o mesmo da depuração online: primeiro obtenha o Prompt ID e, em seguida, consulte o resultado da inferência.

  1. Visualize as informações de chamada.

    1. Na aba Inference Services, clique no nome do serviço de destino para abrir a página Overview.

    2. Na seção Basic Information, clique em View Call Information.

    3. No painel Call Information, copie o endpoint de acesso e o token:

      O painel Call Information possui duas abas: Shared Gateway e VPC Direct Connection. O endpoint público, o endpoint VPC e o token estão na aba Shared Gateway.

      • Selecione o Public Endpoint ou VPC Endpoint conforme necessário.

      • Os exemplos abaixo usam o endpoint de acesso e o token correspondentes.

  2. Envie uma solicitação para obter o Prompt ID.

    • Método HTTP: POST.

    • URL da solicitação: <EAS_ENDPOINT>/prompt. Se o <EAS_ENDPOINT> terminar com /, remova a barra final. A URL final se parece com http://comfyui****.175805416243****.cn-beijing.pai-eas.aliyuncs.com/prompt.

    • Cabeçalhos da solicitação:

      Cabeçalho

      Valor

      Descrição

      Authorization

      <EAS_TOKEN>

      O token de autorização.

      Content-Type

      application/json

      Especifica o formato do corpo da solicitação.

    Exemplos de código:

    cURL

    curl --location --request POST '<EAS_ENDPOINT>/prompt' \
    --header 'Authorization: <EAS_TOKEN>' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "prompt":
        ...omitted
    }'

    O parâmetro --data-raw contém o corpo da solicitação.

    Python

    Exemplo de código:

    import requests
    import json
    
    # Replace <EAS_ENDPOINT> and <EAS_TOKEN> with the endpoint and token from Step 1.
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    # Request body: set the "prompt" value to the workflow JSON content.
    payload = """{
        "prompt":
        ...omitted
    }"""
    payload = json.loads(payload)
    
    session = requests.session()
    session.headers.update({"Authorization": token})
      
    response = session.post(url=f'{service_url}/prompt', json=payload)
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    Exemplo de resposta:

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

    Extraia o Prompt ID da resposta.

  3. Recupere o resultado da inferência.

    • Método HTTP: GET

    • URL da solicitação: <EAS_ENDPOINT>/history/<prompt_id>. Substitua <prompt_id> pelo prompt_id obtido na Etapa 1.

    • Cabeçalhos da solicitação:

      Cabeçalho

      Valor

      Descrição

      Authorization

      <EAS_TOKEN>

      O token de autorização obtido na Etapa 1.

    Exemplos de código:

    cURL

    curl --location --request GET '<EAS_ENDPOINT>/history/<prompt_id>' \
         --header 'Authorization: <EAS_TOKEN>'

    Python

    import requests
    
    # Replace <EAS_ENDPOINT> and <EAS_TOKEN> with the endpoint and token from Step 1.
    # Replace <prompt_id> with the prompt_id obtained in Step 2.
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    prompt_id = "<prompt_id>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    session = requests.session()
    session.headers.update({"Authorization": token})
    
    response = session.get(url=f'{service_url}/history/{prompt_id}')
    
    if response.status_code != 200:
        raise Exception(response.content)
    
    data = response.json()
    print(data)

    Exemplo de resposta:

    Clique para ver o exemplo de resposta

    {
        "130bcd6b-5bb5-496c-9c8c-3a1359a0****": {
            "prompt": "...(omitted)",
            "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": "...(omitted)"
            }
        }
    }

    O campo outputs na resposta contém as imagens geradas (PNG/WebP) e vídeos (MP4). Localize esses arquivos no diretório output do armazenamento montado pesquisando pelos nomes dos arquivos.

Chamadas assíncronas

As chamadas assíncronas são projetadas para cargas de trabalho de produção com alta simultaneidade. Este método usa o serviço de fila do PAI-EAS para balanceamento de carga. As solicitações retornam imediatamente e você recupera os resultados assinando a fila de resultados.

Importante

As chamadas assíncronas suportam apenas o endpoint api_prompt. O parâmetro task_id é um identificador crítico para corresponder solicitações a resultados. Atribua um valor exclusivo a cada solicitação para correlacioná-la com o resultado da fila correspondente. O caminho da solicitação é:

{service_url}/api_prompt?task_id={assign a unique value}

  1. Visualize as informações de chamada.

    Na aba Inference Services, clique no nome do serviço de destino para abrir a página Overview. Na seção Basic Information, clique em View Call Information. Na aba Asynchronous Call da caixa de diálogo Call Information, visualize o endpoint de acesso do serviço e o token.

    image

    As seções a seguir usam <EAS_ENDPOINT> para se referir ao Internet Input Endpoint (se o chamador estiver na mesma VPC que o PAI-EAS, use o VPC Input Endpoint), e <EAS_TOKEN> para se referir ao Token.

  2. Envie uma solicitação.

    Exemplo de código:

    import requests
    import json
    
    service_url = "<EAS_ENDPOINT>"
    token = "<EAS_TOKEN>"
    
    if service_url[-1] == "/":
        service_url = service_url[:-1]
    
    session = requests.session()
    session.headers.update({"Authorization":token})
    
    # Request body - workflow JSON, no need to wrap with "prompt".
    # Use triple quotes to define it as a multi-line string, otherwise you need to capitalize the boolean values (true and false) in the workflow JSON.
    payload = """{
        '3':
        ...omitted
      }
      """
    payload = json.loads(payload)
    
    for i in range(5):
      # task_id is the key identifier for matching requests and results. Assign a unique value to each request to correlate with the queue results later.
      response = session.post(url=f'{service_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}")
  3. Assine os resultados.

    1. Instale o SDK eas_prediction.

      pip install eas_prediction  --user
    2. Execute o seguinte código para recuperar o resultado.

      from eas_prediction import QueueClient
      from urllib.parse import urlparse, urlunparse
      
      service_url     = "<EAS_ENDPOINT>"
      token           = "<EAS_TOKEN>"
      
      # Parse service URL
      def parse_service_url(service_url):
          parsed = urlparse(service_url)
          service_domain = f"{parsed.scheme}://{parsed.netloc}"
          path_parts = [p for p in parsed.path.strip('/').split('/') if p]
          service_name = path_parts[-1]
          return service_domain, service_name
      
      service_domain, service_name = parse_service_url(service_url)
      print(f"service_domain: {service_domain}, service_name: {service_name}.")
      
      # Create result queue client
      sink_queue = QueueClient(service_domain, f'{service_name}/sink')
      sink_queue.set_token(token)
      sink_queue.init()
      
      # Listen for results
      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}')
          # Commit acknowledgment
          sink_queue.commit(x.index)

      Exemplo de resposta:

      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"}]}}]'

      Encontre os arquivos de resultado da inferência no diretório output do armazenamento montado.

Perguntas frequentes

Abaixo estão os problemas e as soluções comuns para a implantação e o uso do ComfyUI.

Problemas de implantação

P: O serviço permanece em estado de espera ou o ComfyUI não consegue gerar saída

Isso geralmente é causado por recursos insuficientes. Verifique se a imagem do serviço e o tipo de instância estão configurados corretamente. Recomendamos os tipos de instância com GPU GU30, NVIDIA A10 ou NVIDIA T4. O tipo de instância ml.gu7i.c16m60.1-gu30 oferece a melhor relação custo-benefício.

Problemas de modelos e nós

P: O WebUI exibe um erro "Missing Model"

Problema: Uma caixa de diálogo intitulada Missing Model indica que o modelo em checkpoints/v1-5-pruned-emaonly-fp16.safetensors está ausente, e um aviso vermelho à esquerda mostra Invalid directory specified (does this require custom nodes?).

Solução: Você pode ignorar este erro com segurança. Esta verificação de validação não se aplica ao ComfyUI implantado no PAI. Consulte os erros reais de tempo de execução.

Recomendamos selecionar a opção para não mostrar esta mensagem novamente ou desativar a validação de modelo nas Settings.

P: Fiz o upload de um novo modelo, mas não consigo encontrá-lo

Solução:

  1. Clique em PAICustom e selecione Load New Models.

  2. Se o modelo ainda não aparecer, clique em Restart Process.

P: O carregador de modelo mostra "undefined"

Primeiro, verifique se o arquivo do modelo está colocado no diretório correto, conforme exigido pelo nó do carregador de modelo.

Se você fez o upload do modelo após a implantação, reinicie o serviço.

P: Não consigo encontrar um nó

P: O ComfyUI Manager falha ao baixar modelos ou instalar nós

Não recomendamos o uso do ComfyUI Manager com o ComfyUI implantado no PAI-EAS. O ComfyUI Manager baixa modelos e códigos de plugins de fontes externas, como o GitHub, o que frequentemente falha devido a restrições de rede.

Em vez disso, faça o upload de arquivos de modelo e nó para o armazenamento montado. Para mais informações, consulte Use modelos personalizados e instale nós (plugins do ComfyUI).

P: Como visualizar arquivos de modelo e nós disponíveis (plugins do ComfyUI)

  • Arquivos de modelo: Verifique a partir do nó do carregador de modelo correspondente. Por exemplo, visualize os arquivos de modelo disponíveis na lista suspensa do carregador de Checkpoint.

  • Nós: Clique com o botão direito na página do WebUI e clique em Add Node no menu de contexto para visualizar todos os plugins do ComfyUI instalados.

Erros de tempo de execução

P: A página congela ou demora muito para carregar

  • Atualize a página, limpe o cache do navegador ou acesse o serviço no modo de navegação anônima/privada.

  • Se o armazenamento estiver montado, exclua os arquivos nos diretórios input/, output/ e temp/.

  • Tente reiniciar o serviço.

P: O processo reinicia no meio do workflow

Se os logs da instância contiverem run.sh: line 54: 531285 Killed python -u main_run.py "$@", o processo foi encerrado devido a uma condição de falta de memória (OOM). O processo reinicia automaticamente após um evento OOM.

P: RuntimeError: CUDA error: out of memory

Isso significa que a memória da GPU está esgotada. Para modelos de imagem, reduza a resolução da imagem ou o tamanho do lote. Para modelos de vídeo, reduza a contagem de quadros ou a resolução.

P: A chamada de API retorna "URL not found" ou "404 page not found"

  1. Verifique se a URL do endpoint da API está completa. Para chamadas síncronas, anexe o caminho /prompt.

Outros

P: Desempenho de aceleração do xFormers

O xFormers é uma ferramenta de aceleração de código aberto baseada em Transformer que reduz o tempo de geração de imagens e vídeos e o uso de memória da GPU.

As imagens do ComfyUI têm a aceleração xFormers ativada por padrão. O efeito de aceleração depende do tamanho do workflow, com as melhorias mais notáveis em operações intensivas de GPU, especialmente com GPUs NVIDIA.

Próximos passos

Após implantar o serviço do ComfyUI, você pode:

  • Monitoramento do serviço: Configure monitoramento e alertas para rastrear a integridade do serviço em tempo real. Para mais informações, consulte Visualize métricas de monitoramento do serviço.

  • Auto Scaling: Configure políticas de Auto Scaling com base na carga de trabalho para otimizar o uso de recursos e os custos. Para mais informações, consulte Auto Scaling.

  • Otimização de custos: Entenda os métodos de faturamento do PAI-EAS e escolha o tipo de instância e a edição de implantação adequados para reduzir custos. Para mais informações, consulte Visão geral do faturamento.

Referência

Instale pacotes Python

Instale um pacote .whl executando um comando

  1. Certifique-se de que o serviço tenha uma montagem de armazenamento configurada. Por exemplo, suponha que o caminho do OSS (URI) seja oss://examplebucket/comfyui/ e o caminho de montagem seja /mnt/data/.

  2. Faça o upload do arquivo .whl para o diretório oss://examplebucket/comfyui/models/whl. Se o diretório whl não existir, crie-o primeiro.

  3. Atualize o Command to Run do serviço: Adicione pip install /mnt/data/models/whl/xxx.whl no início do comando de execução. Neste comando, /mnt/data é o caminho de montagem do OSS, e xxx.whl é o nome do pacote .whl.

  4. Reinicie o serviço.

Instale pacotes através da configuração de biblioteca de terceiros

  1. Na página de detalhes do serviço, clique em Update no canto superior direito.

  2. Se o serviço foi implantado através da implantação de modelo baseada em cenários, mude para a implantação de modelo personalizado.

  3. Na seção Environment Information, configure as dependências na área Third-party Library Configuration.

  4. Clique em Update na parte inferior da página para aplicar a atualização.

Atualize a versão da imagem

Se o serviço tiver armazenamento OSS ou NAS montado, os modelos personalizados são armazenados no armazenamento OSS ou NAS e não são afetados por uma atualização de versão da imagem.

  1. Na página de detalhes do serviço, clique em Update no canto superior direito.

  2. Se o serviço foi implantado através da implantação de modelo baseada em cenários, mude para a implantação de modelo personalizado.

  3. Na seção Service Configuration, edite a configuração JSON e atualize o campo image em containers para a versão desejada (por exemplo, altere 1.9 para a versão de destino).

  4. Clique em Update Directly.

Gere uma URL sem necessidade de login com um período de validade personalizado

Use uma API para gerar uma URL de acesso web sem necessidade de login com um período de validade especificado.

  1. Acesse a página da API DescribeServiceSignedUrl.

  2. Selecione o endpoint do serviço.

  3. Defina os seguintes parâmetros:

    • ClusterId (Region) e ServiceName: Insira a região e o nome do serviço.

      Na página de visão geral do serviço EAS, o campo Service Name mostra o valor de ServiceName. A região está incorporada no endereço da imagem do nó (por exemplo, cn-hangzhou corresponde ao valor da região ClusterId). Clique em View Web Application no canto superior direito para abrir o serviço.

    • Type (Page type): Selecione webview na lista suspensa.

    • Expire (Expiration time): Insira um número inteiro em segundos. O valor máximo é 43200 segundos (12 horas). Defina conforme necessário.

    • Internal (VPC access): Defina como false (acesso público) ou true (acesso VPC).

  4. Clique em Call API. O campo SignedUrl na resposta contém a URL de acesso web sem necessidade de login.

Altere o idioma padrão do WebUI

  1. Na página do WebUI, clique no botão Settings no canto inferior esquerdo.

  2. Configure o idioma nos dois locais a seguir e, em seguida, atualize e recarregue a página.

    • Comfy > Locale.

    • Language > Locale.

Apêndice

Como a Cluster Edition funciona

O diagrama a seguir mostra a arquitetura:

image

A Cluster Edition é projetada para cenários com vários usuários. Ela introduz uma camada de proxy que desacopla os clientes das instâncias de inferência de backend.

  • Cada usuário possui um ambiente de backend e um diretório de trabalho independentes, mas compartilha o pool de instâncias de inferência de GPU do backend.

  • Quando um usuário envia uma solicitação de inferência, o proxy seleciona uma instância disponível no pool para lidar com a solicitação.

Este mecanismo de multiplexação por tempo isola os ambientes dos usuários, ao mesmo tempo em que maximiza a utilização dos recursos de GPU e reduz os custos de inferência em cenários com vários usuários.