pharmacy-pos-qr-system/docs/ai-upselling-crm.md
thug0bin 5042cffb9f feat: AI CRM 어드민 대시보드 + 바텀시트 드래그 닫기 + UTF-8 인코딩 + 문서화
- /admin/ai-crm: AI 업셀링 추천 생성 현황 대시보드 (통계 카드 + 로그 테이블 + 아코디언 상세)
- 마이페이지 바텀시트: 터치 드래그로 닫기 기능 추가 (80px 임계값)
- Windows 콘솔 UTF-8 인코딩 강제 (app.py, clawdbot_client.py)
- admin.html 헤더에 AI CRM 네비 링크 추가
- docs: ai-upselling-crm.md, windows-utf8-encoding.md 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:38:04 +09:00

6.1 KiB

AI 업셀링 CRM — 마이페이지 맞춤 추천 시스템

개요

키오스크 적립 시 고객 구매이력을 AI가 분석하여 맞춤 제품을 추천. 고객이 알림톡 → 마이페이지 접속 시 바텀시트 팝업으로 자연스럽게 표시.

기술 스택

  • AI 엔진: Clawdbot Gateway (Claude Max 구독 재활용, 추가 비용 없음)
  • 통신: WebSocket (ws://127.0.0.1:18789) — JSON-RPC 프로토콜
  • 저장소: SQLite ai_recommendations 테이블
  • 프론트: 바텀시트 UI (드래그 닫기 지원)

전체 흐름

키오스크 적립 (POST /api/kiosk/claim)
  │
  ├─ 1. 적립 처리 (기존)
  ├─ 2. 알림톡 발송 (기존)
  └─ 3. AI 추천 생성 (fire-and-forget)
       │
       ├─ 최근 구매 이력 수집 (SQLite + MSSQL SALE_SUB)
       ├─ Clawdbot Gateway → Claude 호출
       ├─ 추천 결과 → ai_recommendations 저장
       └─ 실패 시 무시 (추천은 부가 기능)

고객: 알림톡 버튼 클릭 → /my-page
  │
  ├─ 1.5초 후 GET /api/recommendation/{user_id}
  │
  ├─ 추천 있음 → 바텀시트 슬라이드업
  │     ├─ 아래로 드래그 → 닫기
  │     ├─ "다음에요" → dismiss
  │     └─ "관심있어요!" → dismiss + 기록
  │
  └─ 추천 없음 → 아무것도 안 뜸

핵심 파일

backend/services/clawdbot_client.py

Clawdbot Gateway Python 클라이언트.

Gateway WebSocket 프로토콜 (v3):

  1. WS 연결 → ws://127.0.0.1:{port}
  2. 서버 → connect.challenge 이벤트 (nonce 전달)
  3. 클라이언트 → connect 요청 (token + client info)
  4. 서버 → connect 응답 (ok)
  5. 클라이언트 → agent 요청 (message + systemPrompt)
  6. 서버 → accepted ack → 최종 응답 (payloads[].text)

주요 함수:

함수 설명
_load_gateway_config() ~/.clawdbot/clawdbot.json에서 port, token 읽기
_ask_gateway(message, ...) async WebSocket 통신
ask_clawdbot(message, ...) 동기 래퍼 (Flask에서 호출)
generate_upsell(user_name, current_items, recent_products) 업셀 프롬프트 구성 + 호출 + JSON 파싱
_parse_upsell_response(text) AI 응답에서 JSON 추출

Gateway 설정:

  • 설정 파일: ~/.clawdbot/clawdbot.json
  • Client ID: gateway-client (허용된 상수 중 하나)
  • Protocol: v3 (minProtocol=3, maxProtocol=3)

backend/db/mileage_schema.sql — ai_recommendations 테이블

CREATE TABLE IF NOT EXISTS ai_recommendations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    transaction_id VARCHAR(20),
    recommended_product TEXT NOT NULL,      -- "고려은단 비타민C 1000"
    recommendation_message TEXT NOT NULL,   -- 고객에게 보여줄 메시지
    recommendation_reason TEXT,             -- 내부용 추천 이유
    trigger_products TEXT,                  -- JSON: 트리거된 구매 품목
    ai_raw_response TEXT,                   -- AI 원본 응답
    status VARCHAR(20) DEFAULT 'active',    -- active/dismissed
    displayed_count INTEGER DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    expires_at DATETIME,                    -- 7일 후 만료
    displayed_at DATETIME,
    dismissed_at DATETIME,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

backend/app.py — API 엔드포인트

엔드포인트 메서드 설명
/api/recommendation/<user_id> GET 최신 active 추천 조회 (마이페이지용)
/api/recommendation/<rec_id>/dismiss POST 추천 닫기 (status→dismissed)

추천 생성 위치: api_kiosk_claim() 함수 끝부분, _generate_upsell_recommendation() 호출

backend/templates/my_page.html — 바텀시트 UI

기능:

  • 페이지 로드 1.5초 후 추천 API fetch
  • 💊 아이콘 + AI 메시지 + 제품명 배지 (보라색 그라디언트)
  • 터치 드래그 닫기: 아래로 80px 이상 드래그하면 dismiss
  • 배경 탭 닫기, "다음에요"/"관심있어요!" 버튼
  • 슬라이드업/다운 CSS 애니메이션

AI 프롬프트

시스템 프롬프트:

당신은 동네 약국(청춘약국)의 친절한 약사입니다.
고객의 구매 이력을 보고, 자연스럽고 따뜻한 톤으로 약 하나를 추천합니다.
반드시 JSON 형식으로만 응답하세요.

유저 프롬프트 구조:

고객 이름: {name}
오늘 구매한 약: {current_items}
최근 구매 이력: {recent_products}

규칙:
1. 함께 먹으면 좋은 약 1가지만 추천 (일반의약품/건강기능식품)
2. 메시지 2문장 이내, 따뜻한 톤
3. JSON: {"product": "...", "reason": "...", "message": "..."}

응답 예시:

{
  "product": "고려은단 비타민C 1000",
  "reason": "감기약 구매로 면역력 보충 필요",
  "message": "김영빈님, 감기약 드시는 동안 비타민C도 함께 챙겨드시면 회복에 도움이 돼요."
}

Fallback 정책

상황 동작
Gateway 꺼져있음 추천 생성 스킵, 로그만 남김
AI 응답 파싱 실패 저장 안 함
추천 없을 때 마이페이지 방문 바텀시트 안 뜸
7일 경과 expires_at 만료, 조회 안 됨
dismiss 후 재방문 같은 추천 안 뜸 (새 적립 시 새 추천 생성)

테스트

# 1. Gateway 연결 테스트
PYTHONIOENCODING=utf-8 python -c "
from services.clawdbot_client import ask_clawdbot
print(ask_clawdbot('안녕'))
"

# 2. 업셀 생성 테스트
PYTHONIOENCODING=utf-8 python -c "
import json
from services.clawdbot_client import generate_upsell
result = generate_upsell('홍길동', '타이레놀, 챔프시럽', '비타민C, 소화제')
print(json.dumps(result, ensure_ascii=False, indent=2))
"

# 3. API 테스트
curl https://mile.0bin.in/api/recommendation/1

# 4. DB 확인
python -c "
import sqlite3, json
conn = sqlite3.connect('db/mileage.db')
conn.row_factory = sqlite3.Row
for r in conn.execute('SELECT * FROM ai_recommendations ORDER BY id DESC LIMIT 5'):
    print(json.dumps(dict(r), ensure_ascii=False))
"