Files
2nd/10_Wiki/Topics/AI_and_ML/맞춤형 팩 (Personalized Packs).md
T
2026-05-10 22:08:15 +09:00

185 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: wiki-2026-0508-맞춤형-팩-personalized-packs
title: 맞춤형 팩 (Personalized Packs)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Personalized Packs, Dynamic Bundles, Player-Tailored Offers]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [monetization, mobile-game, personalization, ml]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: python
framework: contextual-bandit / XGBoost
---
# 맞춤형 팩 (Personalized Packs)
## 매 한 줄
> **"매 player의 progression / collection gap / spend tier에 fit한 bundle을 ML로 generate"**. 2018 Supercell의 Brawl Stars Brawl Pass에서 mass-personalization 시작 → 2024 Royal Match · Monopoly Go 의 contextual-bandit 기반 dynamic offer로 evolve. 2026 현재 LLM-augmented offer copy + reinforcement-learning price elasticity가 industry standard.
## 매 핵심
### 매 Personalization Signal
- **Collection gap**: 매 missing card / character / skin → highest "completion utility".
- **Progression stall**: 매 stuck level → relevant booster / energy bundle.
- **Spend tier**: 매 LTV percentile (whale / dolphin / minnow / non-payer).
- **Churn risk**: 매 7-day rolling DAU drop → retention offer.
- **Session context**: 매 just-failed stage → instant-relief bundle.
### 매 Bundle Composition Heuristic
- **Anchor (core item)**: 매 player가 가장 원하는 single SKU — collection gap based.
- **Filler (utility)**: 매 gold / energy / consumables — perceived value 부풀리기.
- **Discount %**: 매 30~80% — perceived savings vs. actual margin.
- **Time pressure**: 매 24~72hr countdown — scarcity-driven conversion.
### 매 응용
1. Monopoly Go: 매 dice + sticker pack 동적 가격.
2. Royal Match: 매 stuck-level relief bundle.
3. Marvel Snap: 매 collection-gap-aware bundle (spotlight key).
4. Genshin Impact: 매 character-specific weapon + materials bundle pre-banner.
## 💻 패턴
### Contextual Bandit Offer Selection
```python
import numpy as np
from sklearn.linear_model import SGDRegressor
class OfferBandit:
def __init__(self, n_arms: int, ctx_dim: int, alpha: float = 0.1):
self.models = [SGDRegressor(learning_rate='constant', eta0=alpha)
for _ in range(n_arms)]
self.ctx_dim = ctx_dim
for m in self.models:
m.partial_fit([np.zeros(ctx_dim)], [0])
def select(self, ctx: np.ndarray, eps: float = 0.1) -> int:
if np.random.rand() < eps:
return np.random.randint(len(self.models))
scores = [m.predict([ctx])[0] for m in self.models]
return int(np.argmax(scores))
def update(self, arm: int, ctx: np.ndarray, reward: float):
self.models[arm].partial_fit([ctx], [reward])
```
### Collection Gap Score
```python
def gap_score(player_inv: set[str], target_set: set[str],
rarity_weight: dict[str, float]) -> dict[str, float]:
missing = target_set - player_inv
return {sku: rarity_weight.get(sku, 1.0) for sku in missing}
def top_anchor(scores: dict[str, float], k: int = 1) -> list[str]:
return sorted(scores, key=scores.get, reverse=True)[:k]
```
### Price Elasticity Estimator
```python
import numpy as np
from scipy.optimize import minimize_scalar
def expected_revenue(price: float, base_demand: float, elasticity: float) -> float:
qty = base_demand * (price ** elasticity) # elasticity < 0
return price * qty
def optimal_price(base_demand: float, elasticity: float,
bounds: tuple = (0.99, 99.99)) -> float:
res = minimize_scalar(lambda p: -expected_revenue(p, base_demand, elasticity),
bounds=bounds, method='bounded')
return float(res.x)
```
### Bundle Builder
```python
from dataclasses import dataclass
@dataclass
class Bundle:
anchor: str
fillers: list[str]
price_usd: float
discount_pct: int
expires_in_hours: int
def build_bundle(player_id: str, anchor_sku: str, ltv_tier: str) -> Bundle:
tier_config = {
'whale': (49.99, 60, 24),
'dolphin': (19.99, 65, 48),
'minnow': (4.99, 70, 72),
'non_payer': (0.99, 80, 168),
}
price, discount, hours = tier_config[ltv_tier]
fillers = recommend_fillers(player_id, count=3)
return Bundle(anchor_sku, fillers, price, discount, hours)
```
### LLM Offer Copy
```python
import anthropic
def generate_copy(bundle: Bundle, player_lang: str = "ko") -> dict:
client = anthropic.Anthropic()
msg = client.messages.create(
model="claude-opus-4-7",
max_tokens=300,
system=f"You write mobile-game offer copy in {player_lang}. "
f"3 outputs: title (max 20ch), subtitle (max 40ch), CTA (max 10ch).",
messages=[{"role": "user", "content": str(bundle)}],
)
return parse_copy(msg.content[0].text)
```
### Frequency Cap & Fatigue
```python
from datetime import datetime, timedelta
def can_show_offer(player_id: str, store: dict) -> bool:
last = store.get(player_id, {}).get('last_offer_ts')
if not last: return True
return datetime.utcnow() - last >= timedelta(hours=6)
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Whale (top 1%) | $49.99~$99.99 high-value bundle, low frequency |
| Dolphin (top 10%) | $9.99~$19.99 staircase progression |
| Minnow | $0.99~$4.99 starter / IAP-onramp |
| Non-payer (D7+) | $0.99 introductory + double-currency |
| Churn risk | retention bundle + 80% discount |
**기본값**: contextual bandit + LTV tier × collection-gap anchor + 6hr frequency cap.
## 🔗 Graph
- 부모: [[Mobile_Monetization]] · [[Personalization]]
- 변형: [[Staircase_Monetization_Model]] · [[Battle_Pass]] · [[Gacha]]
- 응용: [[Royal_Match]] · [[Monopoly_Go]] · [[Marvel_Snap]]
- Adjacent: [[Contextual_Bandit]] · [[LTV_Prediction]] · [[Price_Elasticity]]
## 🤖 LLM 활용
**언제**: offer copy generation, A/B variant ideation, anchor SKU rationale explanation.
**언제 X**: 매 actual price / SKU selection — bandit / RL이 더 robust (LLM은 calibration 약함).
## ❌ 안티패턴
- **Whale-only optimization**: 매 minnow / non-payer cohort revenue ignore — long-tail 손실.
- **Predatory targeting**: 매 churn-risk player에게 last-resort discount → regulatory risk (UK CMA, EU Digital Fairness Act).
- **Static bundles**: 매 player segment 동일 offer → CTR 50%↓.
- **No frequency cap**: 매 offer fatigue → uninstall spike.
## 🧪 검증 / 중복
- Verified (deconstructoroffun.com 2024 case studies, GDC Monetization Summit 2025).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — personalized pack 5-signal model + bandit + price elasticity 정리 |