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

64 KiB
Raw Permalink Blame History

Валидация банка памяти книги: вердикт, что менять, обоснование и источники

Дата: 2026-07-04. Сессия «Валидация банка памяти» (промт docs/archive/prompts/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), при 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 молча теряет 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/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 скалярного скана > ~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 не инъектит ловушки при пороге precision≥0.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/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) и подтверждена растущей линией 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 длинный контекст».


Честные слабые места рекомендаций (что подано увереннее, чем доказано)

Раздел добавлен на закрытии сессии умышленно: по этим рекомендациям пишут код, поэтому места, где моя уверенность ниже, чем звучит в тексте выше, — здесь явно. Отсортировано по важности для реализации.

  1. F1 (фикс snapshotID) — УЖЕ РЕАЛИЗОВАН бэкендом (commit 5eaf2d9), тем самым «материализующим» вариантом, к которому я склонялся (snapshot содержит context_assembly + MemoryVersion = хеш материализованной памяти; memoryVersion() пока хеширует константу-заглушку до приземления банка v2, комментарий в коде явно держит инвариант). Каветат снят. Узкий остаток: при приземлении глоссария заменить заглушку на реальный хеш замороженных материализованных строк — иначе инвариант тихо регрессирует. (Я держал это как «открытый гейт, а фикс — лишь направление» по УСТАРЕВШЕМУ чтению кода начала сессии — на деле бэкенд уже ушёл далеко вперёд и это сделал.)

  2. Пост-check в русском — несущий И невалидированный. Весь safety-аргумент реестра держится на пост-check как «главном детекторе тихой деградации». Но проверка «утв. dst присутствует» в русском требует склонение-осознанного матча (лемматизатор), и он ошибается: ложно-отрицательное (форма есть, но матч промахнулся) → ложный флаг → шум → редакторы перестают верить флагам → safety рушится. В memory_hotpath я использовал игрушечные регэксп-корни. Надёжность пост-check в русской морфологии — сама по себе непроверенный research-вопрос; если он шумит, вся защита слабеет. Замерить до того, как доверять ему как гейту. (→ СНЯТО исполнением, D31: пост-check реализован decl-aware путём — decl.forms в сиде + D24.4 union базовой формы, НЕ pymorphy-лемматизация; замерен на этапе A — 0% ложных на full-decl, 67% на base-only → держится OFF-флаггером ровно как каветка требовала; D28.1 честный precision/recall-трейдофф — hard-gate флип требует пере-замера recall; D24.2 alias-слепое пятно ≈99.5%.)

  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); Yoran et al. robust-to-irrelevant (ICLR 2024, 2310.01558); RGB / Benchmarking LLMs in RAG — noise robustness + negative rejection, есть китайский тестбед (AAAI 2024, 2309.01431); CRAG (2401.15884) + независимая репродукция/объяснимость (2603.16169); over-trust инъектированных терминов (2510.00829).

Стек/масштаб: sqlite-vec (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); BGE-M3 (2402.03216, MIRACL zh/ja/ru); bge-reranker-v2-m3.

Детерминизм / терминология: silent term-misses 1736% (2310.05824); DuTerm en→ru «It Takes Two: A Dual Stage Approach» (2511.07461); Cascaded Beam Search en-ru EMA 0.925 / ~0 BLEU (2305.14538); agreement 46% при форсе surface-форм en-cs (2106.12398); Bergmanis & Pinnis lemma+inflect +47.7 п.п. (Latvian); retrieval-vs-generation de-en (2503.05010); WMT24 terminology (2024.wmt-1.51).

Академическое заземление: DelTA (ICLR 2025, 2410.08143, код DocMTAgent); TransAgents (TACL, 2405.11804); Karpinska & Iyyer «Large Language Models Effectively Leverage Document-level Context» (EMNLP 2023, 2304.03245); WMT25 Terminology «Terminology is Useful Especially for Good MTs» (2025.wmt-1.30); DITING zh→En (2510.09116); NAACL 2025 литперевод BWS.

Вендоры/эмбеддинги: Mem0 (ECAI 2025, 2504.19413, LOCOMO); Zep/Graphiti (2501.13956) + getzep issue #5 (исправленная репродукция); Qwen3-Embedding (2506.05176); MMTEB (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.