# Как монтировать видео с Claude, Codex или любым ИИ-агентом

> Передайте агенту расшифровку, где у каждого слова есть точное время начала и конца. Агент выберет нужные фразы, а небольшой скрипт нарежет видео в ffmpeg по этим меткам. Такую расшифровку для аудио и видео на 100 языках делает API KolWrite. По этим же словам потом собираются субтитры для готового ролика.

Обновлено: 2026-10-18 · HTML: https://console.kolwrite.com/ru/guides/video-editing-with-ai

## Что агент умеет, а что нет

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

Если ваш агент умеет выполнять команды в терминале (например, Claude Code, Codex CLI или агент в Cursor), он сам обратится к API и запустит ffmpeg. Обычный чат без доступа к консоли так не сделает, но сможет написать для вас нужные скрипты.

## Промпт для агента

С этим промптом агент не будет печатать таймкоды вручную, а вернет только номера фрагментов. Так он точно не выдумает несуществующие цифры. В промпте также дана ссылка на документацию API в формате, понятном для моделей.

```text
Edit talk.mp4 down to the parts worth keeping, using the KolWrite API for the transcript.
Read https://console.kolwrite.com/agent-instructions.md first. The key is in KOLWRITE_API_KEY.
1. Extract the audio with ffmpeg and transcribe it with model "oracle", output_format "json", word_timestamps true.
2. Print the segments as numbered lines: S<number> [start-end] text.
3. Choose the segments to keep (remove false starts, filler, repeated takes and tangents) and write
   their numbers to keep.json as {"keep": [0, 2, 4]}. Do not invent timestamps; use segment numbers only.
4. Cut the video from those segments' word times with ffmpeg and write edited.mp4.
5. Make edited.srt for the edited video and attach it as a subtitle track.
Show me the list of removed segments before you cut anything.
```

Чтобы не объяснять работу с API в каждой новой сессии, скопируйте правило из [руководства по инструменту](https://console.kolwrite.com/ru/guides/transcription-tool-for-ai-agents) в файл `AGENTS.md` или `CLAUDE.md`.

## Шаг 1: достаем аудиодорожку и делаем расшифровку

Прямая загрузка по подписанной ссылке принимает файлы до 100 MiB, поэтому из длинных видео лучше сначала вытащить звук. Час монофонического аудио с битрейтом 48 кбит/с весит около 21 МБ.

```bash
ffmpeg -i talk.mp4 -vn -ac 1 -ar 16000 -b:a 48k talk.mp3
```

Вот простая функция, которая отправляет локальный файл на сервер, запускает задачу и дожидается ответа:

kolwrite.py

```python
# kolwrite.py — three small functions around the KolWrite REST API
import os, time, requests

API = "https://app.kolwrite.com"
HEADERS = {"x-api-key": os.environ["KOLWRITE_API_KEY"]}


def upload(path):
    """Upload a local file (up to 100 MiB) and return a URL the API can read."""
    name = os.path.basename(path)
    form = requests.post(f"{API}/upload", headers=HEADERS, json={"filename": name}, timeout=30)
    form.raise_for_status()
    form = form.json()
    with open(path, "rb") as media:
        # No x-api-key on this request: it goes to the storage address.
        sent = requests.post(form["uploadUrl"], data=form["uploadFields"],
                             files={"file": (name, media)}, timeout=600)
    sent.raise_for_status()
    return form["fileUrl"]


def transcribe(audio_url, timeout_s=3600, **options):
    """Start a job, wait for it, return the result: a dict for json, a string for srt and txt."""
    body = {"audio_url": audio_url, "model": "oracle", **options}
    job = requests.post(f"{API}/transcribe", headers=HEADERS, json=body, timeout=30)
    job.raise_for_status()
    job_id = job.json()["jobId"]
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        time.sleep(5)
        reply = requests.get(f"{API}/transcribe/{job_id}", headers=HEADERS, timeout=30)
        reply.raise_for_status()
        reply = reply.json()
        if reply["status"] == "COMPLETED":
            return reply["result"]
        if reply["status"] == "FAILED":
            raise RuntimeError(reply.get("error", "transcription failed"))
    raise TimeoutError(f"job {job_id} did not finish in {timeout_s} seconds")
```

Скрипт `edit.py` ниже запрашивает JSON с временными метками слов (оба параметра стоят по умолчанию, но для наглядности указаны явно). В ответ возвращается `segments[]`, где в каждом элементе есть `start`, `end`, `text`, при необходимости `speaker`, и `words[]`:

Пример ответа в сокращенном виде:

```json
{
  "jobId": "34c63d45-4cb5-473b-bc60-25c175e69c57",
  "status": "COMPLETED",
  "result": {
    "language": "en",
    "segments": [
      {
        "start": 1.0,
        "end": 3.75,
        "text": "Hello everyone and welcome to the show.",
        "speaker": "Speaker 1",
        "words": [
          { "start": 1.0, "end": 1.35, "word": "Hello" },
          { "start": 1.4, "end": 1.75, "word": "everyone" }
        ]
      }
    ]
  }
}
```

## Шаг 2: поручаем агенту решить, что оставить

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

view.py

```python
# view.py — print the transcript the way an agent can read it
import json, sys

result = json.load(open(sys.argv[1]))
for number, segment in enumerate(result["segments"]):
    who = f'{segment["speaker"]}: ' if segment.get("speaker") else ""
    print(f'S{number} [{segment["start"]:.1f}-{segment["end"]:.1f}] {who}{segment["text"]}')
```

В ответ агент присылает только номера выбранных фрагментов:

keep.json

```json
{"keep": [0, 2, 4]}
```

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

## Шаг 3: режем видео в ffmpeg по границам слов

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

cut.py

```python
# cut.py — ranges from segment numbers, one ffmpeg pass, word times for the new timeline
import subprocess

PAD = 0.08  # seconds of room kept around every range


def ranges_from_ids(segments, keep_ids, pad=PAD):
    """Turn the segment numbers the agent chose into [start, end] ranges in seconds."""
    ranges = []
    for i in sorted(set(keep_ids)):
        segment = segments[i]
        words = segment.get("words") or []
        start = words[0]["start"] if words else segment["start"]
        end = words[-1]["end"] if words else segment["end"]
        start, end = max(0.0, start - pad), end + pad
        if ranges and start <= ranges[-1][1]:
            ranges[-1][1] = max(ranges[-1][1], end)
        else:
            ranges.append([start, end])
    return ranges


def cut(video, ranges, out):
    """Re-encode the kept ranges into one file (frame accurate)."""
    parts = []
    for i, (start, end) in enumerate(ranges):
        parts.append(f"[0:v]trim=start={start:.3f}:end={end:.3f},setpts=PTS-STARTPTS[v{i}]")
        parts.append(f"[0:a]atrim=start={start:.3f}:end={end:.3f},asetpts=PTS-STARTPTS[a{i}]")
    joined = "".join(f"[v{i}][a{i}]" for i in range(len(ranges)))
    parts.append(f"{joined}concat=n={len(ranges)}:v=1:a=1[v][a]")
    subprocess.run(["ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-i", video,
                    "-filter_complex", ";".join(parts), "-map", "[v]", "-map", "[a]", out], check=True)


def remap(words, ranges):
    """Move word times onto the edited timeline, so subtitles match the cut video."""
    moved, offset = [], 0.0
    for start, end in ranges:
        for word in words:
            if word["start"] >= start and word["end"] <= end:
                moved.append({**word, "start": word["start"] - start + offset,
                              "end": word["end"] - start + offset})
        offset += end - start
    return moved
```

edit.py

```python
# edit.py — the whole pipeline: transcript in, edited video and subtitles out
import json
from kolwrite import upload, transcribe
from cut import ranges_from_ids, cut, remap
from srt_tools import cues_from_words, write_srt

result = transcribe(upload("talk.mp3"), output_format="json", word_timestamps=True)
with open("talk.json", "w", encoding="utf-8") as out:
    json.dump(result, out, ensure_ascii=False)
# ... the agent reads talk.json (python view.py talk.json) and writes keep.json ...

keep = json.load(open("keep.json"))["keep"]
ranges = ranges_from_ids(result["segments"], keep)
cut("talk.mp4", ranges, "edited.mp4")

words = [w for segment in result["segments"] for w in segment.get("words", [])]
write_srt(cues_from_words(remap(words, ranges)), "edited.srt")
```

Копировать потоки напрямую (`-c copy`) быстрее, но тогда разрезы пойдут только по ключевым кадрам и могут съехать на пару секунд. Если важна точная склейка, видео нужно перекодировать.

## Шаг 4: добавляем субтитры к готовому ролику

Скрипт `edit.py` уже пересчитал таймкоды слов и сохранил файл `edited.srt`. О том, как именно слова группируются в строки субтитров, подробно рассказано в [руководстве по SRT](https://console.kolwrite.com/ru/guides/srt-subtitles-api).

srt_tools.py

```python
# srt_tools.py — neat subtitle cues from word timings
def srt_time(seconds):
    ms = round(seconds * 1000)
    h, ms = divmod(ms, 3_600_000)
    m, ms = divmod(ms, 60_000)
    s, ms = divmod(ms, 1000)
    return f"{h:02}:{m:02}:{s:02},{ms:03}"


def cues_from_words(words, max_chars=42, max_seconds=6.0, max_gap=0.8):
    """Group timed words into subtitle cues: (start, end, text)."""
    cues, line = [], []
    for word in words:
        text = word["word"].strip()
        if not text:
            continue
        if line:
            length = sum(len(w["word"]) + 1 for w in line) + len(text)
            if (length > max_chars
                    or word["end"] - line[0]["start"] > max_seconds
                    or word["start"] - line[-1]["end"] > max_gap):
                cues.append(line)
                line = []
        line.append({"start": word["start"], "end": word["end"], "word": text})
    if line:
        cues.append(line)
    return [(c[0]["start"], c[-1]["end"], " ".join(w["word"] for w in c)) for c in cues]


def cues_from_result(result):
    """Cues for a whole JSON result; a segment without word times becomes one cue."""
    cues = []
    for segment in result["segments"]:
        if segment.get("words"):
            cues += cues_from_words(segment["words"])
        elif segment["text"].strip():
            cues.append((segment["start"], segment["end"], segment["text"].strip()))
    return cues


def write_srt(cues, path):
    with open(path, "w", encoding="utf-8") as out:
        for number, (start, end, text) in enumerate(cues, 1):
            out.write(f"{number}\n{srt_time(start)} --> {srt_time(end)}\n{text}\n\n")
```

Остается либо наложить субтитры прямо поверх картинки, либо упаковать их отдельной дорожкой, которую зритель сможет включать и выключать в плеере:

```bash
ffmpeg -i edited.mp4 -vf "subtitles=edited.srt" -c:a copy burned.mp4
```

```bash
ffmpeg -i edited.mp4 -i edited.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng soft.mp4
```

## Что еще можно сделать по временным меткам слов

- Вырезать тишину: оставьте только куски с непрерывной речью и удалите все паузы длиннее заданного порога (подробности в [руководстве по временным меткам слов](https://console.kolwrite.com/ru/guides/word-timestamps-api)).
- Извлечь конкретную цитату: найдите нужную фразу в массиве слов и вырежьте кусок видео ровно по ее границам.
- Почистить речь от слов-паразитов: агент может сам отметить фрагменты, состоящие только из мусорных междометий, либо вы можете найти такие слова по списку.
- Разметить главы и лучшие моменты: агент выбирает номера подходящих реплик, а скрипт переводит их в таймкоды (см. [руководство по подкастам и YouTube](https://console.kolwrite.com/ru/guides/podcast-youtube-captions)).
- Вырезать реплики одного из говорящих: включите параметр `include_diarization` и отфильтруйте фрагменты по нужному спикеру.

## Сколько это стоит

| Задача | Модель | Стоимость |
| --- | --- | --- |
| Ролик на 10 минут, одна расшифровка | `oracle` | 0,24 $ |
| Час видео с чистым звуком | `base` | 0,48 $ |
| Час видео, качество для продакшена | `oracle` | 1,44 $ |
| Час записи со сложным звуком | `oracle_max` | 3,60 $ |

Тарификация посекундная, минимального порога нет. Если какой-то сложный кусок нужно прогнать заново через более мощную модель, вырежьте этот отрывок в отдельный аудиофайл и заплатите только за его секунды. Все цены: [/llms/pricing.md](https://console.kolwrite.com/llms/pricing.md).

## Ограничения

- Точности меток вполне хватает для разреза в паузах между словами. На самой границе звука таймкоды могут слегка гулять, но небольшой отступ в скрипте `cut.py` это сглаживает. Перед публикацией все же прослушайте места склеек.
- Автоматическая расшифровка иногда ошибается. Обязательно вычитывайте имена и цифры перед публикацией. У KolWrite нет официальной статистики по точности распознавания разных языков для этого API.
- Не удаляйте исходник видео. Нарезка идет с перекодированием из оригинала, и он вам еще пригодится, если решите перемонтировать ролик по-другому.
- Если фрагментов наберется слишком много, строка фильтра для ffmpeg выйдет огромной. Когда счет идет на сотни кусков, режьте видео за несколько подходов.

## Вопросы и ответы

### Может ли Claude монтировать видео?

Да, если у него есть доступ к выполнению команд. Тогда Claude сам запрашивает расшифровку с метками слов через API KolWrite, отбирает нужные реплики и запускает нарезку в ffmpeg. Так работают Claude Code, Codex CLI и агенты в Cursor. Обычный веб-чат выполнить код не сможет, но напишет вам готовые скрипты.

### Как нарезать видео по текстовой расшифровке?

Сделайте расшифровку с временными метками слов и выберите нужные фрагменты. Для каждого из них возьмите начало первого и конец последнего слова, а затем вырежьте и склейте интервалы фильтрами trim и concat в ffmpeg по примеру из `cut.py`.

### Нужно ли ставить плагин или SDK?

Нет. API — это HTTPS и JSON; агент вызывает его через curl или Python. На момент написания пакета SDK и сервера MCP не опубликовано.

### Подойдет ли этот способ для иврита, арабского и других языков?

Да, на этапе расшифровки API понимает 100 языков. Язык определяется сам, либо вы можете жестко задать его в параметре `language`. Метки времени для каждого слова возвращают модели из списка в [руководстве по временным меткам слов](https://console.kolwrite.com/ru/guides/word-timestamps-api). Советуем сначала прогнать небольшой тестовый кусок вашей записи и свериться со страницей [языков](https://console.kolwrite.com/ru/languages/).

### Как отучить агента выдумывать таймкоды?

Никогда не просите модель возвращать таймкоды. Передайте ей пронумерованные фрагменты и запросите в ответ только номера, а само время вычислите в коде по меткам слов.

## Связанные страницы

- [API для создания субтитров SRT](https://console.kolwrite.com/ru/guides/srt-subtitles-api)
- [API временных меток для отдельных слов](https://console.kolwrite.com/ru/guides/word-timestamps-api)
- [Инструмент транскрипции для ИИ-агентов](https://console.kolwrite.com/ru/guides/transcription-tool-for-ai-agents)
- [ИИ-агенты и vibe coding](https://console.kolwrite.com/ru/use-cases/ai-agents)
- [Медиа, подкасты и субтитры](https://console.kolwrite.com/ru/use-cases/media)
- [Документация API](https://console.kolwrite.com/ru/public_api_docs)

---
Получить ключ API: https://console.kolwrite.com/auth?lang=ru · Документация API: https://console.kolwrite.com/ru/public_api_docs · llms.txt: https://console.kolwrite.com/llms.txt
