Перейти к содержимому

Разбор Aginx: LLM-пайплайн, который собирает ролик за 5 минут и 250 рублей

Константин Потапов
12 мин

Разбор экспериментального LLM-пайплайна «бриф → сценарий → генерация медиа → монтаж»: structured output с Pydantic-валидацией, цепочки fallback-провайдеров, экономика одного ролика и грабли — от бана ElevenLabs до субтитров без таймстемпов.

Разбор Aginx: LLM-пайплайн, который собирает ролик за 5 минут и 250 рублей

Aginx — мой контент-завод: CLI-инструмент, который из текстового брифа собирает готовый вертикальный ролик до 60 секунд. Один контур без ручных действий: бриф → сценарий → генерация видео, голоса и музыки → монтаж с субтитрами. На выходе mp4 для Reels / Shorts / TikTok.

Сразу цифры:

  • ~5 минут от брифа до готового файла (без учёта итераций по сценарию);
  • ~250 ₽ себестоимость 60-секундного ролика через API;
  • ≈0 ₽ за видео в экспериментальном контуре — про него в конце.

Ролик ниже собран этим пайплайном целиком — сценарий, видеосцены, озвучка, музыка, субтитры, монтаж. Руками не тронуто ничего:

Сразу рамка, чтобы не было завышенных ожиданий: это не продакшен-система и не SaaS под SLA, а компактный инженерный эксперимент — CLI-контур на десятки прогонов, а не тысячи задач в проде. Собран он для проверки гипотезы: можно ли пройти путь от брифа до ролика, не утонув в хаосе LLM-ответов и внешних API. Интересен он тем, что продакшен-проблемы в нём настоящие: валидация нестабильного вывода модели, падения провайдеров, региональные блокировки, повторный запуск без пересоздания уже оплаченных артефактов.

Дальше — как это устроено, во что обходится и на какие грабли я наступил.

Архитектура: четыре стадии, один контур

Стек: Python, asyncio, Typer, Pydantic, httpx, ffmpeg. Никакого фреймворка для «AI-агентов» — обычный оркестрируемый пайплайн.

Ключевые роли:

  • Orchestrator — единственное место, где живёт LLM. Получает бриф, возвращает структурированный сценарий: сцены с длительностями, промпты для визуала, текст озвучки, промпт для музыки, крючок первой фразы.
  • Три пайплайна — аудио, визуал, сборка. Друг о друге не знают, общаются через файлы на диске.
  • MediaProvider — маршрутизатор медиагенерации: сначала fal.ai, при ошибке — Replicate. Модели задаются конфигом: Flux для картинок, Kling v3 для видео, Stable Audio для музыки.

Пайплайн умеет три формата ролика, и это решение принимает сценарист (или бриф принудительно): broll — сгенерированные видеосцены, slideshow — картинки с движением камеры, talking_head — сгенерированный аватар, анимированный под озвучку через LivePortrait.

Structured output без магии

Самая переносимая часть проекта — то, как LLM встроена в контур. Никакого function calling и цепочек агентов: схема в промпте, json.loads, Pydantic.

Сценарий описан обычными Pydantic-моделями с ограничениями:

class Scene(BaseModel):
    index: int
    duration: float = Field(ge=1.0, le=30.0)
    visual_prompt: str   # английский, cinematic
    narration: str       # текст озвучки
    mood: str
    camera: str = "static"
 
class Script(BaseModel):
    title: str
    content_type: ContentType   # broll | talking_head | slideshow
    total_duration: float
    scenes: list[Scene]
    music_prompt: str
    voice_id: str
    voice_style: str
    hook: str                   # первая фраза-крючок

Модель получает схему прямо в промпте и инструкцию «Return ONLY valid JSON». А дальше главный принцип, на котором держится весь контур:

Модель предлагает — код решает. После парсинга ответа код перезаписывает всё, чему модели доверять нельзя:

  • content_type из брифа затирает выбор модели: если заказчик сказал «broll», сценарист не имеет права решить иначе;
  • voice_id из конфига авторитетнее того, что придумала модель;
  • total_duration пересчитывается кодом как сумма длительностей сцен — арифметике LLM я не доверяю принципиально.
data = json.loads(raw)
 
# Бриф авторитетнее модели
if brief.content_type is not None:
    data["content_type"] = brief.content_type.value
 
scenes = [Scene(**s) for s in data["scenes"]]
# Модели нельзя доверять даже сложение
data["total_duration"] = round(sum(s.duration for s in scenes), 1)
 
return Script(**data)

Если Pydantic не принял ответ — генерация падает сразу, на этапе сценария, когда ещё не потрачено ни рубля на медиа. Это дешёвое место для ошибки, и пайплайн построен так, чтобы ошибки случались именно здесь.

Отдельная деталь: оркестратор дёргает Claude не через SDK, а субпроцессом через CLI — claude -p "<промпт>" --model claude-sonnet-4-6. Это был самый короткий путь до работающего сценариста. Расплата: таймаут 120 секунд, нет стриминга, процесс на каждый вызов. Для одного вызова на ролик — приемлемо; для сервиса с очередью я бы перешёл на API со structured output.

Экономика: из чего складываются 250 ₽

Себестоимость 60-секундного broll-ролика через API — около 250 ₽. Раскладка отрезвляет:

КомпонентСервисДоля стоимости
ВидеосценыKling v3 через fal.aiпочти 100%
ОзвучкаYandex SpeechKit~1 ₽ за минуту
СценарийClaudeкопейки
МузыкаStable Audioкопейки

Из этой таблицы следуют все архитектурные решения про деньги:

1. Оптимизировать стоимость = оптимизировать видео. Всё остальное можно не считать. Отсюда режим slideshow: картинки Flux вместо видеосцен Kling — на порядки дешевле, для части задач (цитаты, анонсы, списки) качества достаточно.

2. Порядок стадий выбран по деньгам. Аудио генерируется первым: если TTS упал, платная видеогенерация даже не стартует. В коде это отдельный комментарий: «If TTS fails, do not start paid video generation».

3. Идемпотентность — это тоже про деньги. Каждый артефакт — сценарий, каждая сцена, голос, музыка — сразу пишется на диск. Пайплайн из пяти внешних API обязательно упадёт, вопрос когда. Команда resume продолжает задачу с места падения и пропускает готовые файлы: упавший на девятой сцене прогон не пересоздаёт восемь уже оплаченных.

if out.exists() and out.stat().st_size > 1000:
    progress_cb(f"Сцена {scene.index + 1} уже есть, пропускаю...")
    return out

И экспериментальная ветка: сейчас я гоняю видеогенерацию через бесплатный Hyperframe, управляемый агентами, — это отдельный от основного кода контур, и он сбивает стоимость видео к нулю. Пока это эксперимент, а не прод-решение: базовый путь через Kling остаётся эталоном по качеству. Но направление понятно — самая дорогая часть пайплайна дешевеет быстрее всех остальных.

Грабли

ElevenLabs забанил Россию

Лучший голос для русского была ElevenLabs. В какой-то момент сервис перестал работать из России — и это грабля, которую я так и не решил «красиво»: реэкспортные шлюзы добавляют хрупкости, которую я в контуре терпеть не хочу.

В коде осталась честная цепочка деградации: ElevenLabs (если ключ работает) → MiniMax speech-02 через fal.ai → Replicate. В рабочем контуре я в итоге оставил Yandex SpeechKit: рубль за минуту, никаких банов — но по живости голоса это хуже ElevenLabs. Это осознанный трейдофф: стабильность контура важнее последних процентов качества озвучки. Осадок остался.

Вывод шире, чем про TTS: в LLM-пайплайне каждый внешний провайдер — точка отказа. Сегодня rate-limit, завтра бан по региону. Поэтому абстракция провайдера с fallback-цепочкой нужна с первого дня — без неё контур встаёт при первой же смене правил на чужой стороне.

У каждой медиамодели свои рамки, и сценарист о них не знает

Kling принимает длительность строкой от «5» до «15» секунд. Сценарист может написать сцену на 3 секунды — модель всё равно сгенерирует 5. Pydantic-схема разрешает сцены от 1 до 30 секунд, а реальность провайдера — 5–15:

kling_duration = str(min(max(int(duration), 5), 15))

Правильное решение — ограничения медиамоделей должны попадать в схему сценариста, а не клампиться молча на нижнем уровне. У меня пока второй вариант, и это техдолг.

«Return ONLY valid JSON» не работает

Сколько ни пиши в промпте «без markdown-фенсов, без пояснений», модель периодически возвращает ```json ... ```. Промптом это не лечится. Лечится тремя строками кода, которые срезают фенсы перед парсингом, — дешевле принять как данность.

Субтитры без таймстемпов

TTS отдаёт mp3 без пословных таймингов. Точную синхронизацию субтитров с речью из этого не получить. Компромисс: реальная длительность аудио берётся через ffprobe, текст распределяется по времени пропорционально длине текста сцен, дальше режется чанками по 7 слов. Не идеально — но стабильно и без дополнительных вызовов.

Вторая грабля там же: SRT + force_style в ffmpeg — боль с экранированием. ASS-формат стилизуется нативно, поэтому субтитры генерируются сразу в ASS и прожигаются одним фильтром.

Rate limit бесплатного тарифа

Replicate на бесплатном тарифе держит burst = 1. Между генерацией сцен стоит asyncio.sleep(2) — некрасиво, но это честная цена нулевого бюджета на резервного провайдера.

Чего здесь нет до настоящего продакшена

Чтобы слово «пайплайн» не звучало громче, чем есть: продакшен-системой я этот контур не называю. До прода ему не хватает как минимум:

  • очереди задач и воркера вместо локального CLI;
  • централизованных логов, метрик и алертов;
  • лимита на стоимость одного прогона;
  • retry/backoff-политик по типам ошибок;
  • хранения артефактов вне локального диска;
  • тестов на регрессии сценария, сборки и субтитров;
  • approval-gate перед дорогой видеогенерацией.

Поэтому Aginx для меня — рабочий прототип: проверка архитектурных решений, которые потом переносятся в боевой контур.

Что в итоге

Контур работает: бриф уходит в CLI, через ~5 минут возвращается ролик с озвучкой, музыкой и субтитрами за ~250 ₽. Узкие места понятны: качество русского TTS после ухода ElevenLabs и цена видеогенерации — именно на неё направлен эксперимент с бесплатной генерацией через агентов.

Главный вывод: даже лёгкий LLM-эксперимент быстро перестаёт быть задачей про промпты. Как только появляются платные API, внешние провайдеры и риск потратить деньги на невалидный результат, начинаются обычные инженерные вопросы: где валидировать, что можно перезапускать, что нельзя пересоздавать и как контролировать стоимость ошибки. Если доводить такой контур до продакшена, главными станут те же решения — строгие схемы данных, fallback-провайдеры, идемпотентные стадии и порядок выполнения, выстроенный по цене ошибки.


Такой же контур собирается под другие задачи — например, генерацию отчётов из сырых данных или персонализированный контент под каждого клиента. Ищете инженера под LLM-пайплайны — резюме здесь. Есть задача под такой контур — пишите в Telegram.

Похожие материалы

·9 мин

Субботние эксперименты: своя платформа деплоя и два ИИ-проекта за день

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

·12 мин

9axis: живой эксперимент с Telegram-ботом без ИИ — продукт и архитектура

Запустил @nineaxis_bot — бот для ежедневного самонаблюдения по 9 психологическим шкалам. Принципиально без ИИ в рантайме: rule-based интерпретации, контент в MDX, всё собирается из файловой системы. Рассказываю про гипотезу, архитектуру (aiogram 3 + FastAPI + Celery + SQLAdmin), узкое место контента и почему polling через свой прокси честнее вебхука в РФ.

·15 мин

Я построил систему, которая знает, почему вы проваливаете собеседования

Как я за несколько недель построил контент-завод на 65+ курсов, почему существующие платформы не решают проблему подготовки и что происходит, когда ИИ зашумляет рынок найма.