Архив рубрики ~Полезное~

Как добавить генерацию изображений в ваш продукт без DevOps-настроек: пошаговый гайд

Как добавить генерацию изображений в ваш продукт без DevOps-настроек: пошаговый гайд
Как добавить генерацию изображений в ваш продукт без DevOps-настроек: пошаговый гайд

Уверен, у многих есть хотя бы один пет-проект, перспективный B2B SaaS, CRM, другими словами — сервис, который когда-нибудь обязательно выстрелит. В процессе разработки у многих стильных, модных и молодежных проектов рано или поздно появляется вопрос: «А что если добавить генерацию изображений или видео?». На первый взгляд все выглядит просто: берем Comfy UI, собираем workflow, выбираем модели — и готово. Локально все действительно можно собрать за один вечер.

Проблемы начинаются ровно в тот момент, когда решением пользуетесь не только вы. Первый пользователь ждет 20-30 секунд, второй стоит в очереди, остальные уже получили свои тайм-ауты, а вы внезапно ловите себя на том, что читаете документацию про Kubernetes и настраиваете балансировку запросов, вместо того, чтобы сконцентрироваться на развитии продукта… И вот тут уже развертывание Comfy UI кажется не настолько простой задачей как в начале.

Если вам знакома эта ситуация или вы только планируете добавить генеративный ИИ в свое приложение, эта статья для вас. Покажу, как превратить Comfy UI workflow в API, которым можно пользоваться из любого приложения, не поднимая инфраструктуру и не привлекая внимания санитаров.

Давайте сразу оговорюсь: это будет разбор типичной проблемы, с которой сталкиваются почти все, кто пытается перенести генеративный ИИ из ноутбука в продакшен. Сервис, который я продемонстрирую, лишь один из способов с этим разобраться. Пользоваться не принуждаем, просто показываем, что айсберг добавления асинхронной генерации в продукт под водой намного больше чем на поверхности, и в некоторых случаях формат управляемого сервиса оправдан. В каких случаях? Давайте по порядку.

За последние пару лет вокруг Comfy UI сформировался практически стандарт де-факто для построения сложных генеративных пайплайнов. Но сам по себе Comfy не решает вопросы масштабирования, очередей, хранения результатов и асинхронной обработки запросов — все это приходится выстраивать самостоятельно. В этой статье покажу, как можно решить вопрос и не мутировать в DevOps’а по дороге.

Почему Comfy UI — это только 20% решения

ComfyUI отлично решает задачу построения генеративного пайплайна. Можно комбинировать модели, подключать LoRA, ControlNet, собственные Python-ноды, генерировать изображения, видео, аудио и вообще строить довольно сложные workflow без написания большого количества кода. Но ComfyUI именно строит пайплайн. Он не отвечает на вопрос, как этот пайплайн превратить в сервис, которым одновременно пользуются десятки или сотни людей. А это уже совсем другой класс задач.

Представьте, что вы решили добавить генерацию изображений в свое приложение. Пока пользователей мало, все выглядит идеально.

Вы делаете запрос, Comfy выплевывает PNG-шку
Вы делаете запрос, Comfy выплевывает PNG-шку

Проходит неделя, о вашем сервисе узнают, приходит несколько десятков пользователей и картина резко меняется. Первый запрос занимает 30 секунд. Второй начинает ждать. Третий получает HTTP-timeout. У четвертого пользователя ничего не происходит. В этот момент становится понятно, что проблема не в Comfy UI runtime, а в инфраструктуре вокруг него.

Задача

Реальность

Запустить Comfy UI

15 минут

Собрать workflow

Пара часов

Интегрироваться с API

Несколько часов

Хранить результаты

День–два на Object Storage и раздачу файлов клиенту

Не терять запросы

Еще несколько дней: очередь, статусы задач, retry и защита от дублей

Масштабировать при нагрузке

От нескольких дней до недель: воркеры, балансировка, несколько GPU-инстансов

Следить, чтобы не упало

Постоянно: мониторинг, алерты, разбор падений GPU и переполнения очереди

Почему генерация изображений и видео — это не HTTP-запрос

Большинство веб-приложений работают по очень простой схеме:

Отправил запрос, немного подождал, получил ответ
Отправил запрос, немного подождал, получил ответ

И это быстро, синхронно, предсказуемо. Однако с генеративными пайплайнами это не работает. Если обернуть ComfyUI в синхронный HTTP-endpoint («отправил запрос — жду картинку в ответе»), прокси или балансировщик оборвет соединение через свой лимит — часто 30–60 секунд, иногда 2 минуты. ComfyUI при этом продолжит работу: задача останется в очереди, а клиент уже получит ошибку. Даже относительно простая генерация может занимать десятки секунд, генерация сложных видео — минуты. Держать HTTP-соединение открытым все время не выйдет, поэтому при использовании схемы выше нас ждут:

  • таймауты;

  • повторные запросы от клиента;

  • потерянные задачи и результаты;

  • невозможность нормально масштабироваться и обрабатывать запросы в параллель.

Именно поэтому почти все продакшен-системы, генерирующие какие-то комплексные артефакты, используют другую архитектуру:

9c1e210b1f6371b9b10d8311b763dbe7

Клиент не ждет окончания генерации. Он получает id задачи и продолжает заниматься своими делами. Сейчас так работают практически все крупные сервисы генерации изображений.

Что обычно приходится строить самостоятельно

На этом этапе многие понимают, что сам Comfy UI занимает лишь небольшую часть всей системы. А вот вокруг нее уже приходится обустраивать целый инфраструктурный хоровод, чтобы она хоть как-нибудь заработала.

Задача

Если делать самостоятельно

Очередь задач

Kafka, Redis, Rabbit MQ

API

Собственный Backend

Масштабирование

Собственный Kubernetes-кластер

Хранение результатов

S3 Object Storage

Мониторинг

Prometheus + Grafana

Задачи и их статусы

Отдельная БД

Нельзя сказать, что это сложно. Все это можно собрать самостоятельно. Вопрос только в том, сколько времени вы хотите потратить именно на инфраструктуру и готовы ли вообще погружаться в хитросплетения. Если ваша цель — разобраться в Kubernetes, это отличный путь. Если же задача — как можно быстрее встроить генерацию изображений в существующий продукт, хочется, чтобы все перечисленное уже было готово.

Именно здесь иногда имеет смысл использовать управляемый сервис.

Что дает Evolution ML Inference

В нашем случае роль такой инфраструктуры выполняет сервис Evolution ML Inference. Если коротко — он берет на себя все, что находится вокруг самого workflow.

Получается примерно такая картина:

13fbfa660dcf3e02396a0333dd1975a7

То есть вам по-прежнему нужно собрать workflow. Но уже не нужно отдельно думать о том:

  • как запускать GPU;

  • как масштабировать обработчики;

  • где хранить результаты;

  • как отдавать статусы задач;

  • как организовать очередь.

Вместо этого можно дальше тестировать продуктовые гипотезы и улучшать уже свой продукт, а не навыки администрирования кластеров Kubernetes.

Давайте запустим Comfy UI

Предположим, что workflow уже работает локально. Следующая задача — превратить его в API. Процесс состоит буквально из нескольких шагов:

Шаг 1. Экспортируем workflow

В ComfyUI экспортируем готовый workflow через Export API. Получаем JSON-файл, который будет использоваться при создании ComfyRun.

Жмем на лого, выбираем экспорт API
Жмем на лого, выбираем экспорт API

Шаг 2. Создаем ComfyRun

Загружаем экспортированный workflow:

Чтобы попасть сюда достаточно зарегаться на платформе, зайти в ЛК и подключить ML Inference в списке продуктов
Чтобы попасть сюда достаточно зарегаться на платформе, зайти в ЛК и подключить ML Inference в списке продуктов

В этом же окне выбираем конфигурацию GPU для запуска.

Шаг 3. Настраиваем масштабирование

На этом шаге задается политика автоматического масштабирования сервиса — определяется, сколько реплик инференса система может запускать при росте нагрузки.

Для сценариев с асинхронной обработкой задач используется масштабирование по длине очереди. В параметре «Длина очереди» указывается, какое количество задач может одновременно приходиться на одну реплику инференса.

4a74222ed8836ccf7ca1a2cc1442c6e0

Шаг 4. Настраиваем Object Storage

Теперь выбираем S3 Bucket в Object Storage Cloud.ru. В него будут автоматически сохраняться результаты генерации. Здесь же можно указать токен Hugging Face для загрузки моделей, используемых внутри workflow.

2d7bdeca6600cdc43ed40b27ba1cded2

Шаг 5.  Связываем параметры API с workflow

Если локально вы вводили prompt вручную, теперь необходимо указать, какие параметры будут приходить через API, являющееся OpenAI-совместимым.

Например:

  • prompt;

  • negative_prompt;

  • seed;

  • width;

  • height.

После этого остается указать имя выходного файла и сопоставить используемые модели с репозиториями Hugging Face.

0b4d041fb09077bc4764b15086b6a4e4

Также для корректной работы нужно указать в output-узлах поле, задающее префикс выходного файла:

6294ab65fcd46d76e8d7338e1dc335aa

Далее во всех узлах, использующих модели, необходимо их добавить из репозиториев Hugging Face.

16ed4e9d93297a75e6299e236cae48af

После успешной конфигурации пайплайна нажимаем Создать.

Готово. Теперь ваш workflow превратился в полноценный сервис.

Работа с API

После создания ComfyRun автоматически становится доступна OpenAPI-спецификация. Можно открыть Scalar Docs и сразу протестировать сервис.

Работа с API выглядит максимально привычно.

0beacedf1b1bec2fe707fa91427f270f

Далее отправляем запрос на генерацию, после чего получаем task_id отправленной задачи:

dcb762d4805834176637e8b1c0694c03

После чего по этому task_id можно получить статус и результат генерации:

99e4700a7efe47719e19d516a4f2aa7f

А теперь о том, как это интегрировать

Scalar Docs — отличный способ «потыкать» API руками. Но в реальном приложении генерация обычно живет в бэкенде: пользователь нажал кнопку, вы поставили задачу, вернули task_id, а дальше — polling или обновление UI.

Хорошая новость: интегрироваться можно через привычный OpenAI Python SDK. Ответы сервиса OpenAI-compatible — task_id, status, data[] с картинками. Единственный нюанс: endpoint не стандартный /v1/images/generations, а POST /api/v1/edits с multipart-полями. Но это пара строк кода.

Ниже — типичный сценарий на async/await, как в FastAPI или любом другом asyncio-бэкенде: отправили задачу → дождались → скачали результат.

Шаг 1: Настраиваем клиент

URL берем из ComfyRun, тот же, что в Scalar Docs, с префиксом /api/v1:

PROXY_BASE_URL=https://your-comfyrun.example.com/api/v1 PROXY_API_KEY=unusedce4a76dbf3490317b82f2cbbb8fb817b

API-ключ сервис не проверяет, но OpenAI SDK требует непустое значение — ставим unused:

import os import httpx from dotenv import load_dotenv from openai import OpenAI load_dotenv() BASE_URL = os.getenv(«PROXY_BASE_URL») API_KEY = os.getenv(«PROXY_API_KEY», «unused») client = OpenAI( base_url=BASE_URL, api_key=API_KEY, http_client=httpx.Client(timeout=600.0), ) http = client._client

Дальше все запросы идут через http — это httpx.AsyncClient внутри OpenAI SDK.

Шаг 2: Отправляем задачу на генерацию

Поля те же, что в Scalar: prompt, n, seed. Они маппятся в ваш ComfyUI workflow на стороне сервиса — JSON пайплайна собирать не нужно.

Ставим задачу в async-режиме — сервис сразу возвращает task_id, соединение не висит 20–30 секунд:

response = await http.post( «/edits», params={«async»: «true»}, data={ «prompt»: «minimalist logo of a mountain, flat design», «n»: «2», «seed»: «42», «response_format»: «url», }, ) job = response.json() task_id = job[«task_id»] print(f»Задача поставлена: {task_id}, статус: {job[‘status’]}»)

Если нужен быстрый прототип или скрипт «запустил и забыл» — можно без async=true. Тогда сервис сам дождется ComfyUI и вернет готовый результат в одном ответе:

result = http.post( «/edits», data={ «prompt»: «minimalist logo of a mountain, flat design», «n»: «1», «seed»: «42», «response_format»: «url», }, ).json() # result[«status»] == «completed», result[«data»] уже заполнен

Шаг 3: Ждем завершения и забираем артефакты

По task_id опрашиваем GET /tasks/{task_id}. Пока задача в работе — приходит статус и прогресс. Когда status станет completed, в data[] появятся ссылки на результат:

import asyncio TERMINAL = {«completed», «failed», «cancelled»} while True: response = await http.get( f»/tasks/{task_id}», params={«response_format»: «url»}, ) task = response.json() print(task[«status»], task.get(«progress»)) if task[«status»] in TERMINAL: break await asyncio.sleep(2) if task[«status»] != «completed»: raise RuntimeError(task.get(«reason», task[«status»]))

Скачиваем файлы по presigned URL из ответа:

async with httpx.AsyncClient() as downloader: for i, image in enumerate(task[«data»]): url = image[«presigned_url»] content = (await downloader.get(url)).content with open(f»output_{i}.png», «wb») as f: f.write(content)

Если удобнее работать с base64 — передайте response_format=b64_json при генерации или при GET, и декодируйте data[].b64_json.

Все вместе одним листингомimport asyncio import httpx from openai import AsyncOpenAI async def generate_image(prompt: str, n: int = 1, seed: int = 42) -> list[str]: client = AsyncOpenAI( base_url=»https://your-comfyrun.example.com/api/v1″, api_key=»unused», http_client=httpx.AsyncClient(timeout=600.0), ) http = client._client try: # 1. Ставим задачу job = (await http.post(«/edits», params={«async»: «true»}, data={ «prompt»: prompt, «n»: str(n), «seed»: str(seed), «response_format»: «url», })).json() task_id = job[«task_id»] # 2. Ждём завершения while True: task = (await http.get( f»/tasks/{task_id}», params={«response_format»: «url»}, )).json() if task[«status»] in {«completed», «failed», «cancelled»}: break await asyncio.sleep(2) if task[«status»] != «completed»: raise RuntimeError(task.get(«reason», task[«status»])) # 3. Скачиваем результат paths = [] async with httpx.AsyncClient() as downloader: for i, img in enumerate(task[«data»]): path = f»output_{i}.png» content = (await downloader.get(img[«presigned_url»])).content with open(path, «wb») as f: f.write(content) paths.append(path) return paths finally: await client.close() if __name__ == «__main__»: paths = asyncio.run(generate_image(«watercolor fox in autumn forest»)) print(«Saved:», paths)

Как это ложится в FastAPI

Тот же код — в endpoint: пользователь отправляет промпт, вы возвращаете task_id, фронт polling’ом дергает статус. Или сразу await-ите результат, если нужен синхронный UX:

from fastapi import FastAPI import httpx from openai import AsyncOpenAI app = FastAPI() client = AsyncOpenAI( base_url=»https://your-comfyrun.example.com/api/v1″, api_key=»unused», http_client=httpx.AsyncClient(timeout=600.0), ) @app.post(«/generate») async def generate(prompt: str): http = client._client job = (await http.post(«/edits», params={«async»: «true»}, data={ «prompt»: prompt, «n»: «1», «seed»: «42», «response_format»: «url», })).json() return {«task_id»: job[«task_id»], «status»: job[«status»]}

Инфраструктура — очереди, ComfyUI, хранение артефактов — остается на стороне сервиса. С вашей стороны — три HTTP-вызова и знакомый OpenAI SDK. Можно вернуться к тому самому пет-проекту, который «обязательно выстрелит» и добавить генерацию картинок за вечер, а не за спринт.

Где посмотреть изображения

После завершения генерации результаты доступны сразу в двух местах.

  • В веб-интерфейсе — в разделе Галерея.

  • В Object Storage — по пути, соответствующему идентификатору задачи.

Это удобно, если изображения дальше используются другими сервисами или сразу попадают в CDN.

Илон Маск заходил на огонек
Илон Маск заходил на огонек

Где это может пригодиться

Подобная архитектура полезна не только для генерации красивых картинок. На практике сценариев гораздо больше:

Маркетплейсы

Генерация карточек товаров, оформленных в фирменном стиле. Можно даже всякие характеристики товара автоматом прописывать на карточке, но мне было лень, поэтому пусть будет примитивная демонстрация.

3b8da363cd184686890b2fe96cbf9f7d
339d1efc5184beb48a559f27f0a4dbd4

Enterprise-сценарии

Маркетинг, дизайн, HR-бренд внутри крупных компаний часто хотят генерировать контент по шаблону. А ИТ-отдел не всегда хочет поддерживать для них инфраструктуру. Решение: завернуть workflow в API, отдать команде простой интерфейс и снять с себя операционную нагрузку.

Легким движением ноды сотрудники получают эксклюзивный мерч
Легким движением ноды сотрудники получают эксклюзивный мерч

AI-фоторедактор

7d24bf73f96d222023f5a7a8dd6f5dcc
40fd6385ffd958c61e4b2c8f23eb714f

GameDev, оживление персонажей и концепт-арт

Я просто взял несколько фоток, сделанных прямо в офисе и наскоро соорудил workflow, чтобы их оживить.

Берем фотки из офиса
Берем фотки из офиса
Получаем небольшой клип, по которому не сразу догадаешься, что на самом деле герои статичны
Получаем небольшой клип, по которому не сразу догадаешься, что на самом деле герои статичны
А вот и ваш покорный слуга (когда увидел коммент «Реклама! Дизлайк-отписка!»)
А вот и ваш покорный слуга (когда увидел коммент «Реклама! Дизлайк-отписка!»)

Во всех случаях интеграция выглядит одинаково. Отправляем запрос. Получаем task_id. Через некоторое время забираем готовый результат.

Что в итоге

ComfyUI — рабочий инструмент для пайплайнов, но добавить генерацию в продукт значит не «поднять ComfyUI», а встроить долгую асинхронную операцию: очередь, статусы, хранение результатов и нормальный UX при десятках одновременных пользователей. Разница между демо и продакшеном — не в модели, а в том, кто поддерживает инфраструктуру вокруг workflow: ваша команда или, например, платформа.

Теперь у вас достаточно информации, чтобы сделать осознанный выбор. Свой Kubernetes и GPU-кластер — если нужен полный контроль и есть команда, которая готова этим заниматься. Управляемый сервис — если цель быстро добавить генерацию в продукт, а не превратиться в DevOps по дороге.

Пользователю все равно, сколько под капотом Redis и GPU. Ему важно: нажал — получил результат, без зависаний и тайм-аутов. Выбор инфраструктуры определяет на что уйдут следующие недели вашей жизни: на фичи продукта или на то, чтобы генерация не падала под нагрузкой.

Уверен, половина читателей уже скажет «мы все это на одном VPS подняли за вечер». Если так — добро пожаловать в комменты: интересно, как это живет, когда пользователей не трое, а тридцать. Ну и смело делитесь альтернативами описанному сервису, чтобы те, кто придет в статью позже, уже могли выбирать из нескольких вариантов под свои запросы. Всем бобра!

Источник: habr.com

Оцените материал:

Поделиться
Понравилась статья? Расскажите другим
ВКонтакте
Читайте также
Архив рубрики ~Лента новостей~ 5 ошибок менеджера, внедряющего ИИ Архив рубрики ~Лента новостей~ Конец эпохи «дешевых промптов»: как ИИ-инфляция меняет правила игры для бизнеса Архив рубрики ~Лента новостей~ Директор Amazon по искусственному интеллекту заявил на конференции VB Transform 2026, что не возможности ИИ-агентств препятствуют их внедрению в корпоративной среде из-за низкой надежности. Архив рубрики ~Лента новостей~ Как мы вынесли семантический поиск на обычный сервер без видеокарты, облака и выделенной поисковой системы Архив рубрики ~Лента новостей~ Обсерватория Einstein Probe заметила слияние шести галактик в центре скопления галактик. Это крайне редкое явление в Местной Вселенной Новости робототехники Страх перед человекоподобными роботами подтолкнул рабочих к забастовке на автомобильном заводе Hyundai. Новости робототехники Основатель Максимо рассказывает, как робототехника строит строительство солнечной энергетики. Архив рубрики ~Лента новостей~ Европейский ИИ: что там вообще происходит? Архив рубрики ~Лента новостей~ «Яндекс Путешествия» запустили раздел для анализа спроса, цен и продаж для отелей Архив рубрики ~Лента новостей~ 2 миллиона токенов против 1 миллиона у OpenAI и Anthropic. Чем Google хочет выиграть гонку AI-флагманов Архив рубрики ~Лента новостей~ Вице-президент Cohere заявил, что для обеспечения суверенитета в сфере корпоративного ИИ необходимо контролировать весь набор агентов на конференции VB Transform 2026. Архив рубрики ~Лента новостей~ Внезапный ProIT Fest 2026 Архив рубрики ~Обо всем~ Астрономы нашли кандидата в самый далекий галактический бар. Он существовал спустя почти 1,2 миллиарда лет после Большого Взрыва Архив рубрики ~Лента новостей~ Линус Торвальдс критикам программирования ИИ в Linux: «Создайте форк. Или просто уйдите». Архив рубрики ~Лента новостей~ 5 ошибок менеджера, внедряющего ИИ Архив рубрики ~Лента новостей~ Конец эпохи «дешевых промптов»: как ИИ-инфляция меняет правила игры для бизнеса Архив рубрики ~Лента новостей~ Директор Amazon по искусственному интеллекту заявил на конференции VB Transform 2026, что не возможности ИИ-агентств препятствуют их внедрению в корпоративной среде из-за низкой надежности. Архив рубрики ~Лента новостей~ Как мы вынесли семантический поиск на обычный сервер без видеокарты, облака и выделенной поисковой системы Архив рубрики ~Лента новостей~ Обсерватория Einstein Probe заметила слияние шести галактик в центре скопления галактик. Это крайне редкое явление в Местной Вселенной Новости робототехники Страх перед человекоподобными роботами подтолкнул рабочих к забастовке на автомобильном заводе Hyundai. Новости робототехники Основатель Максимо рассказывает, как робототехника строит строительство солнечной энергетики. Архив рубрики ~Лента новостей~ Европейский ИИ: что там вообще происходит? Архив рубрики ~Лента новостей~ «Яндекс Путешествия» запустили раздел для анализа спроса, цен и продаж для отелей Архив рубрики ~Лента новостей~ 2 миллиона токенов против 1 миллиона у OpenAI и Anthropic. Чем Google хочет выиграть гонку AI-флагманов Архив рубрики ~Лента новостей~ Вице-президент Cohere заявил, что для обеспечения суверенитета в сфере корпоративного ИИ необходимо контролировать весь набор агентов на конференции VB Transform 2026. Архив рубрики ~Лента новостей~ Внезапный ProIT Fest 2026 Архив рубрики ~Обо всем~ Астрономы нашли кандидата в самый далекий галактический бар. Он существовал спустя почти 1,2 миллиарда лет после Большого Взрыва Архив рубрики ~Лента новостей~ Линус Торвальдс критикам программирования ИИ в Linux: «Создайте форк. Или просто уйдите».

Оставить комментарий