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

9.3 KiB

Why

Раздача «Отчаянные домохозяйки» — один торрент на 8 сезонов, 180 файлов — распозналась негодно: в план попало ровно 100 файлов, потому что промпт усекает список файлов жёсткой константой defaultMaxFiles = 100. Остальные 80 файлов модель не видела, в интерфейсе они не показаны никак, а причины ревью распухли до сорока строк «пропуск серий в сезоне N». Порядок строк раскладки в UI — тот, в каком их вернула модель, то есть вперемешку по сезонам.

Просто поднять лимит нельзя: каждый элемент плана обязан нести дословную копию пути (~70 токенов на кириллице), 180 файлов дают ~15k выходных токенов при нашем же потолке defaultMaxTokens = 4000. Обрыв генерации при этом неотличим от мусора — finish_reason из ответа разбирается, но не проверяется, и обрыв уходит в три бессмысленных ретрая с полным промптом. Вход тут не проблема (12–15k токенов промпта — ничто для современной модели), проблема целиком на стороне ответа.

What Changes

  • BREAKING (контракт с моделью, не с пользователем): элемент files[] в ответе LLM адресует файл номером из напечатанного нами списка (i), а не дословной копией пути. src заполняем сами резолвом индекса. Выход падает с ~85 до ~14 токенов на файл; путь, которого нет в торренте, становится невыразимым. Старый формат с src принимается как запасной, чтобы привычка модели не давала ретрая на пустом месте.
  • Негодный элемент ответа (номер вне диапазона, повторная адресация файла, элемент без номера и пути) отбрасывается поимённой причиной, а не роняет весь план: иначе одна ошибка в одном из 180 элементов воспроизводит ровно тот отказ, против которого change и затевается. Элемент, несущий и номер, и путь, резолвится по номеру, а расхождение между ними становится причиной ревью — бесплатной сверкой против сдвига адресации.
  • Жёсткое усечение списка файлов снимается: модели показываются все файлы раздачи. max_files и max_tokens выводятся в [recognition] с дефолтами 500 и 8000 — сейчас оба зашиты в код и на проде не крутятся.
  • Повторная попытка перестаёт переприсылать список файлов: номера названы в первом сообщении диалога, и цена неудачного распознавания больше не растёт вместе с числом попыток.
  • Порядок файлов, напечатанных в промпте, детерминирован и совпадает с порядком резолва индексов — иначе повторное распознавание из ревью сдвинет привязку.
  • Обрыв генерации (finish_reason = length) распознаётся как отдельная причина ухода в review и не ретраится тем же промптом.
  • В раскладке видны все файлы торрента: попавшие в план — с ролью и целью, не попавшие — отдельной строкой. Молчаливый пропуск файла моделью перестаёт быть невидимым. Загрузки, распознанные до этого изменения, списка файлов не имеют — и виджет говорит об этом прямо, вместо того чтобы показать план как полный перечень.
  • Строки раскладки упорядочены полностью — разложенные по (сезон, серия, путь), затем файлы плана без цели, затем файлы вне плана, — а не по порядку ответа модели.
  • Причины ревью по нумерации серий сворачиваются: перечисление недостающих серий по сезону одной строкой и сводка покрытия плана вместо строки на каждый разрыв. Покрытие показывается, когда покрыты не все файлы, но блокирует авто-раскладку только непокрытый видеофайл: иначе опущенный моделью .nfo выключил бы авто-путь на типовых раздачах. Отсюда же следствие для решения: Reasons перестают быть однородным списком «причин не-авто» — в них появляются информационные строки, и решение считается по блокирующим причинам, а не по длине списка.

Вне объёма: детерминированный разбор сезона/серии из имён файлов и сверка с ответом модели — следующий change (этап B). Здесь модель по-прежнему единственный источник разметки файлов.

Capabilities

New Capabilities

Нет.

Modified Capabilities

  • recognition: контракт ответа LLM (адресация файла индексом вместо дословного пути), отказ от усечения списка файлов и вывод лимитов в конфиг, обрыв генерации как отдельная причина review, форма причин по нумерации серий, решение auto/review считается по блокирующим причинам (в Reasons появились информационные строки).
  • web-ui: раскладка показывает все файлы раздачи, включая не попавшие в план, и упорядочена по сезону и серии.

Impact

  • internal/recognize: prompt.go (нумерация и печать списка, схема в промпте, correction-сообщение без повторной печати списка), validate.go (резолв индекса вместо поиска пути, отбраковка негодных элементов, свёртка предупреждений, покрытие по видеофайлам), recognize.go (упорядочивание входного списка в самом узле, лимиты, обработка обрыва).
  • internal/llm: finish_reason доводится до вызывающего в Response.
  • internal/config: [recognition].max_files, [recognition].max_tokens; config.example.toml и docs/database.md (настройки с числовым значением).
  • internal/httpapi/files.go и web/templates/partials/layout_widget.html: сортировка, файлы вне плана, колонка # (номер строки виджета обязан совпасть с номером адресации либо исчезнуть), строка о недоступном списке файлов.
  • Хранилище: миграция 0012 — колонка со списком файлов раздачи в таблице recognition, и правка ER-схемы в docs/database.md (без неё краснеет шаг canon гейта).
  • Совместимость: сохранённые планы в БД хранят src путями и не меняются. Записи, созданные до миграции, списка файлов не имеют — виджет называет это явно. Уже применённые раскладки не затрагиваются.
  • Риск: ошибка модели в индексе становится тихой (валидный, но не тот файл). Смягчение — требование уникальности индексов и сводка покрытия в причинах; перекрёстная проверка разметки приходит в этапе B.