Движок конфигуратора
Движок конфигуратора — ядро системы автоматического подбора контент-стратегии. На вход получает данные продавца и аналитику SalesFinder, на выходе — персонализированные рекомендации с бюджетом и приоритизацией SKU.
1. Архитектура движка
Конфигуратор реализован как последовательный pipeline из шести модулей.
flowchart LR
A["Seller Profile"] --> B["Category Detector"]
B --> C["Archetype Classifier"]
C --> D["Strategy Selector"]
D --> E["Budget Allocator"]
E --> F["Output"]
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#fff3e0
style D fill:#e8f5e9
style E fill:#fce4ec
style F fill:#f1f8e9
Принципы:
- Каждый модуль — отдельный класс с единственной ответственностью
- Данные передаются через Pydantic-модели (валидация на каждом шаге)
- Любой модуль можно заменить или переопределить через overrides
- Все вычисления детерминированы — одинаковый вход всегда даёт одинаковый выход
class ConfiguratorPipeline:
"""Оркестратор конфигуратора."""
def __init__(self, sf_client: SalesFinderClient):
self.data_provider = SalesFinderDataProvider(sf_client)
self.category_detector = CategoryDetector()
self.archetype_classifier = ArchetypeClassifier()
self.strategy_selector = StrategySelector()
self.budget_calculator = ContentBudgetCalculator()
self.sku_prioritizer = SKUPrioritizer()
async def analyze(
self, seller_id: str, overrides: ConfigOverrides | None = None
) -> StrategyRecommendation:
profile = await self.data_provider.build_profile(seller_id)
profile = self.category_detector.enrich(profile)
archetype = self.archetype_classifier.classify(profile)
if overrides and overrides.force_archetype:
archetype = overrides.force_archetype
preset = self.strategy_selector.select(archetype, profile)
budget = self.budget_calculator.calculate(profile, preset, overrides)
sku_plan = self.sku_prioritizer.prioritize(
await self.data_provider.get_sku_analytics(seller_id), budget,
)
return StrategyRecommendation(
seller_id=seller_id, profile=profile, archetype=archetype,
preset=preset, budget=budget, sku_plan=sku_plan,
)
2. Seller Profile Model
Профиль продавца — центральная модель, агрегирующая все данные для принятия решений.
from pydantic import BaseModel, Field
from enum import Enum
class Marketplace(str, Enum):
WB = "wildberries"
OZON = "ozon"
BOTH = "both"
class ContentState(str, Enum):
NONE = "none" # Нет контента
BASIC = "basic" # Тол ько фото телефоном
STANDARD = "standard" # Базовая студийная съёмка
PROFESSIONAL = "pro" # Профессиональный контент
PREMIUM = "premium" # Полный пакет (видео, 3D, Rich)
class SellerProfile(BaseModel):
"""Профиль продавца для конфигуратора."""
seller_id: str
marketplace: Marketplace
sku_count: int = Field(ge=1)
monthly_revenue: float = Field(ge=0, description="Месячная выручка, руб.")
avg_margin: float = Field(ge=0, le=1)
# Категории и ниша
categories: list[str] = Field(default_factory=list)
primary_category: str | None = None
is_fashion: bool = False
niche: str | None = None
# Бизнес-модель
has_own_brand: bool = False
is_reseller: bool = False
brand_count: int = 1
# Ценовые параметры
avg_price: float = 0
min_price: float = 0
max_price: float = 0
# Сезонность и возвраты
seasonal_coefficient: float = Field(default=1.0, ge=0.1, le=5.0)
return_rate: float = Field(default=0.05, ge=0, le=1)
# Текущее состояние контента
content_state: ContentState = ContentState.NONE
has_video: bool = False
has_infographics: bool = False
has_rich_content: bool = False
@property
def revenue_per_sku(self) -> float:
return self.monthly_revenue / self.sku_count if self.sku_count else 0
@property
def investment_capacity(self) -> float:
"""Инвестиционная ёмкость = выручка * маржа * 0.15."""
return self.monthly_revenue * self.avg_margin * 0.15
Поля sku_count, monthly_revenue, categories, avg_price заполняются автоматически из SalesFinder API. Поля has_own_brand, is_reseller, content_state — из анкеты продавца при первом подключении.
3. Category Detection
CategoryDetector определяет вертикаль продавца (Fashion / Goods) и уточняет нишу.
class CategoryDetector:
"""Детектор категорий по данным маркетплейса."""
FASHION_CATEGORIES: set[str] = {
"Одежда", "Обувь", "Аксессуары", "Нижнее бельё",
"Спортивная одежда", "Детская одежда", "Верхняя одежда",
"Платья", "Костюмы", "Джинсы", "Сумки",
"Женская одежда", "Мужская одежда", # WB
"Женщинам", "Мужчинам", "Детям", # Ozon
}
GOODS_NICHES: dict[str, list[str]] = {
"electronics": ["Электроника", "Смартфоны", "Ноутбуки", "Наушники"],
"home": ["Дом", "Мебель", "Декор", "Текстиль для дома", "Кухня"],
"beauty": ["Красота", "Косметика", "Уход за кожей", "Парфюмерия"],
"kids": ["Игрушки", "Детские товары", "Коляски"],
"sport": ["Спорт", "Фитнес", "Туризм"],
"food": ["Продукты", "Чай", "Кофе", "БАДы"],
"auto": ["Автотовары", "Автоаксессуары", "Автозапчасти"],
"garden": ["Сад", "Дача", "Инструменты"],
"pets": ["Зоотовары", "Корма"],
}
def enrich(self, profile: SellerProfile) -> SellerProfile:
"""Обогатить профиль: определить вертикаль и нишу."""
fashion_score = sum(
1 for cat in profile.categories if cat in self.FASHION_CATEGORIES
)
total = len(profile.categories) or 1
profile.is_fashion = (fashion_score / total) >= 0.5
if not profile.is_fashion:
profile.niche = self._detect_niche(profile.categories)
return profile
def _detect_niche(self, categories: list[str]) -> str | None:
"""Определить товарную нишу по категориям."""
scores: dict[str, int] = {}
for niche, keywords in self.GOODS_NICHES.items():
scores[niche] = sum(1 for cat in categories if cat in keywords)
if not scores:
return None
best = max(scores, key=scores.get)
return best if scores[best] > 0 else None
Если продавец торгует и одеждой, и товарами (fashion_score ~ 50%), система создаёт два плана: Fashion-часть по вертикали Fashion, товарная — по Goods. Бюджет делится пропорционально выручке.
4. Archetype Classification
ArchetypeClassifier — дерево решений, определяющее один из 8 архетипов продавца.
class SellerArchetype(str, Enum):
STARTER = "starter" # <30 SKU, <500K/мес
BRAND_LAUNCHER = "brand_launcher" # Свой бренд, старт
GROWING_BRAND = "growing_brand" # Свой бренд, рост
RESELLER = "reseller" # Перепродажа
MULTI_BRAND = "multi_brand" # Мульти-бренд портфель
NICHE_EXPERT = "niche_expert" # Узкая ниша, высокая маржа
ENTERPRISE = "enterprise" # 500+ SKU или 10M+/мес
FASHION_BRAND = "fashion_brand" # Fashion с собственным брендом
class ArchetypeClassifier:
"""Классификатор архетипов продавца."""
def classify(self, profile: SellerProfile) -> SellerArchetype:
# Enterprise — крупный бизнес (приоритетная проверка)
if profile.sku_count >= 500 or profile.monthly_revenue >= 10_000_000:
return SellerArchetype.ENTERPRISE
# Fashion Brand — fashion + свой бренд
if profile.is_fashion and profile.has_own_brand:
return SellerArchetype.FASHION_BRAND
# Starter / Brand Launcher — начинающий
if profile.sku_count < 30 and profile.monthly_revenue < 500_000:
if profile.has_own_brand:
return SellerArchetype.BRAND_LAUNCHER
return SellerArchetype.STARTER
# Reseller — перепродажа без бренда
if profile.is_reseller and not profile.has_own_brand:
return SellerArchetype.RESELLER
# Multi-Brand — несколько брендов
if profile.brand_count >= 3:
return SellerArchetype.MULTI_BRAND
# Niche Expert — высокая маржа, узкий ассортимент
if profile.avg_margin >= 0.4 and profile.sku_count < 100:
return SellerArchetype.NICHE_EXPERT
# Growing Brand — свой бренд в стадии роста
if profile.has_own_brand:
return SellerArchetype.GROWING_BRAND
return SellerArchetype.RESELLER # Fallback
Дополнительно explain(profile) возвращает скоринг по всем 8 архетипам (0-10 баллов каждый) для прозрачности классификации. Факторы скоринга: SKU count, revenue, margin, brand ownership, fashion flag, return rate.
5. Strategy Preset System
Каждому архетипу соответствует пресет стратегии — набор процентных аллокаций бюджета по типам контента.
class StrategyPreset(BaseModel):
name: str
archetype: SellerArchetype
description: str
# Аллокация бюджета (%, сумма = 100)
photo_pct: float = 0
video_pct: float = 0
infographic_pct: float = 0
seo_pct: float = 0
ugc_pct: float = 0
rich_pct: float = 0
priority_photo: int = 1
priority_video: int = 2
priority_infographic: int = 3
refresh_cycle_months: int = 6
Примеры пресетов
| Параметр | Brand Launcher | Reseller | Enterprise |
|---|---|---|---|
| Photo | 35% | 25% | 20% |
| Video | 20% | 10% | 25% |
| Infographic | 25% | 40% | 15% |
| SEO | 10% | 15% | 15% |
| UGC | 5% | 5% | 10% |
| Rich | 5% | 5% | 15% |
| Цикл обновления | 4 мес | 6 мес | 3 мес |
PRESETS: dict[SellerArchetype, StrategyPreset] = {
SellerArchetype.BRAND_LAUNCHER: StrategyPreset(
name="Brand Launcher", archetype=SellerArchetype.BRAND_LAUNCHER,
description="Максимум визуала для запуска бренда",
photo_pct=35, video_pct=20, infographic_pct=25,
seo_pct=10, ugc_pct=5, rich_pct=5, refresh_cycle_months=4,
),
SellerArchetype.RESELLER: StrategyPreset(
name="Reseller Efficient", archetype=SellerArchetype.RESELLER,
description="Инфографика + SEO для максимума конверсий",
photo_pct=25, video_pct=10, infographic_pct=40,
seo_pct=15, ugc_pct=5, rich_pct=5, refresh_cycle_months=6,
),
SellerArchetype.ENTERPRISE: StrategyPreset(
name="Enterprise Full Stack", archetype=SellerArchetype.ENTERPRISE,
description="Все типы контента, упор на видео и Rich",
photo_pct=20, video_pct=25, infographic_pct=15,
seo_pct=15, ugc_pct=10, rich_pct=15, refresh_cycle_months=3,
),
# ... остальные 5 пресетов аналогично
}
class StrategySelector:
def select(self, archetype: SellerArchetype, profile: SellerProfile) -> StrategyPreset:
preset = PRESETS[archetype].model_copy()
if profile.is_fashion: # Fashion: +10% photo, -10% seo
preset.photo_pct += 10
preset.seo_pct = max(0, preset.seo_pct - 10)
if profile.return_rate > 0.15: # Высокий возврат: +5% video
preset.video_pct += 5
preset.infographic_pct = max(0, preset.infographic_pct - 5)
return preset
После коррекций сумма процентов может отклоняться от 100%. BudgetCalculator нормализует значения перед расчётом абсолютных сумм.
6. ABC-анализ для приоритизации SKU
SKUPrioritizer распределяет SKU по группам A/B/C и назначает уровень контента.
flowchart TD
A["Все SKU продавца"] --> B["Сортировка по выручке"]
B --> C{"Кумулятивная доля"}
C -->|"до 80%"| D["Группа A\nПолный контент"]
C -->|"80-95%"| E["Группа B\nСтандартный"]
C -->|"от 95%"| F["Группа C\nМинимум / AI"]
style D fill:#c8e6c9
style E fill:#fff9c4
style F fill:#ffcdd2
@dataclass
class SKUContentPlan:
sku_id: str
name: str
monthly_revenue: float
abc_group: str # "A", "B", "C"
content_level: str # "full", "standard", "minimal"
estimated_cost: float
priority_order: int
class SKUPrioritizer:
A_THRESHOLD = 0.80 # Топ-20% SKU дают 80% выручки
B_THRESHOLD = 0.95 # Следующие 30% дают ещё 15%
COST_MULTIPLIER = {"A": 1.0, "B": 0.5, "C": 0.15}
def prioritize(self, sku_list: list[dict], budget: "ContentBudget") -> list[SKUContentPlan]:
sorted_skus = sorted(sku_list, key=lambda x: x["revenue"], reverse=True)
total_revenue = sum(s["revenue"] for s in sorted_skus) or 1
plans, cumulative = [], 0.0
for i, sku in enumerate(sorted_skus):
cumulative += sku["revenue"] / total_revenue
if cumulative <= self.A_THRESHOLD:
group, level = "A", "full"
elif cumulative <= self.B_THRESHOLD:
group, level = "B", "standard"
else:
group, level = "C", "minimal"
plans.append(SKUContentPlan(
sku_id=sku["id"], name=sku["name"],
monthly_revenue=sku["revenue"], abc_group=group,
content_level=level, priority_order=i + 1,
estimated_cost=budget.cost_per_sku * self.COST_MULTIPLIER[group],
))
return plans
- Группа A (full): студийное фото, видео-обзор, инфографика, SEO, Rich-контент
- Группа B (standard): студийное фото, инфографика, SEO-текст
- Группа C (minimal): AI-фон, базовый текст, шаблонная инфографика
7. Budget Calculator
ContentBudgetCalculator преобразует пресет в абсолютные суммы с учётом ценовых уровней и ниши.
@dataclass
class ContentBudget:
total: float
per_type: dict[str, float] # photo, video, infographic, seo, ugc, rich
cost_per_sku: float
investment_level: int # 0-4
fashion_multiplier: float
monthly_capacity: int # SKU/месяц
class ContentBudgetCalculator:
# Уровни инвестиций (стоимость за 1 SKU, руб.)
INVESTMENT_LEVELS = {
0: {"name": "Минимальный", "cost_range": (500, 1_500), "desc": "AI + шаблоны"},
1: {"name": "Базовый", "cost_range": (1_500, 5_000), "desc": "Фото + инфографика"},
2: {"name": "Стандартный", "cost_range": (5_000, 15_000), "desc": "+ видео + SEO"},
3: {"name": "Продвинутый", "cost_range": (15_000, 40_000), "desc": "+ UGC + Rich"},
4: {"name": "Премиум", "cost_range": (40_000, 100_000),"desc": "Полный пакет"},
}
FASHION_MULTIPLIER = 2.5
def calculate(self, profile: SellerProfile, preset: StrategyPreset,
overrides: "ConfigOverrides | None" = None) -> ContentBudget:
level = self._detect_level(profile.revenue_per_sku)
cost_range = self.INVESTMENT_LEVELS[level]["cost_range"]
base_cost = (cost_range[0] + cost_range[1]) / 2
multiplier = self.FASHION_MULTIPLIER if profile.is_fashion else 1.0
cost_per_sku = base_cost * multiplier * profile.seasonal_coefficient
total = cost_per_sku * profile.sku_count
if overrides and overrides.budget_limit:
total = min(total, overrides.budget_limit)
cost_per_sku = total / profile.sku_count
# Аллокация по типам (с нормализацией)
alloc = {"photo": preset.photo_pct, "video": preset.video_pct,
"infographic": preset.infographic_pct, "seo": preset.seo_pct,
"ugc": preset.ugc_pct, "rich": preset.rich_pct}
if overrides and overrides.exclude_content_types:
for ct in overrides.exclude_content_types:
alloc.pop(ct, None)
pct_sum = sum(alloc.values()) or 1
per_type = {k: round(total * v / pct_sum, 2) for k, v in alloc.items()}
return ContentBudget(
total=round(total, 2), per_type=per_type,
cost_per_sku=round(cost_per_sku, 2), investment_level=level,
fashion_multiplier=multiplier,
monthly_capacity=max(1, int(total / cost_per_sku / 4)),
)
def _detect_level(self, revenue_per_sku: float) -> int:
if revenue_per_sku < 10_000: return 0
if revenue_per_sku < 50_000: return 1
if revenue_per_sku < 200_000: return 2
if revenue_per_sku < 500_000: return 3
return 4
Fashion-контент стоит в 2.5x дороже: больше фотографий на SKU (5-8 vs 3-4), обязательное видео с моделями, сезонные пересъёмки каждые 3 месяца.
8. SalesFinder API Integration
SalesFinderDataProvider собирает данные из нескольких эндпоинтов для построения профиля.
flowchart TD
SF["SalesFinder API"] --> A["/sellers/{id}/summary"]
SF --> B["/sellers/{id}/products"]
SF --> C["/analytics/revenue"]
SF --> D["/analytics/returns"]
A & B & C & D --> P["SellerProfile"]
style SF fill:#e3f2fd
style P fill:#c8e6c9
class SalesFinderDataProvider:
def __init__(self, client: SalesFinderClient):
self.client = client
async def build_profile(self, seller_id: str) -> SellerProfile:
summary, products, revenue, returns = await asyncio.gather(
self.client.get(f"/sellers/{seller_id}/summary"),
self.client.get(f"/sellers/{seller_id}/products", params={"limit": 1000}),
self.client.get(f"/analytics/revenue", params={"seller_id": seller_id, "period": "30d"}),
self.client.get(f"/analytics/returns", params={"seller_id": seller_id, "period": "30d"}),
)
categories = list({p["category"] for p in products.get("items", [])})
prices = [p["price"] for p in products.get("items", []) if p.get("price")]
return SellerProfile(
seller_id=seller_id,
marketplace=self._detect_marketplace(summary),
sku_count=summary.get("active_products", 0),
monthly_revenue=revenue.get("total", 0),
avg_margin=summary.get("avg_margin", 0.25),
categories=categories,
avg_price=sum(prices) / len(prices) if prices else 0,
min_price=min(prices) if prices else 0,
max_price=max(prices) if prices else 0,
return_rate=returns.get("rate", 0.05),
brand_count=summary.get("brand_count", 1),
has_own_brand=summary.get("has_own_brand", False),
is_reseller=summary.get("is_reseller", False),
)
async def get_sku_analytics(self, seller_id: str) -> list[dict]:
"""Аналитика по SKU для ABC-анализа."""
data = await self.client.get(
f"/sellers/{seller_id}/products",
params={"limit": 5000, "fields": "id,name,revenue,orders,returns"},
)
return [{"id": p["id"], "name": p["name"], "revenue": p.get("revenue", 0)}
for p in data.get("items", [])]
| Эндпоинт | Данные | Назначение |
|---|---|---|
/sellers/{id}/summary | Общая информация | SKU, маржа, бренды |
/sellers/{id}/products | Список товаров | Категории, цены, ABC |
/analytics/revenue | Выручка за период | monthly_revenue |
/analytics/returns | Статистика возвратов | return_rate |
/categories/tree | Дерево категорий | Маппинг в вертикали |
9. Recommendation Output
Итоговая модель рекомендации — всё, что возвращает конфигуратор.
class StrategyRecommendation(BaseModel):
seller_id: str
generated_at: datetime = Field(default_factory=datetime.utcnow)
profile: SellerProfile
archetype: SellerArchetype
archetype_confidence: float = 0.0
preset: StrategyPreset
budget: ContentBudget
sku_plan: list[SKUContentPlan]
sku_summary: dict = Field(default_factory=dict)
highlights: list[str] = Field(default_factory=list) # Ключевые выводы
warnings: list[str] = Field(default_factory=list) # Предупреждения
next_steps: list[str] = Field(default_factory=list) # План действий
Пример: продавец электроники, 150 SKU
{
"seller_id": "sf_seller_48291",
"archetype": "growing_brand",
"profile": {
"marketplace": "wildberries", "sku_count": 150,
"monthly_revenue": 4500000, "avg_margin": 0.32,
"is_fashion": false, "niche": "electronics",
"has_own_brand": true, "avg_price": 3200, "return_rate": 0.08
},
"budget": {
"total": 487500,
"per_type": {
"photo": 146250, "video": 121875, "infographic": 97500,
"seo": 73125, "ugc": 24375, "rich": 24375
},
"cost_per_sku": 3250, "investment_level": 1
},
"sku_summary": {
"A": {"count": 30, "total_cost": 97500},
"B": {"count": 45, "total_cost": 73125},
"C": {"count": 75, "total_cost": 36562}
},
"highlights": [
"Свой бренд электроники — фокус на видео-обзорах",
"30 ключе вых SKU (группа A) генерируют 80% выручки"
],
"next_steps": [
"1. Съёмка 30 товаров группы A (4 недели)",
"2. Инфографика для групп A+B (2 недели)",
"3. SEO-оптимизация всех 150 карточек (3 недели)",
"4. Видео-обзоры для топ-15 SKU (2 недели)"
]
}
10. Configuration Overrides
Продавец может переопределить любое решение конфигуратора.
class ConfigOverrides(BaseModel):
force_archetype: SellerArchetype | None = None # Принудительный архетип
budget_limit: float | None = Field(None, gt=0) # Лимит бюджета
exclude_content_types: list[str] = Field(default_factory=list) # Исключить типы
priority_skus: list[str] = Field(default_factory=list) # Всегда группа A
force_fashion: bool | None = None # Принудительная вертикаль
seasonal_override: float | None = Field(None, ge=0.1, le=5.0) # Сезонность
Пример:
overrides = ConfigOverrides(
force_fashion=True,
budget_limit=500_000,
exclude_content_types=["ugc", "rich"],
priority_skus=["sku_001", "sku_002"],
)
result = await pipeline.analyze("seller_123", overrides=overrides)
Overrides применяются после автоматических расчётов. priority_skus всегда попадают в группу A вне зависимости от выручки. budget_limit масштабирует все суммы пропорционально.
11. REST API Endpoints
POST /api/v1/configurator/analyze — Полный анализ продавца по seller_id
POST /api/v1/configurator/recommend — Рекомендация по готовому профилю (без SalesFinder)
GET /api/v1/configurator/presets — Список всех пресетов стратегий
POST /api/v1/configurator/override — Пересчёт с пользовательскими overrides
@router.post("/analyze", response_model=StrategyRecommendation)
async def analyze_seller(request: AnalyzeRequest,
pipeline: ConfiguratorPipeline = Depends(get_pipeline)):
return await pipeline.analyze(request.seller_id, request.overrides)
@router.post("/recommend", response_model=StrategyRecommendation)
async def recommend_from_profile(profile: SellerProfile,
overrides: ConfigOverrides | None = None,
pipeline: ConfiguratorPipeline = Depends(get_pipeline)):
profile = pipeline.category_detector.enrich(profile)
archetype = pipeline.archetype_classifier.classify(profile)
if overrides and overrides.force_archetype:
archetype = overrides.force_archetype
preset = pipeline.strategy_selector.select(archetype, profile)
budget = pipeline.budget_calculator.calculate(profile, preset, overrides)
return StrategyRecommendation(
seller_id=profile.seller_id, profile=profile,
archetype=archetype, preset=preset, budget=budget, sku_plan=[],
)
@router.get("/presets", response_model=list[StrategyPreset])
async def list_presets():
return list(PRESETS.values())
@router.post("/override", response_model=StrategyRecommendation)
async def apply_override(recommendation_id: str, overrides: ConfigOverrides,
pipeline: ConfiguratorPipeline = Depends(get_pipeline)):
saved = await load_recommendation(recommendation_id)
return await pipeline.analyze(saved.seller_id, overrides)
12. Full Pipeline Example
Полный путь от подключения продавца до готового отчёта.
sequenceDiagram
participant S as Продавец
participant API as REST API
participant SF as SalesFinder
participant P as Pipeline
S->>API: POST /analyze {seller_id}
API->>SF: 4 параллельных запроса
SF-->>API: Данные продавца
API->>P: CategoryDetector.enrich()
API->>P: ArchetypeClassifier.classify()
API->>P: StrategySelector.select()
API->>P: BudgetCalculator.calculate()
API->>P: SKUPrioritizer.prioritize()
API-->>S: StrategyRecommendation
Пошаговый разбор (продавец электроники, 150 SKU, 4.5M/мес)
| Шаг | Модуль | Результат |
|---|---|---|
| 1 | SalesFinderDataProvider | Профиль: 150 SKU, маржа 32%, собственный бренд |
| 2 | CategoryDetector | is_fashion=false, niche=electronics |
| 3 | ArchetypeClassifier | growing_brand (has_own_brand + revenue >= 500K) |
| 4 | StrategySelector | Пресет Growing Brand без коррекций |
| 5 | BudgetCalculator | 487,500₽ (уровень 1, 3,250₽/SKU) |
| 6 | SKUPrioritizer | A: 30 SKU, B: 45 SKU, C: 75 SKU |
| 7 | Output | JSON с рекомендациями и планом действий |
Логика классификации на шаге 3:
- Enterprise? Нет (150 < 500 SKU, 4.5M < 10M)
- Fashion Brand? Нет (is_fashion=false)
- Starter? Нет (150 >= 30 SKU)
- Reseller? Нет (has_own_brand=true)
- Growing Brand? Да (has_own_brand + revenue >= 500K)
Полный pipeline: 1-3 секунды. Основное время — запросы к SalesFinder API. Вычисления конфигуратора занимают менее 50мс.