# AI 업셀링 시스템 아키텍처

> 청춘약국 AI 기반 맞춤 제품 추천 시스템의 전체 구조 및 데이터 흐름

## 개요

고객이 마일리지를 적립할 때, 실시간으로 AI가 추가 구매 추천을 생성하는 시스템.

**핵심 특징:**
- POS(PIT3000) 판매 데이터 기반 추천
- 고객별 구매 이력 분석
- 약국 실제 재고(최근 판매 제품) 기반
- Clawdbot Gateway를 통한 Claude 연동 (추가 API 비용 없음)

---

## 아키텍처 다이어그램

```
┌─────────────────────────────────────────────────────────────────┐
│                         전체 흐름                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   [POS 판매]                                                    │
│       │                                                         │
│       ▼                                                         │
│   [MSSQL: PM_PRES]  ←─── PIT3000 POS 데이터                    │
│       │                                                         │
│       ▼                                                         │
│   [키오스크 적립 요청]  POST /api/kiosk/claim                    │
│       │                                                         │
│       ├──────────────────────────────────────┐                  │
│       │                                      │                  │
│       ▼                                      ▼                  │
│   [SQLite: mileage.db]              [백그라운드 스레드]          │
│   - claim_tokens                    _generate_upsell_recommendation()
│   - users                                    │                  │
│                                              │                  │
│                         ┌────────────────────┼────────────────┐ │
│                         │                    ▼                │ │
│                         │        ┌─────────────────────┐     │ │
│                         │        │  데이터 수집         │     │ │
│                         │        ├─────────────────────┤     │ │
│                         │        │ 1. 현재 구매 품목    │     │ │
│                         │        │ 2. 고객 구매 이력    │     │ │
│                         │        │ 3. 약국 보유 제품    │     │ │
│                         │        └──────────┬──────────┘     │ │
│                         │                   │                 │ │
│                         │                   ▼                 │ │
│                         │        ┌─────────────────────┐     │ │
│                         │        │  Clawdbot Gateway   │     │ │
│                         │        │  (WebSocket)        │     │ │
│                         │        │                     │     │ │
│                         │        │  Model: Sonnet      │     │ │
│                         │        │  (비용 최적화)       │     │ │
│                         │        └──────────┬──────────┘     │ │
│                         │                   │                 │ │
│                         │                   ▼                 │ │
│                         │        ┌─────────────────────┐     │ │
│                         │        │  Claude AI 응답     │     │ │
│                         │        │  {product, reason,  │     │ │
│                         │        │   message}          │     │ │
│                         │        └──────────┬──────────┘     │ │
│                         │                   │                 │ │
│                         └───────────────────┼─────────────────┘ │
│                                             │                   │
│                                             ▼                   │
│                              ┌─────────────────────────┐       │
│                              │  SQLite: ai_recommendations     │
│                              │  - recommended_product   │       │
│                              │  - recommendation_message│       │
│                              │  - trigger_products      │       │
│                              │  - expires_at            │       │
│                              └──────────┬──────────────┘       │
│                                         │                       │
│                                         ▼                       │
│                              ┌─────────────────────────┐       │
│                              │  마이페이지 / 키오스크   │       │
│                              │  추천 카드 노출          │       │
│                              └─────────────────────────┘       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

---

## 데이터 흐름 상세

### 1단계: 트리거 (키오스크 적립)

```python
# POST /api/kiosk/claim
# 고객이 전화번호로 마일리지 적립 요청

# 적립 완료 후 백그라운드에서 AI 추천 생성
threading.Thread(target=_bg_upsell, daemon=True).start()
```

**포인트:** 적립 응답은 즉시 반환, AI 추천은 백그라운드에서 처리 (non-blocking)

---

### 2단계: 데이터 수집

#### 2-1. 현재 구매 품목

```python
# 키오스크 트리거 시 전달받은 sale_items에서 추출
current_items = ', '.join(item['name'] for item in sale_items)
# 예: "타이레놀, 판피린, 비타민C"
```

#### 2-2. 고객 구매 이력 (최근 5건)

```sql
-- SQLite: 최근 적립한 거래 ID 조회
SELECT ct.transaction_id
FROM claim_tokens ct
WHERE ct.claimed_by_user_id = ? AND ct.transaction_id != ?
ORDER BY ct.claimed_at DESC LIMIT 5

-- MSSQL: 각 거래의 품목 조회
SELECT ISNULL(G.GoodsName, '') AS goods_name
FROM SALE_SUB S
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE S.SL_NO_order = :tid
```

#### 2-3. 약국 보유 제품 목록 (TOP 40)

```sql
-- MSSQL: 최근 30일 판매 상위 40개 제품
SELECT TOP 40
    ISNULL(G.GoodsName, '') AS name,
    COUNT(*) as sales,
    MAX(G.Saleprice) as price
FROM SALE_SUB S
LEFT JOIN PM_DRUG.dbo.CD_GOODS G ON S.DrugCode = G.DrugCode
WHERE S.SL_DT_appl >= CONVERT(VARCHAR(8), DATEADD(DAY, -30, GETDATE()), 112)
  AND G.GoodsName IS NOT NULL
  AND G.GoodsName NOT LIKE N'%(판매불가)%'
GROUP BY G.GoodsName
ORDER BY COUNT(*) DESC
```

**왜 TOP 40?**
- AI 컨텍스트 토큰 절약
- 실제로 많이 팔리는 제품만 추천 (재고 있음 보장)
- 판매불가 제품 자동 제외

---

### 3단계: AI 프롬프트 구성

```python
UPSELL_MODEL = 'anthropic/claude-sonnet-4-5'  # Opus 대신 Sonnet (비용 최적화)

SYSTEM_PROMPT = """당신은 동네 약국(청춘약국)의 친절한 약사입니다.
고객의 구매 이력을 보고, 약국에 실제로 있는 제품 중에서 하나를 추천합니다.
반드시 [약국 보유 제품 목록]에 있는 제품명을 그대로 사용하세요.
목록에 없는 제품은 절대 추천하지 마세요.
강압적이거나 광고 같은 느낌이 아닌, 진심으로 건강을 걱정하는 약사의 말투로 작성해주세요.
반드시 아래 JSON 형식으로만 응답하세요."""

USER_PROMPT = f"""고객 이름: {user_name}
오늘 구매한 약: {current_items}
최근 구매 이력: {recent_products}

[약국 보유 제품 목록 — 이 중에서만 추천하세요]
{product_list}

규칙:
1. 위 목록에 있는 제품 중 오늘 구매한 약과 함께 먹으면 좋거나, 구매 패턴상 필요해보이는 약 1가지만 추천
2. 오늘 이미 구매한 제품은 추천하지 마세요
3. 메시지는 2문장 이내, 따뜻하고 자연스러운 톤
4. product 필드에는 목록에 있는 제품명을 정확히 그대로 적어주세요

응답 JSON:
{{"product": "목록에 있는 정확한 제품명", "reason": "추천 이유 (내부용)", "message": "고객용 메시지"}}"""
```

---

### 4단계: AI 응답 및 저장

```json
// Claude 응답 예시
{
  "product": "종근당 비타민D 1000IU",
  "reason": "감기약과 함께 면역력 강화에 도움",
  "message": "홍길동님, 감기약 드시면서 비타민D도 같이 챙기시면 회복에 도움이 되실 거예요. 요즘 일조량 적을 때 특히 좋답니다."
}
```

```sql
-- SQLite: ai_recommendations 테이블에 저장
INSERT INTO ai_recommendations
(user_id, transaction_id, recommended_product, recommendation_message,
 recommendation_reason, trigger_products, ai_raw_response, expires_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
```

---

### 5단계: 추천 노출

```
GET /api/recommendation/{user_id}
```

- 마이페이지에서 조회
- 키오스크에서 적립 직후 표시
- 7일 후 만료 (expires_at)

---

## 핵심 쿼리 정리

| 용도 | DB | 쿼리 |
|------|-----|------|
| 고객 최근 거래 | SQLite | `claim_tokens WHERE claimed_by_user_id = ?` |
| 거래별 품목 | MSSQL | `SALE_SUB JOIN CD_GOODS WHERE SL_NO_order = ?` |
| 보유 제품 TOP 40 | MSSQL | `SALE_SUB GROUP BY GoodsName ORDER BY COUNT DESC` |
| 추천 저장 | SQLite | `INSERT INTO ai_recommendations` |
| 추천 조회 | SQLite | `SELECT FROM ai_recommendations WHERE user_id = ?` |

---

## 비용 최적화 전략

### 1. 모델 선택

```python
# 업셀링은 Sonnet (빠르고 저렴)
UPSELL_MODEL = 'anthropic/claude-sonnet-4-5'

# 복잡한 분석은 Opus (메인 세션)
# sessions.patch로 세션별 모델 오버라이드
```

### 2. 토큰 절약

- 보유 제품 TOP 40개만 전달 (전체 재고 X)
- 시스템 프롬프트 간결하게
- JSON 응답 강제 (불필요한 설명 제거)

### 3. 세션 분리

```python
# 고객별 세션 분리 → 컨텍스트 축적 방지
session_id = f'upsell-real-{user_name}'
```

---

## Fallback 전략

```python
# 1차 시도: 실데이터 기반 (보유 제품 목록 제공)
rec = generate_upsell_real(user_name, current_items, recent_products, available)

# 2차 시도: 자유 생성 (보유 제품 목록 없이)
if not rec:
    rec = generate_upsell(user_name, current_items, recent_products)
```

**왜 Fallback?**
- MSSQL 연결 실패 시에도 추천 가능
- 보유 제품 쿼리 실패해도 서비스 지속

---

## 관련 파일

```
pharmacy-pos-qr-system/
├── backend/
│   ├── app.py
│   │   ├── _get_available_products()      # 보유 제품 조회
│   │   ├── _generate_upsell_recommendation()  # 메인 로직
│   │   └── /api/recommendation/{user_id}  # 추천 조회 API
│   │
│   ├── services/
│   │   └── clawdbot_client.py
│   │       ├── generate_upsell()          # 자유 생성
│   │       ├── generate_upsell_real()     # 실데이터 기반
│   │       └── ask_clawdbot()             # Gateway 호출
│   │
│   ├── templates/
│   │   └── admin_ai_crm.html              # CRM 관리 페이지
│   │
│   └── db/
│       └── mileage.db                     # SQLite (ai_recommendations)
│
└── docs/
    ├── ai-upselling-architecture.md       # 이 문서
    └── clawdbot-gateway-api.md            # Gateway 연동 가이드
```

---

## 향후 개선 방향

### 1. 추천 정확도 향상
- 제품 카테고리 분류 추가 (감기약, 영양제, 외용제 등)
- 계절/시간대별 추천 가중치
- 고객 연령대/성별 기반 필터

### 2. 성과 측정
- 추천 → 실제 구매 전환율 추적
- A/B 테스트 (추천 vs 비추천)
- 인기 추천 제품 통계

### 3. 실시간 재고 연동
- 현재: 최근 30일 판매 기준 (간접 재고)
- 개선: 실제 재고 수량 기반 추천

### 4. 멀티 추천
- 현재: 1개 제품만 추천
- 개선: 상황별 2-3개 옵션 제시

---

*작성: 2026-02-27 | 용림 🐉*
