36 KiB
Промт сессии «Бэкенд — Шаг 4: банк памяти v2 (глоссарий + инъекционная поверхность + горячий путь)»
Ты — инженер-бэкенд проекта TextMachine (Go, бэкенд AI-перевода крупных художественных текстов ранобэ/вебновелл, zh/ja/en→ru, мультиагентный пайплайн). Рабочий каталог /home/ubuntu/projects/textmachine, твоя зона — backend/. Это продолжающая сессия: Фаза 0 (каркас), Веха 1 (capability + snapshotID), Веха 2 (chunk-loop + disposition), Веха 2.5 (coverage-гейт + single-hop эскалация + D12) и шаг 3a (настоящий чанкер + импорт txt/epub + ruby-захват; принят внешним ревью, все 7 находок закрыты) сделаны и закоммичены. Ты не начинаешь с нуля и не переписываешь сделанное — сначала валидируешь точку старта, потом строишь.
Метод жёсткий, без хаков: доки+код → валидация старта → код в ратифицированном порядке → агентское адверсариальное селфревью на вехе → сдаёшь на внешнее ревью. Никаких срезов углов, закомментированных проверок, обходов инварианта детерминизма, «потом починю». Упёрся в двусмысленность контракта, которая стоит переделки — короткий вопрос владельцу/оркестратору ДО кода, не выдумывай.
Память v2 — главный блокер качества перевода (консистентность имён/терминов — жалоба №1 читателей, 04-unhappy §1). Это самый рискованный шаг: тихая деградация тут — не «пусто», а ошибочная инъекция, которой сильная модель послушно следует (уверенность растёт, точность падает — arXiv 2510.00829). Реестр рисков architecture/06 — твоё ТЗ по механизмам защиты; читай его дословно.
0. ПЕРВОЕ ДЕЙСТВИЕ — валидация точки старта (обязательно, до любого кода)
- git:
git -C /home/ubuntu/projects/textmachine log --oneline -8. SHA плавают (параллельные сессии). Последний backend-код-коммит — «Close step-3a external-review findings…» (7 находок 3a закрыты: spine-reconciliation, UTF-8-нормализация, quote-depth, ruby full-replace, tiling-fuzz). Рабочее дерево поbackend/чистое (грязныеeval/,docs/experiments|research/*— зоны Полигона/памяти, НЕ трогать/НЕ коммитить). - Сборка/тесты (Go 1.26.4 в
~/sdk/go; изbackend/):export PATH="$HOME/sdk/go/bin:$PATH"; go build ./... && go vet ./... && go test ./... -race -count=1. Всё зелёное. Не зелёное — находка, разбирайся до кода. (cwd Bash может сбрасываться — заходи вbackend/явно; пример проекта —backend/example/, НЕ repo-root.) - Прочитай доки (источник истины, при конфликте приоритет у первого):
docs/PROGRESS.md§Бэкенд (последние записи: шаг 3a + закрытие 7 находок) и §Память (журнал памяти-сессии — вердикт+реестр).docs/architecture/06-memory-risk-registry.md— ЦЕНТРАЛЬНЫЙ вход, читай дословно. §«Гейты для бэкенда перед фиксацией схемы store/глоссария v2» (что обязан закрыть до заморозки схемы), §«Контракт горячего пути» (НЕ FTS5, НЕ вектор — детерминированный multi-pattern матч), §«Ruby-seed контракт» (вход из 3a), таблицы A–G (каждая строка — механизм или дыра со статусом ✅/🔶/❌).docs/research/13-memory-bank-validation.md§«Честные слабые места рекомендаций» — ОБЯЗАТЕЛЬНО. Два несущих НЕдоказанных места: F1 (snapshot материализует байты инъекции vs store версионирует глоссарий) и E1 post-check (надёжность в русской морфологии невалидирована — склонение-осознанный матч шумит → ложные флаги → редакторы игнорируют → safety рушится). Померь post-check на русском ДО того, как доверять ему как жёсткому гейту.docs/architecture/05-decisions-log.md— D7 (схема памяти v1:src,dst,type,aliases,gender,speech,decl,since_ch/until_ch,status,note+ alias-граф +gender=hidden+translit_policy+first_person+ series-bible-lite = gender+ты/вы-журнал;nickname_translationв v1), D8 (memory-компонент snapshotID = content-hash материализации, не version-counter), D10 (знаменатель консистентности = ВСЕ approved-термины, порог 98% на детерминированном слое), D5.1/D5.2 (проактивныйgender=hidden; snapshotID покрывает память).docs/architecture/03-implementation-notes.md— §3.2 (snapshot: замороженная версия approved-глоссария; STM в snapshot НЕ входит; батч-подтверждение терминов только на границе джоб), §3.6 (SQLite: FTS5 в горячий путь НЕ тащить; кириллица нормализуется в Go), §3.7 (coverage-гейт), §3.8 (граница Р2: режим инъекции/бюджеты — конфиг; сборка/матч — код).docs/architecture/04-unhappy-paths.md§1/§2/§4 + «Следствия» п.2;02-mvp-plan.md«Фаза 1» (банк памяти + приёмка ≥98%);01-decisions.mdР3/Р5/Р6 (не редактировать).
- Прочитай код, который расширяешь (инварианты не ломать):
pipeline/render.go—RenderVars{Book,Text,Draft}— ФИКС-структура,Renderпадает на неизвестном{{…}};Messages()=[system(CacheBoundary:true), user], инъекция глоссария/STM встаёт МЕЖДУ ними (комментарий в коде это фиксирует), Р5-раскладка.memoryVersion()— сейчас хеширует КОНСТАНТУtm-memory-v1\x00(заглушка; твоя задача — заменить на хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк, F1).RequestHashвключает контентmsgs;NormalizeSource;EstimateTokens.pipeline/runner.go—snapshotID()уже сворачиваетcontextSnap(glossary_injection/budget/stm/overlap/ttl) +MemoryVersion(F1 структурно, 5eaf2d9 — активируется твоей материализацией);translateChunk/runStage/runAttempt(цикл стадий — семантику НЕ меняешь);persistRuby(шаг 3a —ReplaceRubyReadings, полная замена агрегата). Инъекция память→msgs встаёт вrunStageпередMessages().pipeline/chunker.go/ingest.go— источникChunk.Textиruby_readings(твой сид). Не трогаешь, потребляешь.store/migrate.go— миграции v1→v4 (v4 =ruby_readings). Твоя — v5 (append-only, IF NOT EXISTS, tx на шаг, никогда не редактируй прошлый шаг).store/ruby.go—RubyReadingsForBook(сид auto-кандидатов).config/pipeline.go—ContextAssembly(glossary_injectionselective|full_prefix, glossary_token_budget, stm_depth, overlap_tokens, cache_ttl) — схема есть, инъекции нет.
- Прогони
tmctl report --config example/book.yaml(изbackend/) — $0, LLM не зовётся. (Протухшийexample/*.db*— gitignored, удали, пересоздастся — schema бампнута v4→после сменыchunkerVersionпример пере-снапшотится.) - Исполнимый эталон (зона Полигона — читать, НЕ редактировать/коммитить):
eval/memory_hotpath.py(T1–T10 → Go-юниттесты),eval/retrieval_bench.py+eval/data/retrieval_bench/(точный многосимвольный подстрочный матч precision 1.0; фаззи/одиночные ключи — все ловушки в top-5),eval/extract_bench.py(спот=локаль OK, рендер dst=облако/человек).
Инвариант, который держишь свято: любой инпут, меняющий wire-запрос ИЛИ max_tokens, обязан входить либо в snapshot, либо в RequestHash. Материализованная память (approved-глоссарий/резюме/series-bible на момент старта джобы) обязана войти в memoryVersion() → snapshot; рендер читает замороженную версию, не live (F1 — 🔴🔇, класс D5.2). STM в snapshot НЕ входит (пересобирается из чекпоинтов).
1. Состояние на входе (что готово — НЕ пересоздавать)
- Chunk-loop + disposition + coverage + escalation (Вехи 2/2.5), чанкер+ingest+ruby (3a, 7 находок закрыты) — семантику не трогаешь, потребляешь.
- snapshotID уже сворачивает
context_assembly+MemoryVersion(F1 структурно, 5eaf2d9): остаётся заменить константу вmemoryVersion()на хеш материализации — иначе F1 тихо регрессирует. ruby_readings(v4): capture-only, полная замена агрегата (не в промпте/hash). Твой сид auto-кандидатов (не approved напрямую).- Config-ручки
context.*заведены, но вmsgsНЕ инъектятся — твоя работа. RenderVars— фикс-структура,Renderпадает на неизвестном{{…}}(инъекция = НЕ template-плейсхолдер по умолчанию; см. §3).
2. МИССИЯ — банк памяти v2 (предлагаемая первая веха; полный скоуп согласуй в §8)
Дать пайплайну детерминированную селективную инъекцию approved-глоссария в горячий путь + обязательный post-check, построив инъекционную msgs-поверхность ОДИН раз (её же ждут отложенные overlap-инъекция и ruby-лок шага 3a). Ратифицированный порядок реестра (§«Гейты перед фиксацией схемы»):
A. Схема store v2 (миграция v5) — заморозка глоссария
База D7 + добавь из реестра: поле sense (A3/полисемия); ось «редакционное время» ОТДЕЛЬНО от since_ch/until_ch (B1 stale — append-only журнал ревизий рендеринга); UNIQUE(src,sense,window) + обратная проверка dst-коллизий инъективности (B2); min_key_len per-язык + запрет одиночного Han/каны/латинской буквы как самостоятельного ключа (A3, schema-констрейнт); статус-машина термина (auto|draft|approved + журнал). Нормализационный артефакт (A4, ❌ самый вероятный «тихо пусто»): NFKC + OpenCC упрощ./трад. + kana-folding + захват ruby-чтений, симметрично к ключам/алиасам И к тексту чанка; версионированный, тестируемый, детерминированный. Кириллица нормализуется в Go до записи (§3.6).
B. Горячий путь — детерминированный матчер (НЕ FTS5, НЕ вектор, НЕ эмбеддинги)
Multi-pattern матч (Aho-Corasick / double-array trie) по src+алиасам над нормализованным чанком; для ru/en — по леммам/decl. Longest-match, замена целой сущности. «Лучше ничего, чем мусор» (A1): пустой матч — норма/безопасно; бюджет допустимо недозаполнить; НЕ добивать соседями; точность > полнота. Sticky scene-inertia (A5: персонажи прошлых 1–2 чанков — состояние из детерминированной пересборки чекпоинтов, НЕ эмбеддинги). Трёхсторонний disposition инъекции (A2, ❌): CONFIRMED→авторитетно; AMBIGUOUS (короткий/общий алиас, коллизия поверхности, auto/draft)→инъекция с пометкой «unverified» + принудительный post-check; REJECT→выкинуть+залогировать; каждая ветка наблюдаема. Спойлер-окно since_ch/until_ch = жёсткий reject-гейт с логируемым событием (C1, safety, не UX-фича). retrieval-state per-chunk: n_exact_hits, n_sticky, n_ambiguous_flagged, n_spoiler_blocked, embedding_tier_used(=0 в v1).
C. Инъекционная msgs-поверхность (строим ОДИН раз, Р5)
Раскладка system(стабильный префикс, кэш-граница) → [глоссарий/STM] → user(волатильный хвост: чанк/черновик) (A6: авторитетный блок глоссария рядом с чанком, не в середине стопки резюме). Селективный глоссарий в волатильном хвосте не ломает DeepSeek-префикс-кэш (Р5). Это та же поверхность, что ждут отложенные overlap-инъекция и ruby→глоссарий-лок 3a — строишь один раз, они встают на неё.
D. memoryVersion() — закрыть F1
Замени константу на хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк (версия approved-глоссария + резюме/series-bible на старте джобы). Рендер читает pinned-версию из snapshot, не live. Смена approved → громкий --resnapshot.
E. Post-check — ОБЯЗАТЕЛЬНЫЙ гейт (E1/E2, не опциональный флаггер)
Для каждого approved-термина с src в чанке — проверить, что склонённая форма dst (через decl/лемматизатор) есть в выходе; нет → флаг/точечный ре-перевод. Ловит и «модель проигнорировала (17–36% терминов)», и «мы инъектили неверное». ЗАПРЕТ слепого post-replace форм глоссария в ru-выход (E2): форс только когда термин морфологически инвариантен ИЛИ полный decl И падеж резолвится; иначе флаг (модели даём лемму, склоняет сама). decl — safety-механизм грамматичности. ⚠ Померь надёжность post-check на русском (E1, невалидирована) ДО жёсткого гейта (порт eval/memory_hotpath.py T1–T10 в Go); шумит → флаг-режим, не форс.
Ruby-seed (вход из 3a, реестр §Ruby-seed, подтверждён оркестратором)
base→src; first_chapter→since_ch; occurrences→приор уверенности (НЕ approved напрямую). reading→НЕ dst напрямую, а мост к Поливанов-dst-валидации (усиливает B6). Различай имя-чтение vs авторское двойное чтение: 鈴木→すずき (name-lock, Поливанов) против 強敵→とも (значат РАЗНОЕ → сноска, не лок; блайнд-лок = mistranslation); неоднозначное → человеку. Ruby — СИД в auto-кандидатный пул, промоушен в approved батч-судьёй/человеком (G1). (Ruby-хранилище 3a уже делает полную замену агрегата — фантомов нет.)
3. Шов, который сохраняешь (контракт цикла и рендера)
RenderVars{Book,Text,Draft}— фикс-структура;Renderпадает на неизвестном{{…}}. Инъекция = НЕ template-плейсхолдер по умолчанию. Два чистых пути: (а) код-собранный injection-message вMessages()из матченных записей (предпочтительно — без правки шаблонов), ИЛИ (б) новое полеRenderVars+ плейсхолдер + правкаMessages(). Реши по Р5; кэш-граница остаётся на стабильном префиксе.ch.Textв ТРЁХ местах (3a-контракт):{{text}}на проводе, source coverage-гейта,RequestHash. Инъекция — ОТДЕЛЬНЫЙ блок, НЕ вch.Text(иначе течёт в coverage len_ratio и хеш).- LLM только батч/граница джоб, НИКОГДА в горячем пути инъекции (F3/§3.2). Цикл последовательный (D12: гонка
Runner.clients+ коммит терминов только на границе джоб — наивный параллелизм чанков НЕ строить). - Смена материализованной памяти →
memoryVersion()→ snapshot → громкий--resnapshot(не тихий промах, F1).
4. Что НЕ делать в шаге 4 (отложено — реестр §5, не блокирует v1-схему; заложи поля/место)
- Эмбеддинги/вектор на горячем пути (A7/D2) — Фаза 2, low-trust; sqlite-vec не сейчас.
- Транслит-стандарты Палладий/Поливанов post-check (B6) — поле/место в v1, валидатор — Фаза 2 (reading как мост заложи).
- Гейт фактичности резюме (D1, ❌ первоклассный пробел), морфо-гейт глагольного рода (C3) — Python-сайдкар Фазы 2.
- Whole-book RAG по резюме (D2), бэкап/интеграл файла книги (F4) — Фаза 2.
- Автоэкстракция/адъюдикация конфликтов/батч-подтверждение (G1/B2/F5), резюме глава→арка→книга, series-bible-lite — если не влезает в первую веху, отдельная веха (спот=локаль / рендер dst=облако+человек; экстракция — LLM только на границе джоб, НЕ в горячем пути). Согласуй границу вехи (§8).
Если автопопуляция окажется критична для приёмки раньше — короткий вопрос оркестратору, не тащи молча.
5. Инварианты (жёстко)
- Детерминизм священен. Матч/инъекция/post-check/нормализация — чистые детерминированные функции;
memoryVersion()= хеш замороженной материализации в snapshot; нормализация — версионированный артефакт; никаких map-итераций в порядке вывода, time/rand. - Тихую деградацию конвертируем в громкую, а не устраняем обещанием: post-check + disposition-лог + retrieval-state. Пустой retrieval — норма; ошибочная инъекция — главный враг (A2).
- Деньги. Горячий путь — $0 (без LLM). LLM-экстракция/адъюдикация — батч на границе джоб, покрыта reserve/settle/эскалацией/F3 — не сломай.
- Границы Р2/Р3. Матч/сборка — код; режим инъекции/бюджеты/TTL — конфиг. Кириллица нормализуется в Go. FTS5/вектор — НЕ на горячий путь.
- disposition/coverage/escalation Вех 2/2.5 не ломать.
6. Правила проекта
- Метод: доки+код → валидация → код → агентское селфревью на вехе (адверсариальный многоагентный Workflow: дименсии → находки → независимая верификация CONFIRMED/REFUTED с конкретным сценарием инпут→неверный-выход → исправь подтверждённые + регресс-тесты мутационно-проверенные: сломай — тест должен упасть) → сдаёшь оркестратору/владельцу.
- Дименсии селфревью шага 4 (минимум): детерминизм (матч/инъекция/post-check/
memoryVersion— F1 не регрессирует); нормализация/матч (A3/A4 — одиночные ключи запрещены, симметрия ключ↔текст, 0 ложных на ловушкахretrieval_bench); disposition инъекции (A2 три ветки, AMBIGUOUS→post-check); спойлер (C1 жёсткий reject); post-check (E1/E2 — форс только при резолвимом падеже, слепой replace запрещён, русская морфология померена); интеграция (msgs-поверхность Р5,ch.Textчист, кэш-граница цела, snapshot-fold памяти); регрессии (Вехи 0–2.5 + 3a целы; e2e инъекция→перевод→post-check; resume $0; ре-снапшот при смене approved). - Не редактировать
docs/research/*,01/02-decisions,06-memory-risk-registry— правки только через03-implementation-notes.mdиPROGRESS.md(§Бэкенд). Не трогать/не коммититьeval/,docs/experiments|research/*(читать можно). Не коммититьsettings.local.json,*.db. - Коммиты: одно предложение на английском ≤30 слов, без трейлера
Co-Authored-By, без многострочного тела. Наmainпрямо, толькоbackend/+PROGRESS, никогдаeval/. PUML не рендерить в картинки. Автопамять (MEMORY.md) — сверься.
7. Риски интеграции (проверено картой кода)
- (F1)
memoryVersion()— константа-заглушка. Заменишь на хеш материализации — иначе смена approved-глоссария молча промахивается мимо resnapshot-гейта и переоплачивает расходящимся переводом (🔴🔇). Материализуй замороженные строки (pinned), не live. - (A4) нормализация — самый вероятный «тихо пусто». Ключ есть в тексте, но в другой форме (упрощ./трад., полу/полноширина, кандзи↔кана, ruby). Симметричная нормализация ключа И текста — версионированный артефакт.
- (A2/A3) ошибочная инъекция — главный источник тихой деградации. Одиночный CJK/короткий алиас срабатывает на чужом смысле (阿Q, 炎=пламя vs имя 萧炎). Запрет одиночных ключей + longest-match + трёхсторонний disposition.
- (E1) post-check в русской морфологии невалидирован. Склонение-осознанный матч шумит. Померь ДО жёсткого гейта; шумит → флаг, не форс.
- (Р5/кэш) инъекция между system и user не должна сдвигать кэш-границу со стабильного префикса (иначе DeepSeek-автокэш промахивается, Anthropic
cache_controlне там).
8. Сборка/прогон + открытые вопросы
Из backend/: go build/vet/test -race -count=1 — зелёные на каждом шаге. Тесты: нормализация (A4, версионированная), матчер (longest-match/одиночные запрещены/ловушки retrieval_bench 0 ложных), disposition (3 ветки), спойлер-reject, post-check (T1–T10 порт + русская морфология), memoryVersion-материализация (смена approved → --resnapshot), e2e инъекция→мок-перевод→post-check, resume $0. Файлы БД только на ext4 (не /mnt/c).
Открытые (уточни ДО кода, если стоят переделки):
- Граница первой вехи: только A–E (схема+горячий путь+инъекция+F1+post-check) на РУЧНОМ/ruby-сиде глоссарии, а автопопуляцию/адъюдикацию/резюме/series-bible — в следующие вехи? (Рекомендация: да — поверхность+post-check+F1 разблокируют качество и отложенные overlap/ruby-лок; популяция — отдельно.)
- F1-развилка (research/13 §слабые места): snapshot материализует БАЙТЫ инъекции vs store версионирует глоссарий-счётчиком. 5eaf2d9 выбрал материализующий вариант — следуй ему (хеш замороженных строк); подтверди, если сомнение.
- Post-check как гейт vs флаггер до замера русской морфологии (E1) — жёсткий гейт только после валидации precision.
- Раскладка инъекции (код-message vs RenderVars-поле) и кэш-граница Р5 — реши по §3, зафиксируй в PROGRESS.
- Ruby: классификатор имя-чтение vs авторское двойное чтение — детерминированное правило (фонетическая согласованность/паттерн имени) vs флаг человеку; согласуй порог.
Оркестратор ответил на §8 (05.07) — можешь стартовать с этими ответами
- F1-развилка: ПОДТВЕРЖДАЮ материализацию (5eaf2d9). Это ровно D8 (content-hash материализации, не version-counter — drift-proof: забыть бампнуть нельзя). Следуй; хеш ORDER BY-стабильных ЗАМОРОЖЕННЫХ approved-строк.
- Граница первой вехи: ПОДТВЕРЖДАЮ A–E на seeded-глоссарии (ручной + ruby-сид); автопопуляция/адъюдикация/резюме/series-bible — отдельные вехи. Уточнение приёмки: порог ≥98% консистентности (D10/02-mvp) осмыслен только на ПОПУЛИРОВАННОМ глоссарии — приёмка ПЕРВОЙ вехи = «механизм корректен на seeded-глоссарии» (инъекция/disposition/post-check/F1 работают, 0 ложных на ловушках
retrieval_bench, resnapshot при смене approved), НЕ «≥98% на реальной книге». Это инфраструктурная веха, как Фаза 1. - Post-check: ПОДТВЕРЖДАЮ флаггер-до-замера (жёсткий гейт только после валидации precision на русском). Уточнение: Фаза-1 post-check матчит против записанных
decl-форм (Go, детерминированно;declзаполнен при term-commit одним дешёвым LLM-вызовом), НЕ живой лемматизатор (тот — Python-сайдкар Фазы 2, C3). E1-замер = precision матчаdecl-форм на реальном русском; шумит → флаг-режим, не форс. - Раскладка инъекции: код-собранный injection-message в
Messages()(вариант «а»), НЕ новыйRenderVars-плейсхолдер: держит шаблон стабильным (Renderпадает на неизвестном{{}}), инъекция = динамический per-chunk контент вне фикс-шаблона, кэш-граница остаётся на стабильном system-префиксе (Р5). - Ruby-классификатор: детерминированное правило для ЯСНЫХ случаев (фонетическая согласованность reading↔base / паттерн собственного имени → name-lock), неоднозначное → флаг человеку. Over-flag безопасно, блайнд-лок = mistranslation — зеркалит «точность > полнота» горячего пути. Не умничать: сомнение → флаг.
research/14 (сессия «адаптивный слой памяти») — вход для шага 4 (оркестратор, 05.07)
Сессия исследования гибрида завершена (docs/research/14-adaptive-memory.md) — она не меняет твой скоуп (обучающийся слой отклонён: kNN-MT/frontier-LoRA физически blocked живой пробой; демонстрации — пилот Ф2.5, не сейчас; твоя работа — чисто детерминированная, это подтверждено). Но она эмпирически измерила несущий риск E1, и это меняет, как ты к нему подходишь:
- E1 КОЛИЧЕСТВЕННО (главное). Наивный регэксп-post-check даёт 18–36% ЛОЖНЫХ флагов на трудном чанке (deepseek 2/11, grok 4/11 — склонённые формы, отрендеренные ВЕРНО, которые
\b-регэксп теряет: «Вэйчжуане», «Бородатого Вана», «Дэна», «сюцая»). → Портmemory_hotpath.pyT1–T10 «как есть» = порт РЕГЭКСПА = этот шум. Не порти accept-регэкспы дословно; матч обязан быть decl-осознанным (подтверждает выбор §8 «против записанныхdecl-форм»). - Но stored-decl не бесплатно надёжен. Надёжность = функция ПОЛНОТЫ записанных
decl-форм. Для OOV-транслит-имён склонение нетривиально (C4) → один дешёвый LLM-вызов при term-commit может недозаполнить падежи → те же ложные флаги. Замеряй false-flag rate именно stored-decl-матчера на реальном СКЛОНЁННОМ русском выходе, не «примерно» (это и есть E1-замер §0/§8 «до жёсткого гейта»). - Фолбэк де-рискнут (хорошая новость research/13 §2). pymorphy3 (Python-сайдкар Ф2, C3) эмпирически сворачивает OOV-транслит-склонение (Вана→ван, Дэна→дэн, Вэйчжуане→вэйчжуан, Цзоу→цзоу). В Go сейчас НЕ тащи (нет годного pure-Go рус-лемматизатора — это и есть причина stored-decl в Ф1), но знай: если stored-decl шумит на замере — лемматизатор-сайдкар Ф2 валидированный фолбэк.
- Готовые ассеты E1-замера (зона Полигона — читать/портировать МЕТРИКУ в Go, не редактировать/коммитить):
eval/adaptive_reask.py(strict-регэксп vs лемма на реальном zh→ru, канон Рогова — диагностика ложных флагов),eval/adaptive_levers.py(кривая L1-лемма-post-check / L2-recall-норм+alias+sticky / L3-re-ask на трудном чанке 11 сущностей). Это ровно тот «замер русской морфологии», что требует §8. - A3 подтверждён кейсом.
钱(односимвольный ключ, гомограф Цянь-фамилия / 钱=деньги) упорно матчится/инъектится не туда даже после re-ask → конкретный кейс в тест-корпусmin_key_len/запрета одиночных ключей. - A4 подтверждён живым багом. Неполная trad-карта (у меня не было
爺→爷) молча промахнула 赵太爷 → это ровно «тихо пусто» A4. Полный OpenCC, не мини-карта; трап-корпус матчера (по образцуretrieval_bench) должен включать trad-варианты И склонённые ru-формы, иначе тест зелёный, а бой промахивается. - Подтверждение подхода (не расслабляйся). Soft-глоссарий на реальном zh→ru индикативно тянет 0.458→~1.0 канон-консистентности (валидация банка этого на нашем направлении не мерила) — детерминированная инъекция на zh→ru эмпирически не пуста. Чанк был «лёгкий» (насытился); реальную кривую L1→L3 меряй на трудных чанках.
Welcome back to the backend. Валидируй старт, заморозь схему по реестру, построй горячий путь и инъекционную поверхность ОДИН раз, закрой F1 и post-check — и держи тихую деградацию громкой.