# Claude Haiku로 클러스터의 의도·테마명을 분류하고 마케팅 제안까지 한 번에 생성하는 모듈 import os import json import anthropic MODEL = "claude-haiku-4-5" USD_TO_KRW = 1400 # 비용 환산용 대략값 INTENT_LABELS = { "info": "정보 탐색형", "transactional": "구매/거래형", "navigational": "브랜드/내비게이션형", "mixed": "혼합형", } SYSTEM = """너는 검색 키워드 클러스터를 분석하는 마케팅 분석가다. 각 클러스터에 대해 두 가지를 판단한다. 1. intent — 검색 의도를 정확히 하나로 분류 - info: 방법·추천·비교·후기·가이드 등 정보 탐색 - transactional: 구매·가격·최저가·주문 등 거래 - navigational: 특정 브랜드·사이트로 이동 - mixed: 위가 섞여 하나로 단정하기 어려움 2. theme — 클러스터를 대표하는 2~6어절의 자연스러운 한국어 테마 그룹명 (예: "캠핑 가구", "초보 입문 가이드", "동계 난방") 그리고 전체 클러스터 패턴을 보고 중소기업(SMB)이 바로 실행할 수 있는 마케팅 액션 제안 1개를 2~3문장으로 구체적으로 작성한다. 반드시 JSON만 출력한다. 형식: {"clusters":[{"cluster_id":0,"intent":"transactional","theme":"캠핑 가구"}],"marketing_suggestion":"..."}""" def _client() -> anthropic.Anthropic: return anthropic.Anthropic() # ANTHROPIC_API_KEY는 환경변수에서 로드 def classify_clusters(clusters: list[dict]) -> dict: # 클러스터에 intent/intent_label/theme를 채우고, 도넛 비중·마케팅 제안·토큰 사용량을 반환 if not clusters: return {"intent_breakdown": {}, "marketing_suggestion": None, "usage": None} listing = "\n".join( f"{c['cluster_id']}: {c['cluster_label']} — {', '.join(c['top_keywords'])}" for c in clusters ) res = _client().messages.create( model=MODEL, max_tokens=1500, system=SYSTEM, messages=[{"role": "user", "content": f"다음 클러스터를 분석해라.\n{listing}"}], ) text = next(b.text for b in res.content if b.type == "text").strip() if text.startswith("```"): text = text.split("```")[1].lstrip("json").strip() parsed = json.loads(text) by_id = {item["cluster_id"]: item for item in parsed.get("clusters", [])} for c in clusters: item = by_id.get(c["cluster_id"], {}) intent = item.get("intent", "mixed") if intent not in INTENT_LABELS: intent = "mixed" c["intent"] = intent c["intent_label"] = INTENT_LABELS[intent] c["theme"] = item.get("theme") or c["cluster_label"] # 테마명, 없으면 대표 키워드 breakdown: dict[str, int] = {} for c in clusters: breakdown[c["intent"]] = breakdown.get(c["intent"], 0) + c["total_search_volume"] usd = res.usage.input_tokens / 1e6 * 1 + res.usage.output_tokens / 1e6 * 5 return { "intent_breakdown": breakdown, "marketing_suggestion": parsed.get("marketing_suggestion"), "usage": { "input_tokens": res.usage.input_tokens, "output_tokens": res.usage.output_tokens, "cost_usd": round(usd, 5), "cost_krw": round(usd * USD_TO_KRW, 1), }, }