From 4c3e1d08b27ecd2f2985bacf5e12917e26c58156 Mon Sep 17 00:00:00 2001 From: thug0bin Date: Thu, 26 Feb 2026 21:21:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=8B=A4=EB=8D=B0=EC=9D=B4=ED=84=B0=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20AI=20=EC=97=85=EC=85=80=EB=A7=81=20?= =?UTF-8?q?=EC=B6=94=EC=B2=9C=20=E2=80=94=20=EC=95=BD=EA=B5=AD=20=EB=B3=B4?= =?UTF-8?q?=EC=9C=A0=20=EC=A0=9C=ED=92=88=20=EB=AA=A9=EB=A1=9D=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=B6=94=EC=B2=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - generate_upsell_real(): MSSQL 최근 30일 판매 TOP 40 제품 목록을 AI에 제공 - AI가 실제 약국 보유 제품 중에서만 선택하여 추천 - 실데이터 실패 시 기존 자유 생성(generate_upsell) fallback - 기존 generate_upsell은 그대로 보존 Co-Authored-By: Claude Opus 4.6 --- backend/app.py | 40 +++++++++++++++++++++--- backend/services/clawdbot_client.py | 47 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/backend/app.py b/backend/app.py index ed822ae..ff9389e 100644 --- a/backend/app.py +++ b/backend/app.py @@ -1897,9 +1897,32 @@ def admin(): # AI 업셀링 추천 # ============================================================================ +def _get_available_products(): + """약국 보유 제품 목록 (최근 30일 판매 실적 기준 TOP 40)""" + try: + mssql_session = db_manager.get_session('PM_PRES') + rows = mssql_session.execute(text(""" + 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 + """)).fetchall() + return [{'name': r.name, 'price': float(r.price or 0), 'sales': r.sales} for r in rows] + except Exception as e: + logging.warning(f"[AI추천] 보유 제품 목록 조회 실패: {e}") + return [] + + def _generate_upsell_recommendation(user_id, transaction_id, sale_items, user_name): """키오스크 적립 후 AI 업셀링 추천 생성 (fire-and-forget)""" - from services.clawdbot_client import generate_upsell + from services.clawdbot_client import generate_upsell, generate_upsell_real if not sale_items: return @@ -1940,9 +1963,18 @@ def _generate_upsell_recommendation(user_id, transaction_id, sale_items, user_na except Exception as e: logging.warning(f"[AI추천] 구매 이력 수집 실패 (현재 품목만 사용): {e}") - # Claude로 추천 생성 - logging.info(f"[AI추천] 생성 시작: user={user_name}, items={current_items}") - rec = generate_upsell(user_name, current_items, recent_products) + # 실데이터 기반 추천 (보유 제품 목록 제공) + available = _get_available_products() + rec = None + + if available: + logging.info(f"[AI추천] 실데이터 생성 시작: user={user_name}, items={current_items}, 보유제품={len(available)}개") + rec = generate_upsell_real(user_name, current_items, recent_products, available) + + # 실데이터 실패 시 기존 방식 fallback + if not rec: + logging.info(f"[AI추천] 자유 생성 fallback: user={user_name}") + rec = generate_upsell(user_name, current_items, recent_products) if not rec: logging.warning("[AI추천] 생성 실패 (AI 응답 없음)") diff --git a/backend/services/clawdbot_client.py b/backend/services/clawdbot_client.py index cd732d3..327b7a3 100644 --- a/backend/services/clawdbot_client.py +++ b/backend/services/clawdbot_client.py @@ -247,6 +247,53 @@ def generate_upsell(user_name, current_items, recent_products): return _parse_upsell_response(response_text) +UPSELL_REAL_SYSTEM_PROMPT = """당신은 동네 약국(청춘약국)의 친절한 약사입니다. +고객의 구매 이력을 보고, 약국에 실제로 있는 제품 중에서 하나를 추천합니다. +반드시 [약국 보유 제품 목록]에 있는 제품명을 그대로 사용하세요. +목록에 없는 제품은 절대 추천하지 마세요. +강압적이거나 광고 같은 느낌이 아닌, 진심으로 건강을 걱정하는 약사의 말투로 작성해주세요. +반드시 아래 JSON 형식으로만 응답하세요. 다른 텍스트 없이 JSON만 출력하세요.""" + + +def generate_upsell_real(user_name, current_items, recent_products, available_products): + """ + 실데이터 기반 업셀링 추천 생성 + available_products: 약국 보유 제품 리스트 [{'name': ..., 'price': ..., 'sales': ...}, ...] + """ + product_list = '\n'.join( + f"- {p['name']} ({int(p['price'])}원, 최근 {p['sales']}건 판매)" + for p in available_products if p.get('name') + ) + + prompt = f"""고객 이름: {user_name} +오늘 구매한 약: {current_items} +최근 구매 이력: {recent_products} + +[약국 보유 제품 목록 — 이 중에서만 추천하세요] +{product_list} + +규칙: +1. 위 목록에 있는 제품 중 오늘 구매한 약과 함께 먹으면 좋거나, 구매 패턴상 필요해보이는 약 1가지만 추천 +2. 오늘 이미 구매한 제품은 추천하지 마세요 +3. 메시지는 2문장 이내, 따뜻하고 자연스러운 톤 +4. product 필드에는 목록에 있는 제품명을 정확히 그대로 적어주세요 + +응답은 반드시 아래 JSON 형식으로만: +{{"product": "목록에 있는 정확한 제품명", "reason": "추천 이유 (내부용, 1문장)", "message": "{user_name}님, [추천 메시지 2문장 이내]"}}""" + + response_text = ask_clawdbot( + prompt, + session_id=f'upsell-real-{user_name}', + system_prompt=UPSELL_REAL_SYSTEM_PROMPT, + timeout=30 + ) + + if not response_text: + return None + + return _parse_upsell_response(response_text) + + def _parse_upsell_response(text): """AI 응답에서 JSON 추출""" import re