ADR

ADR: Search Intelligence Layer MVP

ADR: Search Intelligence Layer MVP

Status: Proposed
Date: 2026-06-26
Context: В Яндекс.Директе накоплено ~6 500 уникальных ключевых слов из 16 архивных кампаний. В Яндекс.Метрике — ежедневно ~1 000 поисковых фраз от реальных посетителей. Необходимо создать слой, который переводит разрозненные поисковые сигналы в связку «кластер → JTBD → оффер → поверхность размещения» и даёт управленческие рекомендации.


Decision

1. Search Intelligence Layer — не отдельный модуль, а cross-domain слой

SIL не живёт сам по себе. Он — связующий контур между:

  • коннекторами (raw data)
  • словарём фраз (normalization)
  • семантическим слоем (clustering)
  • бизнес-сущностями (JTBD, Offer, Product)
  • поверхностями размещения (Campaign, Landing, TrafficSource)
  • action layer (Opportunity Cards)

Архитектурная цепочка:

raw phrase → canonical phrase → search_cluster → JTBD / buyer stage → offer → placement surface

2. Cluster-first, не phrase-first

Основная единица принятия решений — search_cluster, не отдельная фраза.

Правила:

  • Прямая связь phrase → entity — только override/high-confidence
  • Основные связи — на уровне кластеров:
    • cluster → JTBD
    • cluster → buyer stage
    • cluster → offer fit
    • cluster → placement recommendation

3. FastAPI как backend source of truth

  • Бизнес-логика — только в FastAPI
  • Next.js API routes — только BFF (прокси/агрегация для UI)
  • Sync-воркфлоу — batch через cron внутри FastAPI (или n8n)

4. Две отдельные сущности: buyer stage и CRM pipeline stage

СущностьНазначение
buyer_stageСтадия принятия решения пользователем
crm_pipeline_stageСтадия движения лида в воронке

Маппинг на buyer_stage: search_cluster → intent_classification
Связь с CRM: через аналитику конверсий

5. Placement surface — нормализованная сущность

  • placement_surface — таблица-справочник
  • Связь с LandingVariant/Campaign/TrafficSource — через существующие link-таблицы
  • Не хранить как TEXT[]

6. Opportunity Score → Opportunity Cards

  • search_opportunity_score — аналитический расчётный слой (веса, нормализация)
  • opportunity_cards — управленческий action-слой
  • opportunity_cards расширяем: source='search_intelligence', search_score_id FK

7. Строгая таксономия каналов

  • direct_trafficyandex_direct_ads
  • Единый словарь traffic_channel на уровне домена

8. Wordstat — deferred dependency

  • Доступа к Search API v2 / Wordstat нет
  • Модель учитывает Wordstat (search_fact_wordstat)
  • MVP не блокируется
  • На старте: Директ history + Forecast + Метрика

9. AI-автоматизация — позже

Условия запуска:

  • data quality стабильна
  • taxonomy зафиксирована
  • buyer stages проходят валидацию
  • opportunity score работает

MVP: batch-кластеризация + ручная валидация. AI = Phase 6.

10. Технологический стек на MVP

КомпонентРешение
DBPostgreSQL 16 (существующий)
Vector searchpgvector (уже включён)
Text searchgin_trgm_ops
BackendFastAPI (существующий)
SyncBatch cron (1×/сутки)
Raw storagePostgreSQL → object storage после 30 дней
EmbeddingVECTOR(384) (как в research_clusters)

Alternatives Considered

A1. Отдельный Vector DB (Qdrant/Milvus)

Отклонено. 1 млн записей за год — pgvector справится. Отдельный сервис = лишняя сложность на старте.

A2. Real-time sync

Отклонено. Ежедневного batch достаточно для аналитики и рекомендаций. Real-time добавит цену без пропорциональной пользы.

A3. AI-first: сразу ML-кластеризация

Отклонено. Без валидированной taxonomy и качественных данных ML будет генерировать шум. Сначала — ручная разметка топ-кластеров.

A4. Phrase-first: каждая фраза ко всем сущностям

Отклонено. Приведёт к N×M связям и мусору. Cluster-first — чище и полезнее.

A5. Использовать research_clusters как основу

Отклонено. ResearchCluster жёстко привязан к research_queries. Создаём отдельные search_clusters.


Consequences

Positive

  • Модуль не дублирует существующие сущности
  • Cluster-first даёт меньше мусора и больше полезных решений
  • FastAPI-first сохраняет единый backend
  • Retention policy не даёт БД раздуваться
  • Quick Wins видны уже через 2-4 дня

Negative

  • Нужна миграция для расширения opportunity_cards
  • Search clusters — новые таблицы (миграции 0022+)
  • Sync jobs нужно писать с нуля (или через n8n)
  • Wordstat отложен — не будет сезонности и related queries на MVP

Risks

  • Дублирование: search_clusters ≠ research_clusters, задокументировать связь
  • Перегрузка: 7 000+ фраз × 16 кампаний = ~100 000 фактов. Индексы решают
  • Multi-tenant: workspace_id пронести во все новые сущности

Deferred Items

ФичаПричина откладыванияПлан
Wordstat APIНет доступа к Yandex Cloud Search APIКогда появится сервис-аккаунт
ML-автокластеризацияНет quality данных и валидированной taxonomyPhase 6
AI-рекомендации офферовЗависит от ML-кластеризацииPhase 6
Real-time syncНе нужно для MVPОтложено
Отдельный vector DBpgvector достаточноЕсли > 10 млн записей

References

  • docs/search-intelligence/01-repo-gap-analysis.md
  • docs/search-intelligence/02-architecture-dna-corrections.md
  • docs/search-intelligence/03-domain-taxonomy.md
  • docs/search-intelligence/04-data-model-v2.md
  • docs/search-intelligence/05-roadmap-v2.md
  • docs/search-intelligence/06-implementation-backlog.md
  • docs/search-intelligence/07-open-questions-for-architect.md
  • docs/adr/ADR-direct-ecospros-campaign-integration.md
  • apps/api/app/domain/direct.py — direct_keywords
  • apps/api/app/domain/research.py — research_clusters
  • apps/api/app/domain/opportunity.py — opportunity_cards
  • apps/api/app/domain/activation.py — campaigns, landing_variants, traffic_sources