pharmacy-pos-qr-system/docs/ai-upselling-architecture.md
thug0bin 9bd2174501 feat: 제품 검색 페이지 및 QR 라벨 인쇄 기능
- /admin/products: 전체 제품 검색 페이지 (OTC)
- /api/products: 제품 검색 API (세트상품 바코드 포함)
- qr_printer.py: Brother QL-710W 프린터 연동
- /api/qr-print, /api/qr-preview: QR 라벨 인쇄/미리보기 API
- 판매상세 페이지에 QR 인쇄 버튼 추가
- 수량 선택 UI (+/- 버튼, 최대 10장)
- 세트상품 제조사 표시 개선
- 대시보드 헤더에 제품검색/판매조회 탭 추가
2026-02-27 13:56:26 +09:00

13 KiB

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단계: 트리거 (키오스크 적립)

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

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

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


2단계: 데이터 수집

2-1. 현재 구매 품목

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

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

-- 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)

-- 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 프롬프트 구성

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 응답 및 저장

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

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

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

2. 토큰 절약

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

3. 세션 분리

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

Fallback 전략

# 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 | 용림 🐉