textmachine/docs/architecture/01-decisions.md

128 lines
32 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.

# Архитектурные решения (v2, 2026-07-04)
Синтез по итогам исследований `docs/research/01…11-*`. v2 — после адверсариального ревью тремя критиками (техархитектура, экономика, red-team); все найденные противоречия разрешены явно. Формат — ADR-подобный: решение → обоснование → что отвергнуто. Статусы: **принято** (можно кодить), **гипотеза** (нужен пилот/валидация).
---
## Р1. Язык бэкенда — Go. **Принято**
Из [10-vojo-ai-bot-review.md](../research/10-vojo-ai-bot-review.md):
- Из `vojo/apps/ai-bot` прямо переносится ~22,5 тыс. строк проверенной инфраструктуры: OpenAI-совместимый транспорт с retry/backoff, фейловер «локальная GPU → облако» с circuit breaker, прайсинг + reserve/settle-ledger, телеметрия (request_log, трейсы W3C), fail-fast конфиг. Это 36 недель сэкономленной разработки.
- Goroutines/context — идеальная модель для тысяч параллельных LLM-вызовов; один статический бинарь без рантайма — идеально для локального режима у пользователя.
- **Отвергнуто:** C++/userver — ноль переиспользования, производительность здесь не монетизируется (мы ждём сеть, а не CPU), а код пишет Claude, не владелец. Python — только оффлайн-евалы и Python-сайдкар качества (CometKiwi, морфология русского — в Go-экосистеме зрелого лемматизатора нет). TypeScript — для будущего IDE-фронтенда.
План выделения пакетов: `pkg/llm` (llm.go + httpllm.go + адаптеры + failover), `pkg/ledger` (pricing + reserve/settle), `pkg/obs` (trace + logging), `pkg/store` (миграции). Первый новый код — нативный Anthropic-адаптер с `cache_control`. Известная ловушка из vojo: per-adapter семантика reasoning-токенов (недоучёт до 3044% расходов).
## Р2. Ядро пайплайна — конфигурируемый граф стадий; выбор ядра решает пилот. **Принято (каркас) / гипотеза (ядро)**
Наука разошлась ([03](../research/03-academic-agentic-mt.md), [11-gap-3](../research/11-gap-3.md)): arXiv:2603.20324 («When Agents Disagree», март 2026 — та самая статья про судью-селектора, но перевода среди её задач нет) говорит «selection бьёт synthesis», а Fusion-of-N (arXiv:2510.00931) и Amazon (arXiv:2605.13368) — что refinement/fusion даёт стабильный прирост, и финальный стиль задаёт модель-**редактор**. На zh→ru / ja→ru никто ничего не мерил.
Решение:
- **Граница «конфиг vs код» фиксирована** (урок ревью: без этого DSL расползается в кривой язык): циклы по главам/чанкам, ветвление по гейтам, эскалация с лимитом бюджета, fan-out кандидатов, ретраи — **фиксированная логика раннера**; конфиг задаёт только состав и порядок стадий, роли→модели, версии промптов и пороги гейтов. Четыре ядра (C0 baseline, C1 draft→editor, C2 generate-then-select, C3 select-then-refine) — четыре YAML-конфига одного раннера. Конкретный YAML-пример C1 пишется до старта Фазы 1 (приложение к плану MVP).
- Ролевая модель — по издательскому процессу ([07](../research/07-translation-methodology.md)): **Analyst** (вся книга → book brief), **Terminologist** (глоссарий + style sheet до перевода — паттерн preparation из TransAgents), **Translator** (черновик/кандидаты; режим «аннотированный подстрочник» — советская модель «подстрочник+поэт»), **Stylist/Editor** (сильнейшая по русскому стилю модель), **Judge** (шкала Комиссарова + Nida-вопрос; пара судей: монолингвальный на художественность + билингвальный на верность). Роли второй очереди (Фаза 2+): **Line Editor** (чек-лист Норы Галь), **Continuity Editor** (series bible), **анти-translationese пасс** (>40% переводов даже GPT-4 содержат кальки).
- Пункты брифа, по которым решение принято явно: **консультант поп-культуры / языковой аналитик** (бриф п.2; прототип — CRAT arXiv:2410.21067) и **tool-calling / переводческие тулзы** (п.32; в vojo tool-calling нет) — вне скоупа MVP, кандидаты Фазы 3; **webfetch за культурным контекстом** (п.16) — post-MVP, только по явному сигналу «незнакомый термин» и с подтверждением через глоссарий (риск многозначности, о котором предупреждал сам бриф).
- У каждого пасса **узкий мандат** (запрет менять чужую зону) и диффы вместо полной перегенерации.
- Приоритет силы моделей: **редактор > черновик** (вывод arXiv:2605.13368 — refinement проецирует текст в распределение редактора).
- Пилот 4 рук: ~2535 глав zh/ja/en→ru; решающий критерий — человеческие попарные сравнения (протокол LAIT; для двух систем это pairwise, не BWS — BWS только с ≥3 объектами, третьим якорем берём человеческий перевод) + панель LLM-судей чужих семейств. Правило: если C2/C3 лучше C1 менее чем на 5 п.п. win rate — берём C1 (в 23 раза дешевле). Бюджет: <$50 API + внешние редакторы 2040 ч; **elapsed 23 недели, отдельная фаза** (см. план MVP v2) — подготовка корпуса стартует параллельно с Фазы 0 (работа владельца + сессия «Полигон»).
## Р3. Банк памяти книги — собственный модуль на SQLite. **Принято**
Из [05-memory-glossary.md](../research/05-memory-glossary.md):
- **MemPalace существует, но форкать нельзя**: независимый разбор показал, что заявленные метрики измеряли дефолтные эмбеддинги ChromaDB, «contradiction detection» в коде отсутствует. Generic agent-memory (Letta/Mem0/Zep/LangMem) заточены под чат-память о пользователе и противоположны нужному детерминированному реестру «термин → утверждённый перевод». Банк памяти — ядро продукта, его пишем сами.
- Хранилище: **SQLite (один файл на книгу) + FTS5**. Драйвер — modernc.org/sqlite (CGO-free). **sqlite-vec в MVP не используется** (разрешение конфликта из ревью: это C-расширение, pure-Go драйвер его не загрузит): на масштабе одной книги (тысячи эмбеддингов) достаточно brute-force KNN по BLOB-таблице прямо в Go; если вектора станут узким местом — ncruces/go-sqlite3 (WASM) или cgo, решение отложено. Эмбеддинги (bge-m3 локально) — **второй эшелон, Фаза 2**, и не в горячем пути инъекции.
- Спецификация модуля = **SillyTavern World Info / NovelAI lorebook** (ключи/алиасы, вторичные ключи, токен-бюджет, sticky-инерция сцены, inclusion groups) + **4-уровневая память DelTA** (Proper Noun Records, двуязычные резюме глава/арка/книга, LTM/STM; +4.58 п.п. консистентности имён, +3.16 COMET).
- Схема записи глоссария: `src, dst, type, aliases, gender, speech, decl (склонения для ru), since_ch/until_ch (спойлер-окно), status (approved/auto), note`. Поле `decl` заполняется автоматически при коммите термина (один дешёвый LLM-вызов на термин) — без него метрика консистентности по склоняемому русскому тексту неизмерима.
- Инъекция в промпт — **селективная и детерминированная** (v1: только ключи/алиасы/леммы + sticky, без эмбеддингов; ~100800 токенов), отбор без LLM в горячем пути. Раскладка промпта под кэш — см. Р5.
- Трёхслойная защита консистентности (паттерн GalTransl): pre-replace нормализация → мягкий глоссарий в промпте → post-check + флаг редактору. Жёсткий constrained decoding не применять (WMT: мягкое следование даёт качество выше).
- Series bible — first-class сущность: персонажи, матрица ты/вы с версионированием по главам, речевые профили; в MVP входит **series-bible-lite** (gender + матрица ты/вы) — без него не проверяется главная жалоба ru-читателей MTL (род, регистр обращения).
- Экспорт/импорт TMX/TBX + tab-глоссарии OmegaT — дешёвая фича с высокой ценностью для проф. аудитории.
## Р4. Модельный стек и контент-роутинг. **Принято (стартовый состав) / цены и составы — в конфиге**
Из [04](../research/04-api-providers.md), [09](../research/09-cost-model.md), [11-gap-1](../research/11-gap-1.md), [11-gap-2](../research/11-gap-2.md) (актуально на 2026-07-04, пересматривать ежеквартально):
| Роль | Основная модель | Альтернативы/эскалация |
|---|---|---|
| Черновик | DeepSeek V4 Flash ($0.14/$0.28; cache-hit input $0.0028; лучший zh-токенизатор) | Qwen-Flash, GPT-5 nano |
| Редактор (ru-стиль) | GLM-5 / Kimi K2.6 (доступны из РФ напрямую) | Claude Sonnet 5 (промо $2/$10 до 31.08.2026) — только в контуре прямых ключей |
| Судья | Gemini 3.1 Pro (batch 50%) | Claude Opus 4.8 (адресно, только канал A и прямые ключи), локальный POLLUX-judge-7B как бесплатный гейт |
| Эмбеддинги/скрининг/экстракция терминов | локально: bge-m3, Qwen3.5-9B | Qwen3-Embedding-0.6B |
| 18+ (канал B) | Интерим до Фазы 2: DeepSeek офиц. API + локальная abliterated Qwen3.5 | open-weights через OpenRouter (Chutes/Venice); Grok 4.3 — дорогая эскалация. ⚠ Grok 4.1 Fast retired 15.05.2026, слаги молча редиректят на grok-4.3 с ценой ×9 (фактчек бэкенд-сессии) — перевыбор дешёвой модели канала B по итогам refusal-бенчмарка полигона |
Контент-роутинг ([11-gap-2](../research/11-gap-2.md)):
- Локальный классификатор «горячести» 03. Уровни 01 → канал A. Уровень 2 → канал B (пермиссивный). Уровень 3 (несовершеннолетние в сексуальном контексте) — **жёсткий отказ на уровне чанка**: чанк не переводится, помечается в отчёте, глава и книга продолжают обрабатываться (ложное срабатывание классификатора не должно убивать многочасовую оплаченную джобу).
- **Эскалация channel-aware**: для чанков канала B дорогая эскалация — Grok 4.3 / Gemini (safety off) / крупные open-weights; **Anthropic исключён структурно** из канала B, включая эскалации. OpenAI в NSFW-канал не включать (adult mode заморожен бессрочно, к API не применялся). Anthropic — только стерильные уровни 01 с refusal-мониторингом и автопереносом чанка в канал B.
- Гигиена аккаунтов: выделенный xAI-аккаунт под NSFW, отдельный биллинг на канал.
- Обязателен **детектор молчаливых вырезаний** (сверка покрытия предложений вход↔выход) — Qwen-подобные внешние фильтры молча портят перевод.
- Внутренний refusal/excision-бенчмарк (50100 фрагментов), прогонять при каждой смене версии модели.
- Для политически чувствительных zh-текстов — не полагаться на китайские API (цензура в весах), фолбэк на западные/локальные.
Локальная GPU (8GB) — **«спецслужба», не переводчик** ([06](../research/06-local-models.md)): роли — NSFW-скрининг и перевод флагованных фрагментов (abliterated), эмбеддинги, экстракция кандидатов в глоссарий, дешёвый судья-гейт. Рантайм — llama-server (llama.cpp) как ещё один OpenAI-совместимый «провайдер» за фейловером из vojo; для качества — Qwen3.6-35B-A3B через MoE-офлоад (~2530 tok/s).
## Р5. Экономика вызовов: два контура, caching-first, премиум-бюджет. **Принято (структура) / цифры — пересчитать на прототипе**
Из [09](../research/09-cost-model.md), [11-gap-1](../research/11-gap-1.md) + критика эконом-ревью:
**Экономика считается раздельно по контурам «чей ключ»** (главная правка v2 — цифры doc 09 валидны только для прямых ключей):
| Контур | Модели | Cache/Batch | COGS тома ранобэ |
|---|---|---|---|
| Прямые ключи (наш зарубежный контур, «позже»; DeepSeek/GLM/Kimi — доступны и из РФ) | весь стек Р4 | да / да | стандарт на DeepSeek ~$0.250.6; премиум-микс ~$4 (batch ~$2) |
| RU-агрегаторы (ProxyAPI/VseGPT/AITunnel), наш ключ | дешёвые модели ×1.21.3; флагманы ×26 (**Sonnet-редактор в ru-контуре экономически недоступен** — премиум-микс через агрегатора ≈ съедает всю маржу per-book $99) | неизвестно — **эмпирически проверить в Фазе 01** (скорее всего cache/batch не пробрасываются) | стандарт ~$0.30.8; премиум — только точечные вызовы |
| BYOK (ключ пользователя) | что настроит пользователь | его тарифы | COGS токенов — не наш; мы продаём софт; в UI — оценка стоимости по тарифам его провайдера |
- **Prompt caching — главный рычаг**, и раскладка промпта фиксируется под него (разрешение конфликта Р3↔Р5 из ревью): **стабильный кэшируемый префикс** = системный промпт роли + book brief + style sheet + резюме книги/арок (меняется редко, раз в главу-арку); **волатильный хвост после кэш-брейкпоинта** = селективный глоссарий + STM + чанк. На стадии edit кэшу ловить почти нечего (уникальный черновик в промпте) — ожидаемый cache-hit считать по стадиям, а не «≥50% в среднем». Альтернатива для DeepSeek (cache-hit $0.0028/M): полный глоссарий в стабильном префиксе может оказаться дешевле селективной инъекции по miss-тарифу — решить расчётом на прототипе, обе схемы поддержать конфигом.
- **Батчуемость — свойство стадии**: Analyst, Terminologist, судья/оценки, книжный QA — батчуемы (50%); черновик и редактура чанков — последовательны (STM/резюме создают зависимость, DelTA-паттерн). «С batch ≈$2» относится только к батчуемым стадиям. Batch API — Фаза 2 (к судье), не Фаза 0.
- **Бюджет премиум-токенов на книгу**: одиночный полный Sonnet-проход 500-главной вебновеллы ~$92 — впритык к якорю GlobeScribe $100 и выходит за него с буфером +25%; двойной ~$185 — ломает экономику. Отсюда: дорогая модель — только адресно по сигналу судьи/гейтов, бюджет ≤2× input-объёма книги по премиум-тарифам.
- **Формула премиум-бюджета** (уточнение по запросу бэкенд-сессии, заменяет эвристику «≤2×»): в коде — конфигурируемый $-потолок на книгу, `premium_budget_usd = clamp(price_target × (1 target_margin) est_base_cogs, 0, hard_cap)`; для локального режима без прайса — дефолты $5/том ранобэ, $30/вебновелла-500. «≤2× input-объёма» остаётся прикидочной эвристикой в доках, не контрактом кода.
-**Абсолютные COGS этого раздела устарели после токен-калибровки** (эксперимент 01 полигона): zh→ru дороже в ~1.41.75 раза (премиум-микс вебновеллы ≈$49, с batch ≈$26; ранобэ ja→ru, наоборот, дешевле — ≈$3.1). Структура выводов не меняется; актуальные множители — `docs/experiments/01-token-calibration.md`.
- Буфер +25% к расчётному COGS (ретраи, глоссарий-строительство, регенерации). Цены/имена моделей — только в конфиге с датой проверки. Миграция deepseek-chat → deepseek-v4-flash до 24.07.2026. Для DeepSeek — планировщик внепиковых часов (надбавка ×2 в пик; сам планировщик — Фаза 3, до тех пор — просто не гнать массовые прогоны в пик).
## Р6. Хранилище и оркестрация: SQLite + durable jobs + чанк-чекпоинты. **Принято**
- Локальный режим MVP: SQLite (modernc.org/sqlite, CGO-free, без нативных расширений — см. Р3) для проектных данных; отдельный файл БД на книгу для банка памяти. Интерфейс хранилища тонкий — миграция на Postgres (pgx уже в vojo) при появлении сервера.
- Оркестрация — **durable jobs в БД с resume**; джоба = глава×стадия. **Гранулярность сохранности — чанк, не глава** (правка red-team): каждый сырой ответ LLM персистится сразу после settle (ключ: request-hash), отдельно от смысловой TM; kill -9 посреди главы теряет максимум один незавершённый вызов. Из ключа resume исключаются волатильные части контекста (snapshot контекста фиксируется на джобу до её завершения) — иначе любое подтверждение термина инвалидирует TM-хиты уже переведённых чанков. `brief_hash` входит в ключ TM; изменение translation brief посреди книги — явная команда с показом стоимости пере-перевода.
- Проверяется тестом: kill -9 в середине главы → рестарт → доплата только за прерванный вызов.
- **Ключ TM подтверждён** (запрос бэкенд-сессии): консервативный вариант `(chunk_src_hash, lang_pair, model_id, prompt_version, brief_hash, context_snapshot_hash)`; перечанкование инвалидирует TM by design (ключ от src-чанка) — это осознанная плата за корректность.
## Р7. Телеметрия и качество. **Принято**
- Деньги: дисциплина vojo целиком — биллинг по usage из ответа API (включая reasoning-токены), reserve-before-call/settle-after, потолок $ на книгу/день, request_log per-агент/per-глава/per-стадия, trace_id насквозь.
- Качество онлайн: **CometKiwi** (reference-free, локальная, не умеет отказывать — валидировать на 18+ ru-корпусе до доверия) + периодический GEMBA-MQM-подобный LLM-судья; **сырой BLEU/COMET как KPI художественности запрещён** (LitEval: автометрики отличают человеческий перевод максимум в 20% случаев).
- **Не-LLM гейты по фазам**: Фаза 1 (реализуемо в Go без морфологии) — детектор CJK-артефактов, глоссарная консистентность, coverage-гейт v1 (счёт предложений регэксп-сегментацией `。!?/.!?`, соотношение длин ±порог, blacklist refusal-паттернов; сам гейт проверяется мини-набором из 2030 фрагментов с искусственно выпиленными предложениями), правило «в TM коммитится только вывод, прошедший все гейты». Фаза 2 (Python-сайдкар) — морфодетектор канцелярита по чек-листу Галь (pymorphy/Natasha), MTLD/distinct-n, дисперсия длин, доля пассива, порт BlonDe-подобных дискурсивных проверок на русскую морфологию.
- Метрика глоссарной консистентности — с процедурой: знаменатель = вхождения `src`/алиасов в исходных чанках; числитель = наличие леммы `dst` (по полю `decl`) в соответствующем выводе; прономинализация («Ли Вэй → он») не штрафуется, гейт считает только `approved`-термины; в безлюдном режиме auto→approved автоматом с записью в журнал.
- Оффлайн: паттерн routedecide/routereval из vojo → **golden-set реплей** решений об эскалации и вердиктов судьи со свипом порогов. Золотой набор — приватно из лицензионных изданий; GuoFeng Webnovel V2 — только dev (non-commercial, референсы MTPE-качества).
## Р8. Юридическая и продуктовая позиция. **Принято**
Из [08-legal.md](../research/08-legal.md), [11-gap-1](../research/11-gap-1.md):
- **Продаём инструмент, не переводы и не платформу** (Sony/Grokster/Cox; аналогия Trados/Photoshop). Никакого хостинга переведённого контента, UGC, встроенных парсеров пиратских библиотек и кнопок «опубликовать на агрегатор».
- ToS: пользователь гарантирует права на тексты + логика «перевод ≠ генерация» (transformation exception). Жёсткий отказ — только CSAM-категория.
- Фича **«журнал творческого вклада»** — доказательство human authorship (USCO/РФ) и аргумент «перевод принадлежит вам».
- RU-сегмент: **BYOK-модель** (ключ агрегатора или прямой DeepSeek/GLM/Kimi); мы продаём подписку за софт в рублях (ИП/самозанятость + ЮKassa/СБП). **Не перепродавать** токены OpenAI/Anthropic/Google российским пользователям. Крипту в ru-контуре не рекламировать (259-ФЗ). Минимум ПДн, ru-БД в РФ (152-ФЗ).
- 18+: риск концентрируется в **распространении** (Ficbook, ст. 242 УК, ЛГБТ-законы) — помогаем клиенту комплаенсом (маркировка, age-gate-метаданные), но не размещаем витрину.
## Р9. Прайсинг и сегменты. **Гипотеза до полевой валидации**
Из [01](../research/01-market.md), [11-gap-4](../research/11-gap-4.md), [11-gap-5](../research/11-gap-5.md) + правки эконом-ревью:
- Сегментация SAM честно: **fan-B2C $0,150,7 млн ARR**; вместе с per-book авторами (строка «не проверено») — $0,41,5 млн. Fan-B2C — **beachhead** (пользователи, данные, соцдоказательство), не основа выручки.
- Тарифная сетка (в каждой строке помечено, чьи токены): metered 23 ₽/глава или 290490 ₽/мес — **только эконом-конфигурация пайплайна, наши токены через дешёвый контур** (расхождение COGS/глава между doc 09 и gap-5 разрешить пересчётом на прототипе до фиксации цены); Pro 9901490 ₽/мес — **BYOK** (иначе упираемся в закупку флагманов в ru-контуре); Studio $2949 usage-based — BYOK/прямые ключи; per-book $4999 — наши токены, контур прямых ключей.
- **Якоря с двух сторон**: сверху GlobeScribe $100/книга; **снизу booktranslator.ai $6,999,99 за 100k слов (≈$720 за роман, уже с ru-лендингом)**. Дельту оправдываем издательским качеством, отчётом, журналом вклада; в сетке предусмотреть дешёвый per-book «стандарт» ($1525) как ответ на нижний якорь.
- **Центр тяжести выручки MVP+1 — B2B/платформенные пилоты** (white-label для Rulate-класса площадок; валидированный путь по gap-5), а не per-book: сегмент авторов-самиздата тянет за собой ru→en, которого MVP не делает — per-book остаётся гипотезой со сроком валидации после пилота качества.
- Под сегмент «ИИ-фабрик» (38 владельцев на 80 случайных онгоингов Rulate) в план MVP включён **режим онгоинга** (дозагрузка глав в проект с наследованием памяти) и ранний импорт бэк-каталога — без них Studio-тариф нечем обслуживать.
- Последовательность: сначала пилот качества (Р2) → потом A/B прайсинга против якоря VseGPT (999 ₽/мес + токены).
- Позиционирование — «инструмент переводчика/редактора» (модель Nuanxed), не «замена переводчиков» (PR-бэклэш GlobeScribe). Дифференциация — не «первый в ru» (ниша сырого MTL занята: Rulate AI-раздел 14,4 тыс. работ, MLate, VseGPT), а издательское качество + память серии + 18+.
## Р10. Главные риски (мониторить)
1. **Судья — бутылочное горлышко**: ставка generate-then-select зависит от качества судьи (порог s* из arXiv:2603.20324); LLM-судьи расходятся с живыми читателями (LAIT; человеческая валидация режет LLM-judge win rates на 5367%). Митигция: пилот Р2, пара судей, человеческий pairwise в golden-set.
2. **Смена контент-политик провайдеров** (GLM-4.6→4.7, заморозка OpenAI adult mode): refusal-бенчмарк на каждую смену версии, мультипровайдерность by design.
3. **Западные сервисы добавят ru-таргет** (booktranslator.ai уже имеет ru-лендинг и демпингует): окно — не первенство, а глубина (память серии, редакционная петля).
4. **Платёжная/санкционная фрикция РФ**: BYOK снимает главное; двухконтурность (ru-юрлицо + позже зарубежная сущность для прямых ключей и en-сегмента).
5. **Волатильность цен API**: конфиг с датами, ежеквартальный пересмотр, буфер +25%.
6. **Cache/batch-поведение агрегаторов неизвестно** — эмпирическая проверка в Фазе 01; до неё экономику ru-контура считать без кэша.