Гайд · TNWS AI
Как собрать WebVTT из JSON-сегментов AI-расшифровки
Конвертация сегментов распознавания речи из JSON в WebVTT: строгие целые миллисекунды, проверка текста и пример результата.
Практическая задача
Получить воспроизводимый WebVTT из структурированных сегментов AI-расшифровки перед ручной вычиткой. Это небольшой сериализатор для контролируемого входного формата, а не полноценный WebVTT-парсер. Согласно актуальному черновику W3C файл начинает строка WEBVTT; интервалы записываются через -->, а пустая строка разделяет cues. Тексты от модели — недоверенные данные: не позволяйте последовательности --> попасть в payload и не выпускайте пустые реплики. Миллисекунды передавайте целыми; источник исходных времён должен быть явно указан в контракте AI-сервиса. Не подменяйте реальное аудио результатом синтетического теста. Этот сценарий применим, когда у команды есть контроль над входом или выходом AI-приложения и требуется конкретный сигнал о нарушении правила, а не общее впечатление от нескольких ответов. Все входы ниже вымышлены; числовые результаты относятся только к этим примерам.
Документация методов Python и синтаксиса workflow сверена 20.09.2026. Если вы используете другое окружение, проверьте совместимость до копирования команд. Статья не утверждает, что показанный код измеряет качество конкретной модели или заменяет ручную проверку.
Подготовка и проверяемый контракт
Перед запуском запишите, какие данные считаются входом, что является ожидаемым результатом, что именно означает отказ и кому предстоит разбирать расхождение. Для такой проверки храните тестовый вход, ожидаемый результат и фактический исход раздельно: если они смешаны, тест может подтверждать сам себя. Используйте стабильные синтетические ID, чтобы видеть ошибку в журнале, не раскрывая содержимое клиентских обращений.
Сначала прогоните тест на маленьком входе, где правильное поведение известно вручную. Затем внесите одну контролируемую ошибку и убедитесь, что код её замечает. После этого верните корректный набор и только тогда расширяйте проверку до реальных данных. Не переносите демонстрационные численные значения или пороги в производство без отдельного обоснования: они выбраны для наглядности, а не рассчитаны по вашей аудитории.
Вход и ожидаемый результат
JSON: [{"id":"a","start_ms":0,"end_ms":1200,"text":"Добрый день"},{"id":"b","start_ms":1300,"end_ms":2400,"text":"Начинаем"}]. Вывод начинается с WEBVTT, затем ID a и b, интервалы 00:00:00.000 --> 00:00:01.200 и 00:00:01.300 --> 00:00:02.400, русский текст сохранён. Перед тем как применять правило к закрытым данным, обсудите пограничные случаи, которые могут поменять интерпретацию входа. Запись в документации о границах теста не менее важна, чем зелёный check.
Пошаговые действия
- Создайте
scripts/check.pyв корне репозитория и вставьте код из блока ниже. Он использует названия функций стандартной библиотеки, указанные в официальных ссылках в конце статьи. - Выполните
python scripts/check.pyлокально. Не продолжайте, если уже демонстрационная фикстура даёт иной результат: сначала проверьте копирование кода и кодировку файла. - Согласуйте поля JSON и единицы времени до обработки файла. Примените строгий json.loads к входу, затем сохраните исходный JSON для ревью. Запустите сериализатор на демонстрационных сегментах: убедитесь, что порядок cues сохранён и длительности не обнулились. В производственном пайплайне проверяйте последовательность стартов отдельно, если вашему видеоплееру требуется порядок; сама задача экспортера прежде всего проверяет каждый интервал. Откройте готовый трек вместе с исходным видео, исправьте ошибки распознавания и только после этого публикуйте.
- Создайте
.github/workflows/ai-corpus-check.ymlиз YAML-шаблона. Событиеpull_requestполезно для проверки данных до слияния; чтение репозитория ограниченоpermissions: contents: read. Скрипт не должен получать ключ внешней модели или реальные секреты. - В разделе Actions откройте job
check, сопоставьте его код завершения с локальным прогоном и зафиксируйте ссылку на проверенный коммит в PR. Не принимайте зелёный результат на старой ревизии за проверку последнего изменения.
# scripts/check.py
import json
def stamp(ms):
h,r=divmod(ms,3600000)
m,r=divmod(r,60000)
s,ms=divmod(r,1000)
return f'{h:02d}:{m:02d}:{s:02d}.{ms:03d}'
def export(rows):
seen=set()
blocks=['WEBVTT']
for row in rows:
ident,start,end,content=(row[k] for k in ('id','start_ms','end_ms','text'))
if not isinstance(ident,str) or not ident or '-->' in ident or '\n' in ident or ident in seen:
raise ValueError('bad cue ID')
if type(start) is not int or type(end) is not int or start<0 or end<=start:
raise ValueError('bad cue time')
if not isinstance(content,str) or not content.strip() or '-->' in content:
raise ValueError('bad cue text')
seen.add(ident)
blocks.append(f'{ident}\n{stamp(start)} --> {stamp(end)}\n{content}')
return '\n\n'.join(blocks)+'\n'
rows=json.loads('[{"id":"a","start_ms":0,"end_ms":1200,"text":"Добрый день"},{"id":"b","start_ms":1300,"end_ms":2400,"text":"Начинаем"}]')
out=export(rows)
assert out.startswith('WEBVTT\n\n')
assert '00:00:01.300 --> 00:00:02.400' in out
print(out)
name: AI corpus checks
on:
pull_request:
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: '3.13'
- run: python scripts/check.py
Ошибки интерпретации и границы
Код намеренно отклоняет сложный WebVTT markup, NOTE и metadata; его нужно расширить и проверить отдельно, если такие функции нужны. Это форматный экспорт, не доказательство качества расшифровки. Если вы расширяете набор случаев, добавляйте не только новые примеры, но и отрицательные случаи, на которых тест обязан упасть. Проверяйте, что изменение expected прошло ревью отдельно от изменения кода: иначе можно незаметно изменить правило под текущий ответ системы. Избегайте вывода полного приватного набора в CI и не сохраняйте в артефактах ответ закрытой модели без решения о доступе и хранении.
Ошибка выполнения самого Python, нераспознанный формат входа и обнаруженный бизнес-дефект — разные ситуации. В сообщении теста указывайте тип проблемы и безопасный ID. Если тест требует внешний сервис, вынесите его из PR с недоверенным кодом и явно ограничьте доступ к токенам; этот пример намеренно работает локально. Перед публикацией каких-либо результатов опишите методику, область применения и происхождение примеров, иначе читатель воспримет демонстрационную арифметику как характеристику продукта.
Практический разбор именно этого контроля
Получить воспроизводимый WebVTT из структурированных сегментов AI-расшифровки перед ручной вычиткой. Для самостоятельной проверки сначала выпишите три наблюдаемых состояния: допустимый вход, намеренно недопустимый вход и видимый сигнал отказа. В данной фикстуре это выглядит так: JSON: [{"id":"a","start_ms":0,"end_ms":1200,"text":"Добрый день"},{"id":"b","start_ms":1300,"end_ms":2400,"text":"Начинаем"}]. При успешном тесте ожидается: Вывод начинается с WEBVTT, затем ID a и b, интервалы 00:00:00.000 --> 00:00:01.200 и 00:00:01.300 --> 00:00:02.400, русский текст сохранён. Не превращайте это в числовой KPI: здесь проверяется контракт на конкретных примерах, а не поведение модели на пользовательском трафике.
Код намеренно отклоняет сложный WebVTT markup, NOTE и metadata; его нужно расширить и проверить отдельно, если такие функции нужны. Это форматный экспорт, не доказательство качества расшифровки. Подключая проверку к продукту, расположите её до той операции, ради которой она написана: парсинга, сохранения, отображения или экспорта. Для каждого нового формата добавляйте отдельный негативный пример и письменное решение, почему он отклоняется. Отдельно просмотрите обработку ошибок: система должна отличать невалидный ответ AI от сбоя тестовой инфраструктуры, а пользователь не должен видеть сырой стек или приватное содержимое.
Чек-лист финальной проверки
- Синтетический вход и ожидаемый исход согласованы предметным владельцем.
- Команда
python scripts/check.pyработает локально и в CI на одной версии файлов. - Негативный случай обнаруживается, а не только успешный случай проходит.
- Имена ID и меток уникальны там, где этого требует контракт.
- В репозитории, логах и артефактах нет реальных секретов и приватных текстов.
- Понятно, какой дефект тест находит и каких выводов о качестве AI он не даёт.
- Ссылки на применённые функции и синтаксис workflow перепроверены при переносе.
FAQ
Почему не выдавать время в секундах с float?
Сначала согласуйте единицы и переведите в целые миллисекунды по проверяемым правилам округления.
Что делать с отсутствующим ID?
Контракт этого шаблона требует ID; в WebVTT в целом cue может быть без ID.
Проверяется ли язык реплики?
Нет, сериализатор гарантирует структуру, а не точность распознавания.
Можно сразу отправлять файл клиенту?
Сначала просмотрите субтитры на видеодорожке и проверьте текст и тайминг.
Что делать после обнаружения сбоя
Сохраните конкретный ID, воспроизведите его на текущей ревизии и отделите ошибку входных данных от ошибки функции. Если неверна контракт, исправьте её после предметного ревью; если неверен код, сначала оставьте проваливающийся тест, затем вносите исправление. После зелёного запуска просмотрите соседние случаи с похожими признаками, чтобы локальная правка не изменила другое согласованное поведение. Сохраните версии кода, набора и параметров запуска рядом с выводом: без них сравнивать результаты в будущем нельзя. Не приводите цифру из синтетического примера как измеренный показатель продукта.
Официальные первоисточники
Проверено 20.09.2026: 1, 2, 3, 4, 5. Описание переписано своими словами; изменяемые версии и поля нужно повторно сверять перед дальнейшим использованием.
Читайте также
Как исключить bias из weight decay через Optax
Optax 0.2.6: маска add_decayed_weights применяет decay к kernel и оставляет bias без изменения.
Как использовать Huber loss в Optax для регрессии с выбросами
Optax 0.2.6: Huber loss квадратичен около нуля и линеен после delta; проверяем ошибки 0,5 и 2.
Как использовать sigmoid focal loss в Optax
Optax 0.2.6: focal loss для logits 0 и labels 0/1 при gamma=2; проверка симметричного базового значения.
Комментарии
Пока тихо. Скажите первое слово