внутренняя модель перестроена вокруг аудиозаписи
- audiorecords вместо transcribe_jobs: приложения (texts, structures, recognitions, record_events, topics) живут своими коллекциями, ссылки на исходник и на приведённую копию перестали переставляться - рубеж называет достигнутое, отказ стал признаком остановки с причиной, а сторожей стало двое: число отказов и время в рубеже - воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
# recognition Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change record-centric-model. Update Purpose after archive.
|
||||
## Requirements
|
||||
### Requirement: Попытка распознавания хранится отдельно от записи
|
||||
|
||||
Сервис SHALL держать всё, что принадлежит внешнему распознавателю, отдельной
|
||||
строкой, связанной с аудиозаписью, и MUST не хранить это колонками самой записи.
|
||||
К попытке относятся имя провайдера, имя модели, идентификатор операции у
|
||||
провайдера, адрес, по которому провайдер читал аудио, время начала и время
|
||||
завершения.
|
||||
|
||||
Разрез проходит по одной границе: **зависит ли вещь от провайдера
|
||||
распознавания**. Идентификатор операции — самое провайдерское, что есть в
|
||||
модели, а копия аудио во внешнем хранилище существует только потому, что
|
||||
сегодняшний провайдер читает запись по адресу; другой провайдер её не потребует.
|
||||
Оставленные колонками записи, они делают смену провайдера правкой доменной
|
||||
сущности.
|
||||
|
||||
Копия аудио во внешнем хранилище MUST не считаться файлом записи: у записи
|
||||
остаётся ровно две своих копии — принятая и приведённая, — а ключ объекта живёт
|
||||
в строке попытки.
|
||||
|
||||
#### Scenario: Идентификатор операции лежит в попытке
|
||||
|
||||
- **GIVEN** запись отправлена на распознавание
|
||||
- **WHEN** смотрят, где лежит идентификатор операции у провайдера
|
||||
- **THEN** он лежит в строке попытки распознавания
|
||||
- **AND** колонки с ним у самой записи нет
|
||||
|
||||
#### Scenario: Копия во внешнем хранилище не подменяет файл записи
|
||||
|
||||
- **GIVEN** запись прошла отправку на распознавание
|
||||
- **WHEN** смотрят ссылки записи на файлы
|
||||
- **THEN** они ведут на принятую и на приведённую копии
|
||||
- **AND** ключ объекта во внешнем хранилище лежит в строке попытки
|
||||
|
||||
### Requirement: Сырой ответ провайдера сохраняется целиком
|
||||
|
||||
Сервис SHALL сохранять ответ распознавателя целиком, в том виде, в каком он
|
||||
пришёл, и MUST хранить его вложением, а не колонкой строки попытки.
|
||||
|
||||
Хранится он потому, что **результат операции у провайдера не переспрашивается**:
|
||||
связь реплики с говорящим сервис строить пока не умеет, и когда научится, архив
|
||||
пересчитается из сохранённого без повторной оплаты.
|
||||
|
||||
Вложением, а не колонкой, — потому что шаг опроса читает строку попытки часто, а
|
||||
хранилище читает запись целиком: ответ на многочасовую запись, положенный
|
||||
колонкой, ехал бы в память при каждом опросе.
|
||||
|
||||
Чтение строки попытки шагом опроса MUST не тянуть за собой сохранённый ответ.
|
||||
|
||||
Сохранённый ответ — это полный текст речи, и закрыт он MUST быть наравне с самой
|
||||
записью: поле вложения помечено защищённым, правило просмотра пускает только
|
||||
владельца связанной записи, ссылка не попадает ни в журнал, ни в метку метрики.
|
||||
Норму держит capability `storage`, требование «Содержимое записи закрыто во всех
|
||||
коллекциях, где лежит»; здесь она названа потому, что коллекция попыток — то
|
||||
место, куда содержимое приезжает впервые.
|
||||
|
||||
#### Scenario: Ответ сохранён и читается позже
|
||||
|
||||
- **GIVEN** распознавание завершилось и ответ провайдера получен
|
||||
- **WHEN** запись доходит до конечного рубежа
|
||||
- **THEN** сохранённый ответ доступен по строке попытки целиком
|
||||
|
||||
#### Scenario: Опрос не тянет сохранённый ответ
|
||||
|
||||
- **GIVEN** у попытки распознавания есть сохранённый ответ
|
||||
- **WHEN** шаг опроса читает строку попытки
|
||||
- **THEN** сохранённый ответ в память при этом не читается
|
||||
|
||||
### Requirement: Структура реплик строится из сохранённого ответа
|
||||
|
||||
Сервис SHALL строить структуру реплик записи из сохранённого ответа провайдера и
|
||||
MUST не обращаться к провайдеру повторно ради неё. Структура MUST хранить время
|
||||
каждой реплики и MUST лежать отдельной строкой со ссылкой с записи, а не
|
||||
колонкой записи.
|
||||
|
||||
У структуры MUST быть номер версии её вида: разбор сохранённого ответа изменится
|
||||
раньше, чем архив пересчитают, и по номеру видно, какой разбор её построил.
|
||||
|
||||
Говорящих структура сегодня не размечает: связь реплики с разбором говорящего у
|
||||
провайдера не выяснена. Требование этого и не заказывает — оно заказывает
|
||||
источник, из которого разметка станет возможной без повторной оплаты.
|
||||
|
||||
#### Scenario: Структура собрана без обращения к провайдеру
|
||||
|
||||
- **GIVEN** ответ провайдера сохранён
|
||||
- **WHEN** сервис строит структуру реплик
|
||||
- **THEN** структура собрана с временем каждой реплики
|
||||
- **AND** к провайдеру не уходит ни одного обращения
|
||||
|
||||
### Requirement: Разбор формата провайдера не выходит за адаптер
|
||||
|
||||
Распознаватель SHALL отдавать сервису доменный результат — реплики со временем,
|
||||
плоский текст и байты ответа на хранение, — и MUST не отдавать сырой формат
|
||||
провайдера. Ни один шаг конвейера MUST не знать, каким потоком и какими полями
|
||||
провайдер отвечает.
|
||||
|
||||
Сегодня разбор потока лежит в шаге: адаптер отдаёт строку, склеенную из
|
||||
альтернатив, и всё, что провайдер сказал сверх текста, теряется на границе
|
||||
контракта.
|
||||
|
||||
#### Scenario: Шаг получает реплики, а не поток провайдера
|
||||
|
||||
- **WHEN** шаг конвейера забирает результат распознавания
|
||||
- **THEN** он получает реплики со временем, плоский текст и байты на хранение
|
||||
- **AND** формата провайдера в этом результате нет
|
||||
|
||||
### Requirement: Заливка и отправка на распознавание разделены
|
||||
|
||||
Сервис SHALL разделять укладку аудио туда, откуда провайдер его прочитает, и
|
||||
отправку операции распознавания: это два разных обращения с разной ценой
|
||||
повтора. Повтор укладки MUST быть бесплатен и класть объект под тем же ключом;
|
||||
повтор отправки оплачивается наружу и MUST не происходить, когда операция уже
|
||||
заведена.
|
||||
|
||||
Разделение нужно затем, чтобы шаг мог проверить сделанное прежде, чем платить:
|
||||
объект нужного размера на месте — укладку MUST не повторять; идентификатор
|
||||
операции в строке попытки есть — отправку MUST не повторять.
|
||||
|
||||
Строка попытки MUST заводиться **до** обращения к провайдеру: окно между ответом
|
||||
провайдера и записью идентификатора — то место, где теряется оплаченное. Мягкую
|
||||
остановку сервиса отправка MUST переживать своим пределом по времени; полной
|
||||
защиты от жёсткого обрыва процесса требование не даёт и дать не может — это
|
||||
остаточный риск, названный в дизайне, а не норма.
|
||||
|
||||
#### Scenario: Объект уже лежит, а операции ещё нет
|
||||
|
||||
- **GIVEN** аудио уже уложено туда, откуда провайдер его читает, и размер совпадает
|
||||
- **AND** идентификатора операции в строке попытки нет
|
||||
- **WHEN** шаг повторяется
|
||||
- **THEN** укладка не повторяется
|
||||
- **AND** операция отправляется
|
||||
|
||||
#### Scenario: Операция уже заведена
|
||||
|
||||
- **GIVEN** в строке попытки есть идентификатор операции
|
||||
- **WHEN** шаг повторяется
|
||||
- **THEN** отправка не повторяется
|
||||
- **AND** шаг переходит к опросу этой операции
|
||||
|
||||
#### Scenario: Операция принята, а сервис мягко останавливают
|
||||
|
||||
- **GIVEN** отправка операции ушла провайдеру
|
||||
- **AND** сервис в эту минуту останавливают мягко
|
||||
- **WHEN** провайдер отвечает идентификатором операции
|
||||
- **THEN** идентификатор сохраняется в строке попытки
|
||||
- **AND** повторная отправка той же записи не заводится
|
||||
|
||||
Reference in New Issue
Block a user