import os
import re
import time
import requests
import numpy as np
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sentence_transformers import SentenceTransformer
#####################################
# 1) 앱 및 모델 초기화
#####################################
app = FastAPI()
os.environ["HF_HOME"] = "/tmp/huggingface"
# 🟢 (A) 모델 로딩
print("Loading SentenceTransformer model... (msmarco-distilbert-base-v4)")
model_bert = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
print("Model loaded.")
# 🟢 (B) 예시 아이템 목록
items = [
# ========= 1~12: 운동 (헬스, 요가, 필라테스, 수영, 테니스, 골프, 클라이밍, 축구, 농구, 볼링, 배드민턴, 러닝) =========
{
"item_id": 1,
"title": "헬스 퍼스널 프로젝트",
"desc": "PT 전문 코치와 함께 근력·유산소를 체계적으로 관리 (운동, 헬스)",
"personality": "외향형, 이성형"
},
{
"item_id": 2,
"title": "빈야사 요가 in 강릉",
"desc": "강릉 바다 풍경 속에서 호흡·동작을 익히는 빈야사 요가 클래스 (운동, 요가)",
"personality": "내향형, 감정형"
},
{
"item_id": 3,
"title": "매트 필라테스 집중 워크숍",
"desc": "매트 동작 위주로 기초 코어 힘을 기르는 소그룹 훈련 (운동, 필라테스)",
"personality": "내향형, 이성형"
},
{
"item_id": 4,
"title": "오픈워터 스위밍 체험",
"desc": "바다나 호수에서 수영 실력을 시험하는 자연 친화형 이벤트 (운동, 수영)",
"personality": "외향형, 감정형"
},
{
"item_id": 5,
"title": "테니스 포핸드 마스터클래스",
"desc": "포핸드 스윙·풋워크를 집중 훈련하고 미니 게임으로 실습 (운동, 테니스)",
"personality": "외향형, 감정형"
},
{
"item_id": 6,
"title": "골프 숏게임 특화 레슨",
"desc": "어프로치·퍼팅 등 숏게임 구간을 집중적으로 연습하는 프로그램 (운동, 골프)",
"personality": "내향형, 이성형"
},
{
"item_id": 7,
"title": "아웃도어 클라이밍 교실",
"desc": "실외 바위 지형에서 암벽등반 기초와 안전 수칙을 배우는 챌린지 (운동, 클라이밍)",
"personality": "외향형, 감정형"
},
{
"item_id": 8,
"title": "풋살 드리블&패스 이벤트",
"desc": "축구 소규모 버전 풋살에서 전술·팀워크를 즐기는 주말 클래스 (운동, 축구)",
"personality": "외향형, 감정형"
},
{
"item_id": 9,
"title": "스트리트 농구 대결전",
"desc": "도심 코트에서 농구 토너먼트를 열어 실력을 겨루는 액션 (운동, 농구)",
"personality": "외향형, 이성형"
},
{
"item_id": 10,
"title": "볼링 스핀&릴리스 테크닉",
"desc": "볼링장 전문 강사와 함께 볼 스핀, 릴리스 자세를 교정 (운동, 볼링)",
"personality": "내향형, 이성형"
},
{
"item_id": 11,
"title": "배드민턴 기술 업 워크숍",
"desc": "스매시·드롭샷·푸트워크를 체계적으로 배우는 배드민턴 교실 (운동, 배드민턴)",
"personality": "외향형, 감정형"
},
{
"item_id": 12,
"title": "10K 러닝 크루 레이스",
"desc": "도심 10km 달리기에 함께 도전하며 기록 향상을 목표 (운동, 러닝)",
"personality": "내향형, 감정형"
},
# ========= 13~20: 여행 (국내여행, 해외여행, 백패킹, 캠핑, 도시여행, 맛집탐방, 문화탐방, 힐링여행) =========
{
"item_id": 13,
"title": "부산 해안 로드트립",
"desc": "차로 부산 해안 도로를 달리며 해산물과 해변 풍경 만끽 (여행, 국내여행)",
"personality": "외향형, 감정형"
},
{
"item_id": 14,
"title": "미국 서부 배낭투어",
"desc": "LA·샌프란시스코·라스베이거스 등 미국 서부 도시에 떠나는 자유여행 (여행, 해외여행)",
"personality": "외향형, 감정형"
},
{
"item_id": 15,
"title": "설악산 백패킹 트레킹",
"desc": "설악산 국립공원에서 배낭 야영하며 산세와 계곡을 체험 (여행, 백패킹)",
"personality": "내향형, 감정형"
},
{
"item_id": 16,
"title": "모닥불 캠핑 나이트",
"desc": "텐트 피칭과 모닥불 요리로 캠핑 문화를 느끼는 하룻밤 (여행, 캠핑)",
"personality": "내향형, 감정형"
},
{
"item_id": 17,
"title": "홍콩 시티 골목탐방",
"desc": "홍콩 소호·몽콕 등 골목 시장과 길거리 음식을 즐기는 도시여행 (여행, 도시여행)",
"personality": "외향형, 이성형"
},
{
"item_id": 18,
"title": "전국 맛집 로드쇼",
"desc": "블로그·SNS 추천 맛집을 직접 찾아다니며 식도락을 즐기는 투어 (여행, 맛집탐방)",
"personality": "외향형, 감정형"
},
{
"item_id": 19,
"title": "경주 역사 문화탐방",
"desc": "신라시대 유적·박물관·전통 공연을 둘러보며 문화유산을 학습 (여행, 문화탐방)",
"personality": "내향형, 이성형"
},
{
"item_id": 20,
"title": "지중해 요가 힐링 투어",
"desc": "지중해 연안 소도시에서 요가·명상으로 몸과 마음을 재충전 (여행, 힐링여행)",
"personality": "외향형, 감정형"
},
# ========= 21~30: 독서 (소설, 시, 에세이, 자기계발, 인문, 역사, 과학, 경제/경영, 철학, 예술) =========
{
"item_id": 21,
"title": "이문열 '우리들의 일그러진 영웅' 독서토론",
"desc": "한국 현대소설의 학교 폭력·권력 관계를 토론하며 생각 확장 (독서, 소설)",
"personality": "내향형, 감정형"
},
{
"item_id": 22,
"title": "김소월 시 낭독 살롱",
"desc": "'진달래꽃' 등 한국적 정서가 깃든 시를 낭송하고 감상을 교류 (독서, 시)",
"personality": "내향형, 감정형"
},
{
"item_id": 23,
"title": "공지영 '즐거운 나의 집' 에세이 톡",
"desc": "가족과 일상 이야기를 담은 에세이를 함께 읽고 공감 (독서, 에세이)",
"personality": "내향형, 감정형"
},
{
"item_id": 24,
"title": "하이 퍼포먼스 습관 만들기",
"desc": "브렌든 버처드 등 자기계발서를 기반으로 아침 루틴을 실천 (독서, 자기계발)",
"personality": "내향형, 이성형"
},
{
"item_id": 25,
"title": "장하석 '온도계의 철학' 인문 스터디",
"desc": "온도·측정의 철학적 의미를 다룬 책을 통해 사고 확장 (독서, 인문)",
"personality": "내향형, 이성형"
},
{
"item_id": 26,
"title": "유홍준 '나의 문화유산답사기' 역사 독해",
"desc": "한국 문화유산 답사기를 읽고 현장 답사 욕구를 높이는 토론 (독서, 역사)",
"personality": "내향형, 이성형"
},
{
"item_id": 27,
"title": "리처드 도킨스 '이기적 유전자' 과학 세션",
"desc": "유전학 이론을 대중적으로 풀어낸 도서를 함께 읽고 토론 (독서, 과학)",
"personality": "내향형, 이성형"
},
{
"item_id": 28,
"title": "짐 콜린스 '좋은 기업을 넘어 위대한 기업으로' 세미나",
"desc": "기업 성공 사례를 분석하며 경제·경영 통찰을 얻는 과정 (독서, 경제/경영)",
"personality": "내향형, 이성형"
},
{
"item_id": 29,
"title": "장자 '소요유' 철학 살롱",
"desc": "도가 사상을 담은 장자의 글을 읽고 자유로운 삶의 가치 논의 (독서, 철학)",
"personality": "내향형, 이성형"
},
{
"item_id": 30,
"title": "에드가 드가 '발레 수업' 예술책 토크",
"desc": "인상주의 화가 드가의 발레 시리즈와 예술서를 같이 탐독 (독서, 예술)",
"personality": "내향형, 감정형"
},
# ========= 31~40: 영화 (로맨스, 코미디, 액션, 스릴러, 공포, SF, 판타지, 드라마, 애니메이션, 다큐멘터리) =========
{
"item_id": 31,
"title": "'시간 여행자의 아내' 로맨스 스크리닝",
"desc": "시간여행을 소재로 한 사랑 이야기를 함께 보고 울고 웃는 자리 (영화, 로맨스)",
"personality": "외향형, 감정형"
},
{
"item_id": 32,
"title": "코미디 '슈퍼배드' 웃음 파티",
"desc": "'슈퍼배드' 시리즈를 감상하며 유머 코드를 분석하고 떠들썩한 웃음을 공유 (영화, 코미디)",
"personality": "외향형, 감정형"
},
{
"item_id": 33,
"title": "'킹스맨' 액션씬 관전",
"desc": "'킹스맨' 시리즈의 액션 연출·스파이 코드를 짚어보는 상영회 (영화, 액션)",
"personality": "외향형, 이성형"
},
{
"item_id": 34,
"title": "'서스펙트' 스릴러 미스터리",
"desc": "긴장감 넘치는 한국 스릴러 영화를 함께 감상하고 반전 요소 해석 (영화, 스릴러)",
"personality": "외향형, 감정형"
},
{
"item_id": 35,
"title": "'에일리언' 공포 SF 밤샘",
"desc": "'에일리언' 시리즈를 몰아보며 외계 생명체 공포와 SF 설정을 토론 (영화, 공포)",
"personality": "내향형, 감정형"
},
{
"item_id": 36,
"title": "'그래비티' SF 토크",
"desc": "우주 공간 조난 스토리를 그린 '그래비티'의 과학·연출을 분석 (영화, SF)",
"personality": "외향형, 감정형"
},
{
"item_id": 37,
"title": "'해리 포터' 판타지 앤딩",
"desc": "호그와트 마법 세계관을 연속 상영하고 팬심을 나누는 스팟 (영화, 판타지)",
"personality": "외향형, 감정형"
},
{
"item_id": 38,
"title": "'인생은 아름다워' 드라마 감동",
"desc": "전쟁 속 가족사랑을 그린 이탈리아 명작으로 희망 메시지 나눔 (영화, 드라마)",
"personality": "외향형, 감정형"
},
{
"item_id": 39,
"title": "'너의 이름은' 애니메이션 스페셜",
"desc": "신카이 마코토 감독의 빛나는 작화·청춘 판타지를 감상 (영화, 애니메이션)",
"personality": "외향형, 감정형"
},
{
"item_id": 40,
"title": "'마이클 무어' 다큐 포럼",
"desc": "'화씨 9/11' 등 사회·정치적 메시지가 담긴 다큐를 보고 토론 (영화, 다큐멘터리)",
"personality": "외향형, 이성형"
},
# ========= 41~50: 게임 (RPG, FPS, 액션, 전략, 시뮬레이션, 스포츠, 퍼즐, 음악/리듬, 카드, MMORPG) =========
{
"item_id": 41,
"title": "'젤다의 전설: 야생의 숨결' RPG 탐험",
"desc": "오픈월드 명작 '젤다' 시리즈를 탐험하며 퍼즐·전투 방식을 공유 (게임, RPG)",
"personality": "내향형, 감정형"
},
{
"item_id": 42,
"title": "'콜 오브 듀티' FPS 워존 배틀",
"desc": "밀리터리 FPS에서 팀 단위 전술을 실행하고 승리를 노리는 대전 (게임, FPS)",
"personality": "외향형, 이성형"
},
{
"item_id": 43,
"title": "'갓 오브 워' 액션 앤 레이지",
"desc": "크레토스의 여정·스킬 콤보를 파고들며 액션게임의 재미 체험 (게임, 액션)",
"personality": "외향형, 감정형"
},
{
"item_id": 44,
"title": "'문명6' 전략 빌드 세션",
"desc": "턴제 전략 '문명6'에서 문명별 특성과 승리 조건을 분석 (게임, 전략)",
"personality": "내향형, 이성형"
},
{
"item_id": 45,
"title": "'플래닛 코스터' 시뮬레이션 제작소",
"desc": "놀이공원 경영 게임에서 놀이기구 배치·경영을 디자인 (게임, 시뮬레이션)",
"personality": "내향형, 감정형"
},
{
"item_id": 46,
"title": "'NBA 2K' 스포츠 매치",
"desc": "농구게임 'NBA 2K'로 온라인·오프라인 토너먼트를 개최 (게임, 스포츠)",
"personality": "외향형, 감정형"
},
{
"item_id": 47,
"title": "'폴가이즈' 퍼즐 액션 배틀",
"desc": "귀엽고 혼란스러운 장애물 코스로 다수 인원이 경쟁 (게임, 퍼즐)",
"personality": "외향형, 이성형"
},
{
"item_id": 48,
"title": "'비트세이버' 리듬 VR 체험",
"desc": "VR 기기를 착용해 박자에 맞춰 블록을 베는 리듬 대결 (게임, 음악/리듬)",
"personality": "내향형, 감정형"
},
{
"item_id": 49,
"title": "'매직: 더 개더링' 카드 덱실험",
"desc": "TCG의 원조격인 ‘매직’에서 덱 빌드와 대전 전략을 연구 (게임, 카드)",
"personality": "내향형, 이성형"
},
{
"item_id": 50,
"title": "'월드 오브 워크래프트' MMORPG 길드레이드",
"desc": "아제로스에서 길드원이 힘을 합쳐 레이드를 진행 (게임, MMORPG)",
"personality": "외향형, 이성형"
},
# ========= 51~60: 공예 (뜨개질, 자수, 도자기, 가죽공예, 목공예, 비즈공예, 캔들/디퓨저, 페이퍼크래프트, 마크라메, 레진아트) =========
{
"item_id": 51,
"title": "코바늘 뜨개질 애착인형 DIY",
"desc": "코바늘 기법으로 인형이나 소품을 만드는 초급자 대상 워크숍 (공예, 뜨개질)",
"personality": "내향형, 감정형"
},
{
"item_id": 52,
"title": "일본 자수 '사시코' 교실",
"desc": "사시코 기법으로 앞치마·티매트를 장식하는 레슨 (공예, 자수)",
"personality": "내향형, 이성형"
},
{
"item_id": 53,
"title": "도자기 핸드페인팅 체험",
"desc": "빚은 도자기에 직접 그림·문양을 그려 소성까지 경험 (공예, 도자기)",
"personality": "내향형, 이성형"
},
{
"item_id": 54,
"title": "가죽 파우치 제작 스튜디오",
"desc": "가죽 재단·바느질·마감까지 소형 파우치를 만드는 실습 (공예, 가죽공예)",
"personality": "내향형, 이성형"
},
{
"item_id": 55,
"title": "목공예 숟가락 만들기 워크숍",
"desc": "나무를 깎고 다듬어 주방용 숟가락을 만드는 기초 체험 (공예, 목공예)",
"personality": "내향형, 이성형"
},
{
"item_id": 56,
"title": "비즈공예 액세서리 디자인",
"desc": "비즈 구슬로 팔찌·목걸이를 만들고 색감 패턴을 익히는 클래스 (공예, 비즈공예)",
"personality": "내향형, 감정형"
},
{
"item_id": 57,
"title": "젤 캔들 & 디퓨저 아틀리에",
"desc": "투명 젤 왁스로 캔들과 디퓨저를 창작하며 향기를 디자인 (공예, 캔들/디퓨저)",
"personality": "내향형, 이성형"
},
{
"item_id": 58,
"title": "페이퍼크래프트 동물 피규어",
"desc": "종이로 입체 동물 모형을 만들고 채색·디테일 작업 (공예, 페이퍼크래프트)",
"personality": "내향형, 이성형"
},
{
"item_id": 59,
"title": "마크라메 해먹 의자 만들기",
"desc": "매듭 기법을 이용해 해먹 의자·월행잉 등 인테리어 소품 제작 (공예, 마크라메)",
"personality": "내향형, 이성형"
},
{
"item_id": 60,
"title": "레진아트 트레이 공방",
"desc": "레진과 색소·글리터로 컵받침이나 작은 트레이를 만드는 작업 (공예, 레진아트)",
"personality": "내향형, 이성형"
},
# ========== 61~71: 추가 11개 자유 항목 (임의 분야) ==========
{
"item_id": 61,
"title": "보드게임 '카탄' 챌린지",
"desc": "전략 보드게임 '카탄' 대회로 무역·개척 전술을 겨루는 이벤트 (기타, 보드게임)",
"personality": "외향형, 이성형"
},
{
"item_id": 62,
"title": "스페인어 기초 회화 교실",
"desc": "hola! 간단한 문장·회화를 배워 여행·일상에서 활용 (기타, 언어학습)",
"personality": "내향형, 감정형"
},
{
"item_id": 63,
"title": "홈브루잉 커피 실습",
"desc": "원두 고르기부터 핸드드립·에스프레소 추출까지 커피 제작 전과정 체험 (기타, 요리/음료)",
"personality": "내향형, 이성형"
},
{
"item_id": 64,
"title": "K-POP 댄스 커버 스튜디오",
"desc": "최신 아이돌 안무를 함께 연습·촬영해보는 퍼포먼스 프로젝트 (기타, 댄스)",
"personality": "외향형, 감정형"
},
{
"item_id": 65,
"title": "플라워 레슨 & 꽃다발 디자인",
"desc": "생화를 활용해 부케·꽃다발을 직접 만드는 교실 (기타, 플라워)",
"personality": "내향형, 감정형"
},
{
"item_id": 66,
"title": "드론 항공촬영 워크숍",
"desc": "드론 조종법·촬영 기법·영상 편집을 배우고 실습 (기타, 드론)",
"personality": "외향형, 이성형"
},
{
"item_id": 67,
"title": "비건 베이커리 클래스",
"desc": "달걀·버터 없이 쿠키·빵을 만드는 레시피와 노하우 학습 (기타, 요리/베이킹)",
"personality": "내향형, 이성형"
},
{
"item_id": 68,
"title": "애견 행동교정 & 트릭 클래스",
"desc": "반려견 기본 행동교정·산책 요령·간단한 재주를 훈련 (기타, 반려동물)",
"personality": "외향형, 감정형"
},
{
"item_id": 69,
"title": "영화 시나리오 작법 워크숍",
"desc": "시놉시스·캐릭터 구성을 배우고 단편 시나리오를 써보는 활동 (기타, 글쓰기)",
"personality": "내향형, 이성형"
},
{
"item_id": 70,
"title": "홈칵테일 믹솔로지 이벤트",
"desc": "칵테일 재료·비율을 익혀 집에서 간단히 만들 수 있는 레시피 실습 (기타, 요리/음료)",
"personality": "외향형, 감정형"
},
{
"item_id": 71,
"title": "유튜브 브이로그 크리에이팅",
"desc": "영상 기획·촬영·편집 노하우를 배우고 브이로그를 완성 (기타, 영상콘텐츠)",
"personality": "내향형, 이성형"
},
# ==== (운동 - 12개 대응, item_id 73~84) ====================================
{"item_id": 73, "title": "크로스핏 열정단", "desc": "고강도 크로스핏 운동으로 체력을 단련하는 열정적 모임 (온동, 헬스장)", "personality": "외향형, 감정형"},
{"item_id": 74, "title": "명상 요가 서클", "desc": "몸의 이완과 마음의 평화를 함께 추구하는 명상 요가 모임 (운동, 요가)", "personality": "외향형, 감정형"},
{"item_id": 75, "title": "코어 트레이닝 팸", "desc": "필라테스로 코어 근력과 유연성을 키우는 그룹 (운동, 필라테스)", "personality": "외향형, 감정형"},
{"item_id": 76, "title": "파도타기 워터팀", "desc": "수영과 워터 스포츠를 함께 즐기며 체력을 키우는 모임 (운동, 수영)", "personality": "외향형, 감정형"},
{"item_id": 77, "title": "라켓 드림클럽", "desc": "테니스를 비롯한 라켓 스포츠를 같이 배우고 즐기는 동호회 (운동, 테니스)", "personality": "외향형, 감정형"},
{"item_id": 78, "title": "필드 골프 리더스", "desc": "골프 스윙 기술과 필드 매너를 함께 익히는 라운딩 팀 (운동, 골프)", "personality": "외향형, 감정형"},
{"item_id": 79, "title": "슬링 텀블러즈", "desc": "암벽등반과 슬링 트레이닝으로 색다른 운동을 체험하는 그룹 (운동, 클라이밍)", "personality": "외향형, 감정형"},
{"item_id": 80, "title": "스포츠 골든리그", "desc": "주중·주말에 축구 경기를 주기적으로 여는 커뮤니티 (운동, 축구)", "personality": "외향형, 감정형"},
{"item_id": 81, "title": "하프코트 슈터스", "desc": "농구 코트에서 함께 뛰고 실력을 배양하는 팀 (운동, 농구)", "personality": "외향형, 감정형"},
{"item_id": 82, "title": "스트라이크 러버스", "desc": "볼링 점수를 올리기 위해 함께 연구하고 즐기는 동아리 (운동, 볼링)", "personality": "외향형, 감정형"},
{"item_id": 83, "title": "스매싱 배드민턴", "desc": "배드민턴 스매시 기술과 경기를 함께 연습하는 학습 모임 (운동, 배드민턴)", "personality": "외향형, 감정형"},
{"item_id": 84, "title": "나이트 러너스", "desc": "야간 러닝을 통해 도시를 누비며 체력을 기르는 크루 (운동, 러닝)", "personality": "외향형, 감정형"},
# ==== (여행 - 8개 대응, item_id 85~92) =====================================
{"item_id": 85, "title": "오지여행 탐험대", "desc": "국내 숨겨진 오지를 찾아 떠나며 모험심을 기르는 모임 (여행, 국내여행)", "personality": "외향형, 감정형"},
{"item_id": 86, "title": "글로벌 백팩 트립", "desc": "해외 배낭여행 루트를 함께 계획하고 실행하는 동호회 (여행, 해외여행)", "personality": "외향형, 감정형"},
{"item_id": 87, "title": "필드 캠핑 어드벤처", "desc": "백패킹과 캠핑을 결합해 자연에서 살아보는 체험 모임 (여행, 백패킹)", "personality": "외향형, 감정형"},
{"item_id": 88, "title": "패밀리 캠핑 잼", "desc": "가족 단위로 캠핑하며 자연 속 소통을 즐기는 그룹 (여행, 캠핑)", "personality": "외향형, 감정형"},
{"item_id": 89, "title": "골목길 투어 클럽", "desc": "도시 골목길과 역사적 공간을 함께 탐방하는 여행 서클 (여행, 도시여행)", "personality": "외향형, 감정형"},
{"item_id": 90, "title": "미식헌터 크루", "desc": "전국 맛집을 조사하고 직접 시식 투어를 떠나는 모임 (여행, 맛집탐방)", "personality": "외향형, 감정형"},
{"item_id": 91, "title": "문화유산 탐방기획사", "desc": "전통 문화유산과 축제를 기획·탐방하는 여행 동아리 (여행, 문화탐방)", "personality": "외향형, 감정형"},
{"item_id": 92, "title": "숲속 힐링 트래블", "desc": "자연 속에서 치유와 재충전을 목적으로 여행하는 그룹 (여행, 힐링여행)", "personality": "외향형, 감정형"},
# ==== (독서 - 10개 대응, item_id 93~102) ==================================
{"item_id": 93, "title": "문학 리뷰 서클", "desc": "소설과 문학작품을 읽고 비평하는 책동아리 (독서, 소설)", "personality": "내향형, 감정형"},
{"item_id": 94, "title": "시인 꿈나무 모임", "desc": "서로의 시를 낭독하고 공유하는 창작 시클럽 (독서, 시)", "personality": "내향형, 감정형"},
{"item_id": 95, "title": "마음글쓰기 에세이회", "desc": "에세이로 일상과 감정을 표현하며 소통하는 글모임 (독서, 에세이)", "personality": "내향형, 이성형"},
{"item_id": 96, "title": "실천형 자기계발단", "desc": "자기계발서를 읽고 즉시 실천 계획을 세우는 스터디 (독서, 자기계발)", "personality": "내향형, 이성형"},
{"item_id": 97, "title": "깊이읽기 인문소사이어티", "desc": "인문학 텍스트를 심층 토론하며 사고력을 확장하는 그룹 (독서, 인문)", "personality": "내향형, 이성형"},
{"item_id": 98, "title": "역사공부 디스커션", "desc": "시대별 역사적 사건을 공부하고 자유롭게 논의하는 모임 (독서, 역사)", "personality": "내향형, 이성형"},
{"item_id": 99, "title": "과학탐구 북클럽", "desc": "최신 과학 서적을 중심으로 아이디어를 나누는 독서회 (독서, 과학)", "personality": "내향형, 이성형"},
{"item_id": 100, "title": "경영전략 독해팀", "desc": "경제/경영 관련 서적을 함께 분석하고 사례를 연구하는 그룹 (독서, 경제/경영)", "personality": "내향형, 이성형"},
{"item_id": 101, "title": "철학강독 세미나", "desc": "철학 원전을 읽고 핵심 개념을 토론하는 학술 모임 (독서, 철학)", "personality": "내향형, 이성형"},
{"item_id": 102, "title": "아트북 인사이트회", "desc": "예술 관련 서적을 통해 예술사와 작품 세계를 탐구하는 모임 (독서, 예술)", "personality": "내향형, 감정형"},
# ==== (영화 - 10개 대응, item_id 103~112) =================================
{"item_id": 103, "title": "낭만 영화 애호가들", "desc": "감성적인 로맨스 영화를 좋아하는 사람들의 모임 (영화, 로맨스)", "personality": "외향형, 감정형"},
{"item_id": 104, "title": "폭소 코미디 페스티벌", "desc": "웃음을 주제로 코미디 영화를 함께 보는 소모임 (영화, 코미디)", "personality": "외향형, 감정형, 이성형"},
{"item_id": 105, "title": "액션매니아즈", "desc": "박진감 넘치는 액션 영화의 매력을 해부하는 그룹 (영화, 액션)", "personality": "외향형, 감정형"},
{"item_id": 106, "title": "서스펜스 시네마분석단", "desc": "스릴러 영화 속 긴장감과 스토리를 분석하는 모임 (영화, 스릴러)", "personality": "외향형, 감정형"},
{"item_id": 107, "title": "호러매틱 스터디", "desc": "공포영화의 분위기와 연출을 집중 탐구하는 팀 (영화, 공포)", "personality": "외향형, 감정형"},
{"item_id": 108, "title": "SF 월드 디스커버리", "desc": "공상과학 영화를 보고 미래 기술과 세계관을 토론하는 모임 (영화, SF)", "personality": "외향형, 감정형"},
{"item_id": 109, "title": "판타지 이매지네이션", "desc": "환상적인 판타지 영화를 감상하며 아이디어를 공유하는 서클 (영화, 판타지)", "personality": "외향형, 감정형"},
{"item_id": 110, "title": "휴먼드라마 인사이트", "desc": "감동적인 드라마 장르 영화를 함께 보고 교훈을 찾는 모임 (영화, 드라마)", "personality": "외향형, 감정형"},
{"item_id": 111, "title": "애니 애호회", "desc": "다양한 애니메이션 영화를 정기 상영하며 감상하는 팬클럽 (영화, 애니메이션)", "personality": "외향형, 감정형"},
{"item_id": 112, "title": "다큐 리뷰 포럼", "desc": "다큐멘터리 영화를 보고 사회·환경 문제를 논의하는 모임 (영화, 다큐멘터리)", "personality": "외향형, 이성형"},
# ==== (게임 - 10개 대응, item_id 113~122) =================================
{"item_id": 113, "title": "RPG 모험단", "desc": "판타지 세계에서 함께 파티를 이뤄 모험하는 RPG 팀 (게임, RPG)", "personality": "외향형, 이성형"},
{"item_id": 114, "title": "FPS 고인물 클랜", "desc": "고난도 슈팅 기술을 연습하며 대회에도 나가는 FPS 동호회 (게임, FPS)", "personality": "외향형, 이성형"},
{"item_id": 115, "title": "액션코어 전략팀", "desc": "액션 게임의 숨겨진 테크닉과 콤보를 연구하는 그룹 (게임, 액션)", "personality": "외향형, 이성형"},
{"item_id": 116, "title": "전략모드 시뮬클럽", "desc": "전략 시뮬레이션 장르를 심도 있게 플레이·분석하는 동호회 (게임, 전략)", "personality": "외향형, 이성형"},
{"item_id": 117, "title": "시뮬레이션 마니아들", "desc": "도시건설, 경영 시뮬레이션 등 다양한 시뮬 게임을 즐기는 모임 (게임, 시뮬레이션)", "personality": "외향형, 이성형, 감정형"},
{"item_id": 118, "title": "스포츠게이머 챌린지", "desc": "축구·농구 등 스포츠게임 리그를 운영하는 커뮤니티 (게임, 스포츠)", "personality": "외향형, 이성형"},
{"item_id": 119, "title": "퍼즐 브레인홀릭", "desc": "퍼즐·두뇌게임을 함께 풀며 아이디어를 공유하는 팀 (게임, 퍼즐)", "personality": "외향형, 이성형"},
{"item_id": 120, "title": "리듬게임 오케스트라", "desc": "음악/리듬게임에서 합주하는 느낌으로 협력 플레이하는 모임 (게임, 음악/리듬)", "personality": "외향형, 이성형"},
{"item_id": 121, "title": "카드플레이 매니아", "desc": "포커·TCG 등 카드 기반 게임을 심도 있게 즐기는 동아리 (게임, 카드)", "personality": "외향형, 이성형"},
{"item_id": 122, "title": "MMORPG 연합길드", "desc": "대규모 온라인 RPG에서 길드원들과 함께 도전하는 팀 (게임, MMORPG)", "personality": "외향형, 이성형"},
# ==== (공예 - 10개 대응, item_id 123~132) =================================
{"item_id": 123, "title": "니트디자인 스터디오", "desc": "뜨개질로 패션 소품과 의류를 창작하는 워크숍 (공예, 뜨개질)", "personality": "내향형, 이성형"},
{"item_id": 124, "title": "자수 디테일연구회", "desc": "섬세한 자수 기법을 연구하며 작품을 완성하는 모임 (공예, 자수)", "personality": "내향형, 이성형"},
{"item_id": 125, "title": "도예 공예단", "desc": "도자기 공예로 식기·인테리어 소품을 제작해보는 그룹 (공예, 도자기)", "personality": "내향형, 이성형"},
{"item_id": 126, "title": "가죽공예 스킬랩", "desc": "가죽 재단부터 바느질까지 실습하고 디자인을 공유하는 모임 (공예, 가죽공예)", "personality": "내향형, 이성형"},
{"item_id": 127, "title": "목공 마스터스", "desc": "목재를 활용해 가구·장식품을 제작하며 기술을 배우는 모임 (공예, 목공예)", "personality": "내향형, 이성형"},
{"item_id": 128, "title": "비즈 디자인 팩토리", "desc": "비즈공예로 액세서리·장신구를 창작하는 교실 (공예, 비즈공예)", "personality": "내향형, 이성형"},
{"item_id": 129, "title": "캔들/디퓨저 크리에이티브", "desc": "향초와 디퓨저를 예술적으로 표현해보는 창작 스튜디오 (공예, 캔들/디퓨저)", "personality": "내향형, 이성형"},
{"item_id": 130, "title": "페이퍼아트 디자인랩", "desc": "종이 공예로 독특한 작품과 미니어처를 만드는 연구회 (공예, 페이퍼크래프트)", "personality": "내향형, 이성형"},
{"item_id": 131, "title": "마크라메 공예실", "desc": "마크라메 매듭 기법을 다양하게 시도해보는 작업실 (공예, 마크라메)", "personality": "내향형, 이성형"},
{"item_id": 132, "title": "레진아트 크리에이터즈", "desc": "레진을 활용해 장식품·소품을 디자인하고 공유하는 모임 (공예, 레진아트)", "personality": "내향형, 이성형"},
# ===== [133~144] 운동 (12개)
{
"item_id": 133,
"title": "나이키 헬스 피트니스 챌린지",
"desc": "헬스장에서 나이키 프로그램을 통해 웨이트 및 유산소를 마스터 (운동, 헬스)",
"personality": "외향형, 감정형"
},
{
"item_id": 134,
"title": "아쉬탕가 요가 클래스 in 발리",
"desc": "발리 자연 속에서 명상·아사나 요가를 심도 있게 체험 (운동, 요가)",
"personality": "내향형, 감정형"
},
{
"item_id": 135,
"title": "리포머 필라테스 스튜디오",
"desc": "기구 필라테스로 자세 교정과 코어 강화에 집중 (운동, 필라테스)",
"personality": "내향형, 이성형"
},
{
"item_id": 136,
"title": "아쿠아 스위밍 프로젝트",
"desc": "실내 수영장에서 영법·호흡법을 익히고 오픈워터 도전 (운동, 수영)",
"personality": "외향형, 감정형"
},
{
"item_id": 137,
"title": "윔블던 테니스 레슨",
"desc": "프로 코치와 함께 랠리·발리를 집중 훈련, 미니 매치 진행 (운동, 테니스)",
"personality": "외향형, 이성형"
},
{
"item_id": 138,
"title": "스크린 골프 라운딩",
"desc": "필드 대신 스크린 골프로 스윙 교정하고 퍼팅 실력을 키움 (운동, 골프)",
"personality": "내향형, 이성형"
},
{
"item_id": 139,
"title": "실내 클라이밍 챌린지",
"desc": "암벽등반 센터에서 다양한 코스를 공략하며 근지구력 향상 (운동, 클라이밍)",
"personality": "외향형, 감정형"
},
{
"item_id": 140,
"title": "풋볼 매치데이 이벤트",
"desc": "축구 팬들이 모여 포메이션 짜고 미니 경기를 즐기는 일정 (운동, 축구)",
"personality": "외향형, 감정형"
},
{
"item_id": 141,
"title": "NBA 농구 액션타임",
"desc": "코트에서 드리블·슛을 연습하고 3:3 스크리미지를 진행 (운동, 농구)",
"personality": "외향형, 감정형"
},
{
"item_id": 142,
"title": "락볼링 스트라이크 파티",
"desc": "볼링장에서 음악과 함께 스트라이크 확률을 높이는 교류전 (운동, 볼링)",
"personality": "내향형, 감정형"
},
{
"item_id": 143,
"title": "셔틀콕 배드민턴 교실",
"desc": "배드민턴 스매시·푸트워크 집중 연습 후 미니 토너먼트 (운동, 배드민턴)",
"personality": "외향형, 감정형"
},
{
"item_id": 144,
"title": "하프마라톤 러닝 레이스",
"desc": "도심 10km·21km 레이스에 도전하며 기록 향상을 목표 (운동, 러닝)",
"personality": "내향형, 감정형"
},
# ===== [145~152] 여행 (8개)
{
"item_id": 145,
"title": "강원도 로드트립",
"desc": "차로 강원도 해안·산골 명소를 돌아보며 자연을 만끽 (여행, 국내여행)",
"personality": "내향형, 감정형"
},
{
"item_id": 146,
"title": "유럽 배낭 자유여행",
"desc": "파리·로마·바르셀로나를 포함해 유럽 도시들을 여행 (여행, 해외여행)",
"personality": "외향형, 감정형"
},
{
"item_id": 147,
"title": "지리산 백패킹 트레일",
"desc": "배낭 하나로 지리산 둘레길을 걸으며 자연 속 야영 (여행, 백패킹)",
"personality": "내향형, 감정형"
},
{
"item_id": 148,
"title": "캠핑 파이어나이트",
"desc": "텐트 치고 모닥불을 즐기며 가족·친구와 소통하는 프로그램 (여행, 캠핑)",
"personality": "외향형, 감정형"
},
{
"item_id": 149,
"title": "서울 골목투어",
"desc": "익선동·을지로 등 오래된 골목길 카페·맛집을 탐방 (여행, 도시여행)",
"personality": "외향형, 감정형"
},
{
"item_id": 150,
"title": "전주 한옥마을 미식 탐방",
"desc": "전주의 비빔밥·막걸리·한정식을 둘러보는 식도락 여행 (여행, 맛집탐방)",
"personality": "외향형, 감정형"
},
{
"item_id": 151,
"title": "경복궁 문화탐방",
"desc": "왕궁·전통 공연·박물관을 돌며 한국 역사를 배우는 코스 (여행, 문화탐방)",
"personality": "내향형, 이성형"
},
{
"item_id": 152,
"title": "제주 오름 힐링투어",
"desc": "제주 오름·용암동굴을 걸으며 자연 속에서 재충전 (여행, 힐링여행)",
"personality": "내향형, 감정형"
},
# ===== [153~162] 독서 (10개)
{
"item_id": 153,
"title": "무라카미 '1Q84' 북클럽",
"desc": "하루키 장편소설을 함께 읽고 상징·메타포를 토론 (독서, 소설)",
"personality": "내향형, 감정형"
},
{
"item_id": 154,
"title": "윤동주 시 낭독회",
"desc": "‘하늘과 바람과 별과 시’ 등 윤동주 시를 낭송하며 문학적 감성을 공유 (독서, 시)",
"personality": "내향형, 감정형"
},
{
"item_id": 155,
"title": "김영하 '여행의 이유' 에세이 살롱",
"desc": "여행과 삶의 관계를 에세이로 탐구하고 느낀 점을 나누는 시간 (독서, 에세이)",
"personality": "내향형, 감정형"
},
{
"item_id": 156,
"title": "스티븐 코비 '성공하는 사람들의 7가지 습관'",
"desc": "자기계발서를 함께 읽고 실천 전략을 세우는 성장 프로젝트 (독서, 자기계발)",
"personality": "내향형, 이성형"
},
{
"item_id": 157,
"title": "인문학 토크: '총, 균, 쇠' & '사피엔스'",
"desc": "문명의 발전 과정을 다룬 명저를 비교·분석하는 심층 독서 (독서, 인문)",
"personality": "내향형, 이성형"
},
{
"item_id": 158,
"title": "유발 하라리 '사피엔스' 역사 세션",
"desc": "인류 역사를 조망하는 '사피엔스'를 읽고 현재 사회와 연결 (독서, 역사)",
"personality": "내향형, 이성형"
},
{
"item_id": 159,
"title": "칼 세이건 '코스모스' 과학 독서모임",
"desc": "우주와 과학적 사고를 확장해보는 '코스모스' 함께 읽기 (독서, 과학)",
"personality": "내향형, 이성형"
},
{
"item_id": 160,
"title": "애덤 스미스 '국부론' 경제포럼",
"desc": "고전 경제학을 토대로 시장경제 원리를 이해하는 세미나 (독서, 경제/경영)",
"personality": "내향형, 이성형"
},
{
"item_id": 161,
"title": "니체 '차라투스트라는 이렇게 말했다' 철학 토론",
"desc": "니체 철학을 깊이 있게 탐독하고 현대적 의미를 해석 (독서, 철학)",
"personality": "내향형, 이성형"
},
{
"item_id": 162,
"title": "빈센트 반 고흐 예술책 리뷰",
"desc": "화가 반 고흐의 작품 세계를 미술서적으로 접하고 교감 (독서, 예술)",
"personality": "내향형, 감정형"
},
# ===== [163~172] 영화 (10개)
{
"item_id": 163,
"title": "'러브 액츄얼리' 로맨스 상영회",
"desc": "여러 커플의 사랑 이야기를 담은 '러브 액츄얼리'를 감상 후 토론 (영화, 로맨스)",
"personality": "외향형, 감정형"
},
{
"item_id": 164,
"title": "코미디 '미스터 빈' 특별전",
"desc": "로완 앳킨슨의 바디 코미디를 감상하며 웃음 포인트를 분석 (영화, 코미디)",
"personality": "외향형, 감정형"
},
{
"item_id": 165,
"title": "'매드 맥스: 분노의 도로' 액션 토크",
"desc": "포스트 아포칼립스 배경의 질주 액션을 해부하는 상영회 (영화, 액션)",
"personality": "외향형, 감정형"
},
{
"item_id": 166,
"title": "스릴러 '겟 아웃' 심리분석",
"desc": "조던 필의 '겟 아웃'을 통해 공포와 사회적 메시지를 살피는 이벤트 (영화, 스릴러)",
"personality": "외향형, 감정형"
},
{
"item_id": 167,
"title": "공포 '컨저링' 오싹 체험",
"desc": "심령현상을 다룬 공포영화 '컨저링'으로 사운드·분위기 연출을 탐구 (영화, 공포)",
"personality": "외향형, 감정형"
},
{
"item_id": 168,
"title": "'인터스텔라' SF 디스커션",
"desc": "크리스토퍼 놀란의 우주·중력 이론을 담은 영화를 보고 과학적 설정 토론 (영화, SF)",
"personality": "외향형, 감정형"
},
{
"item_id": 169,
"title": "'반지의 제왕' 판타지 데이",
"desc": "톨킨 세계관을 기반으로 한 대서사시를 연속 상영·분석 (영화, 판타지)",
"personality": "외향형, 감정형"
},
{
"item_id": 170,
"title": "'쇼생크 탈출' 드라마 감동",
"desc": "인생영화 '쇼생크 탈출'을 보고 희망·인내의 메시지를 토론 (영화, 드라마)",
"personality": "외향형, 감정형"
},
{
"item_id": 171,
"title": "지브리 애니메이션 스페셜",
"desc": "'센과 치히로' 등 지브리 작품을 상영하며 판타지·동심을 느끼는 감상회 (영화, 애니메이션)",
"personality": "외향형, 감정형"
},
{
"item_id": 172,
"title": "다큐 '지구(Earth)' 환경 포럼",
"desc": "BBC 제작 다큐멘터리 '지구'를 보고 자연·환경 이슈를 논의 (영화, 다큐멘터리)",
"personality": "외향형, 이성형"
},
# ===== [173~182] 게임 (10개)
{
"item_id": 173,
"title": "'엘든 링' RPG 도전",
"desc": "프롬소프트웨어의 ‘엘든 링’에서 난이도 극복 전략과 빌드 연구 (게임, RPG)",
"personality": "외향형, 이성형"
},
{
"item_id": 174,
"title": "'오버워치2' FPS 팀플",
"desc": "‘오버워치2’에서 역할 분담·전술 플레이를 협력하는 대전 (게임, FPS)",
"personality": "외향형, 이성형"
},
{
"item_id": 175,
"title": "'데빌 메이 크라이' 액션 콤보 연구",
"desc": "콤보 액션의 정수 ‘데메크’ 시리즈를 플레이하며 기술을 익힘 (게임, 액션)",
"personality": "외향형, 이성형"
},
{
"item_id": 176,
"title": "'스타크래프트' 전략 빌드토론",
"desc": "테란·저그·프로토스 종족별 빌드 오더·리플레이 분석 (게임, 전략)",
"personality": "내향형, 이성형"
},
{
"item_id": 177,
"title": "'심시티' 시뮬레이션 건설",
"desc": "도시건설 게임 ‘심시티’로 자신만의 도시를 디자인하고 운영 (게임, 시뮬레이션)",
"personality": "내향형, 감정형"
},
{
"item_id": 178,
"title": "'피파23' 스포츠 대전",
"desc": "축구게임 ‘피파23’에서 오프라인 리그전을 열고 순위를 집계 (게임, 스포츠)",
"personality": "외향형, 이성형"
},
{
"item_id": 179,
"title": "'테트리스99' 퍼즐 배틀로얄",
"desc": "닌텐도 스위치 ‘테트리스99’로 다수 인원이 동시에 경쟁 (게임, 퍼즐)",
"personality": "외향형, 이성형"
},
{
"item_id": 180,
"title": "'DJMAX 리스펙트 V' 리듬 페스티벌",
"desc": "국산 리듬게임 DJMAX로 콤보와 노트 패턴에 도전 (게임, 음악/리듬)",
"personality": "외향형, 이성형"
},
{
"item_id": 181,
"title": "'하스스톤' 카드 덱실험실",
"desc": "블리자드 TCG ‘하스스톤’에서 전략적 덱 구성과 토너먼트 플레이 (게임, 카드)",
"personality": "외향형, 이성형"
},
{
"item_id": 182,
"title": "'로스트아크' MMORPG 레이드",
"desc": "대규모 온라인 RPG ‘로스트아크’에서 길드원과 협동해 레이드를 공략 (게임, MMORPG)",
"personality": "외향형, 이성형"
},
# ===== [183~192] 공예 (10개)
{
"item_id": 183,
"title": "니트 코바늘 인형 만들기",
"desc": "뜨개질 기법으로 귀여운 인형이나 소품을 제작하는 워크숍 (공예, 뜨개질)",
"personality": "내향형, 이성형"
},
{
"item_id": 184,
"title": "프랑스자수 브로치 교실",
"desc": "세심한 자수로 브로치·미니 파우치를 장식하는 공예 클래스 (공예, 자수)",
"personality": "내향형, 감정형"
},
{
"item_id": 185,
"title": "도예 핸드빌딩 체험",
"desc": "흙을 손으로 빚어 머그·접시를 만드는 도자기 공예 (공예, 도자기)",
"personality": "내향형, 이성형"
},
{
"item_id": 186,
"title": "가죽공예 카드지갑 제작",
"desc": "가죽 재단·바느질·엣지 마감 실습으로 카드지갑을 완성 (공예, 가죽공예)",
"personality": "내향형, 이성형"
},
{
"item_id": 187,
"title": "나무도마 목공 교실",
"desc": "원목을 가공·샌딩하여 주방용 나무도마를 제작하는 기초 체험 (공예, 목공예)",
"personality": "내향형, 이성형"
},
{
"item_id": 188,
"title": "비즈공예 액세서리 디자인",
"desc": "비즈 구슬로 팔찌·목걸이를 만들고 색채 감각을 익히는 클래스 (공예, 비즈공예)",
"personality": "내향형, 감정형"
},
{
"item_id": 189,
"title": "소이 캔들 & 디퓨저 만들기",
"desc": "천연 소이왁스로 향초, 디퓨저를 제작해 감성 분위기를 조성 (공예, 캔들/디퓨저)",
"personality": "내향형, 이성형"
},
{
"item_id": 190,
"title": "페이퍼크래프트 3D 미니어처",
"desc": "종이 공예로 입체 구조물을 제작하고 채색·디테일을 살리는 교실 (공예, 페이퍼크래프트)",
"personality": "내향형, 이성형"
},
{
"item_id": 191,
"title": "마크라메 인테리어 월행잉",
"desc": "매듭 기법을 응용해 벽걸이·화분홀더 등 인테리어 소품을 만드는 실습 (공예, 마크라메)",
"personality": "내향형, 이성형"
},
{
"item_id": 192,
"title": "레진아트 키링 & 액세서리",
"desc": "투명 레진에 색소·글리터를 활용해 키링·반지를 만드는 창작 (공예, 레진아트)",
"personality": "내향형, 이성형"
}
]
#####################################
# 2) 아이템 임베딩 로직
#####################################
def make_item_embedding_dict(items, model):
item_embedding_dict = {}
for item in items:
text = f"{item['title']} {item['desc']}"
emb = model.encode(text, convert_to_numpy=True)
item_embedding_dict[item['item_id']] = emb
return item_embedding_dict
item_embedding_dict = make_item_embedding_dict(items, model_bert)
#####################################
# 3) 사용자 프로필 -> 문장화
#####################################
def make_user_profile_text(user_profile: dict) -> str:
ext = user_profile.get("extroversion", "")
ft = user_profile.get("feeling_thinking", "")
hobby = user_profile.get("hobby", "")
detail = user_profile.get("detail_hobby", "")
if hobby == "여행":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"여행을 정말 좋아하며, 특히 {detail}을 즐깁니다. "
"다양한 문화와 맛집을 직접 체험하고 싶어합니다.")
elif hobby == "운동":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"운동에 열정을 가지고 있으며, 특히 {detail}을 즐깁니다. "
"건강과 체력을 증진시키고자 꾸준히 활동합니다.")
elif hobby == "독서":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"독서를 사랑하며, 특히 {detail}에 깊은 관심을 가지고 있습니다. "
"새로운 지식과 다양한 관점을 배우고자 합니다.")
elif hobby == "영화":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"영화를 감상하는 것을 좋아하며, 특히 {detail} 장르에 큰 흥미를 느낍니다. "
"감동과 재미를 동시에 경험하고 싶어합니다.")
elif hobby == "게임":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"게임을 즐기며, 특히 {detail} 게임에 많은 관심을 가지고 있습니다. "
"전략적 사고와 도전을 통해 성취감을 느끼고자 합니다.")
elif hobby == "공예":
return (f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. "
f"공예 활동에 열정을 가지고 있으며, 특히 {detail}을 즐깁니다. "
"창의적인 아이디어를 실현하며 섬세한 작업을 즐깁니다.")
else:
return f"저는 {ext}이며 {ft}한 성향을 가진 사람입니다. 다양한 취미를 즐기고 있습니다."
#####################################
# 4) 사용자 임베딩 + 추천 로직
#####################################
def get_user_embedding(user_profile: dict):
user_text = make_user_profile_text(user_profile)
return model_bert.encode(user_text, convert_to_numpy=True)
def extract_hobby(desc):
match = re.search(r"\((.*?)\)", desc)
return match.group(1) if match else ""
def cosine_similarity(vec1, vec2):
norm1 = np.linalg.norm(vec1)
norm2 = np.linalg.norm(vec2)
if norm1 == 0 or norm2 == 0:
return 0
return np.dot(vec1, vec2) / (norm1 * norm2)
def recommend_content_based(user_profile, top_n=5):
user_emb = get_user_embedding(user_profile)
scored = []
user_hobby = user_profile.get("hobby", "")
user_detail_hobby = user_profile.get("detail_hobby", "")
user_extroversion = user_profile.get("extroversion", "")
user_feeling_thinking = user_profile.get("feeling_thinking", "")
for item in items:
item_id = item["item_id"]
item_emb = item_embedding_dict[item_id]
sim = cosine_similarity(user_emb, item_emb)
# 가중치
weight = 1.0
# (1) 취미
desc_hobby = extract_hobby(item["desc"]) # 예: (운동, 헬스)
if user_hobby and user_hobby in desc_hobby:
weight *= 1.05
if user_detail_hobby and user_detail_hobby in desc_hobby:
weight *= 1.2
# (2) 성향
personality_match_count = sum(trait in item["personality"] for trait in [user_extroversion, user_feeling_thinking])
if personality_match_count == 1:
weight *= 1.15
elif personality_match_count == 2:
weight *= 1.3
final_score = sim * weight
scored.append((item, final_score))
scored.sort(key=lambda x: x[1], reverse=True)
return scored[:top_n]
#####################################
# 5) 챗봇 로직
#####################################
HF_API_KEY = os.environ.get("HF_API_KEY", "YOUR_HF_API_KEY")
API_URL = "https://api-inference.huggingface.co/models/Chanjeans/tfchatbot_2"
HEADERS = {"Authorization": f"Bearer {HF_API_KEY}"}
def chat_response(user_input, mode="emotion", max_retries=5):
if mode not in ["emotion", "rational"]:
raise HTTPException(status_code=400, detail="mode는 'emotion' 또는 'rational'이어야 합니다.")
prompt = f"<{mode}>{user_input}"
payload = {
"inputs": prompt,
"parameters": {
"max_new_tokens": 128,
"temperature": 0.7,
"top_p": 0.9,
"top_k": 50,
"repetition_penalty": 1.2,
"do_sample": True
},
"options": {"wait_for_model": True}
}
for attempt in range(max_retries):
response = requests.post(API_URL, headers=HEADERS, json=payload)
if response.status_code == 200:
try:
result = response.json()
if isinstance(result, list) and "generated_text" in result[0]:
generated_text = result[0]["generated_text"]
return generated_text.replace(prompt, "").strip()
else:
return "응답 형식이 예상과 다릅니다."
except Exception as e:
return f"JSON 파싱 오류: {e}"
elif response.status_code == 503:
# 모델 로딩 중
error_info = response.json()
estimated_time = error_info.get("estimated_time", 15)
time.sleep(min(estimated_time, 15))
else:
return f"API Error: {response.status_code}, {response.text}"
return "🚨 모델 로딩이 너무 오래 걸립니다. 잠시 후 다시 시도하세요."
#####################################
# 6) FastAPI Endpoint
#####################################
# (1) 메인 페이지
@app.get("/")
def home():
return {"message": "안녕하세요! 추천 & 챗봇 FastAPI 서버입니다."}
# (2) 추천 Endpoint
class UserProfile(BaseModel):
extroversion: str
feeling_thinking: str
hobby: str
detail_hobby: str
@app.post("/recommend")
def recommend_api(profile: UserProfile):
# "솔루션 제공" 로직과 동일
top_items = recommend_content_based(profile.dict(), top_n=5)
results = []
for (item, score) in top_items:
results.append({
"item_id": item["item_id"],
"title": item["title"],
"desc": item["desc"],
"personality": item["personality"],
"score": round(score, 4)
})
return {"recommendations": results}
# (3) 챗봇 Endpoint
class ChatRequest(BaseModel):
user_input: str
mode: str = "emotion"
@app.post("/chat")
def chat_api(req: ChatRequest):
user_text = req.user_input
mode = req.mode
reply = chat_response(user_text, mode=mode)
return {"response": reply}