textmachine/docs/research/13-memory-bank-validation.md

238 lines
57 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Валидация банка памяти книги: вердикт, что менять, обоснование и источники
Дата: 2026-07-04. Сессия **«Валидация банка памяти»** (промт `docs/MEMORY_RESEARCH_SESSION_PROMPT.md`). Предмет — решение **Р3 «Банк памяти книги»** (`architecture/01-decisions.md`) и исследовательская база `research/05-memory-glossary.md`. Мандат: **адверсариально провалидировать** подход до заморозки в коде — не поверить, а попытаться сломать.
Метод: многоагентный ресёрч по 7 направлениям (Q1Q7) + независимая адверсариальная верификация каждой находки по первоисточникам + завершающий 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), при precision0.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 молча теряет 1736% терминов** (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/jaru литература и есть сдвиг распределения. **Наша эмпирика это подтверждает напрямую** (см. §Q2/Q3, таблица): у лучшей модели bge-m3 ROC-AUC=0.970, но релевантные (rel_mean 0.555, min 0.374) и мусорные (irr_mean 0.357, p95 0.447) cosine **пересекаются**; чтобы держать precision0.9, порог cos0.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 скалярного скана > ~150200 мс → перейти на SIMD-brute-force sqlite-vec через **ncruces/go-sqlite3 + asg017/sqlite-vec-go-bindings/ncruces** (pure-Go WASM, официально, stable v0.35.1). ×310 без сервера. Реальная цена — миграция всего драйвера с 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 > ~500k1M или интерактивный 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 (~560600M, XLM-R-large, 1024-d, ~2.22.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 **не** инъектит ловушки при пороге precision0.9 но **только потому, что порог высок** (сырые cosine ловушек 0.390.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-3050 продакшн-стандарт. Прирост гибрида над 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`** (разделы BD). Ключевые находки этой сессии:
**Самая опасная — академический якорь сам порождает отказы.** 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:** промптинг оставляет **1736% терминов молча неиспользованными** (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. Свежие 20252026.**
**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 4352 — большой дрейф от источника); авторы **сами предупреждают**, что человеческие референсы могли быть Google Translate, а оценщики — не реальные читатели вебновелл; **zh→En only**. Реюзать только проверенный вклад — **подготовительную стадию «translation guidelines / story bible»**; не позиционировать продукт как «за пределами человека» (NAACL 2025: LLM всё ещё проигрывают проф. литпереводчикам 80100% под BWS).
**Сильнейшее переносимое доказательство ставки на память — не DelTA, а Karpinska & Iyyer (2023):** контекст уровня абзаца предпочитают ~7178% и он режет ошибки инконсистентности **25→2**, мерено на **ja/zh-источнике и морфологически богатых японском/польском таргетах** (**польский — лучший доступный прокси для русского** по роду/падежу). НО та же работа: **mistranslation и omission ПЕРСИСТЯТ даже при идеальном контексте** — прямая валидация опасения владельца: **память необходима, но недостаточна**, post-check/редактор сохранять.
> ⚠ Уточнение: в Karpinska&Iyyer русский — только **источник** (ru-en/ru-ja/ru-pl), никогда таргет; переводчик — GPT-3.5 (2023). Даже сильнейшее переносимое доказательство содержит **ноль ru-таргет-оценок**.
**Свежее (20252026):** **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.140.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/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-гейт (`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) и подтверждена растущей линией 20252026 (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 длинный контекст».
---
## Источники (проверены 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 1736% ([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.