Runbook: cold-start product completeness (empty prod DB → full DoD)
Как на ПУСТОЙ prod-базе довести каждый товар до полноты: категория + характеристики + цена + остаток. Основано на live-диагностике 2026-05-29/30. DoD-запросы внизу — проверяй РЕАЛЬНЫЕ карточки, не «воркеры запущены».
Архитектурный принцип (важно понять)
Большая часть LLM-работы — построение СЛОВАРЕЙ, ограниченных числом УНИКАЛЬНЫХ кодов, НЕ числом товаров. Это разовый bootstrap + малый инкремент:
| Слой | LLM маппит | Объём | Реюз |
|---|---|---|---|
| charnorm | supplier char-код → каноническая характеристика | ~уникальные коды (тысячи) | 1М+ raw-chars проецируются детерминированно |
| valuenorm | enum-значение → каноническое | уникальные значения | реюз |
| taxonomy | supplier_native категория → canonical дерево | ~уникальные native | реюз |
| classifier | товар → категория | per-товар | должен идти fast-path через mapping, LLM только fallback |
После словарей обогащение детерминированное (без LLM). LLM на каждый товар — только classifier fallback когда native→canonical mapping пуст.
LLM throughput (критично для bootstrap скорости)
- Модель PER-TASK (важно, live-замерено):
- charnorm / classifier →
claude-sonnet-4-6: ~2x быстрее gpt-5.4 на batch (40s vs 84s/chunk), НЕ ловит context-deadline, И classifier assign-rate 21% vs 2% у gpt-5.4 (10x точнее категоризирует). gpt-5.x reasoning на больших output (4300 токенов) = 84s + таймауты + ДРОП работы. - taxonomy-builder →
gpt-5.4(TAXONOMY_BUILDER_LLM_MODEL, ОБЯЗАТЕЛЬНО): gpt-5.4 отдаёт строгий JSON. Если основной JSON-strict model уходит в cooling-down/rate-limit, переключай на live-проверенную fallback-модель черезTAXONOMY_BUILDER_LLM_MODEL; builder умеет извлекать JSON из fenced/prose ответа, но downstream LLM всё равно держи на паузе, чтобы не съесть quota. - Коннект к gateway быстрый (минимальный вызов 2.7s — НЕ bottleneck).
- charnorm / classifier →
- Параллелизм:
CHARNORM_CONCURRENCY/CATEGORY_CLASSIFIER_CONCURRENCYзадаются явно под текущий provider pool. Если gateway отдаёт429 Too Many Requests/all credentials ... cooling down, сначала снижайLLM_MAX_CONCURRENCYи per-worker concurrency, а не увеличивай batch size. - valuenorm throughput:
VALUENORM_BATCH_SIZEзадаёт сколько unmapped rows берём за tick,VALUENORM_CONCURRENCYпараллелит LLM-группы внутри tick. Для cold-start держиVALUENORM_BATCH_SIZE=1000,VALUENORM_MAX_GROUP_SIZE=50, но подбирайVALUENORM_CONCURRENCYпо фактическим 429/cooldown логам. - category reclassify throughput: после tree-rebuild очередь
canonical_reclassify_queueдолжна разгребаться тысячами строк за tick:CATEGORY_RECLASSIFY_BATCH_SIZE=5000+. Исторический batch50превращает крупный rebuild в многодневный backlog и визуально выглядит как «категории не работают». - matcher Tier 3 output budget:
WorkloadMatcherиспользует увеличенный JSON output budget (4096 tokens). Если видишьtier3 chunk dropped+unexpected end of JSON input, это обычно не provider outage, а усечённый JSON: не уменьшай весьMATCHER_BATCH_SIZEвслепую, сначала проверь output budget и chunk size. - Template parity:
deploy/prod-env-template.envдолжен оставаться на той же стратегии:CATEGORY_CLASSIFIER_LLM_MODELS=claude-sonnet-4-6, а gpt добавляется только временным fallback при provider cooldown. - LLM JSON tolerance:
taxonomy-builder,category-classifierиvaluenormдолжны принимать не только чистый JSON, но и fenced/prose-wrapped JSON. Live Sonnet иногда отвечает коротким текстом перед блоком JSON даже при JSONMode; при логахinvalid character 'I' looking for beginning of valueне меняй модель вслепую, сначала проверь extractor/парсер. Если ответ содержит ранний не-JSON code fence перед итоговым объектом, extractor должен брать первый полноценный JSON object, а не первый fence.category-classifierдополнительно делает parse-retry и пишет только короткийresponse_preview, чтобы понять, вернула ли модель JSON, без вывода полного payload. Если превью говоритI don't see a specific question or task, значит gateway/model не использовал system prompt как инструкцию: продублируй задачу и JSON-схему в начале user prompt. - Taxonomy rebuild parallelism:
TAXONOMY_BUILDER_LLM_CONCURRENCYограничивает одновременные LLM-вызовы по независимым builder chunks. Default1сохраняет старый безопасный режим; для cold-start/rebuild поднимать до2-4, еслиLLM_MAX_CONCURRENCYи provider rate limit оставляют запас. - Round-robin моделей:
CHARNORM_LLM_MODELS/CATEGORY_CLASSIFIER_LLM_MODELScsv — распределяет нагрузку на gateway между провайдерами. - Timeout:
LLM_TIMEOUT_SECONDS=240,CHUNK_SIZE=40(>40 рискует deadline).
Cold-start последовательность
Используй deploy/prod-env-template.env cold-start значения (в скобках комментов:
tick 1-2m, RUN_ON_START=true, batch крупнее, concurrency 8-16).
- Миграции — первый boot с
POSTGRES_MIGRATE_ON_START=true(потом → false). - Credentials — supplier creds через Vault →
cmd/seed-credentials→ active rows вsupplier_credentials(system scope). Без них supplier-sync не тянет. - supplier-sync (catalog + commerce + details тики):
- catalog →
supplier_offers+ цена. - details (ETM
SUPPLIER_ETM_DETAILS_SKUS/enumerator; IEK detailsentity=all) →offer_characteristic_raw(богатые сырые характеристики). - commerce →
offer_observations.stock_current(ETM warehouse qty, russvet residue, systeme; IEK остаток зависит от наличия stock-endpoint — см. iek connector). Остаток у товара = столько, сколько отдаёт поставщик. - category source различается: systeme отдаёт native-категорию; russvet —
в specs-фазе (RS_CATALOG/ETIM_CLASS, rate-limited — дать добежать;
raw_attributes.categoryЗКЗ/НОВ = статус наличия, НЕ таксономия); iek — нет в фиде (только LLM-классификатор).
- catalog →
- charnorm-worker (parallel, sonnet, multi-model): дренирует
characteristic_value_unmapped/char-коды →char_name_mappings. Bounded уникальными кодами — завершается (live: ~3259 кодов → 0). После — facts проецируются. Pause-флагWORKER_PAUSED_CHARNORM_WORKERдолжен быть проброшен в compose так же, как у matcher/valuenorm/category: иначе приllm_global_pause=trueworker будет каждую минуту стартовать, получатьplatform.llm.pausedи писать error-такты без полезного прогресса. - taxonomy bootstrap (КРИТИЧНО для категорий):
cmd/category-tree-builder --mode=fullстроит canonical дерево из текущих supplier_native →cmd/category-tree-publish→category_canonical_mapping(native→canonical). Без этого classifier падает в медленный per-товар LLM (~2% assign rate) вместо fast-path.- На время tree build/publish держи downstream LLM-потребителей на паузе,
но не останавливай non-LLM assignment fill:
WORKER_PAUSED_VALUENORM_WORKER=true,WORKER_PAUSED_MATCHER_WORKER=true,WORKER_PAUSED_CANONICAL_ASSIGNMENT_WORKER=,WORKER_PAUSED_CANONICAL_ASSIGNMENT_WORKER_RESOLVER=true,WORKER_PAUSED_CATEGORY_CLASSIFIER=true,WORKER_PAUSED_CHARNORM_WORKER=true,CANONICAL_ASSIGNMENT_BATCH_SIZE=1000,CATEGORY_CLASSIFIER_MAX_BATCHES=1. Иначе deploy может перезапустить LLM-потребителей, они начнут тратить лимит на pre-tree fallback и ловить 429; если поставить на паузу сам canonical-assignment-worker, карточки не будут получать уже готовые facts. - После publish дерева возвращай category warm-up в post-tree режим:
WORKER_PAUSED_CATEGORY_CLASSIFIER=false,CATEGORY_CLASSIFIER_BATCH_SIZE=50,CATEGORY_CLASSIFIER_MAX_BATCHES=4,CATEGORY_CLASSIFIER_CONCURRENCY=1. На prod 2026-06-15 этот режим держал примерно 130 canonical/min без OOM;MAX_BATCHES=1оставляет только 50 canonical/tick и растягивает прогрев. - Builder перед валидацией чинит LLM-slug в ASCII kebab-case и синхронно
переписывает
parent_slug/canonical_slug. Это защищает full build от confusable-символов вроде кириллическойовнутри визуально латинского slug и восстанавливает отсутствующийslugизname, чтобы один неполный LLM-node не валил весь multi-chunk build перед persist. - Если full build упал до persist на validator error, перезапускай builder тем же
входом после hotfix:
taxonomy_buildsиcategory_canonical_mappingостаются пустыми. - taxonomy-orchestrator (P3) мониторит drift + уведомляет о готовности к rebuild (manual-default).
- Ошибки builder CLI/LLM adapter должны оставаться typed
platform/errorsс safe details (env,attempt,reason), иначе CIbackend-checksблокирует релиз: это нужно, чтобы prod-runbook видел причину падения без вывода секретов или сырого payload.
- category-classifier (parallel, sonnet): fast-path через mapping (без LLM,
массово) + LLM fallback для непокрытых.
CONFIDENCE_FLOOR=0.25. После tree-rebuild проверьcanonical_reclassify_queue: если pending сотни тысяч, поднимиCATEGORY_RECLASSIFY_BATCH_SIZEдо тысяч перед запуском FIFO LLM. - valuenorm-worker: нормализует enum-значения характеристик. На холодном старте не оставляй последовательный режим без причины, но при provider cooldown держи concurrency низким и меряй tick summaries вместо слепого увеличения параллелизма.
- canonical-assignment-worker: обогащает
canonical_assignments(характеристики на canonical-уровень из mapped facts) — детерминированно, без LLM. В production composition worker обязан подключатьcatalog-projector.queueemitter: каждый успешный upsert характеристик должен добавлятьassignment.upsertedвcatalog_projector_queue. Иначе Postgres уже наполнен, но ES-backed admin/catalog остаётся пустым по характеристикам. После историческогоfacts-projectorseed пустые canonical без единогоcanonical_assignmentsдолжны выбираться отдельным unassigned-priority probe перед recent-first dirty rows. Иначе новые supplier updates могут постоянно отодвигать старые карточки с готовыми facts, и UI будет выглядеть пустым несмотря на заполненныйoffer_characteristic_facts. - catalog-projector / reconciler: проекция в ES для search/analogs.
При большом backlog
catalog_projector_queueсначала подними batch/poll (CATALOG_PROJECTOR_BATCH_SIZE=2000,CATALOG_PROJECTOR_POLL_INTERVAL_MS=500), затем временно масштабируйcatalog-projectorчерезdocker compose up -d --scale. Очередь используетFOR UPDATE SKIP LOCKED+CATALOG_PROJECTOR_CLAIM_TTL, поэтому несколько реплик берут непересекающиеся пачки; после drain верни одну реплику. Ручная кнопкаPOST /api/v1/admin/catalog/projector/reindexдолжна быть смонтирована через reconciler control-модуль вapi-server: status-only wiring показывает метрики, но не может поставить hot reindex run.
Порядок зависимостей: offers+raw (3) → char-mappings (4) → facts → canonical
chars (8); параллельно native→canonical (5) → categories (6). Не запускай
pre-tree category-classifier/valuenorm/matcher; после publish сними
WORKER_PAUSED_* и дренируй downstream очереди.
DoD-проверка (РЕАЛЬНЫЕ карточки, не статус воркеров)
-- Полнота по всем активным товарам
SELECT count(*) AS total,
count(*) FILTER (WHERE EXISTS(SELECT 1 FROM canonical_category_assignments c WHERE c.canonical_id=cp.id)) AS with_category,
count(*) FILTER (WHERE EXISTS(SELECT 1 FROM canonical_assignments a WHERE a.canonical_id=cp.id)) AS with_chars,
count(*) FILTER (WHERE EXISTS(SELECT 1 FROM supplier_offers so JOIN offer_observations o ON o.offer_id=so.id WHERE so.canonical_id=cp.id AND o.prices<>'{}'::jsonb)) AS with_price,
count(*) FILTER (WHERE EXISTS(SELECT 1 FROM supplier_offers so JOIN offer_observations o ON o.offer_id=so.id WHERE so.canonical_id=cp.id AND o.stock_current<>'{}'::jsonb)) AS with_stock
FROM canonical_products cp WHERE status='active';
-- char-mapping словарь добит? (0 = готово)
SELECT count(DISTINCT supplier_code) FROM offer_characteristic_raw WHERE mapping_id IS NULL;
-- native→canonical mapping coverage
SELECT (SELECT count(*) FROM category_canonical_mapping) AS mapped,
(SELECT count(*) FROM categories WHERE tree_type='supplier_native' AND state='active') AS native_total;
-- drift / health
SELECT * FROM v_taxonomy_health;Критерий готовности: with_chars/with_category → высокий % (остаток/цена ограничены тем, что отдают поставщики). Конкретный товар в admin: «Характеристики» не пусто, «Категория» не «—».
Известные ограничения по поставщикам
- Остаток: ETM (warehouse qty) ✓, russvet residue (частично) ✓, systeme ✓, IEK — зависит от stock-endpoint (см. iek connector capabilities).
- Категория в фиде: systeme ✓, russvet (в specs, rate-limited), IEK ✗ (LLM).
- Характеристики: богатые у IEK details (34/товар) — но требуют charnorm mapping; ETM details; russvet specs.
Ссылки
deploy/prod-env-template.env— cold-start значения per-worker.docs/superpowers/plans/2026-05-29-prod-2m-readiness-plan.md— 2M scale.- Memory:
tracium-dod-completeness-diagnosis-2026-05-29(per-supplier per-link диагноз).