From 4a00c833925db5a2ec7b6be0e9241bdece4a77db Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Tue, 16 Jun 2026 15:39:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=B5=D1=89=D0=B5=20=D0=B8=D0=B4=D0=B5=D0=B8=20=D0=B2=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/todo.md | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/docs/todo.md b/docs/todo.md index 254acf6..b2a7917 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -68,6 +68,57 @@ [architecture.md](specs/architecture.md) → «Раскладка файлов» (undo, инвариант источника), [workflow.md](specs/workflow.md) (`done → reverted`). +### Наблюдаемость: метрики и учёт стоимости LLM + +Сейчас единственное окно в систему — `slog`. Нет быстрых ответов на +вопросы «сколько задач висит в review», «сколько токенов и денег съело +распознавание», «какова медиана времени ingest → done». Нужны метрики: +эндпоинт `/metrics` (Prometheus-формат) со счётчиками загрузок по +состояниям, длительностями стадий и расходом LLM (токены/стоимость на +задачу). + +**Расход LLM уже снимается с провода** — `llm.openai` парсит `usage` +(prompt/completion/total tokens и `cost`, который отдаёт шлюз) в +`llm.Response.Usage` и пишет в лог. Не хватает только **персистентности и +отображения**: сохранять `usage` у попытки распознавания (`recognition`) и +показывать в карточке загрузки + агрегатом в `/metrics`. Где провайдер не +шлёт `cost` — считать из токенов по таблице «модель → цена» в конфиге. + +Отдельный LLM-прокси (LiteLLM и т.п.) для этого **не нужен** и противоречит +принципам «один бинарь» / «минимум компонентов»: подсчёт токенов уже в коде, +а роль мульти-модельного шлюза играет используемый OpenAI-совместимый +эндпоинт (он и возвращает `cost`); разные модели подключаются сменой +`[llm].model` или новым типом провайдера за интерфейсом `llm.Provider`. + +Связано: [architecture.md](specs/architecture.md) → «Логирование», +[recognition.md](specs/recognition.md) (провайдер LLM, `[llm].type`), +пакеты `worker`, `llm`, `httpapi`. + +### Ретеншн и очистка БД + +Терминальные задачи (`done`/`cancelled`/`failed`/`reverted`), их попытки +`recognition` с сырыми ответами LLM и `metadata_candidate` копятся вечно — +со временем БД и список загрузок распухают и становятся нечитаемыми. Нужна +авточистка старше N дней (с настройкой в `[storage]` или `[worker]`) и/или +ручное удаление. Маленькая задача, но без неё интерфейс деградирует по мере +эксплуатации. + +Связано: [architecture.md](specs/architecture.md) → «Хранилище» (таблицы +`download`/`recognition`/`metadata_candidate`/`file_link`), пакет `store`. + +### Eval-харнес распознавания (корпус кейсов + метрика точности) + +Распознавание — ядро продукта, но смена модели или правка промпта сейчас +вслепую: регрессий не видно. Нужен корпус размеченных кейсов (русские +релизы, аниме, сезон-паки, репаки, спецвыпуски) и прогон распознавания по +нему с метрикой точности (тип/название/год/нумерация). Тогда можно +сравнивать LLM-провайдеры и версии промпта по числам, а не на ощупь. +Прогон — отдельной командой (`jellybit eval` или тестом), на фикстурах, без +реального qBittorrent. + +Связано: [recognition.md](specs/recognition.md) (конвейер, модель +уверенности), пакет `recognize`. + ## Средний ### Машина состояний на go-библиотеке @@ -94,6 +145,33 @@ веб = точные правки), [architecture.md](specs/architecture.md) → «Транспорты». +### Раздачи с докачиванием (слияние при повторном добавлении) + +Свежий сериал часто раздают по мере выхода: торрент содержит 5 эпизодов из +10. Позже его перезаливают целиком (или добавляют недостающие серии), и +пользователь повторно добавляет тот же торрент. Нужно распознать, что это +**та же** раздача/сезон, и повторить раскладку с **слиянием**: доложить +недостающие хардлинки, не дублируя уже разложенное и не перезаписывая +существующее (инвариант «существующее не трогаем»). Перекликается с +«Проблемой второго сезона», но здесь доливаются эпизоды внутри одного +сезона, а не новый сезон. Нужно продумать: как опознать повторное +добавление (хеш торрента / провайдер-id + сезон), как сверять состав файлов +и доливать только новые. + +Связано: [«Проблема второго сезона»](#проблема-второго-сезона), +[jellyfin-layout.md](specs/jellyfin-layout.md) (раскладка, идемпотентность), +[workflow.md](specs/workflow.md) (повторный прогон загрузки). + +### Улучшения UI клиентов: показывать матч с записью метабазы + +Во всех транспортах (веб, Telegram) показывать, **с какой именно записью** +метабазы (TMDB/TVDB) сматчилась загрузка: название, год, провайдер-id, +ссылку. Сейчас результат распознавания непрозрачен — пользователь не видит, +к чему привязались, и не может быстро поймать ошибочный матч. + +Связано: [review-ux.md](specs/review-ux.md), [recognition.md](specs/recognition.md) +(матч в базе), [architecture.md](specs/architecture.md) → «Транспорты». + ### Добавление торрентов файлом/ссылкой — «единое окно» Поддержать источники помимо magnet: `.torrent`-файл и URL (отдаём их в @@ -106,6 +184,61 @@ qBittorrent, без исходящих запросов на пользоват (`source_type = magnet|torrent|url` уже в схеме), пакет `ingest` (сейчас поддержан только magnet). +### Бэкап SQLite + +`architecture.md` требует «бекапить data-том», но *как* — не описано. Без +понятной стратегии сбой или редеплой стирают всё in-flight состояние. +Зафиксировать решение и реализовать: периодический `VACUUM INTO` в +`/data/backups` по расписанию (с ротацией) либо потоковая репликация +(litestream). Лучше сделать, пока БД маленькая. + +Связано: [architecture.md](specs/architecture.md) → «Деплой» (data-том, +«бекапить-и-не-терять»), пакет `store`. + +### Версии/качество одного тайтла (репаки, апгрейд 1080p → 2160p) + +Фильм уже разложен, позже добавили раздачу лучшего качества — сейчас это +просто новая задача, упирающаяся в «коллизию цели → review», без понятия +«это та же вещь, заменить версию». Нужно осознанно обработать апгрейд +качества: распознать тот же тайтл, предложить замену существующей раскладки +либо сосуществование версий (Jellyfin поддерживает несколько версий одного +фильма). Близко к «докачиванию», но про качество, а не про эпизоды. + +Связано: [«Раздачи с докачиванием»](#раздачи-с-докачиванием-слияние-при-повторном-добавлении), +[jellyfin-layout.md](specs/jellyfin-layout.md) (never-overwrite, коллизия), +[architecture.md](specs/architecture.md) → «Идентификация торрента» +(репаки = разные infohash → разные задачи). + +### Глубокий healthcheck и статус зависимостей + +`/healthz` проверяет только сам сервис. Если qBittorrent, LLM или метабаза +недоступны — узнаёшь лишь по застрявшим задачам. Нужна readiness-проверка +ключевых зависимостей и отражение их состояния в UI (бейдж «qBittorrent +недоступен»), чтобы причина простоя была видна сразу. + +Связано: [architecture.md](specs/architecture.md) → «Деплой» (healthcheck), +пакеты `qbt`, `llm`, `metadata`, `httpapi`. + +### Обучение на правках человека (few-shot из прошлых ревью) + +Когда человек поправил матч, тип или нумерацию — сохранять это как пример и +подмешивать похожие в будущие промпты. Системно повышает точность на «твоих» +трекерах и форматах имён без смены модели. Развитие идеи многоступенчатой +верификации, но дешевле: учимся на уже собранных `hint`/`override`. + +Связано: [recognition.md](specs/recognition.md) (конвейер, промпт), +[«Многоступенчатая верификация»](#многоступенчатая-верификация-привязки-тема-для-размышления), +[architecture.md](specs/architecture.md) → «Хранилище» (`hint`, `override`). + +### Список загрузок: фильтр, поиск, пагинация + +Прямое следствие роста БД (см. «Ретеншн»): плоский список загрузок со +временем становится непригоден. Нужны фильтр по состоянию, поиск по +названию и пагинация. Естественно ложится на экран расширенной информации. + +Связано: [«Расширенная информация о загрузке в web-UI»](#расширенная-информация-о-загрузке-в-web-ui), +пакет `httpapi`. + ## Низкий ### Многоступенчатая верификация привязки (тема для размышления) @@ -119,6 +252,45 @@ qBittorrent, без исходящих запросов на пользоват Связано: [recognition.md](specs/recognition.md) (конвейер и модель уверенности). +### Расширенная информация о загрузке в web-UI + +Экран просмотра деталей одной загрузки: исходный контекст и magnet, лог +переходов состояний, распознанные данные и матч в метабазе (см. «показывать +матч»), целевые пути и созданные хардлинки. Помогает разбираться, когда +что-то пошло не так, без чтения логов сервера. + +Связано: [review-ux.md](specs/review-ux.md), пакет `httpapi`. + +### Выбор из нескольких находок метабазы в Telegram + +Когда распознавание даёт несколько подходящих кандидатов в метабазе, +предлагать их в Telegram списком (кнопки) для ручного выбора, а не молча +брать первый/лучший. Веб остаётся точкой точных правок, бот — быстрый выбор +из готового короткого списка. + +Связано: [review-ux.md](specs/review-ux.md) (боты — быстрые действия, веб — +точные правки), [recognition.md](specs/recognition.md) (кандидаты матча). + +### Проверка свободного места перед copy-fallback + +Когда хардлинк невозможен (`EXDEV`/`ENOTSUP`/…), `layout` копирует файл, +дублируя место на диске. На забитом диске это упрётся в полку посреди +раскладки. Перед копированием проверять доступное место и при нехватке +внятно уходить в `failed` с понятной причиной, а не падать на полпути. + +Связано: [architecture.md](specs/architecture.md) → «Раскладка файлов» +(фолбэк-копирование), пакет `layout`. + +### Кэш метабаз (и опционально LLM) + +Повторные и ретраящиеся прогоны распознавания бьют TMDB/TVDB/TVMaze одним и +тем же запросом. Кэш ответов с TTL экономит лимиты API и ускоряет «Распознать +заново»/«Уточнить». При желании — кэш ответов LLM по хешу входа (но он менее +полезен, т.к. вход меняется подсказками). + +Связано: [recognition.md](specs/recognition.md) (сверка с базой), пакеты +`metadata`, `llm`. + ### Современный Web-UI как PWA Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое,