спека: заведён change large-release-recognition для больших раздач
- контракт с моделью переводится на адресацию файла номером строки списка, усечение промпта снимается, лимиты уходят в конфиг - раскладка показывает все файлы раздачи, покрытие плана блокирует авто только при непокрытом видеофайле
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Разбор сигналов LLM в структурированный план
|
||||
|
||||
Система SHALL передавать LLM недоверенные сигналы (имя торрента, дерево файлов с
|
||||
размерами, текстовый контекст и накопленные подсказки, пред-парс) и получать
|
||||
структурированный план в схеме: `type` (`movie`|`series`), `title`,
|
||||
`original_title`, `year`, `provider_hint`, `files[]` и `confidence`. Каждый
|
||||
элемент `files[]` SHALL адресовать файл раздачи номером строки из напечатанного
|
||||
системой списка файлов, а не копией пути, и нести `role`
|
||||
(`main`|`episode`|`subtitle`|`extra`|`sample`|`ignore`) и, для сериала,
|
||||
per-file `season`/`episode` (отдельного скалярного `season` быть SHALL NOT — так
|
||||
выражаются мультисезонные паки и спецвыпуски). Путь файла (`src`) SHALL
|
||||
подставлять сама система резолвом номера по своему списку. Ответ, где элемент
|
||||
`files[]` несёт путь вместо номера, система SHALL принимать как запасной формат,
|
||||
проверяя совпадение пути с реальным файлом торрента. Элемент, несущий и номер, и
|
||||
путь, система SHALL резолвить по номеру; несовпадение присланного пути с
|
||||
резолвом номера SHALL давать причину ухода в `review` (сигнал сдвига адресации),
|
||||
а не молчаливое затирание.
|
||||
|
||||
Негодный элемент `files[]` — номер вне диапазона списка, повторная адресация уже
|
||||
адресованного файла, отсутствие и номера, и пути — система SHALL отбрасывать с
|
||||
причиной ухода в `review`, сохраняя остальной план; при повторной адресации в
|
||||
плане SHALL оставаться первый элемент. Отбраковка отдельных элементов SHALL NOT
|
||||
считаться ошибкой разбора и SHALL NOT порождать повторный запрос к модели. План,
|
||||
в котором после отбраковки не осталось ни одного файла, система SHALL считать
|
||||
неразобранным.
|
||||
|
||||
Порядок списка файлов SHALL быть свойством самого распознавания, а не
|
||||
дисциплиной вызывающего: распознавание SHALL приводить полученный список к
|
||||
детерминированному порядку само, печатать промпт и резолвить номера по одному и
|
||||
тому же упорядоченному списку. Повторное распознавание той же раздачи SHALL
|
||||
давать ту же нумерацию.
|
||||
|
||||
План MAY дополнительно нести опциональное скалярное поле `director` (режиссёр).
|
||||
Это поле НЕ требуется от LLM и НЕ участвует в структурной валидации или гейте
|
||||
авто-раскладки; его заполняют подтверждённый матч метабазы (авто) или закреплённый
|
||||
в ревью выбранный источник (через override, см. `metadata-match`/`review`) как
|
||||
недоверенное косметическое значение для вывода отображаемого имени. Как недоверенное
|
||||
человекочитаемое поле, `director` SHALL NOT входить в plan-санитайзинг (он чистит
|
||||
`title`/`original_title`/`provider_hint`); очистка режиссёра применяется при рендере
|
||||
имени. Пустой `director` SHALL быть штатным (режиссёр неизвестен).
|
||||
|
||||
#### Scenario: План сериала с per-file нумерацией
|
||||
|
||||
- **GIVEN** сезон-пак из 10 видеофайлов
|
||||
- **WHEN** LLM возвращает план
|
||||
- **THEN** `type` = `series`, а каждый видеофайл несёт свои `season`/`episode`
|
||||
|
||||
#### Scenario: Номер файла резолвится в путь
|
||||
|
||||
- **GIVEN** список из 180 файлов, напечатанный в промпте
|
||||
- **WHEN** модель возвращает элемент плана с номером 13
|
||||
- **THEN** система подставляет в `src` путь тринадцатого файла своего списка
|
||||
|
||||
#### Scenario: Номер вне диапазона отбрасывает элемент, а не план
|
||||
|
||||
- **GIVEN** раздача из 180 файлов и ответ модели, где один элемент несёт номер 181,
|
||||
а остальные 179 корректны
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** негодный элемент отброшен, остальные 179 файлов остаются в плане
|
||||
- **AND** задача уходит в `review` с причиной, называющей отброшенный элемент
|
||||
- **AND** повторный запрос к модели не выполняется
|
||||
|
||||
#### Scenario: Повторная адресация одного файла
|
||||
|
||||
- **GIVEN** ответ модели, где два элемента адресуют один и тот же файл
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** в плане остаётся первый элемент, второй отброшен
|
||||
- **AND** задача уходит в `review` с причиной, называющей повторную адресацию
|
||||
|
||||
#### Scenario: Все элементы негодны — план не разобран
|
||||
|
||||
- **GIVEN** ответ модели, где ни один элемент `files[]` не адресует реальный файл
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** план не принимается как валидный
|
||||
|
||||
#### Scenario: Путь вместо номера принимается как запасной формат
|
||||
|
||||
- **GIVEN** ответ модели, где элемент `files[]` несёт путь файла вместо номера
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** разбор успешен, если путь совпадает с реальным файлом торрента
|
||||
- **AND** correction-ретрай не выполняется
|
||||
|
||||
#### Scenario: Номер и путь одновременно — главенствует номер
|
||||
|
||||
- **GIVEN** элемент `files[]`, несущий и номер, и путь, которые указывают на разные файлы
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** `src` берётся резолвом номера
|
||||
- **AND** задача уходит в `review` с причиной о несовпадении присланного пути с резолвом
|
||||
|
||||
#### Scenario: Несуществующий src отклоняется
|
||||
|
||||
- **GIVEN** ответ LLM, где `files[].src` не совпадает ни с одним файлом торрента
|
||||
- **WHEN** план разбирается
|
||||
- **THEN** такой элемент отбрасывается с причиной, а посторонний путь в план не попадает
|
||||
|
||||
#### Scenario: Нумерация не зависит от вызывающего
|
||||
|
||||
- **GIVEN** два вызова распознавания одной раздачи, получившие список файлов в разном порядке
|
||||
- **WHEN** собирается промпт
|
||||
- **THEN** напечатанная нумерация в обоих вызовах одинакова
|
||||
|
||||
#### Scenario: Режиссёр не требуется от LLM и не влияет на гейт
|
||||
|
||||
- **GIVEN** ответ LLM без поля `director`
|
||||
- **WHEN** план разбирается и оценивается
|
||||
- **THEN** разбор успешен, `director` пуст
|
||||
- **AND** отсутствие режиссёра не влияет на структурную валидацию и решение
|
||||
auto/review
|
||||
|
||||
### Requirement: Провайдер LLM за абстракцией со структурированным выводом
|
||||
|
||||
Доступ к LLM SHALL быть за интерфейсом с выбором реализации по полю `[llm].type`
|
||||
(первый тип — `openai-compat`). Система SHALL запрашивать JSON-режим
|
||||
(`response_format: {"type":"json_object"}`), срезать ```-ограждения и
|
||||
валидировать ответ в Go против схемы плана. При ошибке разбора система SHALL
|
||||
ретраить до `[llm].max_retries`, передавая модели саму ошибку и схему. Если после
|
||||
ретраев ответ не разобран, задача SHALL уходить в `review` (НЕ в `failed`) с
|
||||
причиной «ответ LLM не разобран».
|
||||
|
||||
Повторный запрос SHALL NOT переприсылать список файлов раздачи: список назван в
|
||||
первом сообщении диалога, и его номера сохраняют смысл на всех попытках. Размер
|
||||
запроса SHALL NOT расти пропорционально числу попыток.
|
||||
|
||||
Признак завершения генерации (`finish_reason`) SHALL доходить до распознавания.
|
||||
Ответ, оборванный по длине, система SHALL уводить в `review` с отдельной
|
||||
причиной «ответ модели обрезан» и SHALL NOT повторять запрос ни тем же промптом,
|
||||
ни его вариантом: обрыв означает, что ответ не поместился, а не что модель
|
||||
ошиблась.
|
||||
|
||||
#### Scenario: Неразобранный ответ уходит в review
|
||||
|
||||
- **GIVEN** LLM, чей ответ не проходит валидацию схемы после всех ретраев
|
||||
- **WHEN** завершается распознавание
|
||||
- **THEN** задача переходит в `review` с причиной «ответ LLM не разобран»
|
||||
- **AND** задача НЕ переходит в `failed`
|
||||
|
||||
#### Scenario: Повторная попытка не переприсылает список файлов
|
||||
|
||||
- **GIVEN** раздача из 180 файлов и ответ модели с ошибкой разбора
|
||||
- **WHEN** выполняется correction-ретрай
|
||||
- **THEN** список файлов раздачи повторно не печатается
|
||||
- **AND** размер запроса второй попытки сопоставим с размером первой
|
||||
|
||||
#### Scenario: Обрыв по длине не ретраится
|
||||
|
||||
- **GIVEN** ответ модели с признаком обрыва генерации по длине
|
||||
- **WHEN** завершается распознавание
|
||||
- **THEN** задача переходит в `review` с причиной «ответ модели обрезан»
|
||||
- **AND** повторных запросов к модели не выполняется
|
||||
|
||||
### Requirement: Модель уверенности и решение auto/review
|
||||
|
||||
Система SHALL раскладывать автоматически (без review) только при выполнении
|
||||
ВСЕГО: (1) подтверждённый единичный сильный матч в базе (`metadata-match`) с
|
||||
`provider_id`; (2) структурная валидация без предупреждений (фильм — ровно один
|
||||
основной видеофайл; сериал — число серий бьётся с базой, нумерация S·E
|
||||
консистентна); (3) согласованность пред-парса и LLM по типу/названию/году. Иначе
|
||||
задача SHALL уходить в `review` с явной причиной. Самооценку LLM (`confidence`)
|
||||
система SHALL учитывать лишь как вспомогательный сигнал, НЕ как единственный гейт.
|
||||
|
||||
Список причин распознавания SHALL быть неоднородным: кроме блокирующих причин он
|
||||
MAY содержать информационные строки, которые показываются человеку и сохраняются
|
||||
вместе с распознаванием, но авто-раскладку НЕ отменяют — такова сводка покрытия
|
||||
плана (см. «Причины по нумерации серий и покрытию плана»). Поэтому решение
|
||||
auto/review система SHALL считать по **блокирующим** причинам, а НЕ по длине
|
||||
списка причин: непустой список сам по себе SHALL NOT означать `review`.
|
||||
|
||||
#### Scenario: Нет матча в базе — всегда review
|
||||
|
||||
- **GIVEN** план без подтверждённого матча в базе (база выключена или матча нет)
|
||||
- **WHEN** принимается решение auto/review
|
||||
- **THEN** задача уходит в `review`, авто-раскладка не делается
|
||||
|
||||
#### Scenario: Матч и чистая валидация — авто
|
||||
|
||||
- **GIVEN** подтверждённый единичный матч, чистая структурная валидация и
|
||||
согласованность сигналов
|
||||
- **WHEN** принимается решение
|
||||
- **THEN** допускается авто-раскладка (при отсутствии `force_review`)
|
||||
|
||||
#### Scenario: Информационная причина авто не отменяет
|
||||
|
||||
- **GIVEN** подтверждённый матч и чистая валидация, но покрытие плана неполно
|
||||
только за счёт файлов-спутников
|
||||
- **WHEN** принимается решение auto/review
|
||||
- **THEN** список причин непуст — в нём сводка покрытия
|
||||
- **AND** авто-раскладка допускается
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Полный список файлов раздачи в промпте
|
||||
|
||||
Промпт распознавания SHALL включать все файлы раздачи, а не фиксированную их
|
||||
часть. Предельное число файлов и предельный размер ответа модели SHALL задаваться
|
||||
настройками `[recognition].max_files` и `[recognition].max_tokens`; предел числа
|
||||
файлов служит предохранителем от аномальной раздачи, а не рабочим ограничением, и
|
||||
его значение по умолчанию SHALL соответствовать размеру промпта, который заведомо
|
||||
принимает модель. Если список всё же усечён пределом, распознавание SHALL называть
|
||||
усечение отдельной причиной ухода в `review`. Отказ модели, вызванный размером
|
||||
запроса, распознавание SHALL называть причиной о размере запроса, а не текстом
|
||||
провайдера.
|
||||
|
||||
#### Scenario: Раздача на сотни файлов показана модели целиком
|
||||
|
||||
- **GIVEN** раздача из 180 файлов и `max_files` = 500
|
||||
- **WHEN** собирается промпт распознавания
|
||||
- **THEN** в списке файлов промпта присутствуют все 180 файлов
|
||||
|
||||
#### Scenario: Усечение названо причиной
|
||||
|
||||
- **GIVEN** раздача, число файлов которой превышает `max_files`
|
||||
- **WHEN** завершается распознавание
|
||||
- **THEN** задача уходит в `review`, и среди причин названо усечение списка файлов
|
||||
|
||||
### Requirement: Причины по нумерации серий и покрытию плана компактны
|
||||
|
||||
Причины ухода в `review`, порождённые нумерацией серий, SHALL быть свёрнуты: на
|
||||
сезон приходится не более одной причины, перечисляющей недостающие серии.
|
||||
|
||||
Распознавание SHALL называть покрытие плана — сколько файлов раздачи попало в
|
||||
план из общего числа, — когда покрыты не все файлы. Блокировать авто-раскладку
|
||||
SHALL только непокрытый видеофайл: файл, который модель не адресовала и который
|
||||
по расширению является видео, означает потерянную серию или фильм. Непокрытые
|
||||
файлы-спутники (субтитры, изображения, тексты, служебные файлы) SHALL
|
||||
показываться в покрытии, но SHALL NOT входить в структурную валидацию и SHALL NOT
|
||||
влиять на решение auto/review: модель вправе не перечислять то, что не
|
||||
раскладывается.
|
||||
|
||||
#### Scenario: Пропуски сезона свёрнуты в одну причину
|
||||
|
||||
- **GIVEN** план сезона, где недостают серии E05, E07 и E11
|
||||
- **WHEN** формируются причины ухода в review
|
||||
- **THEN** этому сезону соответствует одна причина, перечисляющая недостающие серии
|
||||
|
||||
#### Scenario: Непокрытый видеофайл блокирует авто
|
||||
|
||||
- **GIVEN** раздача, где один видеофайл не адресован ни одним элементом плана
|
||||
- **WHEN** принимается решение auto/review
|
||||
- **THEN** задача уходит в `review` с причиной о непокрытом видеофайле
|
||||
|
||||
#### Scenario: Непокрытые файлы-спутники авто не блокируют
|
||||
|
||||
- **GIVEN** раздача, где не адресованы только `.nfo`, скриншоты и текстовый файл,
|
||||
а все видеофайлы покрыты, матч подтверждён и валидация чиста
|
||||
- **WHEN** принимается решение auto/review
|
||||
- **THEN** авто-раскладка допускается
|
||||
- **AND** покрытие плана показано человеку, но причиной ухода в review не является
|
||||
Reference in New Issue
Block a user