Compare commits
3 Commits
d149cb7481
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
1e8000429a
|
|||
|
70b3c7ae14
|
|||
|
d727966f29
|
@@ -0,0 +1,69 @@
|
|||||||
|
# Авто-раскладка только при подтверждённом матче в метабазе
|
||||||
|
|
||||||
|
- Дата: 2026-06-13
|
||||||
|
|
||||||
|
## Контекст
|
||||||
|
|
||||||
|
jellybit распознаёт содержимое релиза через LLM по **недоверенным**
|
||||||
|
сигналам: имя торрента, текстовый контекст человека, распарсенное
|
||||||
|
сообщение бота — всё управляется извне и может содержать инъекции. По
|
||||||
|
результату распознавания нужно решить: разложить файлы хардлинками
|
||||||
|
автоматически или отправить на ревью человеку. Цена ошибки авто-раскладки
|
||||||
|
реальна — мусор в библиотеке Jellyfin под неверным названием/папкой,
|
||||||
|
возможно поверх чужого. Хочется максимум авто, но не ценой тихих ошибок.
|
||||||
|
|
||||||
|
Силы и ограничения:
|
||||||
|
|
||||||
|
- LLM хорошо разбирает русские и релиз-имена, но галлюцинирует, а его
|
||||||
|
самооценка (`confidence`) плохо откалибрована и тривиально поддаётся
|
||||||
|
инъекции из тех же недоверенных сигналов.
|
||||||
|
- Внешние базы (TMDB/TVDB/TVMaze) дают **независимый** авторитетный сигнал:
|
||||||
|
каноническое имя + `provider_id`. Но русские релизы и аниме часто в них
|
||||||
|
отсутствуют.
|
||||||
|
- Безопасность раскладки уже держится на валидации пути, не на промпте
|
||||||
|
(см. [recognition.md](../specs/recognition.md)); решение «авто vs review» —
|
||||||
|
второй слой защиты, на уровне доверия результату.
|
||||||
|
|
||||||
|
## Рассмотренные варианты
|
||||||
|
|
||||||
|
- **Гейт по самооценке LLM (`confidence ≥ порог`).** Просто и даёт
|
||||||
|
максимум авто. Но `confidence` не откалибрована и инъектируема —
|
||||||
|
«уверенный» неверный ответ прошёл бы молча. Небезопасно.
|
||||||
|
- **LLM + структурная валидация, без обязательной базы.** Ловит часть
|
||||||
|
ошибок (число файлов у фильма, дыры/дубли в нумерации S·E), но не ловит
|
||||||
|
«правильную структуру под неверным названием». Недостаточно как
|
||||||
|
единственный гейт авто.
|
||||||
|
- **Авто только при подтверждённом матче в базе + валидация +
|
||||||
|
согласованность сигналов.** Независимый авторитет снимает риск «LLM
|
||||||
|
придумал». Цена — рус/аниме (нет в базах) всегда идут в review, но это и
|
||||||
|
так нужный кейс.
|
||||||
|
|
||||||
|
## Решение
|
||||||
|
|
||||||
|
Авто-раскладку делаем, только если выполнено **всё**: (1) единственный
|
||||||
|
сильный матч в метабазе по названию+году, давший `provider_id`;
|
||||||
|
(2) структурная валидация без предупреждений; (3) пред-парс (`go-ptn`) и
|
||||||
|
LLM не противоречат по типу/названию/году. Нет матча или база выключена →
|
||||||
|
**всегда review**. Самооценку LLM учитываем лишь как вспомогательный
|
||||||
|
сигнал, не как гейт.
|
||||||
|
|
||||||
|
Почему так: безопасность держится на **независимой** проверке (база), а не
|
||||||
|
на доверии к выходу LLM, построенному из недоверенных данных. Это разом
|
||||||
|
закрывает основной кейс (рус/аниме отсутствуют в базах → человек
|
||||||
|
подтверждает) и убирает целый класс тихих ошибок «модель уверенно
|
||||||
|
ошиблась». Review здесь — не наказание, а штатный режим для всего, что
|
||||||
|
база не подтвердила (петля «догадка → подсказка → перераспознавание», см.
|
||||||
|
[review-ux.md](../specs/review-ux.md)). Полная модель уверенности — в
|
||||||
|
[recognition.md](../specs/recognition.md).
|
||||||
|
|
||||||
|
## Последствия
|
||||||
|
|
||||||
|
- `+` Нет тихих авто-ошибок раскладки: всё неподтверждённое видит человек.
|
||||||
|
- `+` `provider_id` из базы заодно даёт каноническое имя папки
|
||||||
|
(`[tmdbid-…]`) — Jellyfin не путает русские названия.
|
||||||
|
- `−` Рус/аниме и всё, чего нет в базах, всегда требует ручного
|
||||||
|
подтверждения — авто там недоступно by design.
|
||||||
|
- `−` Без включённых TMDB/TVDB/TVMaze авто-раскладки нет вовсе: сервис
|
||||||
|
работает в режиме «распознал → review».
|
||||||
|
- Делает цикл ревью критичным: если он неудобен, ручное подтверждение
|
||||||
|
станет узким местом — поэтому review-ux вынесен в отдельную спеку.
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
|
|
||||||
| Дата | Запись | Статус |
|
| Дата | Запись | Статус |
|
||||||
| ---------- | ---------------------------------------------------------------- | ------ |
|
| ---------- | ---------------------------------------------------------------- | ------ |
|
||||||
|
| 2026-06-13 | [Авто-раскладка только при матче в метабазе](ADR-2026-06-13-auto-link-requires-db-match.md) | — |
|
||||||
| 2026-06-13 | [Docker как единица деплоя](ADR-2026-06-13-docker-deploy.md) | — |
|
| 2026-06-13 | [Docker как единица деплоя](ADR-2026-06-13-docker-deploy.md) | — |
|
||||||
| 2026-06-13 | [Хардлинки вместо копирования и симлинков](ADR-2026-06-13-hardlinks.md) | — |
|
| 2026-06-13 | [Хардлинки вместо копирования и симлинков](ADR-2026-06-13-hardlinks.md) | — |
|
||||||
| 2026-06-13 | [Go и доставка одним бинарём](ADR-2026-06-13-go-single-binary.md) | — |
|
| 2026-06-13 | [Go и доставка одним бинарём](ADR-2026-06-13-go-single-binary.md) | — |
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ notes пояснения, неоднозначности
|
|||||||
|
|
||||||
## Модель уверенности
|
## Модель уверенности
|
||||||
|
|
||||||
|
Почему авто только при матче в базе, а не по самооценке LLM —
|
||||||
|
[ADR-2026-06-13-auto-link-requires-db-match](../adr/ADR-2026-06-13-auto-link-requires-db-match.md).
|
||||||
|
|
||||||
Авто-раскладка — только если выполнено **всё**:
|
Авто-раскладка — только если выполнено **всё**:
|
||||||
|
|
||||||
1. **Подтверждённый матч в базе** — единственный сильный результат
|
1. **Подтверждённый матч в базе** — единственный сильный результат
|
||||||
|
|||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
Конкретные задачи на будущее, ранжированные по приоритету. Это не план
|
||||||
|
реализации (он — в [drafts/roadmap.md](drafts/roadmap.md)) и не свалка
|
||||||
|
идей ([drafts/ideas.md](drafts/ideas.md)): сюда попадает то, что уже решили
|
||||||
|
сделать, но ещё не сделали. Принятое и реализованное переезжает в
|
||||||
|
`docs/specs`/`docs/adr`.
|
||||||
|
|
||||||
|
Приоритет — грубая оценка «ценность / стоимость», не обязательство к
|
||||||
|
порядку.
|
||||||
|
|
||||||
|
## Высокий
|
||||||
|
|
||||||
|
### Проблема второго сезона
|
||||||
|
|
||||||
|
Если первый сезон сериала уже разложен, а мы добавляем второй/третий/…,
|
||||||
|
распознавание должно привязать новый сезон к **тому же** названию и папке,
|
||||||
|
а не завести рядом почти одинаковую вторую папку. Ключ — стабильный
|
||||||
|
`provider_id`: один и тот же `[tvdbid-…]` → одна папка сериала, новые
|
||||||
|
`Season NN` доливаются внутрь. Нужно: при матче учитывать уже существующие
|
||||||
|
в библиотеке сериалы (или прошлые распознавания с тем же провайдер-id) и
|
||||||
|
склонять LLM/выбор кандидата к согласованности с ними.
|
||||||
|
|
||||||
|
Связано: [recognition.md](specs/recognition.md) (модель уверенности,
|
||||||
|
матч в базе), [jellyfin-layout.md](specs/jellyfin-layout.md) (папка
|
||||||
|
сериала с провайдер-id).
|
||||||
|
|
||||||
|
### Название из контекста при добавлении в qBittorrent
|
||||||
|
|
||||||
|
При создании magnet-загрузки передавать в qBittorrent человекочитаемое имя
|
||||||
|
из контекста (если оно есть), чтобы в списке qBit не было безликих
|
||||||
|
`rutracker-topic-6852853`. Небольшая задача с заметной отдачей в
|
||||||
|
повседневной эксплуатации.
|
||||||
|
|
||||||
|
Связано: [architecture.md](specs/architecture.md) → «Транспорты», пакет
|
||||||
|
`ingest`/`qbt`.
|
||||||
|
|
||||||
|
### Рассинхрон состояния с реальностью (удалённый торрент / файлы)
|
||||||
|
|
||||||
|
Состояние jellybit может разойтись с тем, что реально лежит на диске.
|
||||||
|
Несколько сценариев разной остроты:
|
||||||
|
|
||||||
|
- **Жёсткий — удалён источник.** Раздачу удаляют (вручную или авто по
|
||||||
|
достижении seed limit), и qBittorrent стирает скачанные файлы. Тогда
|
||||||
|
хардлинк в библиотеке становится **последней** ссылкой на inode, и
|
||||||
|
обычный `undo` (`unlink` цели + чистка пустых каталогов) сотрёт
|
||||||
|
единственную копию насовсем — прямая потеря данных. Инвариант «источник
|
||||||
|
неприкосновенен» молчаливо перестаёт держаться: источника уже нет.
|
||||||
|
- **Мягкий — удалена цель.** Файлы убрали из библиотеки Jellyfin (вручную
|
||||||
|
или из самого Jellyfin), а jellybit по-прежнему числит загрузку в
|
||||||
|
`done`. Состояние врёт: ссылок уже нет, а сервис думает, что всё
|
||||||
|
разложено.
|
||||||
|
|
||||||
|
Нужно продумать сверку записанного состояния (`file_link`, состояние
|
||||||
|
загрузки) с фактом на ФС:
|
||||||
|
|
||||||
|
- как `worker` реагирует на исчезновение раздачи из qBittorrent
|
||||||
|
(состояние/пометка загрузки);
|
||||||
|
- как `undo` защищается, когда источник недоступен — например,
|
||||||
|
отказываться удалять, если у целевого файла счётчик ссылок == 1 (нет
|
||||||
|
второй копии) или исходный путь не существует, и явно об этом сообщать.
|
||||||
|
Откат снимает **лишний** хардлинк, а не последнюю копию файла;
|
||||||
|
- как ловить пропажу целевых файлов и отражать её в состоянии (напр.
|
||||||
|
периодическая сверка или проверка при показе — «разложено, но файлов
|
||||||
|
нет»), чтобы можно было осознанно перепривязать/переразложить.
|
||||||
|
|
||||||
|
Связано: [ADR-2026-06-13-hardlinks](adr/ADR-2026-06-13-hardlinks.md),
|
||||||
|
[architecture.md](specs/architecture.md) → «Раскладка файлов» (undo,
|
||||||
|
инвариант источника), [workflow.md](specs/workflow.md) (`done → reverted`).
|
||||||
|
|
||||||
|
## Средний
|
||||||
|
|
||||||
|
### Машина состояний на go-библиотеке
|
||||||
|
|
||||||
|
Сейчас FSM реализована вручную в `worker`. Выбрать подходящую go-библиотеку
|
||||||
|
для описания воркфлоу/машины состояний и перевести переходы на неё — ради
|
||||||
|
декларативности, проверяемости переходов и единого места правды. Кандидаты
|
||||||
|
для оценки: `looplab/fsm`, `qmuntal/stateless` (и аналоги). Граф и переходы
|
||||||
|
уже формализованы — переносим один в один.
|
||||||
|
|
||||||
|
Связано: [workflow.md](specs/workflow.md) (текущий граф состояний).
|
||||||
|
|
||||||
|
### Привязка уведомлений к источнику в ботах (мульти-бот)
|
||||||
|
|
||||||
|
Уведомления и запросы подтверждения должен получать тот, кто прислал
|
||||||
|
загрузку: автор сообщения о новой раздаче — адресат пингов и ревью по ней.
|
||||||
|
Транспортов-ботов может быть несколько (Telegram, в перспективе Matrix и
|
||||||
|
др.); каждый адресует «своему» отправителю. Веб-интерфейс остаётся
|
||||||
|
**единым для всех** и точкой правды по функциональности (боты — тонкие
|
||||||
|
адаптеры над тем же ядром). Нужно: хранить у загрузки источник/транспорт и
|
||||||
|
идентификатор отправителя, маршрутизировать пинги по нему.
|
||||||
|
|
||||||
|
Связано: [review-ux.md](specs/review-ux.md) (разделение труда транспортов,
|
||||||
|
веб = точные правки), [architecture.md](specs/architecture.md) →
|
||||||
|
«Транспорты».
|
||||||
|
|
||||||
|
### Добавление торрентов файлом/ссылкой — «единое окно»
|
||||||
|
|
||||||
|
Поддержать источники помимо magnet: `.torrent`-файл и URL (отдаём их в
|
||||||
|
qBittorrent, без исходящих запросов на пользовательский URL — SSRF
|
||||||
|
исключён). Идеал — одно поле «единого окна»: кидаем туда текст или файл, а
|
||||||
|
сервис сам разбирает, что это (magnet / ссылка / .torrent / сообщение
|
||||||
|
бота), и заводит загрузку.
|
||||||
|
|
||||||
|
Связано: [architecture.md](specs/architecture.md) → «Транспорты»
|
||||||
|
(`source_type = magnet|torrent|url` уже в схеме), пакет `ingest` (сейчас
|
||||||
|
поддержан только magnet).
|
||||||
|
|
||||||
|
## Низкий
|
||||||
|
|
||||||
|
### Многоступенчатая верификация привязки (тема для размышления)
|
||||||
|
|
||||||
|
Идея: несколько раз извлекать данные из раздачи и контекста разными
|
||||||
|
промптами, искать в метабазах, затем сводить результаты в общий вердикт
|
||||||
|
(голосование/консенсус) — выше точность ценой нескольких вызовов LLM и
|
||||||
|
запросов к базам. Требует проработки: когда включать, как мерджить
|
||||||
|
расхождения, стоимость/латентность.
|
||||||
|
|
||||||
|
Связано: [recognition.md](specs/recognition.md) (конвейер и модель
|
||||||
|
уверенности).
|
||||||
|
|
||||||
|
### Современный Web-UI как PWA
|
||||||
|
|
||||||
|
Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое,
|
||||||
|
отзывчивое, удобное с телефона). Текущий server-rendered UI функционален,
|
||||||
|
поэтому это улучшение, а не блокер; большой объём работы.
|
||||||
|
|
||||||
|
Связано: [review-ux.md](specs/review-ux.md) (веб = точные правки),
|
||||||
|
пакет `httpapi`.
|
||||||
Reference in New Issue
Block a user