Монтаж видео через Claude Code

от автора

Разбираю архитектуру открытого проекта от browser-use: как заставить LLM монтировать без необходимости «смотреть» видео

Когда речь заходит про обработку видео нейросетями, у большинства из нас в голове всплывает одна и та же картина: модель смотрит на кадры, что-то понимает на основе изображения, режет по визуальным признакам. На практике это упирается в простую арифметику. Часовое видео в 30 fps — это 108 000 кадров. Если каждый кадр стоит хотя бы 1500 токенов, получаем 162 миллиона токенов на одно видео. Никакая модель столько не возьмёт за один проход, а если резать на куски — теряется глобальный контекст.

Команда browser-use недавно опубликовала проект video-use, который решает эту задачу с другой стороны. И решает, на мой взгляд, очень изящно. Идея простая: LLM не «смотрит» видео, а читает его. Через текстовое представление транскрипта с word-level таймкодами. Видео подгружается только в редкие моменты решений, точечно, через визуальный композит.

Я провёл вечер, разбирая исходный код, SKILL.md и архитектуру. Ниже расскажу, как это устроено, что мне понравилось как инженеру, и где у подхода есть ограничения, о которых стоит знать перед использованием.


Что делает проект

Кратко по фичам, чтобы было понятно, о чём вообще речь. Положили исходники в папку, запустили Claude Code (или Codex, или другой агент с shell-доступом), дали инструкцию вида «смонтируй мне launch-видео из этих исходников». На выходе получили final.mp4, в котором:

  • вырезаны слова-паразиты (um, uh, фальстарты);

  • удалены паузы между дублями;

  • применён цветокор;

  • наложены анимации (через Manim, Remotion или PIL — параллельно, разными саб-агентами);

  • сожжены субтитры в стиле, выбранном для проекта;

  • проведена самопроверка результата перед показом пользователю. Проект использует внешний сервис ElevenLabs Scribe для транскрипции (требуется API-ключ) и ffmpeg для собственно рендеринга. Всё остальное — это тонкий слой Python-хелперов плюс инструкции для агента в формате Claude Code Skill.

Лицензия не указана явно, но репозиторий публичный. Реализация на Python с долей HTML — около 75% Python, 23% HTML, 1% Shell.


Главная архитектурная идея

Это самая интересная часть. Создатели проекта прямо проводят параллель со своей основной разработкой — browser-use. Там агенту вместо скриншота страницы дают структурированный DOM. Здесь та же логика: вместо кадров — структурированный транскрипт.

Вот как это выглядит в виде процесса.

Слой 1: транскрипт как первичная поверхность. ElevenLabs Scribe вызывается один раз на каждый исходник, возвращает word-level таймкоды, диаризацию говорящих и метки звуковых событий — (laughter), (applause), (sigh). Транскрипты упаковываются в один компактный файл takes_packed.md, который занимает порядка 12 КБ для часа видео.

Формат строки в этом файле:

## C0103  (duration: 43.0s, 8 phrases)  [002.52-005.36] S0 Ninety percent of what a web agent does is completely wasted.  [006.08-006.74] S0 We fixed this.

Каждая строка — это фраза, ограниченная слева и справа таймкодами, с указанием говорящего (S0, S1) и текстом. Фразы разбиваются на куски по паузам длиннее 0.5 секунды или при смене говорящего. Этот файл — основная «поверхность чтения» для модели.

Слой 2: визуальный композит по запросу. Если в каком-то моменте модель не уверена — например, нужно сравнить два дубля одной и той же реплики, или проверить, что в момент монтажной склейки не происходит визуального скачка — вызывается отдельный хелпер timeline_view. Он генерирует PNG с filmstrip (полоса кадров), waveform (звуковой формой) и метками слов в нужном временном окне. Модель смотрит этот композит и принимает решение.

Ключевой момент: визуальный композит вызывается только в точках принятия решений, не для сканирования. Создатели в README прямо подчёркивают: это не tool для «просмотра видео», это tool для «уточнения сомнений».

Сравнение по токенам, которое они приводят:

Наивный подход: 30 000 кадров × 1500 токенов = 45 миллионов токенов шума. Video-use: 12 КБ текста + горсть PNG.

Цифры для часового видео с 30 fps. Разница в три порядка.


Пайплайн

В упрощённом виде процесс выглядит так:

Transcribe ──> Pack ──> LLM Reasons ──> EDL ──> Render ──> Self-Eval                                                              │                                                              └─ issue? fix + re-render (max 3)

Прохожусь по каждому этапу.

Transcribe. Хелпер transcribe_batch.py параллельно (4 воркера) прогоняет все исходники через Scribe API. Результаты кешируются в transcripts/<name>.json, повторная транскрипция не делается, если файл не менялся.

Pack. Хелпер pack_transcripts.py собирает все transcripts/*.json в один takes_packed.md. Это формат, оптимизированный под чтение моделью, а не человеком.

LLM Reasons. Здесь самая «думающая» часть. Агент читает packed-транскрипт, пред-сканирует на оговорки, потом ведёт диалог с пользователем — какой формат, какая длина, какой стиль. Когда стратегия согласована, спавнится отдельный sub-agent с brief для редактуры. Этот sub-agent выбирает лучшие дубли каждой реплики и собирает их в Edit Decision List.

EDL. Edit Decision List — стандартный термин из мира видеомонтажа. Это JSON-файл, в котором перечислены все куски: какой исходник, с какой по какую секунду, какому биту повествования относится. Формат такой:

{  "version": 1,  "sources": {    "C0103": "/abs/path/C0103.MP4",    "C0108": "/abs/path/C0108.MP4"  },  "ranges": [    {      "source": "C0103",      "start": 2.42,      "end": 6.85,      "beat": "HOOK",      "quote": "Ninety percent of what a web agent does is completely wasted.",      "reason": "Cleanest delivery, stops before slip at 38.46."    }  ],  "grade": "warm_cinematic",  "overlays": []}

Render. Хелпер render.py берёт EDL, нарезает исходники по сегментам, применяет per-segment цветокоррекцию (важно — именно посегментно, не на финальном файле, об этом ниже), накладывает анимации со сдвигом PTS, собирает всё через lossless concat, в самом конце прожигает субтитры.

Self-Eval. Самая нестандартная часть. После рендера агент запускает timeline_view на готовом выходе в каждой точке склейки (плюс несколько контрольных точек: первые 2 секунды, последние 2 секунды, пара mid-points). Смотрит на каждый PNG и проверяет:

  • нет ли визуального скачка на склейке;

  • нет ли пика в waveform — это значит, что в звуке проскочил pop, который не отрезали 30-миллисекундные fade-ы;

  • не закрыт ли субтитр анимационным наложением;

  • не сдвинута ли анимация относительно своего временного окна. Если что-то не так — фикс и повторный рендер. Максимум три итерации, потом агент честно говорит пользователю «вот эти проблемы остались, я не смог их закрыть автоматически».

Это и есть главная инженерная фишка. Цикл render → self-eval → fix даёт агенту возможность ловить собственные косяки до того, как они попадут к пользователю.


Hard Rules

Отдельная вещь, которая мне понравилась в подходе авторов — выделение «жёстких правил» от художественной свободы. В SKILL.md есть раздел Hard Rules — 12 правил, которые касаются производственной корректности и не подлежат обсуждению. Всё остальное — таймы, стили, цветокор, шрифты — отдано на усмотрение агента в зависимости от материала.

Несколько примеров жёстких правил:

Правило 1: Субтитры применяются последними в filter chain, после всех overlay. Иначе анимационные наложения просто скрывают субтитры. Молчаливая ошибка — на превью в маленьком окне может быть незаметно.

Правило 2: Per-segment extract → lossless concat. Каждый сегмент вырезается отдельной командой ffmpeg с нужным цветокором, потом всё склеивается через -c copy. Альтернативный подход — собирать всё одним filtergraph — приводит к двойному перекодированию каждого сегмента при добавлении overlay-ов, что и медленнее, и хуже по качеству.

Правило 3: 30 ms audio fades на каждой границе сегмента. Конкретный ffmpeg-фильтр:

afade=t=in:st=0:d=0.03,afade=t=out:st={dur-0.03}:d=0.03

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

Правило 4: Overlay-ы используют setpts=PTS-STARTPTS+T/TB. Это сдвиг кадра 0 анимации на начало временного окна, в котором она показана. Без этого модификатора зритель увидит середину анимации в начале окна — anchor сбит.

Правило 5: Master SRT использует output-timeline offsets. Формула: output_time = word.start - segment_start + segment_offset. Без неё субтитры начинают плыть после концатенации сегментов, потому что таймкоды слов остаются в координатах исходного файла.

Правило 6: Никогда не резать внутри слова. Каждая граница склейки ищется по word boundary из транскрипта Scribe.

Правило 7: Padding 30–200 мс на каждой границе. Scribe-таймкоды дрейфуют на 50–100 мс — этот padding компенсирует дрейф.

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


Параллельные sub-агенты для анимаций

Ещё одна инженерная деталь, которую стоит отметить. Анимации (Manim, Remotion, PIL) — это самая медленная часть пайплайна. Manim-сцена может рендериться 30–60 секунд. Если их в проекте 5–10, последовательный рендер занимает 5–10 минут.

Решение: каждая анимация — отдельный sub-agent, спавнятся параллельно через инструмент Agent в Claude Code. Каждый получает свой self-contained prompt с абсолютным путём для записи (<edit>/animations/slot_<id>/render.mp4), технической спецификацией (разрешение, fps, кодек, длительность), палитрой и шрифтом как конкретными значениями.

Важный момент в инструкции: sub-agent не должен задавать уточняющих вопросов. Если что-то неоднозначно — выбрать наиболее очевидную интерпретацию и ехать дальше. Иначе параллелизация ломается на этапе ожидания ответов пользователя.

В итоге wall-time всех анимаций ≈ wall-time самой медленной из них. Простая идея, но в большинстве проектов с LLM она не реализуется именно потому, что разработчики экономят на сложности оркестрации саб-агентов.


Чему этот проект может научить за пределами «смонтировать видео»

Я не уверен, что буду регулярно использовать video-use по прямому назначению. Видео я монтирую раз в полгода, и для редкой задачи проще включить DaVinci Resolve и сделать руками за два часа, чем настраивать ElevenLabs API, Manim, Remotion и весь стек. Но как архитектурный пример проект очень ценный.

Несколько уроков, которые я для себя из него вынес.

Не надо давать LLM сырые данные. В соблазне «отдать модели всё как есть, она разберётся» — много инженерной лени. Хорошая структура промежуточного представления экономит порядки токенов и улучшает качество решений. browser-use делает это для DOM, video-use для видео. Тот же приём работает для логов, для метрик, для чего угодно с большим объёмом исходных данных.

On-demand drill-down вместо постоянного контекста. Не нужно давать модели весь визуальный контекст всё время. Достаточно дать ей возможность запрашивать визуальный контекст в момент, когда он нужен. Это снижает стоимость, ускоряет принятие решений и фокусирует модель на главном.

Self-eval цикл с жёстким лимитом. Цикл «модель проверяет собственный вывод» — мощный паттерн, но без лимита итераций он превращается в бесконечный цикл правок. Три попытки и затем честный отчёт пользователю — хороший компромисс.

Хард-правила и художественная свобода — это разные вещи. В любой автоматизации с LLM есть параметры, в которых модели нельзя давать выбор (тут будет молчаливая ошибка), и параметры, где модели стоит дать максимум свободы (тут будет скучный шаблонный результат). Разделять эти два класса явно — полезно. video-use делает это в SKILL.md, но тот же принцип применим к любому агентному пайплайну.

Pre-flight проверки в SKILL.md — это документация, которая работает. Раздел «Setup» в проекте начинается с короткого чек-листа: проверь API-ключ, проверь ffmpeg, проверь Python deps. Это не «как установить» — это «что должно быть готово до начала работы». Простая структура, которая сильно снижает количество ошибок.


Чего у проекта нет

Чтобы не выглядело хвалебно, отмечу слабые стороны.

Зависимость от ElevenLabs Scribe — платная и закрытая. Транскрипция — самая дорогая по цене и времени часть пайплайна. Альтернативы вроде локального Whisper (даже large-v3) дают худшее качество таймкодов на word-level. Open-source аналогов с сопоставимым качеством для длинных аудио на сегодня нет.

Нет UI. Всё через чат с агентом. Для людей, которые привыкли тыкать в timeline в DaVinci и видеть результат, переход на чат-интерфейс — серьёзный когнитивный сдвиг. Скорее не альтернатива, а другой класс продукта.

Нет работы с B-roll. Проект ориентирован на talking-head контент: интервью, лекции, лаунч-видео. Если нужно подмешивать кадры с другого источника во время голоса — сейчас это потребует доработки. По описанию архитектура это позволяет, но из коробки сценарий не поддержан.

Self-eval не спасает от семантических ошибок. Цикл проверки ловит технические косяки (склейки, поп-ы, скрытые субтитры). Но если модель неправильно поняла, какую реплику оставить, а какую выкинуть — self-eval это не поймает. Тут нужна человеческая проверка.

Документация местами лаконична до недосказанности. SKILL.md написан для агента, не для человека-читателя. Чтобы понять, как что работает, приходится читать одновременно SKILL.md, helpers/ и install.md. Для open-source приемлемо, для production-ready проекта — на двойку.


Резюме

video-use — рабочий инструмент для тех, кто регулярно монтирует talking-head видео и хочет автоматизировать этот процесс через агента. Если ваш профиль — раз в полгода, проще не тратиться на настройку.

Но как архитектурный пример это стоит изучить любому, кто строит агентные системы для работы с большими данными. Идея «текстовое представление + on-demand визуальный drill-down» применима гораздо шире, чем к видео. И жёсткое разделение на hard rules и художественную свободу — отличный подход к управлению агентным выводом.

Главное, что я взял для себя: не надо пытаться скармливать LLM всё, что у вас есть. Дайте ей структурированную поверхность для чтения и инструмент, которым она может попросить детали. Это работает почти всегда лучше, чем dump всего контекста сразу.


Полезные ссылки:

ссылка на оригинал статьи https://habr.com/ru/articles/1032374/