TextMachine project repository (AI translation of literary books). Includes: v2 architecture decisions, MVP plan, research 01-12, polygon experiments 01-03, backend-session revalidation verdict (03-implementation-notes.md). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
125 lines
30 KiB
Markdown
125 lines
30 KiB
Markdown
# Архитектурные решения (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` прямо переносится ~2–2,5 тыс. строк проверенной инфраструктуры: OpenAI-совместимый транспорт с retry/backoff, фейловер «локальная GPU → облако» с circuit breaker, прайсинг + reserve/settle-ledger, телеметрия (request_log, трейсы W3C), fail-fast конфиг. Это 3–6 недель сэкономленной разработки.
|
||
- 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-токенов (недоучёт до 30–44% расходов).
|
||
|
||
## Р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 рук: ~25–35 глав zh/ja/en→ru; решающий критерий — человеческие попарные сравнения (протокол LAIT; для двух систем это pairwise, не BWS — BWS только с ≥3 объектами, третьим якорем берём человеческий перевод) + панель LLM-судей чужих семейств. Правило: если C2/C3 лучше C1 менее чем на 5 п.п. win rate — берём C1 (в 2–3 раза дешевле). Бюджет: <$50 API + внешние редакторы 20–40 ч; **elapsed 2–3 недели, отдельная фаза** (см. план 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, без эмбеддингов; ~100–800 токенов), отбор без 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, Grok 4.1 Fast |
|
||
| Редактор (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) | Grok 4.1 Fast / Grok 4.x | open-weights через OpenRouter (Chutes/Venice), DeepSeek офиц. API (3-я линия), локальная abliterated Qwen3.5 |
|
||
|
||
Контент-роутинг ([11-gap-2](../research/11-gap-2.md)):
|
||
- Локальный классификатор «горячести» 0–3. Уровни 0–1 → канал A. Уровень 2 → канал B (пермиссивный). Уровень 3 (несовершеннолетние в сексуальном контексте) — **жёсткий отказ на уровне чанка**: чанк не переводится, помечается в отчёте, глава и книга продолжают обрабатываться (ложное срабатывание классификатора не должно убивать многочасовую оплаченную джобу).
|
||
- **Эскалация channel-aware**: для чанков канала B дорогая эскалация — Grok 4.3 / Gemini (safety off) / крупные open-weights; **Anthropic исключён структурно** из канала B, включая эскалации. OpenAI в NSFW-канал не включать (adult mode заморожен бессрочно, к API не применялся). Anthropic — только стерильные уровни 0–1 с refusal-мониторингом и автопереносом чанка в канал B.
|
||
- Гигиена аккаунтов: выделенный xAI-аккаунт под NSFW, отдельный биллинг на канал.
|
||
- Обязателен **детектор молчаливых вырезаний** (сверка покрытия предложений вход↔выход) — Qwen-подобные внешние фильтры молча портят перевод.
|
||
- Внутренний refusal/excision-бенчмарк (50–100 фрагментов), прогонять при каждой смене версии модели.
|
||
- Для политически чувствительных zh-текстов — не полагаться на китайские API (цензура в весах), фолбэк на западные/локальные.
|
||
|
||
Локальная GPU (8GB) — **«спецслужба», не переводчик** ([06](../research/06-local-models.md)): роли — NSFW-скрининг и перевод флагованных фрагментов (abliterated), эмбеддинги, экстракция кандидатов в глоссарий, дешёвый судья-гейт. Рантайм — llama-server (llama.cpp) как ещё один OpenAI-совместимый «провайдер» за фейловером из vojo; для качества — Qwen3.6-35B-A3B через MoE-офлоад (~25–30 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.25–0.6; премиум-микс ~$4 (batch ~$2) |
|
||
| RU-агрегаторы (ProxyAPI/VseGPT/AITunnel), наш ключ | дешёвые модели ×1.2–1.3; флагманы ×2–6 (**Sonnet-редактор в ru-контуре экономически недоступен** — премиум-микс через агрегатора ≈ съедает всю маржу per-book $99) | неизвестно — **эмпирически проверить в Фазе 0–1** (скорее всего cache/batch не пробрасываются) | стандарт ~$0.3–0.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-объёма книги по премиум-тарифам.
|
||
- Буфер +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 в середине главы → рестарт → доплата только за прерванный вызов.
|
||
|
||
## Р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-паттернов; сам гейт проверяется мини-набором из 20–30 фрагментов с искусственно выпиленными предложениями), правило «в 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,15–0,7 млн ARR**; вместе с per-book авторами (строка «не проверено») — $0,4–1,5 млн. Fan-B2C — **beachhead** (пользователи, данные, соцдоказательство), не основа выручки.
|
||
- Тарифная сетка (в каждой строке помечено, чьи токены): metered 2–3 ₽/глава или 290–490 ₽/мес — **только эконом-конфигурация пайплайна, наши токены через дешёвый контур** (расхождение COGS/глава между doc 09 и gap-5 разрешить пересчётом на прототипе до фиксации цены); Pro 990–1490 ₽/мес — **BYOK** (иначе упираемся в закупку флагманов в ru-контуре); Studio $29–49 usage-based — BYOK/прямые ключи; per-book $49–99 — наши токены, контур прямых ключей.
|
||
- **Якоря с двух сторон**: сверху GlobeScribe $100/книга; **снизу booktranslator.ai $6,99–9,99 за 100k слов (≈$7–20 за роман, уже с ru-лендингом)**. Дельту оправдываем издательским качеством, отчётом, журналом вклада; в сетке предусмотреть дешёвый per-book «стандарт» ($15–25) как ответ на нижний якорь.
|
||
- **Центр тяжести выручки 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 на 53–67%). Митигция: пилот Р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-поведение агрегаторов неизвестно** — эмпирическая проверка в Фазе 0–1; до неё экономику ru-контура считать без кэша.
|