Files
jellybit/openspec/changes/large-release-recognition/specs/recognition/spec.md
T
av 01ac0430a9 спека: заведён change large-release-recognition для больших раздач
- контракт с моделью переводится на адресацию файла номером строки списка,
  усечение промпта снимается, лимиты уходят в конфиг
- раскладка показывает все файлы раздачи, покрытие плана блокирует авто
  только при непокрытом видеофайле
2026-09-02 08:54:32 +03:00

19 KiB
Raw Blame History

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 не является