# Валидация банка памяти книги: вердикт, что менять, обоснование и источники Дата: 2026-07-04. Сессия **«Валидация банка памяти»** (промт `docs/archive/prompts/MEMORY_RESEARCH_SESSION_PROMPT.md`). Предмет — решение **Р3 «Банк памяти книги»** (`architecture/01-decisions.md`) и исследовательская база `research/05-memory-glossary.md`. Мандат: **адверсариально провалидировать** подход до заморозки в коде — не поверить, а попытаться сломать. Метод: многоагентный ресёрч по 7 направлениям (Q1–Q7) + независимая адверсариальная верификация каждой находки по первоисточникам + завершающий completeness-критик + **локальный эмпирический бенчмарк retrieval** на реальном PD-корпусе (`eval/retrieval_bench.py`) + чтение реального кода `backend/`. Все вендорские/бенчмарк-числа перепроверены независимо (как MemPalace в research/05); процитированные корректировки — от верификаторов. Реестр рисков (таблица «сценарий → защита → слой → где в коде») — отдельный файл `architecture/06-memory-risk-registry.md` (единственный, что этой сессии разрешено создать в `architecture/`), он же — вход для бэкенда перед фиксацией схемы. Все URL проверены на дату исследования. Версии моделей/бенчмарков зафиксированы. --- ## TL;DR — вердикт **Подход ДЕРЖИТ архитектурно, но с обязательными условиями, и почти ни одно из процитированных research/05 чисел не переносится на zh/ja→ru.** 1. **Опасение владельца уточнено, а не подтверждено буквально.** «Пусто» — не главная угроза: пустой retrieval — **нормальное и безопасное** состояние детерминированного горячего пути (откат к базовому поведению модели). Тихую деградацию порождает **ошибочная инъекция** — модель послушно следует подсунутой неверной строке глоссария как мягкой инструкции, **уверенность растёт, а точность падает** (2510.00829). Дизайн надо переориентировать с «не пусто ли» на «точно ли» и на **обязательный post-check** как детектор. 2. **Горячий путь верен и его надо усилить, а не смягчить.** Детерминированный точный матч ключей — не «дешёвое приближение» dense, а **строго лучший** инструмент для дословных имён собственных (Sciavolino EntityQuestions, 2109.08535). Эмпирика (наш бенчмарк): точный подстрочный матч — **precision 1.0, recall 0.571, 0/4 ложных срабатываний** на ловушках-омографах (送灶/修/炎/气); char-BM25 — все 4 ловушки в top-5. Эмбеддинги/reranker/BM25 на горячем пути — **нет**. 3. **Эмбеддинг-слой — low-trust, только Фаза 2, только кандидаты на ревью.** Эмпирически подтверждено: даже лучшая модель (bge-m3) **не даёт разделяющего порога** — распределения релевантных и мусорных cosine пересекаются (rel_min 0.374 < irr_p95 0.447), при precision≥0.9 recall падает до 0.54. Единый глобальный порог тихо гейтить не может (совпадает с репродукцией CRAG, 2603.16169). Эмбеддинги — генератор кандидатов для человека (флешбеки, редкие термины), не тихая инъекция. 4. **Академический якорь честнее, чем в research/05.** DelTA (2410.08143, **ICLR 2025**) реален, но: (а) полностью En-центричен — **нет русского, нет ja/zh→ru**; (б) флагманская метрика LTCR-1 меряет **самосогласованность, а не корректность** — консистентно-неверное имя даёт 100%; (в) прирост COMET +3.16 — на слабых/средних базовых моделях (GPT-3.5/4o-mini/Qwen2); (г) память DelTA **LLM-в-цикле per-предложение**, а не детерминированная. То есть DelTA валидирует *концепт* многоуровневой памяти, но **не** нашу ставку «детерминированный no-LLM горячий путь» — она **академически нова и не подтверждена**. Это первая строка любого go/no-go. 5. **Детерминизм: soft>hard по качеству подтверждён (в т.ч. в русский), но soft молча теряет 17–36% терминов** (2310.05824) → **post-CHECK обязателен**, а слепой post-replace в русский выход **запрещён** (ломает согласование). Поле `decl` — не «фича ru-рынка», а safety-механизм грамматичности. 6. **Стек по масштабу — в разы с запасом.** Brute-force KNN: <20 мс на 100k×1024-d (наш замер); триггеры миграции на порядки выше нужд одной книги. Одна поправка: sqlite-vec **доступен** CGO-free через ncruces (причину отказа в Р3 переписать). 7. **Итог: строить свой банк памяти — верно** (все вендорские agent-memory числа при честной методологии бьёт ванильный RAG; домен чужой). Но **числа не переносятся** → перед заморозкой ставки нужен **свой замер zh/ja→ru с acceptance-критериями и baseline «банк vs длинный контекст»** (§Вердикт). Реестр рисков делает провалы **явными** — это и есть ответ на опасение владельца: тихую деградацию мы **конвертируем в громкую**, а не устраняем обещанием. --- ## Q1. Робастность retrieval — центральный вопрос **Что делают зрелые системы при пустом/слабом/нерелевантном retrieval и как сделать деградацию явной и безопасной.** **Находка 1 — near-miss дистрактор вреднее пустоты.** «The Power of Noise» (SIGIR 2024, 2401.14887) и Yoran et al. (ICLR 2024, 2310.01558) показывают: семантически близкий, но неответный документ роняет точность существенно (в отдельных случаях до −67%), тогда как пустой контекст лишь возвращает модель к базовому поведению; NLI-фильтр устраняет просадку ценой части релевантных. RGB-бенчмарк (Chen et al., AAAI 2024, 2309.01431) прямо операционализирует нужные нам оси — **Noise Robustness** и **Negative Rejection** (поведение при пустом/полностью нерелевантном retrieval) — и имеет **китайский** тестбед (ближе к нам, чем англоязычные работы). → Принцип **«лучше ничего, чем мусор»**: не добивать токен-бюджет соседями; бюджет допустимо недозаполнить; фильтр по точности **перед** сортировкой по приоритету. > ⚠ Поправка верификатора: контринтуитивный результат Power of Noise «случайный шум помогает (+35%)» специфичен для extractive-QA и **не** оправдывает набивку глоссария низкорелевантными записями — строка глоссария это директива, а не нейтральный филлер. **Находка 2 — пороги уверенности хрупки.** Независимая репродукция CRAG (2603.16169) показала: оценщик релевантности ведёт себя как детектор **пересечения сущностей**, а не семантической релевантности; высокая уверенность ≠ корректность; пороги **не переносятся** между наборами (на OOD-данных 88.3% классифицированы как «Ambiguous» на нестроенных порогах). zh/ja→ru литература — и есть сдвиг распределения. **Наша эмпирика это подтверждает напрямую** (см. §Q2/Q3, таблица): у лучшей модели bge-m3 ROC-AUC=0.970, но релевантные (rel_mean 0.555, min 0.374) и мусорные (irr_mean 0.357, p95 0.447) cosine **пересекаются**; чтобы держать precision≥0.9, порог cos≥0.547 срезает recall до 0.54. Единого глобального порога, тихо отделяющего сигнал от мусора, **нет**. **Находка 3 — безопасный паттерн: явное трёхстороннее решение + post-check.** CRAG (2401.15884), Self-RAG, Adaptive-RAG сходятся на явной диспозиции **accept / verify / reject** с разными действиями по ветке. Переносимый вклад — не конкретные пороги, а **машинерия диспозиции** (логировать/маршрутизировать/отклонять). Плюс дешёвый пост-хок-чек фактичности (RAGAS-триада; у нас — GalTransl layer-3 / post-check). **Рекомендованное поведение пайплайна (явная безопасная деградация):** - Пустой ключевой матч → инъектить **только** активные sticky-записи; ничего не добавлять. «Пусто» — норма, не тревога. - Трёхстороннее решение **на каждую запись-кандидат**: `CONFIRMED` (точный ключ/лемма, `approved`, спойлер-валидна) → авторитетно; `AMBIGUOUS` (короткий/общий алиас, коллизия поверхности, `auto/draft`, эмбеддинг в серой зоне) → инъекция **с пометкой «unverified — проверить» + принудительный post-check**; `REJECT` (спойлер-нарушение / ниже планки) → выкинуть и **залогировать**. - **Реальная тревога** — не пустота, а: чанк содержит имя-подобные/CJK-name-pattern токены, не совпавшие **ни с одной** записью и **ни с одним** sticky → неизвестная сущность → авто-экстракция + подтверждение (DelTA Proper-Noun-Record паттерн). Никогда не «перевести и забыть» новое имя молча. - Спойлер-окно `since_ch/until_ch` — **жёсткий reject с логируемым событием** на обоих путях (не UX-фича, а safety-гейт). - Per-chunk `retrieval-state` запись (`n_exact_hits, n_sticky, n_ambiguous_flagged, n_spoiler_blocked, embedding_tier_used`) — наблюдаемость деградации с первого дня. > **Расхождение с research/05:** эмбеддинг-«второй эшелон» подан как безобидный, без порога/воздержания; доказательства сильнее — он должен быть явно **low-trust, per-книга калиброван, только кандидаты на ревью**. «Лучше ничего, чем мусор» не сформулирован как принцип. Спойлер-окно — safety-гейт, а не продуктовая фича. > ⚠ Честная граница: весь корпус доказательств Q1 — англоязычный QA/MT; **никто не мерил вред неверной строки глоссария на zh/ja→ru прозе**. Магнитуду даёт только свой замер (§Вердикт). ## Q2. Адекватность стека под масштаб **Держит ли SQLite + brute-force KNN + bge-m3 вебновеллу в тысячи записей × сотни глав; где триггеры миграции.** **Держит с большим запасом.** Эмпирика (`retrieval_bench`, чистый numpy, single query, top-10, медиана): | dim | N=1k | N=10k | N=50k | N=100k | N=200k | |---|---|---|---|---|---| | 768 (LaBSE) | 0.02 мс | 0.26 мс | 6.1 мс | 12.3 мс | 25.3 мс | | 1024 (bge-m3/Qwen3) | 0.02 мс | 2.0 мс | 9.3 мс | **19.6 мс** | 33.2 мс | Одна книга — единицы тысяч записей глоссария + десятки тысяч векторов чанков — **на порядки внутри зоны «brute-force норм»**. Первоисточник-якорь: C/SIMD sqlite-vec делает 100k×1024-d <75 мс; его **скалярный** вариант (аналог рукописного Go-цикла) — 81 мс на 1M×128-d, 108 мс на 500k×960-d — то есть даже без SIMD на нашем N это низкие десятки мс. **Триггеры миграции (конкретно):** - **T1 (самый дешёвый, остаться CGO-free + один файл):** per-query N → ~100k при 1024-d или p95 скалярного скана > ~150–200 мс → перейти на SIMD-brute-force sqlite-vec через **ncruces/go-sqlite3 + asg017/sqlite-vec-go-bindings/ncruces** (pure-Go WASM, официально, stable v0.35.1). ×3–10 без сервера. Реальная цена — миграция всего драйвера с modernc (FTS5+хранилище), оценивать заранее. - **T2 (квантизация до индекса):** перед ANN — int8/binary векторы (sqlite-vec: 100k×3072-d bit ~11 мс, Hamming, ×32 меньше памяти) как грубый проход + re-rank top-k float. Скорее всего снимает нужду в ANN на нашем масштабе вовсе. - **T3 (ANN, всё ещё embedded):** per-query N > ~500k–1M или интерактивный SLO <20 мс p99 → sqlite-vec ANN (планируется) / hnswlib. Актуально только если скоуп вырастет далеко за одну книгу. - **T4 (реальный вектор-DB/сервер):** только когда продукт — единый library-wide индекс на много книг (агрегат миллионы векторов) или нужны конкурентные писатели / высокий QPS при 99%+ recall → pgvector+pgvectorscale (если уже Postgres) / Qdrant (50M+). **Явно жертвует гарантией «один SQLite-файл на книгу»** — не пересекать ради per-book масштаба. - **T5 (память, не латентность):** float32 1024-d = 4 КБ/вектор → 100k резидентных = ~400 МБ RAM на открытую книгу; при многих открытых книгах это упрётся раньше латентности → mmap/on-disk или int8. **Важная поправка (расхождение с research/05 и Р3):** «sqlite-vec нельзя, pure-Go не загрузит C-расширение» — **верно только для `modernc.org/sqlite`**, не как свойство pure-Go. CGO-free sqlite-vec **существует** (ncruces WASM). **Решение (brute-force в MVP) остаётся верным**, но рациональ переписать: реальная причина — «держим modernc единым драйвером / избегаем WASM-миграции», а не «pure-Go не может». bge-m3 (~560–600M, XLM-R-large, 1024-d, ~2.2–2.5 ГБ FP32) влезает в 8 ГБ GTX 1070, но **эмбеддинг держать строго офлайн/батч** (Pascal без быстрого FP16; наш замер — bge-m3 ~78 мс/текст на CPU, Qwen3-0.6b **~800 мс/текст**) — на горячем пути недопустимо. > Позитивный факт: весь Q2 — чистая инфраструктура (латентность/драйвер/VRAM), **без zh/ja→ru зависимости** — «англо-доказательства не переносятся» здесь не применимо. Единственное языко-чувствительное — **качество** retrieval bge-m3 (Q3/Q7). ## Q3. Правильность задачи retrieval — dense vs гибрид vs детерминизм **Чистый dense — оптимум, или нужен гибрид (BM25/FTS + dense + reranker)?** **Разделение дизайна верно, и доказательства велят сделать его резче.** **Горячий путь (дословный термин в чанке): dense строго ХУЖЕ детерминизма.** Dense молча промахивается мимо редких имён собственных (ровно тот тихий провал, которого боится владелец); Sciavolino «Simple Entity-Centric Questions Challenge Dense Retrievers» (EMNLP 2021, 2109.08535) — канонический первоисточник: exact/lexical бьёт dense именно на **редких, отличительных** строках (=наш случай имён собственных). BM25/FTS5 туда тоже не годятся: **дефолтный токенайзер FTS5 (unicode61) не сегментирует CJK → молча возвращает ничего**. Reranker — тоже нет. Это **не «дешевле», а корректнее и с более высоким recall**. Наша эмпирика (13 gold-запросов, 4 ловушки): | Метод | R@1 | R@3 | R@5 | MRR | nDCG@5 | ловушки в результате | точность | |---|---|---|---|---|---|---|---| | **substring-exact** (горячий путь Р3) | — | — | — | — | — | **0/4 срабатываний** | **precision 1.0, recall 0.571** | | bm25-char (лексика) | 0.40 | 0.73 | 0.92 | 0.95 | 0.87 | **4/4 в top-5** | — | | **bge-m3** (dense) | 0.40 | 0.86 | **0.97** | 0.96 | **0.94** | 0/4 инъектир. при t* | AUC 0.970 | | Qwen3-Embedding-0.6B | 0.40 | 0.87 | 0.89 | 0.96 | 0.88 | 0/4 при t* | AUC 0.953 | | LaBSE | 0.36 | 0.72 | 0.83 | 0.91 | 0.81 | 0/4 при t* | AUC 0.931 | Читается так: точный подстрочный матч **никогда не ошибается** (precision 1.0) и **не ловится на омографы** (0/4), но **пропускает** семантику/кросс-язык/резюме (recall 0.571) — это его роль. char-BM25/фаззи-ключи ловят **все 4 ловушки** (送灶-обряд на «送到灶», 修为 на «修好», 萧炎 на «炎势», 斗气 на «雾气») — почему одиночные/фаззи ключи на горячий путь пускать нельзя. Dense **не** инъектит ловушки при пороге precision≥0.9 — но **только потому, что порог высок** (сырые cosine ловушек 0.39–0.54 лежат **внутри** диапазона релевантных, rel_min 0.374); dense избегает ловушки ценой recall, а substring — бесплатно. → горячий путь остаётся детерминированным. **Второй эшелон (семантика: перефраз, алиасы, «где это было», флешбеки): гибрид + опциональный reranker.** Чистый dense суб-оптимален; hybrid (лексика + dense, RRF k=60 — Cormack et al. SIGIR 2009) + опциональный bge-reranker-v2-m3 на top-30–50 — продакшн-стандарт. Прирост гибрида над dense на мультиязычной семантике реален, но **скромен** (bge-m3 на MIRACL: dense 67.8 → dense+sparse 68.9 → all-three 70.0 nDCG@10). Хитрость: bge-m3 отдаёт dense+sparse+ColBERT за **один forward** — гибридную лексическую руку второго эшелона можно взять из его же sparse-весов, минуя отдельный CJK-токенизированный BM25. > **Расхождения с research/05:** (1) §4.1 «constant/keyed/**vectorized** — все три нам нужны» — vectorized-активация **не** нужна (и вредна) в горячем пути, только во втором эшелоне; scope-ить по слоям. (2) §5.2 «желательно без эмбеддингов» → **строго без** для дословных терминов (substring не приближение dense, а лучший метод). (3) FTS5-ловушка CJK-токенизации в research/05 не помечена — при опоре на FTS5 для «полнотекст/ключи» это тихий провал; нужен bigram/jieba токенайзер, если FTS5 всё же используется. > ⚠ Поправки верификатора: ни один процитированный retrieval-бенчмарк не меряет **русский** (MIRACL zh/ja без ru; T2-RAGBench английский) — ru-сторона (bilingual dst, ru-резюме) числами не покрыта. Плюс ja-специфика: чистый substring молча промахивается по орфо-вариантам (кандзи/кана/ромадзи 鈴木/すずき/スズキ, полу/полноширина) — нужна NFKC-нормализация + алиасы (см. реестр A4); а zh-substring рискует ложным вложением короткого термина в длинное слово (нет границ слов) — нужен longest-match/Aho-Corasick, «горячий путь не так тривиально точен». ## Q4. Специфические для перевода режимы отказа Полная таблица «сценарий → защита → DET/LLM/HUMAN → где в коде/схеме» — в **`architecture/06-memory-risk-registry.md`** (разделы B–D). Ключевые находки этой сессии: **Самая опасная — академический якорь сам порождает отказы.** DelTA **Proper Noun Records = first-translation-wins без обновления и без детекции конфликтов**; первое (наименее информированное) вхождение имени лочит рендеринг на 1000 глав. То есть механизм — **генератор** режимов **stale / contradictory / drift**, а не защита от них. И решающий довод: LTCR-1 меряет **консистентность, не корректность** — консистентно-неверное имя даёт 100%, метрика **структурно не может** оштрафовать stale-рендеринг, который сама же и производит. По каждому режиму (кратко; детерминированный слой vs LLM): - **Stale/drift:** append-only журнал ревизий рендеринга **отдельной осью** от спойлер-окна (Zep bi-temporal как **паттерн** в SQLite, без Neo4j) + content-hash в snapshot + ReTranslation-by-keyword. `DET`+`HUMAN`. - **Contradictory:** `UNIQUE(src,sense,window)` + обратный `GROUP BY dst`; LLM-адъюдикация **раз в главу батчем**, не в горячем пути. `DET`+`LLM(батч)`. - **Alias:** alias-граф с типами + name-tables + NER-гейт на новую форму имени. `DET`+`LLM`+`HUMAN`. - **Wrong-sense (омографы/полисемия 阿Q, 送灶, 炎, 道/气/修):** запрет одиночных ключей как schema-констрейнт + longest-match + замена целой сущности + условные judgment-words (GalTransl post-dict). Эмпирически подтверждено (Q3). `DET`. - **Спойлер:** жёсткий reject с логом на обоих путях. `DET`. - **Молчаливо-пустой матч из-за орфографии** (упрощ./трад., полу/полноширина, кана, ruby) — **самый вероятный баг «тихо пусто»**: обязательный слой нормализации (NFKC + OpenCC + kana-folding + ruby-захват) симметрично к ключам и тексту. `DET`. **LLM — строго вне горячего пути инъекции.** Роли LLM: авто-экстракция кандидатов, батч-адъюдикация конфликтов, остаточный sense-check, ревью гендер-твиста — всё **раз в главу**, никогда per-chunk. > **Расхождения с research/05:** §4.2 подаёт DelTA-PNR как позитив для автопополняемого глоссария, не отмечая, что это first-wins-без-обновления (генератор stale/contradiction/drift). §1.2 приписывает Zep «63.8% LongMemEval vs 49.0% Mem0» — первоисточник Zep (2501.13956) даёт 71.2% vs full-context 60.2% и **не** сравнивает с Mem0; цифра из вторички. Ценное у Zep — не тяжёлый стек, а **лёгкий паттерн** (invalidate-not-delete bi-temporal + write-time LLM-чек), реализуемый в SQLite. > ⚠ zh error-таксономия, на которую опирается дизайн (DITING, 2510.09116), — zh→**En** only; её вывод «китайские модели бьют западные» — англо-таргетный, для ru-таргета сигнала ноль. Разрыв переноса шире, чем «нет русского в DelTA». ## Q5. Гарантия детерминизма реестра **Трёхслойная защита GalTransl (pre-replace → мягкий глоссарий → post-check) против литературы constrained decoding / soft-following.** **Ставка держит, но research/05 формулирует её слишком резко.** 1. **Soft > hard по КАЧЕСТВУ — подтверждено, в т.ч. в русский.** DuTerm (arXiv 2511.07461, «It Takes Two: A Dual Stage Approach for Terminology-Aware Translation», **en→ru**) и WMT23 (COMET-QE: LLM-refine > constrained decoding на всех парах). НО: 2. **Soft ПРОИГРЫВАЕТ по adherence:** промптинг оставляет **17–36% терминов молча неиспользованными** (2310.05824) — **поэтому post-check обязателен, а не опционален**. Формулировка research/05 §5.3 «soft лучше по обоим (accuracy И fluency)» **не поддержана**: soft меняет adherence на fluency, и post-check — именно это чинит. 3. **Самая важная адверсариальная находка, отсутствующая в research/05, и точно бьющая в опасение владельца:** LLM **сверх-доверяет** инъектированному термину — при неверной/шумной записи внимание уходит с источника, а **уверенность модели растёт при падении точности** (2510.00829). → горячий путь переориентировать на **precision-first** отбор и **громкий флаг** подозрительных инъекций. 4. **Русская морфо-опасность к СОБСТВЕННОМУ post-replace-слою:** форс словарной формы в косвенный падеж ломает согласование (**46% ошибок constrained-моделей в en-cs — согласование**, 2106.12398; лемма+самостоятельное склонение даёт **+47.7 п.п.** term accuracy в латышский — Bergmanis&Pinnis). → **`decl`+`gender` — не фича ru-рынка, а safety-механизм грамматичности**; слепой регэксп-replace форм в русский выход **запретить**; форс только при инвариантном термине ИЛИ полном `decl` И разрешимом падеже, иначе флаг. 5. **«Жёсткий constrained decoding = деградация» — overstated.** ~7 BLEU теряет **наивный** Grid Beam Search; plug-and-play (Cascaded Beam Search, 2305.14538) даёт ~0 BLEU-потерь и EMA **0.925 на en-ru** (vs baseline 0.753). Реальная причина не использовать — **недоступен через закрытые frontier-API** (нужен доступ к beam/декодеру), а **не** «портит качество». **Грань «жёстко нельзя / мягко можно»:** hard find-replace — **только на источнике** (нормализация к каноническому ключу, CJK без пробелов надёжно); в **русском выходе — только soft + post-check + флаг**; hard-форс формы в выход — лишь для морфологически инвариантного термина или при полном `decl` с разрешимым падежом. Для критических «никогда-не-варьировать» имён — не hard-декодинг, а **демонстрация** (короткое прежнее переведённое предложение с именем в нужном падеже — 2503.05010: demonstrations > bare terminology, хотя это de-en). > ⚠ Поправки цитирования (верификатор): research/05 §5.3 ссылается на 2503.05010 как «soft>hard» — та работа про retrieval-vs-generation (de-en), не про soft-vs-hard; заменить на 2310.05824 + 2511.07461. «Все три сходятся» (2024.wmt-1.51 / 2503.05010 / 2511.07461) — overclaim: сравнение soft-vs-hard делает только 2511.07461. **Языковой перенос — крупнейшая оговорка всего Q5:** все результаты — с латинским/английским ИСТОЧНИКОМ (en→ru, en→lv, en→cs); **ни один не оценивает zh/ja источник и zh/ja→ru**. Ставка унаследована по аналогии; уверенность в zh/ja→ru снизить. ## Q6. Академическое заземление **DelTA воспроизводима? На чём мерено? Переносима на zh/ja→ru? Литература document-level MT / RAG-failure / long-context. Свежие 2025–2026.** **DelTA (2410.08143, ICLR 2025):** реальна, код+данные выпущены. Но: - **8 направлений, все X↔English; китайский и японский — только в паре с English; русского нет нигде.** Заявленное «+4.58 п.п. LTCR-1» — это **Xx→En среднее** по 4 backbone-моделям (En→Xx среднее +4.36; до +6.17 на отдельной паре), под рамкой «up to … on average». - **LTCR-1 = самосогласованность, не корректность** (по формуле; выровнена с механизмом Proper-Noun-Records почти по построению). Независимый сигнал качества — только COMET **+~3.1**, и то на **слабых/средних** моделях (GPT-3.5-turbo, GPT-4o-mini, Qwen2), не frontier. - **Память DelTA — LLM-в-цикле:** LTM-ретривер (n=2 из последних l=20 предложений) + двуязычное резюме (обновляется каждые m=20) — **вызовы модели per-предложение**, не детерминированный no-LLM горячий путь. Дорого/медленно на книжном масштабе. - **Валидировано на коротких документах** (IWSLT2017-talks, одиночные главы Guofeng), **никогда на 1000-главном сериальном каноне** — «first-wins на 1000 глав» — непроверенная экстраполяция масштаба. «структурированная память > длинный контекст» **DelTA не A/B-тестировала против long-context frontier-модели** — это гипотеза, не установленный факт (research/05 подаёт как доказанное). - Лицензию Apache-2.0 из репозитория подтвердить **не удалось** — перепроверить перед реюзом. **TransAgents (2405.11804):** «предпочитают GPT-4 и человеческим референсам» — технически верно, но слабо: человеческое предпочтение **маргинально**; сильные 66-vs-30 — от **GPT-4-как-судьи** (self-preferential); d-BLEU **катастрофически низкий** (25 vs 43–52 — большой дрейф от источника); авторы **сами предупреждают**, что человеческие референсы могли быть Google Translate, а оценщики — не реальные читатели вебновелл; **zh→En only**. Реюзать только проверенный вклад — **подготовительную стадию «translation guidelines / story bible»**; не позиционировать продукт как «за пределами человека» (NAACL 2025: LLM всё ещё проигрывают проф. литпереводчикам 80–100% под BWS). **Сильнейшее переносимое доказательство ставки на память — не DelTA, а Karpinska & Iyyer (2023):** контекст уровня абзаца предпочитают ~71–78% и он режет ошибки инконсистентности **25→2**, мерено на **ja/zh-источнике и морфологически богатых японском/польском таргетах** (**польский — лучший доступный прокси для русского** по роду/падежу). НО та же работа: **mistranslation и omission ПЕРСИСТЯТ даже при идеальном контексте** — прямая валидация опасения владельца: **память необходима, но недостаточна**, post-check/редактор сохранять. > ⚠ Уточнение: в Karpinska&Iyyer русский — только **источник** (ru-en/ru-ja/ru-pl), никогда таргет; переводчик — GPT-3.5 (2023). Даже сильнейшее переносимое доказательство содержит **ноль ru-таргет-оценок**. **Свежее (2025–2026):** **WMT25 Terminology** («Terminology is Useful Especially for Good MTs», aclanthology 2025.wmt-1.30) — sentence-level решён, **document-level (наш кейс) НЕ решён даже с глоссарием**; Track 1 включает **En→Ru** (ближайшее к русской терминологии во всём корпусе), Track 2 — doc-level En↔Trad.Chinese с one-to-many словарём (аналог нашего). Ключевой нюанс: **глоссарий помогает СИЛЬНЫМ системам больше** — payoff памяти условен на качестве базовой модели (важно для выбора стека). Их **трёхрежимный протокол (no / correct / RANDOM terminology)** — готовый шаблон адверсариального замера «помогает ли инъекция vs вредит», с «random»-рукой как проба тихой деградации. > **Расхождения с research/05** (сведены): «+4.58 п.п.» подать с точной атрибуцией (Xx→En среднее); LTCR-1 назвать самосогласованностью, не качеством; «LTM = retrieval по всему документу» → ограниченное онлайн-окно; TransAgents-предпочтение смягчить; «структурированная память > длинный контекст» — гипотеза, не доказанное; DelTA-память **не детерминированная** (LLM per-предложение) — наша no-LLM ставка академически нова. ## Q7. Скептицизм к вендорам и эмбеддингам **Независимая перепроверка вендорских memory-чисел и выбор эмбеддинг-модели для zh/ja/ru.** **Вендорские «memory»-числа не переживают первоисточник и меряют не то.** Они про recall фактов о **пользователе** между чат-сессиями, не про детерминированный реестр «термин→перевод»: - **Mem0** (ECAI 2025, 2504.19413): бенчмарк — **LOCOMO, не LongMemEval**; в собственной таблице **full-context (72.9% J) БЬЁТ Mem0 (66.88%)** по точности — выигрыш Mem0 только в токенах/латентности. «94.4% LongMemEval @ ~7k токенов» — отдельный пост-пейпер-маркетинг. - **Zep** (2501.13956): DMR 94.8 vs MemGPT 93.4 — реально, но Zep **сам** называет DMR неадекватной; независимая исправленная репродукция LOCOMO **обрушила** число Zep (getzep issue #5: 84%→58.44%, методологическая критика — single-run vs 10-run, first-4 категории, system_prompt). - Вывод: **не класть ни одно вендор-число в доки как доказательство для глоссария**; при стандартизованной методологии их бьёт ванильный RAG. Цитировать только как «оценено, отвергнуто — чужой домен». (Свежий вход: LoCoMo — быстро-обновляемая гонка самозаявленных чисел, любая одна цифра одноразова.) **Эмбеддинги для zh/ja/ru (мандат сессии) — эмпирический замер, `retrieval_bench` (CPU, реальный корпус):** | Модель | dim | R@5 | nDCG@5 | ROC-AUC | recall@prec≥0.9 | encode CPU | вердикт | |---|---|---|---|---|---|---|---| | **bge-m3** | 1024 | **0.974** | **0.941** | **0.970** | 0.54 | ~78 мс/текст | **дефолт Фазы 2** | | Qwen3-Embedding-0.6B | 1024 | 0.891 | 0.881 | 0.953 | 0.14 | ~800 мс/текст | challenger | | LaBSE | 768 | 0.827 | 0.806 | 0.931 | 0.20 | ~27 мс/текст | **только alignment** | - **bge-m3 — дефолт:** лучший R@5/nDCG/AUC; sparse-голова делает точный матч имён собственных, которую dense-only модели упускают; 8k контекст под резюме; 1024-d держит brute-force BLOB дёшево; опубликован MIRACL zh/ja/ru dense 67.8. - **Qwen3-Embedding-0.6B** (2506.05176, MMTEB 64.33 — **highest open-weight**, Gemini Embedding 68.37 выше) — держать **challenger'ом**: у нас MRR/ранняя точность близки, но **recall@prec≥0.9 = 0.14** (в режиме высокой точности заметно хуже bge-m3), dense-only (нет sparse), тренирован на ~150M **синтетических** парах от самого Qwen3 (риск MMTEB-оверфита/self-distillation). Переключаться только если выиграет на **своём** held-out zh/ja→ru сете. На CPU ×10 медленнее bge-m3. - **LaBSE — только для Bertalign-выравнивания, НЕ для RAG** (эмпирически слабейшая: R@5 0.827; документирована слабость на асимметричном short-query→long-passage). → расхождение с research/05 §7 «один стек на выравнивание и RAG». - **Общий вывод по Q1/Q3:** **ни один** эмбеддинг не даёт разделяющего порога на нашей задаче (пересечение распределений; при prec≥0.9 recall 0.14–0.54) → эмбеддинг-слой low-trust, per-книга калибровка, кандидаты на ревью; спор «какая модель» **вторичен** для MVP (горячий путь детерминирован). > **Расхождения с research/05:** убрать вендор-числа (Mem0 «94.4%/~6787 ток.», Zep «63.8% vs 49.0%») — не поддержаны первоисточником; LaBSE не реюзать для RAG; добавить Qwen3-Embedding-0.6B как основного challenger'а bge-m3. > ⚠ Честная граница: MIRACL/CMTEB/MMTEB включают русский, но как **внутриязыковой** retrieval (ru→ru), **не** кросс-язык zh/ja-запрос→ru-глоссарий — истинную задачу дизайна **ни один публичный бенчмарк не меряет**; свой кросс-язык-замер обязателен. --- ## Cross-cutting: пробелы, найденные completeness-критиком (не покрыты отдельными Q) Все — первоклассные строки реестра рисков (`architecture/06`, разделы D/F/G): 1. **Пропагация ошибки резюме** — вторая половина банка почти не рассмотрена; у резюме **нет post-check-аналога**, галлюцинация факта компаундится по 1000 глав (реестр D1). 2. **Качество автоэкстракции (bootstrap)** — валидировали retrieval, не популяцию; мусор/пропуск на входе — корневой отказ выше всего измеренного (G1). 3. **Token-budget eviction** — какая запись выпадает при превышении бюджета не задано; выпавшая нужная = тихая деградация (F2). 4. **Человеческая ёмкость** — почти каждая защита кончается «флаг → человеку»; объём флагов на главу не оценён; при высоком flag-rate «route to human» пусто (G2). 5. **Конкурентность/порядок в SQLite** — параллельный перевод ломает first-wins/sticky (последовательные по предположению) (F3). 6. **Коррелированный отказ судьи-экстрактора** — одна модель на одних трудных кейсах → тихо промоутит плохие approved-ключи (F5). 7. **SPOF одного файла** — повреждение теряет весь банк молча (F4). 8. **zh/ja→ru-специфика:** стандарты транслитерации **Палладий (zh) / Поливанов (ja)** — имя может быть внутренне-консистентным, но систематически неверным по стандарту (ось корректности ⊥ самосогласованности) — **крупнейший непокрытый пробел** (B6); утечка рода через **русский глагол прош. вр.** («сказал/сказала») мимо `gender=hidden` (C3); склонение транслит-CJK-имён нетривиально (C4); гоноративы/регистр ты/вы (реестр). 9. **Противоречие «горячий путь = FTS5 vs trie»** — разрешено: **не FTS5** (impl-notes §3.6 уже так решает; FTS5 не сегментирует CJK), а детерминированный Aho-Corasick/trie матч (реестр §Контракт горячего пути). ## Инвариант робастности D5.2 — подтверждён кодом (критично) Промт требовал провалидировать инвариант «`snapshotID` покрывает состояние памяти + сборку контекста». **Подтверждаю против реального кода — дыра реальна:** - `snapshotID` (`backend/internal/pipeline/snapshot.go:82 (до пакета №4 — runner.go:145-163)`) хэширует brief_hash, chunker/estimator-версии, pipeline_core, defaults (max_output_ratio/min_max_tokens) и per-stage план (model, prompt_version, PromptSHA256, temperature, reasoning, ExtraBody, provider-оверрайды) — **но НЕ версию approved-глоссария и НЕ параметры сборки контекста** (`glossary_injection`/`stm_depth`/`overlap`). - `RequestHash` (`render.go:158`) включает контент `msgs`; `render.go:23-29` декларирует рендер **чистой функцией snapshot**; `Messages` (`render.go:133-136`) прямо говорит: инъекция глоссария/STM встанет между system и user в Фазе 1. - Следствие: как только память попадёт в `msgs`, изменённый глоссарий/STM меняет `request_hash`, но **не** `snapshotID` → resnapshot-гейт (`bookrun.go:108/stagerun.go:77 (до пакета №4 — runner.go:260)`) молчит → ре-ран старой главы промахивается мимо чекпоинта и **переоплачивает расходящимся переводом**, ломая инвариант Р6, на который D5 опирается. impl-notes §3.2 это уже **предписывает** («зафиксированное состояние approved-глоссария (версия-счётчик)») — **код отстаёт от доков**. - **Фикс (реестр F1):** свернуть в snapshot **оба** — (1) версию-счётчик approved-глоссария + резюме/series-bible (рендер читает **замороженную** версию, не live) и (2) context-assembly-конфиг. Общий гейт Фазы 1, держит D1 и D5; закрыть **до** инъекции памяти. --- ## Вердикт: держит с условиями. Что менять. **GO на архитектуру** (lorebook-активация + иерархические резюме + ограниченное окно STM + 3-слойная защита) — она peer-reviewed (DelTA ICLR 2025) и подтверждена растущей линией 2025–2026 (Karpinska, WMT25). **Но go/no-go на саму ставку нельзя выдать сейчас** — центральный **детерминированный no-LLM горячий путь академически нов и не валидирован** (DelTA — LLM-в-цикле), единственное сравнение, что имеет значение (**банк vs длинный контекст на реальном zh/ja→ru стеке**), **не проведено**, acceptance-критериев/baseline/cost-модели нет. **Обязательно до заморозки схемы (входит в реестр `architecture/06` §Гейты):** 1. **F1 snapshotID под память + context-assembly — до инъекции Фазы 1** (тихий расходящийся ре-пэй). 2. **Post-check из E1/E2 — обязательный гейт**, не опциональный флаггер; слепой post-replace в русский — запрет; `decl` как safety. 3. **Трёхсторонний disposition инъекции + per-chunk retrieval-state** запись (наблюдаемость). 4. **Схема v1 (D7) + добавить:** `sense`; ось «редакционное время» ⊥ спойлер-окну; `UNIQUE(src,sense,window)`+обратная dst-проверка; `min_key_len`+запрет одиночных ключей; нормализационный артефакт NFKC/OpenCC/kana/ruby. 5. **Рациональ sqlite-vec/modernc переписать** (не «pure-Go не может»); brute-force в MVP оставить. **Обязательный in-house eval перед фиксацией ставки (acceptance-критерии — предзарегистрировать):** - **Протокол:** WMT25-трёхрежимный (no / correct / **random** terminology) на реальных zh/ja→ru чанках, измерять **две оси раздельно**: (а) самосогласованность approved-форм по главам, (б) source-fidelity/omission (reference-free QE — CometKiwi/MetricX, + LLM-судья-против-источника, охотящийся на пропуски/mistranslation). Плюс ru-специфика: согласование рода (вкл. глагол прош. вр.) и склонение `dst`. - **Baseline, без которого сборка может быть не оправдана:** **банк памяти ON vs OFF** и **банк vs длинный контекст** frontier-модели на **том же** стеке и направлении (DelTA этого не делала). - **Acceptance (предзарегистрировать до прогона):** мин. Δ качества (напр. CometKiwi/win-rate), макс. omission-rate, макс. name-inconsistency-rate, **допустимый flag-volume на главу** (иначе «route to human» пусто). Голд-сет: размер, квалификация аннотаторов (ru-литчитатели), IAA, BWS/MQM (не сырые автометрики — NAACL 2025). - **Cost:** посчитать батч-LLM-операции (экстракция, адъюдикация, обновление резюме) на 1000 глав против экономического контура Р5. **Резюме одной фразой:** банк памяти строить своим — верно; но **тихую деградацию мы не устраняем, а конвертируем в громкую** (post-check + disposition-лог + retrieval-state + жёсткие спойлер/род-гейты), и **числа литературы для zh/ja→ru не годятся** — их заменяет свой замер с предзарегистрированными критериями и baseline «банк vs длинный контекст». --- ## Честные слабые места рекомендаций (что подано увереннее, чем доказано) Раздел добавлен на закрытии сессии умышленно: по этим рекомендациям пишут код, поэтому места, где моя уверенность ниже, чем звучит в тексте выше, — здесь явно. Отсортировано по важности для реализации. 1. **F1 (фикс snapshotID) — УЖЕ РЕАЛИЗОВАН бэкендом (commit 5eaf2d9), тем самым «материализующим» вариантом, к которому я склонялся** (snapshot содержит `context_assembly` + `MemoryVersion` = хеш материализованной памяти; `memoryVersion()` пока хеширует константу-заглушку до приземления банка v2, комментарий в коде явно держит инвариант). **Каветат снят.** Узкий остаток: при приземлении глоссария заменить заглушку на реальный хеш замороженных материализованных строк — иначе инвариант тихо регрессирует. (Я держал это как «открытый гейт, а фикс — лишь направление» по УСТАРЕВШЕМУ чтению кода начала сессии — на деле бэкенд уже ушёл далеко вперёд и это сделал.) 2. **Пост-check в русском — несущий И невалидированный.** Весь safety-аргумент реестра держится на пост-check как «главном детекторе тихой деградации». Но проверка «утв. `dst` присутствует» в русском требует **склонение-осознанного матча** (лемматизатор), и он ошибается: ложно-отрицательное (форма есть, но матч промахнулся) → ложный флаг → шум → редакторы перестают верить флагам → safety рушится. В `memory_hotpath` я использовал игрушечные регэксп-корни. **Надёжность пост-check в русской морфологии — сама по себе непроверенный research-вопрос; если он шумит, вся защита слабеет.** Замерить до того, как доверять ему как гейту. 3. **«Пусто = безопасно» — подано слишком сильно; дизайн меняет ложную-инъекцию на ложный-ПРОПУСК, а пропуск тоже тихий.** Recall точного матча в моём бенчмарке — **0.571**: ~43% релевантных записей НЕ инъектируются (косвенные упоминания, перефраз, местоимения сверх sticky). Пропущенная запись → несогласованный рендеринг → ровно тот дрейф, которого боимся. Precision-first — защитимая ставка, но это **размен**, и net-положителен ли он — именно то, что должен померить Ф2.5. Я эту напряжённость в собственной рекомендации сгладил. 4. **`retrieval_bench`: корпус, gold И ловушки я построил сам → риск само-подтверждения; малый N; ловушки — «лёгкая» версия.** Мои ловушки (送灶/修/炎/气) — случаи, где точного многосимвольного термина в тексте НЕТ, поэтому substring выигрывает тривиально. **Трудную версию** — полный термин, который сам по себе омоним/совпадает с обычным словом целиком — я не тестировал. «Substring trap-safe» доказано для лёгкой ловушки, не для трудной. И bge-m3 > Qwen3 близко — на реальных данных может перевернуться. Числа индикативны с большими доверительными интервалами. 5. **«Разделяющего cosine-порога нет» — свойство МОЕГО представления, не закон эмбеддингов.** Я эмбедил карточки «src — dst. note». Другое представление (только dst / NL-глосса / sparse-голова bge-m3) может разделять лучше. Читать как «наивный cosine на этой карточке не гейтит», НЕ «векторные пороги бесполезны». 6. **Латентность brute-force — это numpy, не Go.** «<20 мс на 100k» — оптимистичный пол; рукописный Go-цикл с десериализацией BLOB будет медленнее (ресёрч это и флагал — scalar-Go оверхед). Перемерить в Go до того, как доверять триггеру миграции. 7. **Конкретные вендор/академ-числа опираются на веб-чтение моих ресёрч-агентов, не на мою личную перепроверку.** Верификаторы ловили overclaim'ы, но остаточные ошибки возможны (перепутанный arXiv-ID, чуть неверная цифра). DelTA +4.58, Zep 84→58.44, Mem0 72.9/66.88 — доверять на уровне *направления*; перед цитированием точной цифры как факта — перепроверить первоисточник. 8. **Самое глубокое: я рекомендовал ДЕШЁВЫЙ НЕВАЛИДИРОВАННЫЙ путь поверх ДОРОГОГО ДОКАЗАННОГО.** Сильнейшее доказательство (DelTA) валидирует память **с LLM-в-цикле** per-предложение. Наш детерминированный no-LLM горячий путь — это ставка по экономике, а **не** по доказательствам. Защитима на COGS, но это ставка, и я должен сказать прямо: она cost-driven, не evidence-driven. ## Источники (проверены 2026-07-04; с корректировками верификаторов) **Retrieval-robustness / RAG-failure:** Power of Noise (SIGIR 2024, [2401.14887](https://arxiv.org/abs/2401.14887)); Yoran et al. robust-to-irrelevant (ICLR 2024, [2310.01558](https://arxiv.org/abs/2310.01558)); RGB / Benchmarking LLMs in RAG — noise robustness + negative rejection, **есть китайский тестбед** (AAAI 2024, [2309.01431](https://arxiv.org/abs/2309.01431)); CRAG ([2401.15884](https://arxiv.org/abs/2401.15884)) + независимая репродукция/объяснимость ([2603.16169](https://arxiv.org/html/2603.16169)); over-trust инъектированных терминов ([2510.00829](https://arxiv.org/abs/2510.00829)). **Стек/масштаб:** sqlite-vec ([github.com/asg017/sqlite-vec](https://github.com/asg017/sqlite-vec)); ncruces/go-sqlite3 (pure-Go WASM) + sqlite-vec-go-bindings/ncruces; RRF (Cormack, Clarke & Buettcher, SIGIR 2009). **Retrieval-корректность / reranking:** Sciavolino «Simple Entity-Centric Questions Challenge Dense Retrievers» (EMNLP 2021, [2109.08535](https://arxiv.org/abs/2109.08535)); BGE-M3 ([2402.03216](https://arxiv.org/abs/2402.03216), MIRACL zh/ja/ru); bge-reranker-v2-m3. **Детерминизм / терминология:** silent term-misses 17–36% ([2310.05824](https://arxiv.org/abs/2310.05824)); DuTerm en→ru «It Takes Two: A Dual Stage Approach» ([2511.07461](https://arxiv.org/abs/2511.07461)); Cascaded Beam Search en-ru EMA 0.925 / ~0 BLEU ([2305.14538](https://arxiv.org/abs/2305.14538)); agreement 46% при форсе surface-форм en-cs ([2106.12398](https://arxiv.org/abs/2106.12398)); Bergmanis & Pinnis lemma+inflect +47.7 п.п. (Latvian); retrieval-vs-generation de-en ([2503.05010](https://arxiv.org/abs/2503.05010)); WMT24 terminology ([2024.wmt-1.51](https://aclanthology.org/2024.wmt-1.51/)). **Академическое заземление:** DelTA (ICLR 2025, [2410.08143](https://arxiv.org/abs/2410.08143), код DocMTAgent); TransAgents (TACL, [2405.11804](https://arxiv.org/abs/2405.11804)); Karpinska & Iyyer «Large Language Models Effectively Leverage Document-level Context» (EMNLP 2023, [2304.03245](https://arxiv.org/abs/2304.03245)); WMT25 Terminology «Terminology is Useful Especially for Good MTs» ([2025.wmt-1.30](https://aclanthology.org/2025.wmt-1.30/)); DITING zh→En ([2510.09116](https://arxiv.org/abs/2510.09116)); NAACL 2025 литперевод BWS. **Вендоры/эмбеддинги:** Mem0 (ECAI 2025, [2504.19413](https://arxiv.org/abs/2504.19413), LOCOMO); Zep/Graphiti ([2501.13956](https://arxiv.org/abs/2501.13956)) + getzep issue #5 (исправленная репродукция); Qwen3-Embedding ([2506.05176](https://arxiv.org/abs/2506.05176)); MMTEB ([2502.13595](https://arxiv.org/abs/2502.13595)); LaBSE. **Эмпирика (эта сессия):** `eval/retrieval_bench.py`, корпус `eval/data/retrieval_bench/corpus.json` (реальные PD: 阿Q正传/祝福/羅生門/走れメロス + синтетические сянься-ловушки), результаты `eval/data/retrieval_bench/results.json`. Оговорка: микро-бенчмарк (24 записи, 18 запросов, 13 gold, 4 ловушки) — **индикативный**, не финальный eval; разделяющие/пороговые выводы (AUC, пересечение распределений) устойчивы как распределительное свойство, конкретные R@k — с поправкой на малый N.