распознавание: большие раздачи размечаются целиком, файлы вне плана видны

- модель адресует файл номером строки нашего списка вместо копии пути: ответ
  на 180 файлов вместо ~15k токенов стоит ~2.5k, усечение сотней снято,
  max_files и max_tokens ушли в [recognition], correction-ретрай больше не
  переприсылает список
- негодный элемент ответа отбрасывается поимённой причиной, обрыв генерации и
  отказ по размеру запроса названы своими причинами, покрытие плана блокирует
  авто только при непокрытом видеофайле
- раскладка показывает все файлы раздачи со строками «не в плане» и полным
  порядком сортировки; снимок списка файлов лёг рядом с планом (миграция 0012)
This commit is contained in:
av
2026-09-02 08:54:58 +03:00
parent 01ac0430a9
commit fc9a3b4066
26 changed files with 1978 additions and 201 deletions
+1 -1
View File
@@ -191,4 +191,4 @@ Jellyfin указывают на `movies`/`series`, а не на корень
| Идентичность раздачи | split v1/v2-хеши не связаны, паре `xt` из магнета доверяем | `infohash-identity-integrity` |
| Расход внешних лимитов | кэша ответов метабаз нет, повтор распознавания бьёт провайдера заново | `metadata-cache` |
| История переходов | хранится только текущее состояние, «как сюда попали» восстанавливается по логам | `download-transition-history` |
| Предел ответа LLM | лимита на размер ответа нет — единственный недоверенный канал без предела; задачей пока не заведено | — |
| Предел ответа LLM | предел размера ответа есть (`[recognition].max_tokens`), обрыв по нему распознаётся и уводит в review; открытым остаётся согласование предела с потолком провайдера и с `[llm].timeout` | — |
+11 -4
View File
@@ -14,7 +14,8 @@
> `0006_ulid_identity` (Go-миграция: ULID-идентификаторы, `download_infohash`),
> `0007_file_link_size`, `0008_rfc3339_time` (метки времени → RFC 3339 UTC,
> `DEFAULT` убран), `0009_download_torrent` (байты `.torrent`-файла),
> `0010_retried_at`, `0011_parsed_context` (структура имени из контекста, JSON).
> `0010_retried_at`, `0011_parsed_context` (структура имени из контекста, JSON),
> `0012_recognition_source_files` (снимок списка файлов раздачи рядом с планом).
Назначение таблиц и роль компонентов — [architecture.md](architecture.md).
Значения `state` и легальные переходы — нормативно в
@@ -78,9 +79,10 @@ erDiagram
TEXT provider "nullable; tmdb|tvdb|tvmaze|none"
TEXT provider_id "nullable"
REAL confidence "nullable"
TEXT reasons "NOT NULL DEFAULT '[]'; JSON: причины не-авто"
TEXT reasons "NOT NULL DEFAULT '[]'; JSON: причины ухода в review + информационные заметки (сводка покрытия), не отменяющие авто"
TEXT raw_llm "nullable; сырой ответ LLM"
TEXT plan "nullable; JSON recognize.Plan (миграция 0002)"
TEXT source_files "nullable; JSON [{path,size}] — снимок файлов раздачи в порядке нумерации промпта (миграция 0012). NULL — запись старше миграции: перечень неполон, UI говорит об этом прямо"
TEXT created_at "NOT NULL; RFC 3339 UTC (Z), пишет приложение"
}
@@ -165,8 +167,11 @@ erDiagram
- **Всё, кроме одного поля, — плоские колонки.** Никакого сжатия, никаких
внешних файлов: строка читается и пишется целиком обычным запросом.
- **JSON-строками в TEXT** лежат три поля: `recognition.plan` (канонический
`recognize.Plan` — файл → роль/сезон/серия), `recognition.reasons` (список
причин не-авто) и `download.parsed_context` (структура имени из контекста).
`recognize.Plan` — файл → роль/сезон/серия), `recognition.reasons` (диагностика
распознавания: блокирующие причины ухода в review **и** информационные
заметки вроде сводки покрытия, которые авто-раскладку не отменяют, — само
решение auto/review из длины списка не выводится) и `download.parsed_context`
(структура имени из контекста).
Читаются целиком и разбираются в Go; частичного чтения и обновления поля
внутри JSON нет, SQL по содержимому этих полей не делается.
- **`recognition.raw_llm`** — сырой ответ модели как есть, **несжатый**. Это
@@ -204,6 +209,8 @@ erDiagram
| `[worker].catch_timeout` | `10m` | предел для пойманной задачи, не добавившейся в qBittorrent |
| `[worker].source_missing_threshold` | `3` тика | дебаунс пропажи источника |
| `[recognition].auto_confidence_threshold` | `0.85` | порог авто-раскладки (доп. проверка к матчу в базе) |
| `[recognition].max_files` | `500` файлов | сколько файлов раздачи максимум показывать модели в промпте. Предохранитель от аномальной раздачи, а не рабочее ограничение: 500 путей — порядка 35k токенов промпта, что модель принимает. Усечение уводит задачу в `review` отдельной причиной, а знаменатель покрытия остаётся полным числом файлов |
| `[recognition].max_tokens` | `8000` токенов | предел длины ответа модели. При индексной адресации файла (`files[].i` вместо копии пути) элемент плана стоит ~14 токенов, так что 500 файлов укладываются с запасом. Не хватило — `finish_reason = length`, и задача уходит в `review` с причиной «ответ модели обрезан», без повторного запроса |
| `[llm].timeout` / `max_retries` | `120s` / `3` | каждая попытка порождает строку `recognition` с сырым ответом |
| `[metadata.*].timeout` | `10s` | таймаут запроса к метабазе |