Правки после ревью документации
This commit is contained in:
+75
-53
@@ -2,91 +2,113 @@
|
||||
|
||||
## Задача
|
||||
|
||||
По доступным сигналам определить: это фильм или сериал; каноническое
|
||||
название и год; для сериала — сезон и соответствие файлов сериям; при
|
||||
включённых базах — provider-id. На выходе — план раскладки и оценка
|
||||
уверенности.
|
||||
По доступным сигналам определить: фильм или сериал; каноническое название
|
||||
и год; для сериала — сезон(ы) и соответствие файлов сериям; при включённых
|
||||
базах — провайдер и его id. На выходе — план раскладки, оценка уверенности
|
||||
и решение «авто или review».
|
||||
|
||||
## Сигналы
|
||||
|
||||
- Имя торрента и структура каталогов.
|
||||
- Список файлов с размерами и расширениями.
|
||||
- Текстовый контекст от человека.
|
||||
- Распарсенное сообщение торрент-бота (если пришло через Telegram):
|
||||
название с годом, качество, переводы, magnet — см. пример в
|
||||
[BRIEF.md](../../BRIEF.md).
|
||||
- Список файлов с размерами и расширениями. Абсолютный путь источника
|
||||
восстанавливаем как `save_path`/`content_path` из qBit (после трансляции
|
||||
`path_map`) + относительное имя файла; учитываем одно- и многофайловые
|
||||
торренты.
|
||||
- Текстовый контекст человека (+ накопленные подсказки из review).
|
||||
- Распарсенное сообщение торрент-бота (если через Telegram): название с
|
||||
годом, качество, переводы — см. пример в [BRIEF.md](../../BRIEF.md).
|
||||
|
||||
**Все сигналы недоверенные** — имя торрента, сообщение бота и контекст
|
||||
управляются извне и могут содержать инъекции. Выход LLM не отвечает за
|
||||
безопасность: целевой путь всё равно санитизируется и проверяется на
|
||||
выход за пределы библиотеки (см. architecture.md → «Раскладка файлов»).
|
||||
|
||||
## Конвейер
|
||||
|
||||
1. **Пред-парс** имени релиза дешёвым парсером (`go-ptn`): черновые
|
||||
название/год/сезон/серия и качество. Грубо, но бесплатно.
|
||||
2. **LLM** (через провайдер-абстракцию, см. «Провайдер LLM»): получает
|
||||
все сигналы и пред-парс, возвращает структурированный план в нашей
|
||||
схеме. Хорошо справляется с русскими релиз-именами, чего не умеет
|
||||
парсер.
|
||||
3. **Сверка с базой** (опц., если включена TMDB/TVDB): подтверждаем
|
||||
название+год, берём официальный id и каноническое имя.
|
||||
4. **Оценка уверенности** и решение: авто-раскладка или ревью.
|
||||
1. **Пред-парс** имени релиза (`go-ptn`): черновые название/год/сезон/
|
||||
серия и качество. Грубо, но бесплатно.
|
||||
2. **LLM** (через провайдер-абстракцию, см. ниже): получает сигналы и
|
||||
пред-парс, возвращает структурированный план в нашей схеме. Хорошо
|
||||
берёт русские релиз-имена. Длинный список файлов усекаем/семплируем под
|
||||
контекст модели.
|
||||
3. **Сверка с базой** (если включена TMDB/TVDB): ищем по названию+году,
|
||||
берём официальный id и каноническое имя, собираем кандидатов.
|
||||
4. **Оценка уверенности** и решение: авто или review.
|
||||
|
||||
## Структура ответа LLM (черновик)
|
||||
## Структура ответа LLM (предварительная)
|
||||
|
||||
```
|
||||
type movie | series
|
||||
title каноническое название
|
||||
original_title оригинальное название (если есть)
|
||||
year год
|
||||
season номер сезона (для сериала)
|
||||
provider_hint подсказка для поиска в базе
|
||||
files[] { src, role: main|episode|subtitle|extra|sample,
|
||||
season?, episode? }
|
||||
confidence 0..1 — самооценка модели по полям
|
||||
provider_hint строка для поиска в базе (НЕ итоговый id)
|
||||
files[] { src, role: main|episode|subtitle|extra|sample|ignore,
|
||||
season?, episode? } # season/episode — на файл
|
||||
confidence 0..1 — самооценка модели (вспомогательный сигнал)
|
||||
notes пояснения, неоднозначности
|
||||
```
|
||||
|
||||
Сезон/серия — **на файле**: так выражаются мультисезонные паки,
|
||||
спецвыпуски и смешанные раскладки; отдельного скалярного `season` нет.
|
||||
`provider_hint` — только подсказка для поиска; итоговые `provider`
|
||||
(`tmdb|tvdb|none`) и `provider_id` появляются после сверки с базой и
|
||||
хранятся отдельно.
|
||||
|
||||
## Провайдер LLM
|
||||
|
||||
Доступ к LLM — за интерфейсом; конкретная реализация выбирается полем
|
||||
`[llm].type` в конфиге (дискриминатор). Это позволяет подключать
|
||||
локальные модели и сторонние (в т.ч. китайские) эндпоинты — ради экономии
|
||||
и независимости от одного вендора.
|
||||
Доступ к LLM — за интерфейсом; реализация выбирается полем `[llm].type`
|
||||
(дискриминатор). Это позволяет подключать локальные модели и сторонние
|
||||
(в т.ч. китайские) эндпоинты — ради экономии и независимости от вендора.
|
||||
|
||||
- Первый и пока единственный тип — **`openai-compat`**: OpenAI-совместимый
|
||||
Chat Completions API (`base_url` + `api_key` + `model`). Под него
|
||||
подходят локальные серверы (LM Studio, llama.cpp, Ollama) и облачные
|
||||
совместимые провайдеры (DeepSeek, Qwen и др.).
|
||||
- Структурированный вывод: запрашиваем JSON по нашей схеме
|
||||
(`response_format` со схемой там, где поддерживается; иначе json-режим
|
||||
или tool-call), **валидируем в Go** и ретраим при несоответствии —
|
||||
серверы различаются по поддержке строгих схем.
|
||||
Chat Completions API (`base_url` + `api_key` + `model`). Подходят
|
||||
локальные серверы (LM Studio, llama.cpp, Ollama) и облачные совместимые
|
||||
провайдеры (DeepSeek, Qwen и др.).
|
||||
- **Структурированный вывод надёжно:** просим JSON по схеме
|
||||
(`response_format` со схемой где поддерживается; иначе json-режим или
|
||||
tool-call); на приёме срезаем ```-ограждения и извлекаем JSON,
|
||||
**валидируем в Go**, ретраим со схемой-в-промпте до `llm.max_retries`;
|
||||
если так и не распарсилось — уходим в **review** (не в `failed`) с
|
||||
причиной «ответ LLM не разобран». Серверы заметно различаются по
|
||||
поддержке строгих схем, особенно мелкие локальные модели.
|
||||
- Новые типы (напр. нативный `anthropic`) добавляются, не трогая
|
||||
`recognize`.
|
||||
|
||||
## Модель уверенности
|
||||
|
||||
Авто-раскладка только если выполнено всё:
|
||||
Авто-раскладка — только если выполнено **всё**:
|
||||
|
||||
1. **Самооценка LLM** ≥ порога (`recognition.auto_confidence_threshold`).
|
||||
2. **Совпадение с базой** (если включена) — единственный сильный матч по
|
||||
названию+году.
|
||||
3. **Структурная валидация** проходит без предупреждений:
|
||||
- фильм: ровно один основной видеофайл (семплы/экстра отброшены);
|
||||
- сериал: число серий бьётся с базой (если есть), нумерация S·E
|
||||
консистентна, без пропусков и дублей.
|
||||
1. **Подтверждённый матч в базе** — единственный сильный результат
|
||||
TMDB/TVDB по названию+году, давший `provider_id`. **Нет матча (или
|
||||
база выключена) → всегда review.** Это и закрывает основной кейс
|
||||
(рус/аниме часто отсутствуют в базах), и снимает риск «LLM придумал».
|
||||
2. **Структурная валидация** без предупреждений:
|
||||
- фильм: ровно один основной видеофайл (семплы/экстра/ignore отброшены);
|
||||
- сериал: число серий бьётся с базой, нумерация S·E консистентна, без
|
||||
пропусков, дублей и неоднозначных спецвыпусков.
|
||||
3. **Согласованность сигналов** — пред-парс (`go-ptn`) и LLM не
|
||||
противоречат по типу/названию/году.
|
||||
|
||||
Иначе план уходит в **review** (сценарии — [review-ux.md](review-ux.md)).
|
||||
На экране подтверждения всегда видно, *почему* не авто — это страховка на
|
||||
дорогих файлах.
|
||||
Самооценку LLM (`confidence`) учитываем как вспомогательный сигнал, но
|
||||
**не как единственный гейт**: она плохо откалибрована и поддаётся
|
||||
инъекции. Решают матч в базе и валидация.
|
||||
|
||||
Иначе — **review** ([review-ux.md](review-ux.md)) с явной причиной.
|
||||
|
||||
## Что делаем с краёв
|
||||
|
||||
- Семплы и «экстра» отбрасываем (эвристики по размеру/имени + LLM).
|
||||
- Внешние субтитры (`.srt`, `.ass`) привязываем к видео и именуем по
|
||||
Jellyfin (`*.ru.srt`).
|
||||
- Сезон-паки разбираем по сериям; аниме с абсолютной нумерацией —
|
||||
отдельный крайний случай, см. [drafts/ideas.md](../drafts/ideas.md).
|
||||
- Семплы/«экстра»/мусор → роль `ignore` (эвристики размер/имя + LLM).
|
||||
- Внешние субтитры (`.srt`, `.ass`, пары VobSub `.idx`+`.sub`) привязываем
|
||||
к видео и именуем по Jellyfin (`*.ru.srt`).
|
||||
- Сезон-паки разбираем по сериям; смешанные паки, спецвыпуски (`Season
|
||||
00`), двойные серии (`SxxEyy-Eyy`) — через per-file season/episode;
|
||||
любая неоднозначность → review.
|
||||
- Аниме с абсолютной нумерацией — отдельный крайний случай, см.
|
||||
[drafts/ideas.md](../drafts/ideas.md).
|
||||
|
||||
## На будущее
|
||||
|
||||
`go-ptn` слабее питоновского `guessit`. Если точности пред-парса не
|
||||
хватит — завернуть `guessit` лёгким сервисом-спутником (один файл рядом
|
||||
с бинарём). См. [drafts/ideas.md](../drafts/ideas.md).
|
||||
хватит — завернуть `guessit` лёгким сервисом-спутником (один файл рядом с
|
||||
бинарём). См. [drafts/ideas.md](../drafts/ideas.md).
|
||||
|
||||
Reference in New Issue
Block a user