- /admin/products: 전체 제품 검색 페이지 (OTC) - /api/products: 제품 검색 API (세트상품 바코드 포함) - qr_printer.py: Brother QL-710W 프린터 연동 - /api/qr-print, /api/qr-preview: QR 라벨 인쇄/미리보기 API - 판매상세 페이지에 QR 인쇄 버튼 추가 - 수량 선택 UI (+/- 버튼, 최대 10장) - 세트상품 제조사 표시 개선 - 대시보드 헤더에 제품검색/판매조회 탭 추가
13 KiB
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 | 용림 🐉