Распознавание: санитайзинг названий от LLM + безгодовой фолбэк сверки
Кейс «Harold and the Purple Crayon»: LLM отдал title с кириллической буквой-двойником, сырое название ушло в запрос TVDB дословно (не нашлось), а гейт нормализации кир/лат двойники не сворачивал — двойной промах, пустой список кандидатов, ручной ввод id. - recognition: санитайзинг человекочитаемых полей плана (title/original_title/ provider_hint) на границе разбора, до валидации: strip control/zero-width, collapse пробелов, потокенная свёртка homoglyph-двойников по курируемой кир↔лат таблице. files[].src не трогаем (обязаны биться с торрентом). - metadata-match: тот же fold в normalize (гейт) как defense-in-depth; безгодовой второй проход сверки как fallback при известном годе и промахе первого — восстанавливает off-by-one авто-матчи и пополняет кандидатов review. В fallback требуем известный год кандидата (год-unknown → review, не авто); гейт год ±1 и инвариант авто-матча не двигаются. Спеки recognition/metadata-match обновлены, change заархивирован. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package recognize
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -12,7 +13,11 @@ import (
|
||||
// parsePlan извлекает JSON из ответа LLM, разбирает его и проверяет схему.
|
||||
// Ошибка здесь — сигнал к повторной попытке (ответ непригоден). Структурные
|
||||
// предупреждения (см. decide) ошибкой не считаются — они уводят в review.
|
||||
func parsePlan(raw string, in Input) (Plan, error) {
|
||||
//
|
||||
// Человекочитаемые поля плана санитизируются как недоверенный вывод LLM (см.
|
||||
// sanitizePlan) ДО структурной валидации: так, например, title из одних
|
||||
// zero-width символов схлопывается в пустой и корректно уводит в ретрай.
|
||||
func parsePlan(raw string, in Input, log *slog.Logger) (Plan, error) {
|
||||
jsonStr, err := llm.ExtractJSONObject(raw)
|
||||
if err != nil {
|
||||
return Plan{}, fmt.Errorf("no JSON object in response")
|
||||
@@ -29,6 +34,8 @@ func parsePlan(raw string, in Input) (Plan, error) {
|
||||
}
|
||||
}
|
||||
|
||||
sanitizePlan(&p, log)
|
||||
|
||||
if err := validateSchema(&p, in); err != nil {
|
||||
return Plan{}, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user