Реалізація AI-системи рекомендацій Upsell для продаж
AI-upsell рекомендує покупцеві більш дорогу або розширену версію того, що він уже дивиться. Відмінність від cross-sell: пропонуємо той же товар класу вище, а не додаток. ML-модель визначає момент пропозиції, персоналізує аргументацію та вибирає правильний ціновий крок.
Upsell-модель з контекстуальним бандитом
import numpy as np
import pandas as pd
from anthropic import Anthropic
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.calibration import CalibratedClassifierCV
class UpsellRecommender:
def __init__(self):
self.llm = Anthropic()
self.model = None
self.product_catalog = {}
def train(self, sessions_df: pd.DataFrame):
"""
sessions_df: user_id, viewed_item_id, upsell_shown_item_id,
accepted, user_features..., item_features...
"""
features = self._extract_features(sessions_df)
X = features.drop(columns=['accepted'])
y = features['accepted']
base_model = GradientBoostingClassifier(
n_estimators=200, learning_rate=0.05,
max_depth=5, random_state=42
)
# Калібруємо ймовірності для правильного порога
self.model = CalibratedClassifierCV(base_model, cv=3, method='isotonic')
self.model.fit(X, y)
def _extract_features(self, df: pd.DataFrame) -> pd.DataFrame:
"""Feature engineering для upsell моделі"""
features = pd.DataFrame()
# Ціновий розрив між поточним та upsell товаром
features['price_delta'] = df['upsell_price'] - df['current_price']
features['price_ratio'] = df['upsell_price'] / df['current_price'].clip(0.01)
# Користувацькі ознаки
features['user_avg_order'] = df['user_avg_order_value']
features['user_premium_ratio'] = df['user_premium_purchases'] / df['user_total_purchases'].clip(1)
features['session_depth'] = df['pages_viewed']
features['cart_value'] = df['current_cart_value']
# Товарні ознаки
features['upsell_rating_delta'] = df['upsell_rating'] - df['current_rating']
features['current_category_encoded'] = df['category'].astype('category').cat.codes
features['accepted'] = df['accepted']
return features
def recommend_upsell(self, user: dict, current_item: str) -> dict:
"""Рекомендація upsell з поясненням"""
candidates = self._get_upsell_candidates(current_item)
if not candidates:
return None
# Скоринг кандидатів
best_candidate = None
best_prob = 0
for candidate in candidates:
features = self._build_prediction_features(user, current_item, candidate)
if self.model:
prob = self.model.predict_proba([features])[0][1]
else:
prob = 0.3 if candidate['price'] < user.get('avg_order', 0) * 1.5 else 0.15
if prob > best_prob and prob > 0.2: # Показуємо тільки з достатньою ймовірністю
best_prob = prob
best_candidate = (candidate, prob)
if not best_candidate:
return None
candidate, prob = best_candidate
# AI генерує персоналізовану пропозицію
pitch = self._generate_upsell_pitch(user, current_item, candidate)
return {
'recommended_item': candidate['item_id'],
'accept_probability': prob,
'pitch': pitch,
'price_delta': candidate['price'] - self.product_catalog.get(current_item, {}).get('price', 0)
}
def _get_upsell_candidates(self, item_id: str) -> list[dict]:
"""Товари тієї ж категорії але більш дорогі"""
current = self.product_catalog.get(item_id, {})
current_price = current.get('price', 0)
current_category = current.get('category', '')
return [
item for item in self.product_catalog.values()
if item.get('category') == current_category
and current_price * 1.1 <= item.get('price', 0) <= current_price * 2.5
and item.get('rating', 0) >= current.get('rating', 0) - 0.2
]
def _generate_upsell_pitch(self, user: dict, current_item: str,
upsell_item: dict) -> str:
current = self.product_catalog.get(current_item, {})
response = self.llm.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=100,
messages=[{
"role": "user",
"content": f"""Напиши короткий, переконливий upsell-месидж (1-2 речення, розмовний тон).
Покупець дивиться: {current.get('name', current_item)} (${current.get('price', 0)})
Upsell варіант: {upsell_item.get('name', '')} (${upsell_item.get('price', 0)})
Ключова різниця: {upsell_item.get('upgrade_feature', 'краща якість')}
Історія покупця: середній замовлення ${user.get('avg_order', 0):.0f}
Будь прямим, згадай конкретну вигоду, не загальні похвали."""
}]
)
return response.content[0].text
Типовий acceptance rate upsell при правильно налаштованій моделі: 8–15% (без ML: 3–5%). Ключовий інсайт: оптимальний ціновий крок для upsell — 20–40% вище поточної ціни. Вище 50% — конверсія падає вдвічі.







