Legan Studio
Все статьи
~ 5 мин чтения

Аватар-бот: стилизация и обработка фото в Telegram

Как собрать бот для генерации аватаров и стилизации фото в Telegram: ML-модели, очередь GPU, оплата за пакет и оптимизация себестоимости.

  • Telegram
  • AI
  • генерация
  • Stable Diffusion

Боты для генерации аватаров и стилизации фото — одна из самых хайповых ниш Telegram в 2024–2026. Юзер загружает 10–20 селфи, бот обучает LoRA или dreambooth, через 30 минут возвращает 100 стилизованных портретов. ARPU $5–15, при правильной экономике маржа 60–70%.

Разберём архитектуру, стек ML, тарифы и узкие места — особенно с учётом цены GPU-времени.

Базовый функционал

Что должен уметь бот:

  • Загрузка 10–20 селфи (контроль качества: лицо в кадре, не маленькое).
  • Обучение персональной модели (LoRA на SDXL или Flux).
  • Выбор пресета стиля (кибер-панк, барокко, аниме, бизнес-портрет).
  • Генерация пакета 50–100 изображений.
  • Доставка в виде ZIP или галереи в Mini App.
  • Оплата за пакет в Stars или картой.

Архитектура

[Bot/Mini App: загрузка фото + UI]
        ↓
[API: PostgreSQL + S3]
        ↓
[Queue (Redis/RabbitMQ)]
        ↓
[GPU Worker: SD/Flux + LoRA training]
        ↓
[Result S3 → Mini App Gallery]

Ключевое — отделение быстрой части (приём, оплата) от медленной (обучение и генерация на GPU). Иначе один пользователь на 30 минут блокирует ресурс.

Загрузка фото и валидация

@router.message(F.photo)
async def upload(message: Message, state: FSMContext):
    data = await state.get_data()
    photos = data.get("photos", [])
    if len(photos) >= 20:
        await message.answer("Уже 20 фото, достаточно!")
        return
    file = await bot.get_file(message.photo[-1].file_id)
    image_bytes = await bot.download_file(file.file_path)
    quality = await check_face_quality(image_bytes)
    if not quality.has_face:
        await message.answer("Не вижу лица — пришлите другое фото")
        return
    if quality.face_ratio < 0.1:
        await message.answer("Лицо слишком маленькое — нужен крупный план")
        return
    s3_key = f"upload/{message.from_user.id}/{uuid4()}.jpg"
    await s3.put(s3_key, image_bytes)
    photos.append(s3_key)
    await state.update_data(photos=photos)
    await message.answer(f"Принято! Загружено {len(photos)}/20")

Без фильтра качества модель учится на мусоре и выдаёт уродов.

Обучение LoRA

После загрузки 10+ фото запускается тренировка персональной LoRA (Low-Rank Adaptation) поверх базовой модели SDXL или Flux.

async def train_lora(user_id: int, photos: list[str]):
    job_id = await queue.enqueue("train_lora", {
        "user_id": user_id,
        "photos": photos,
        "base_model": "flux-dev",
        "steps": 1500,
        "rank": 16,
    })
    await jobs.set_status(job_id, "queued")
    return job_id

На H100 тренировка LoRA flux-dev с 20 фото занимает ~15 минут. На A100 — ~25 минут. На L40 — ~40 минут.

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

Воркер на GPU забирает задачи последовательно (один LoRA = один GPU полностью):

import asyncio
from arq import Worker

async def train_worker(ctx, user_id: int, photos: list[str]):
    images = await download_all(photos)
    lora_path = await flux_train(
        images=images,
        steps=1500,
        rank=16,
        output_path=f"loras/{user_id}.safetensors",
    )
    await s3.upload(lora_path, f"loras/{user_id}.safetensors")
    await jobs.set_status(user_id, "trained")
    await generate_pack(user_id)


class WorkerSettings:
    functions = [train_worker]
    max_jobs = 1   # один LoRA на воркер

Масштабирование — добавление GPU-воркеров. На 1000 заказов/день с обучением по 15 минут нужно ~10 GPU.

Генерация пресетов

После обучения LoRA — генерация 50–100 изображений по промптам пресета:

PRESETS = {
    "cyberpunk": [
        "a person in cyberpunk city, neon lights, rain, cinematic, 4k",
        "portrait of a person as cyberpunk hacker, blue and pink lighting",
        # ... 30 вариаций
    ],
    "renaissance": [...],
    "anime": [...],
}


async def generate_pack(user_id: int, preset: str = "default"):
    lora = await s3.get(f"loras/{user_id}.safetensors")
    prompts = PRESETS[preset]
    results = []
    for prompt in prompts[:50]:
        img = await flux_generate(
            prompt=prompt + ", <lora:user:0.85>",
            steps=28,
            cfg=3.5,
            width=1024, height=1024,
        )
        s3_key = f"results/{user_id}/{uuid4()}.png"
        await s3.put(s3_key, img)
        results.append(s3_key)
    await deliver(user_id, results)

50 генераций flux на H100 — ~4 минуты. Стоимость GPU-часа H100 на runpod.io ~$2.5 → один пакет ~$0.5–0.8.

Тарифы и юнит-экономика

ТарифЦенаСтилейИзображенийСебестоимостьМаржа
Mini250 Stars (~$3.5)130$0.780%
Standard750 Stars (~$10)3100$1.882%
Pro1500 Stars (~$20)6200$3.582%

С учётом комиссии Apple/Google и Telegram реальная маржа после всех вычетов — 35–45%. По-прежнему отлично.

Оптимизация себестоимости

  • Шеринг GPU между пользователями: тренируется 1 LoRA, генерируется параллельно для нескольких.
  • Распродажа спот-инстансов на runpod, vast.ai — экономия 30–50%.
  • Кеш базовой модели в RAM воркера: первая генерация 10 сек, последующие — 4 сек.
  • Батчинг: генерируем по 4 картинки за один forward — х3 throughput.
  • Меньше шагов: 28 для flux хватает, не 50.

Доставка результата

ZIP-архив через Bot API имеет лимит 50 МБ (или 2 ГБ через self-hosted Bot API). 100 изображений 1024×1024 PNG ≈ 200 МБ — нужны JPG или Mini App-галерея.

async def deliver(user_id: int, s3_keys: list[str]):
    urls = [s3.presigned_url(k, ttl=86400 * 7) for k in s3_keys]
    await bot.send_message(
        user_id,
        "Готово! Откройте галерею:",
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(
                text="Открыть галерею",
                web_app=WebAppInfo(url=f"https://app.example.com/gallery?uid={user_id}"),
            )],
        ]),
    )

Mini App с галереей даёт лучший UX, чем 100 раздельных sendPhoto.

Юридика и этика

  • Согласие на обработку биометрии (фото лица — биометрический ПД).
  • Правила: запрет генерации NSFW, дипфейков чужих лиц, политических деятелей.
  • Удаление LoRA через 30 дней (право на забвение по 152-ФЗ).
  • Запрет загрузки чужих фото (требование чекбокса при первой загрузке).

NSFW-фильтр на этапе генерации — обязательно. AI безопасности через CLIP-классификатор.

Топ-5 ошибок

  1. Не валидируют качество фото на входе — выдают уродов и теряют клиента.
  2. Делают синхронный пайплайн без очереди — один пользователь блокирует всех.
  3. Не считают GPU-time правильно — тариф $5 за пакет, который стоит $7.
  4. Хранят LoRA вечно — расходы на S3 растут линейно с DAU.
  5. Не блокируют генерацию знаменитостей — попадают под иски.

Итого

Аватар-бот — финансово очень привлекательная ниша 2026 года: ARPU $5–20, маржа 35–45%, виральный продукт (юзеры показывают результаты в соцсетях). Технически нужен Mini App для UX, GPU-воркеры с очередью для тренировки LoRA, сильный фильтр входных фото и грамотное хранение результатов. Запуск MVP — 6–8 недель, окупаемость трафика 3–6 недель при CPL $1–3.

Частые вопросы

Сколько фото нужно для качественной LoRA?

Минимум 10–12 фото лица крупным планом, оптимум 18–25. Разные ракурсы, разное освещение, без фильтров. Меньше 10 — модель не выучивает лицо стабильно. Больше 30 — выгода почти нулевая, но время тренировки растёт. Лучше 20 хороших, чем 50 случайных.

Какую модель брать: SDXL, Flux или новее?

Flux Dev / Pro в 2026 даёт лучшее качество для лиц при той же цене. SDXL дешевле и быстрее, но фотореализм хуже. Stable Diffusion 3.5 — компромисс. Для премиум-тарифов используйте Flux, для бюджетных — SDXL Turbo с inference в 3–4 шага.

Как считать GPU-час?

На спот-инстансах H100 (runpod, vast.ai, lambda) — $1.8–2.5/час. A100 — $1.2–1.6. L40 — $0.8–1.2. Расчёт: тренировка 15 минут + генерация 5 минут = 20 минут на пакет = $0.7–0.8 на H100. Это себестоимость одного пакета.

Что делать с NSFW и дипфейками?

NSFW-фильтр на промптах (блокировать «nude», «sexy») и на выходе (CLIP-классификатор). Запрет генерации публичных людей через распознавание лиц на входе и сравнение с базой знаменитостей (использовать VGGFace или DeepFace). Обязательное согласие пользователя «загружаю только свои фото» при первом использовании.

Когда удалять LoRA пользователя?

По 152-ФЗ — по запросу пользователя в любой момент, плюс автоматически через 30–90 дней неактивности. Сохраняйте только LoRA-файл (~50 МБ), исходные фото — удаляйте сразу после успешного обучения. Это и закон, и сильно экономит S3.

Как продвигать аватар-бот?

Виральность через результаты: каждое сгенерированное изображение содержит водяной знак или подпись «@yourbot». Юзеры выкладывают в Stories, Telegram-каналы, Twitter — получаете органический трафик. Платный — Telegram Ads на нишевые каналы (мода, IT, фотография). CPL $1–3 при правильной связке.

Можно ли начать на одном GPU?

Да, для MVP с потоком до 50 пакетов в день одного H100 хватит ($1500–2000 в месяц при 24/7). Дальше масштабируетесь добавлением воркеров. Архитектура с очередью изначально — критична: переход с 1 на 10 GPU тогда становится тривиальным конфигом.