Перейти к содержанию

AWS Serverless транскрибатор аудио — SRS-сборка

Эта страница собирает разделы для спецификации требований к ПО: от контекста и проблемы до безопасности, качества и эксплуатации.

Содержание

Краткое описание

Статус

В эксплуатации

Роль

Системный проектировщик

Стек

Python, AWS Lambda, API Gateway, DynamoDB, AWS S3, Terraform, LLM API integration

Ценность проекта

Для пользователя:

  • быстрая транскрибация длинных аудиозаписей без загрузки в тяжёлые SaaS-сервисы;
  • контролируемый доступ к результатам через Cognito и предподписанные ссылки;
  • минимальные постоянные расходы за счёт serverless-модели.

Для профессионального профиля:

  • практическое проектирование AWS serverless workflow;
  • работа с асинхронной обработкой, webhook, polling UI и жизненным циклом задачи;
  • применение Infrastructure-as-Code через Terraform;
  • выбор решений исходя из эпизодической нагрузки и ограничений стоимости (cost-driven architecture).

Контекст и проблема

Контекст и предпосылки

Контекст и проблема

Есть периодическая потребность транскрибировать длинные аудиозаписи: интервью, рабочие обсуждения, созвоны, заметки и исследовательские материалы. Типовой файл может занимать 1.5+ часа и весить сотни мегабайт.

Готовые SaaS-сервисы закрывают задачу, но для личного/ограниченного сценария дают избыточный функционал, непрозрачную стоимость и лишний операционный контур: пользователь вручную загружает файл, ждёт обработку, скачивает результат и хранит его отдельно.

Проблема

Ключевая архитектурная проблема: транскрибация является долгой асинхронной операцией, а аудиофайлы слишком велики для прямой передачи через API Gateway/Lambda. Поэтому система должна разделить upload, запуск обработки, получение webhook, хранение результата и скачивание transcript.

Часть записей может содержать приватные сведения, поэтому важно ограничить доступ к интерфейсу, файлам и результатам. При этом передача аудио внешнему transcription provider остаётся осознанным компромиссом, принятым ради стоимости, качества и из-за отсутствия собственной GPU-инфраструктуры.

Цели, требования и ограничения

Цели и нецели

Основные цели проекта

  • Реализовать оптимальную по стоимости транскрибацию аудиозаписей для личного использования

Что не входит в проект

  • Не является общедоступной открытой системой
  • Не является платной системой или монетизируемой платформой

Бизнес-требования

  • BR-001. Сервис доступен через интернет.
  • BR-002. Доступ ограничен авторизованными пользователями.
  • BR-003. Система поддерживает транскрибацию длинных аудио.
  • BR-004. Система поддерживает диаризацию.
  • BR-005. Пользователь может скачать готовый транскрипт.

Функциональные требования

  • FR-001. Пользователь получает presigned upload URL.
  • FR-002. Система создаёт задачу транскрибации, актуальный статус которой пользователь может проверять без обновления страницы.
  • FR-003. Система обновляет статус задачи по мере обработки.
  • FR-004. UI отображает список job и текущий статус.
  • FR-005. Пользователь получает предподписанную URL для готового транскрипта.
  • FR-006. Обработчик Webhook принимает обратный вызов от провайдера транскрибации.

Constraints

  • CON-001. Файлы до 300 MB.
  • CON-002. Длительность записи до 6 часов.
  • CON-003. До 5 файлов транскрибируются одновременно.
  • CON-004. 1–3 пользователя.
  • CON-005. Модель на собственных мощностях не рассматривается.
  • CON-006. Постоянные инфраструктурные расходы должны быть минимальны.

Non-functional requirements

  • NFR-001. Access control.
  • NFR-002. Cost efficiency.
  • NFR-003. Portability.
  • NFR-004. Operability.
  • NFR-005. Resilience of async workflow.

Требования (ФТ)

  • BR-001. Доступность системы через интернет Система должна быть доступна с любого устройства, подключенного к сети интернет.
  • BR-002. Доступ к системе должен быть ограничен Система должна обеспечивать авторизацию и аутентификацию пользователя. Регистрация новых пользователей не предполагается, но управление ими должно быть.
  • BR-003. Поддержка диаризации Система должна поддерживать разделение стенограммы по говорящим.
  • BR-004. форматы файлов. Система должна поддерживать наиболее распространенные форматы записей с диктофона - MP3, AAC.

Правила и Ограничения (НФТ)

  • CON-001. Регион размещения: для пользователей из Европы, Кавказа и Турции. AWS-регион с принципиальной доступностью. Низкая latency не является критичным требованием, так как основной сценарий асинхронный.

  • CON-002. Размер файла записи - до 300мб.

  • CON-003. Продолжительность одной записи - до 6 часов. Система должна стабильно работать на продолжитьельных записях.

  • CON-004. Итоговая стоимость транскрибации Стоимость минуты записи при 40 часах в месяц должна быть не более $0.3/минута с учетом расходов на инфраструктуру (расценка https://speech2text.ru/my/rate ).

  • CON-005. Обязательное ограничение доступа Можно ограничиться 1-2 учетными записями для личных целей.

  • CON-006. Запуск opensource модели транскрибации не предполагается Ввиду отсутствия доступного железа необходимого уровня, а также экономической нецелесообразности аренды такого обрудования.

  • CON-007. Переносимость решения Решение должно быть таким, чтобы его можно было легко поднять и при необходимости свернуть, если потребность в нём временно исчезла.

  • CON-008. Минимальные расходы на поддержание Решение должно требовать минимальные ресурсы для поддержки (установку обновлений безопасности и т.п.).

Роль и обязанности

Моя роль

Я выступал как системный проектировщик и технический владелец решения.

Моя работа включала:

  • перевод личной потребности в требования, ограничения и архитектурную модель;
  • выбор serverless-архитектуры с учётом эпизодической нагрузки и стоимости;
  • проектирование асинронного процесса: upload -> запрос транскрибации -> webhook -> сохранение результата -> скачивание;
  • проектирование модели состояний задачи транскрибации;
  • выбор AWS-сервисов и границ ответственности между Lambda, S3, DynamoDB, API Gateway, Cognito и внешним провайдером транскрибации;
  • описание sequence-диаграм и ADR;
  • использование ИИ-инструментов поддержки разработки как ускорителя реализации при ручном контроле архитектуры, границ безопасности и deployment-решений.

Применение ИИ

Проект разрабатывался с использованием AI.

LLM применялись для ускорения реализации, генерации шаблонного кода и быстрых итераций. Ключевые решения оставались под ручным контролем:

  • интерпретация требований;
  • доменное моделирование;
  • архитектурные решения;
  • границы данных;
  • модель доступа;
  • код ревью;
  • дебаг;
  • решения по развертыванию;
  • техническая документация.

Модель системы

Модель данных

TranscriptionJob

Основная сущность процесса транскрибации.

Атрибуты:

  • fileId
  • ownerUserId
  • inputS3Key
  • transcriptS3Key
  • status
  • providerTranscriptId
  • createdAt
  • updatedAt
  • errorReason
  • speakerMode
  • fileSize
  • durationEstimate

Системные инварианты

  • job принадлежит одному пользователю;
  • download URL выдаётся только владельцу job;
  • transcript можно скачать только в статусе READY;
  • webhook должен быть idempotent;
  • повторный callback от provider не должен создавать новый transcript;
  • failed job не должен блокировать список остальных job;
  • presigned URLs имеют ограниченное время жизни.

DynamoDB - статусы джобов

Каждая задача транскрибации хранится как запись БД с жизненным циклом:

State Machine Значение
UPLOADING Выдана предподписанная ссылка; идёт загрузка клиентом
TRANSMITTING Аудио загружено в S3; отправка в сервис
PROCESSING сервис транскрибирует (transcript_id сохранён)
READY Транскрипт сохранён в S3; доступен для скачивания
ERROR Ошибка AssemblyAI или пайплайна (причина в логе)

Amazon S3

  • Bucket файлов: загрузки аудио и сгенерированные транскрипты (Transcript.txt).
  • Bucket статического сайта: SPA, раздаётся через CloudFront.

API-контракты

API Gateway (аутентификация)

Метод Путь Назначение
GET /upload-url Создать запись; вернуть Presigned POST URL и fileId
GET /jobs Список джобов пользователя (polling UI)
GET /download-url?fileId=... Проверка доступа; Presigned GET URL транскрипта
POST /webhook Callback AssemblyAI (transcript_id)

Все маршруты API Gateway требуют JWT (Amazon Cognito), кроме /webhook (callback провайдера транскрибации).

Amazon Cognito (Hosted UI / PKCE)

Метод Путь Назначение
POST /oauth2/token Обмен authorization code на Access & ID tokens

внешний API провайдера транскрибации

Метод Путь Назначение
POST /v2/transcript Отправка URL аудио + webhook URL; возвращает transcript_id
GET /v2/transcript/{id} Получение готового текста транскрипции

Amazon S3 (прямой доступ клиента)

Метод Цель Назначение
POST Presigned POST URL Прямая загрузка аудио (обход лимитов API Gateway)
GET Presigned GET URL Прямое скачивание транскрипта

Архитектура и интеграции

Архитектура

Архитектурная концепция

Система построена на событийной модели.

Статический frontend раздаётся через S3/CloudFront. Пользователь проходит аутентификацию через Cognito Hosted UI и вызывает API Gateway, используя полученный JWT. API Gateway маршрутизирует запросы в Lambda-функции.

Большие аудиофайлы не проходят через API Gateway и Lambda. Backend выдаёт предподписанную POST URL, после чего браузер загружает файл напрямую в S3. Событие S3 ObjectCreated запускает обработчик, который создаёт временный URL для провайдера транскрибации и отправляет запрос на новую транскрибацию. Внешний провайдер выполняет долгую транскрибацию асинхронно и возвращает результат через webhook. Итоговый текстовый транскрипт сохраняется в S3, а статус задачи изменяется и хранится в DynamoDB.

architecture-beta
    service dynamo(aws:dynamodb)[AWS DynamoDB]
    service lambda(aws:lambda)[AWS Lambda] 
    service api(aws:api-gateway)[AWS API Gateway]
    service static(aws:simple-storage-service)[Static website at Amazon S3]
    service storage(aws:simple-storage-service)[File storage at Amazon S3]
    service browser(logos:chrome)[Browser]
    service cognito(aws:cognito)[AWS Cognito]
    service ai(logos:webhooks)[Transcriber API]
    service front(aws:cloudfront)[AWS CloudFront]

    front:T --> B:static
    browser:T --> B:api
    browser:L --> R:front
    browser:B --> T:cognito

    api:R --> L:lambda
    api:T <-- B:ai
    lambda:T --> R:ai
    lambda:R --> L:dynamo
    lambda:B --> T:storage
    storage:L <-- R:browser

Потоки интеграции

Диаграммы последовательности

Отправка файла аудио

sequenceDiagram
    autonumber

    actor U as Браузер (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB

    U->>API: GET /upload-url (+JWT в Header)
    activate API
    API->>L: Вызов get_upload_url
    deactivate API
    activate L
    L->>DB: Создание записи (Status: UPLOADING)
    L->>S3: Генерация Presigned POST URL
    activate S3
    S3-->>L: Ссылка для загрузки
    deactivate S3
    L-->>U: JSON: { uploadUrl, fileId }
    deactivate L

Прямая загрузка и Асинхронный Триггер (Event-Driven)

sequenceDiagram
    autonumber

    actor U as Браузер (SPA)
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB
    participant AI as TranscribeProvider

    U->>S3: POST Загрузка аудио-файла (Обход API Gateway)
    activate S3
    S3-->>U: 204 No Content (Успех)
    deactivate S3

    S3-)L: Event: ObjectCreated (Асинхронный вызов s3_trigger)
    activate L
    L->>DB: Обновление статуса (TRANSMITTING)
    L->>S3: Генерация временной GET Presigned URL для AI
    L->>AI: POST /v2/transcript (Аудио URL + Webhook URL)
    activate AI
    alt TranscribeProvider принимает запрос
        AI-->>L: 201 Created (transcript_id)
        L->>DB: Status = PROCESSING (сохранение ID)
    else Ошибка API (например, HTTP 400/500)
        AI-->>L: 4xx / 5xx Error
        deactivate AI
        L->>DB: Status = ERROR (Запись причины в лог)
    end
    deactivate L

Обработка ИИ и Webhook (до нескольких минут)

sequenceDiagram
    autonumber

    actor U as Браузер (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB
    participant AI as TranscribeProvider

    loop Каждые 15 секунд (Polling)
        U->>API: GET /jobs
        activate API 
        API->>L: Вызов get_jobs
        deactivate API
        activate L
        L->>DB: Запрос списка файлов пользователя
        activate DB
        DB-->>L: Данные (Status: PROCESSING)
        deactivate DB
        L-->>U: Обновление UI
        deactivate L
    end

    Note over AI, DB: TranscribeProvider завершает работу
    AI->>API: POST /webhook (передача transcript_id)
    activate API
    API->>L: Вызов webhook_TranscribeProvider
    deactivate API
    activate L
    L->>AI: GET /v2/transcript/{id}
    activate AI
    AI-->>L: Готовый текст транскрипции
    deactivate AI
    L->>S3: PUT Сохранение текста (Transcript.txt)
    L->>DB: Обновление статуса (READY)
    deactivate L

Получение результата (Скачивание)

sequenceDiagram
    autonumber

    actor U as Браузер (SPA)
    participant API as API Gateway
    participant L as AWS Lambda
    participant S3 as Amazon S3
    participant DB as DynamoDB

    U->>API: GET /jobs (Очередной опрос)
    activate API
    API-->>U: Status: READY (Кнопка скачивания активна)
    deactivate API

    U->>API: GET /download-url?fileId=...
    activate API
    API->>L: Вызов get_download_url
    deactivate API
    activate L
    L->>DB: Проверка прав доступа пользователя к файлу
    L->>S3: Генерация Presigned GET URL (с Content-Disposition)
    L-->>U: JSON: { downloadUrl }
    deactivate L
    U->>S3: Прямое скачивание текста.txt
    activate S3
    S3-->>U: Файл транскрипции
    deactivate S3

Безопасность, качество и эксплуатация

Модель безопасности и доступа

Зона Риск Контроль
Authentication доступ постороннего пользователя Cognito Hosted UI, JWT validation
Authorization скачивание чужого transcript owner check в DynamoDB перед выдачей presigned GET URL
Upload загрузка слишком большого/неподдерживаемого файла client-side и backend-side validation, content-type/size constraints
S3 access прямой публичный доступ к файлам private buckets, presigned URLs only
Webhook поддельный callback shared secret / provider verification
Secrets утечка provider API key Secrets Manager / encrypted env, no secrets in code
Logs попадание приватных данных в logs не логировать transcript/audio content, только status/error metadata
Cost abuse массовый запуск дорогих job ограничение пользователей, quotas, AWS budget alerts

Требования к доступу

  • Необходимо обеспечить ограничение доступа к сервису.
  • Необходимо обеспечить разделение пользовательских данных.

Аутентификация - AWS Cognito

  • Разделение пользовательских данных: AWS Cognito - встроенный менеджмент учётных записей, регистрация, 2FA, защита от брутфорса и т.п. Сервис бесшовно встроен в экосистему AWS.
  • Проверка прав пользователя на файл при скачивании (get_download_url проверяет доступ в DynamoDB перед выдачей Presigned URL).

Аутентификация

sequenceDiagram
    autonumber

    actor U as Браузер (SPA)
    participant API as API Gateway
    participant C as Amazon Cognito

    U->>API: Запрос к API (без JWT / протухший JWT)
    activate API
    API-->>U: 401 Unauthorized
    deactivate API
    U->>U: SPA очищает локальные данные
    U->>C: Редирект на Hosted UI форму логина
    activate C
    C->>U: Форма логина 
    deactivate C
    U->>C: Ввод данных и попытка аутентификации
    activate C
    alt Некорректные реквизиты
        C-->>U: Возврат ошибки (Invalid credentials)
    else Корректные реквизиты
        C-->>U: Возврат Auth Code (через redirect_uri)
        deactivate C
        U->>C: POST /oauth2/token (Обмен Code на JWT)
        activate C
        C-->>U: Access & ID Tokens
        deactivate C
    end

Режимы отказа

Режим отказа Последствие Обнаружение Митигация / восстановление
Lambda timeout при синхронной транскрибации job не завершается логи Lambda async workflow + webhook
Ошибка provider API 4xx/5xx job переходит в ERROR логи провайдера сохранить reason, показать статус пользователю
Webhook не пришёл задача зависает в PROCESSING UI попытка перезапуска через новую задачу
Повторный webhook возможная перезапись результата двойной коллбэк идемпотентная запись через transcriptId
Пользователь пытается скачать чужой transcript утечка данных проверка прав не пройдена проверка владельца перед выдачей предподписанной URL
Presigned URL истёк пользователь не может загрузить/скачать файл ошибка на клиенте сгенерировать новую предподписанную URL
S3 upload не завершился задача остаётся в UPLOADING зависший UPLOADING статус TTL очистка / повторная попытка загрузки
Утечка API key несанкционированные расходы AWS уведомления о превышении квот ротация ключей, сброс квот, ограничение бюджета на счету провайдера транскрибации + выключение овердрафта
Рост стоимости из-за массовых задач неожиданный счет AWS Budgets / CloudWatch квоты, лимит на одновременность, ограничение бюджета на счету провайдера транскрибации + выключение овердрафта
Транскрипт сохранён, но статус не обновлён UI не показывает готовый результат UI не показывает готовый результат проверка целостности / дебаг логики задач

Оценка масштаба и стоимости

Предполагаемая нагрузка

Параметр Значение
Пользователи 1–3
Аудио в месяц до 40 часов
Средняя длина файла 1.5+ часа
Максимальная длина файла 6 часов
Максимальный размер файла 300 MB
Параллельные job до 5

Драйверы стоимости

Компонент Что влияет на стоимость
Transcription provider минуты/часы аудио
S3 объём аудио и transcript files
DynamoDB количество job/status reads
Lambda количество invocation и длительность handlers
API Gateway количество API-запросов
CloudFront/S3 static hosting frontend traffic
Secrets Manager хранение provider API key
CloudWatch logs retention

Логика расчета стоимости

Основная стоимость находится не в AWS compute, а во внешнем transcription provider. AWS Lambda, API Gateway, DynamoDB и S3 при заданном профиле нагрузки остаются вторичными cost drivers. Поэтому архитектура оптимизирована не под высокую нагрузку, а под минимальные постоянные расходы и отсутствие простаивающей инфраструктуры.