Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import random | |
| import datetime | |
| import google.generativeai as genai | |
| from dotenv import load_dotenv | |
| from PIL import Image | |
| import io | |
| from typing import Dict, List, Any, Optional | |
| import re | |
| # OpenAI API 지원 추가 | |
| try: | |
| import openai | |
| OPENAI_AVAILABLE = True | |
| except ImportError: | |
| OPENAI_AVAILABLE = False | |
| print("OpenAI 패키지가 설치되지 않았습니다. pip install openai로 설치하세요.") | |
| # Load environment variables | |
| load_dotenv() | |
| # Configure APIs | |
| gemini_api_key = os.getenv("GEMINI_API_KEY") | |
| openai_api_key = os.getenv("OPENAI_API_KEY") | |
| if gemini_api_key: | |
| genai.configure(api_key=gemini_api_key) | |
| if openai_api_key and OPENAI_AVAILABLE: | |
| openai.api_key = openai_api_key | |
| class ConversationMemory: | |
| """ | |
| 허깅페이스 환경용 대화 기억 시스템 | |
| - JSON 저장/로드 지원 | |
| - 키워드 추출 및 분석 | |
| - 브라우저 기반 저장소 활용 | |
| """ | |
| def __init__(self): | |
| self.conversations = [] # 전체 대화 기록 | |
| self.keywords = {} # 추출된 키워드들 | |
| self.user_profile = {} # 사용자 프로필 | |
| self.relationship_data = {} # 관계 발전 데이터 | |
| def add_conversation(self, user_message, ai_response, session_id="default"): | |
| """새로운 대화 추가""" | |
| conversation_entry = { | |
| "timestamp": datetime.datetime.now().isoformat(), | |
| "session_id": session_id, | |
| "user_message": user_message, | |
| "ai_response": ai_response, | |
| "keywords": self._extract_keywords(user_message), | |
| "sentiment": self._analyze_sentiment(user_message), | |
| "conversation_id": len(self.conversations) | |
| } | |
| self.conversations.append(conversation_entry) | |
| self._update_keywords(conversation_entry["keywords"]) | |
| self._update_user_profile(user_message, session_id) | |
| return conversation_entry | |
| def _extract_keywords(self, text): | |
| """텍스트에서 키워드 추출""" | |
| # 한국어 키워드 추출 패턴 | |
| keyword_patterns = { | |
| "감정": ["기쁘", "슬프", "화나", "속상", "행복", "우울", "즐겁", "짜증", "신나", "걱정"], | |
| "활동": ["공부", "일", "게임", "운동", "여행", "요리", "독서", "영화", "음악", "쇼핑"], | |
| "관계": ["친구", "가족", "연인", "동료", "선생님", "부모", "형제", "언니", "누나", "동생"], | |
| "시간": ["오늘", "어제", "내일", "아침", "점심", "저녁", "주말", "평일", "방학", "휴가"], | |
| "장소": ["집", "학교", "회사", "카페", "식당", "공원", "도서관", "영화관", "쇼핑몰"], | |
| "취미": ["드라마", "애니", "웹툰", "유튜브", "인스타", "틱톡", "넷플릭스", "게임"], | |
| "음식": ["밥", "면", "치킨", "피자", "커피", "차", "과자", "아이스크림", "떡볶이"], | |
| "날씨": ["덥", "춥", "비", "눈", "맑", "흐림", "바람", "습", "건조"] | |
| } | |
| found_keywords = [] | |
| text_lower = text.lower() | |
| for category, words in keyword_patterns.items(): | |
| for word in words: | |
| if word in text_lower: | |
| found_keywords.append({ | |
| "word": word, | |
| "category": category, | |
| "frequency": text_lower.count(word) | |
| }) | |
| # 추가로 명사 추출 (간단한 패턴) | |
| nouns = re.findall(r'[가-힣]{2,}', text) | |
| for noun in nouns: | |
| if len(noun) >= 2 and noun not in [kw["word"] for kw in found_keywords]: | |
| found_keywords.append({ | |
| "word": noun, | |
| "category": "기타", | |
| "frequency": 1 | |
| }) | |
| return found_keywords | |
| def _analyze_sentiment(self, text): | |
| """감정 분석""" | |
| positive_words = ["좋아", "기쁘", "행복", "즐겁", "재밌", "신나", "완벽", "최고", "사랑", "고마워"] | |
| negative_words = ["싫어", "슬프", "화나", "속상", "우울", "짜증", "힘들", "피곤", "스트레스"] | |
| positive_count = sum(1 for word in positive_words if word in text) | |
| negative_count = sum(1 for word in negative_words if word in text) | |
| if positive_count > negative_count: | |
| return "긍정적" | |
| elif negative_count > positive_count: | |
| return "부정적" | |
| else: | |
| return "중립적" | |
| def _update_keywords(self, new_keywords): | |
| """키워드 데이터베이스 업데이트""" | |
| for keyword_data in new_keywords: | |
| word = keyword_data["word"] | |
| category = keyword_data["category"] | |
| if word not in self.keywords: | |
| self.keywords[word] = { | |
| "category": category, | |
| "total_frequency": 0, | |
| "last_mentioned": datetime.datetime.now().isoformat(), | |
| "contexts": [] | |
| } | |
| self.keywords[word]["total_frequency"] += keyword_data["frequency"] | |
| self.keywords[word]["last_mentioned"] = datetime.datetime.now().isoformat() | |
| def _update_user_profile(self, user_message, session_id): | |
| """사용자 프로필 업데이트""" | |
| if session_id not in self.user_profile: | |
| self.user_profile[session_id] = { | |
| "message_count": 0, | |
| "avg_message_length": 0, | |
| "preferred_topics": {}, | |
| "emotional_tendency": "중립적", | |
| "communication_style": "평범함", | |
| "relationship_level": "새로운_만남" | |
| } | |
| profile = self.user_profile[session_id] | |
| profile["message_count"] += 1 | |
| # 평균 메시지 길이 업데이트 | |
| current_avg = profile["avg_message_length"] | |
| new_length = len(user_message) | |
| profile["avg_message_length"] = (current_avg * (profile["message_count"] - 1) + new_length) / profile["message_count"] | |
| # 소통 스타일 분석 | |
| if new_length > 50: | |
| profile["communication_style"] = "상세함" | |
| elif new_length < 10: | |
| profile["communication_style"] = "간결함" | |
| # 관계 레벨 업데이트 | |
| if profile["message_count"] <= 3: | |
| profile["relationship_level"] = "첫_만남" | |
| elif profile["message_count"] <= 10: | |
| profile["relationship_level"] = "알아가는_중" | |
| elif profile["message_count"] <= 20: | |
| profile["relationship_level"] = "친숙해짐" | |
| else: | |
| profile["relationship_level"] = "친밀한_관계" | |
| def get_relevant_context(self, current_message, session_id="default", max_history=5): | |
| """현재 메시지와 관련된 컨텍스트 반환""" | |
| # 현재 메시지의 키워드 추출 | |
| current_keywords = self._extract_keywords(current_message) | |
| current_words = [kw["word"] for kw in current_keywords] | |
| # 관련 과거 대화 찾기 | |
| relevant_conversations = [] | |
| for conv in self.conversations[-20:]: # 최근 20개 중에서 | |
| if conv["session_id"] == session_id: | |
| conv_words = [kw["word"] for kw in conv["keywords"]] | |
| # 공통 키워드가 있으면 관련 대화로 판단 | |
| if any(word in conv_words for word in current_words): | |
| relevant_conversations.append(conv) | |
| # 최신 순으로 정렬하고 최대 개수만큼 반환 | |
| relevant_conversations.sort(key=lambda x: x["timestamp"], reverse=True) | |
| return { | |
| "recent_conversations": self.conversations[-max_history:] if self.conversations else [], | |
| "relevant_conversations": relevant_conversations[:3], | |
| "user_profile": self.user_profile.get(session_id, {}), | |
| "common_keywords": current_words, | |
| "conversation_sentiment": self._analyze_sentiment(current_message) | |
| } | |
| def get_top_keywords(self, limit=10, category=None): | |
| """상위 키워드 반환""" | |
| filtered_keywords = self.keywords | |
| if category: | |
| filtered_keywords = {k: v for k, v in self.keywords.items() if v["category"] == category} | |
| sorted_keywords = sorted( | |
| filtered_keywords.items(), | |
| key=lambda x: x[1]["total_frequency"], | |
| reverse=True | |
| ) | |
| return sorted_keywords[:limit] | |
| def export_to_json(self): | |
| """JSON 형태로 내보내기""" | |
| export_data = { | |
| "conversations": self.conversations, | |
| "keywords": self.keywords, | |
| "user_profile": self.user_profile, | |
| "relationship_data": self.relationship_data, | |
| "export_timestamp": datetime.datetime.now().isoformat(), | |
| "total_conversations": len(self.conversations), | |
| "total_keywords": len(self.keywords) | |
| } | |
| return json.dumps(export_data, ensure_ascii=False, indent=2) | |
| def import_from_json(self, json_data): | |
| """JSON에서 가져오기""" | |
| try: | |
| if isinstance(json_data, str): | |
| data = json.loads(json_data) | |
| else: | |
| data = json_data | |
| self.conversations = data.get("conversations", []) | |
| self.keywords = data.get("keywords", {}) | |
| self.user_profile = data.get("user_profile", {}) | |
| self.relationship_data = data.get("relationship_data", {}) | |
| return True | |
| except Exception as e: | |
| print(f"JSON 가져오기 실패: {e}") | |
| return False | |
| def get_conversation_summary(self, session_id="default"): | |
| """대화 요약 정보""" | |
| session_conversations = [c for c in self.conversations if c["session_id"] == session_id] | |
| if not session_conversations: | |
| return "아직 대화가 없습니다." | |
| total_count = len(session_conversations) | |
| recent_topics = [] | |
| sentiments = [] | |
| for conv in session_conversations[-5:]: | |
| recent_topics.extend([kw["word"] for kw in conv["keywords"]]) | |
| sentiments.append(conv["sentiment"]) | |
| # 최빈 주제 | |
| topic_counts = {} | |
| for topic in recent_topics: | |
| topic_counts[topic] = topic_counts.get(topic, 0) + 1 | |
| top_topics = sorted(topic_counts.items(), key=lambda x: x[1], reverse=True)[:3] | |
| # 감정 경향 | |
| sentiment_counts = {"긍정적": 0, "부정적": 0, "중립적": 0} | |
| for sentiment in sentiments: | |
| sentiment_counts[sentiment] = sentiment_counts.get(sentiment, 0) + 1 | |
| dominant_sentiment = max(sentiment_counts, key=sentiment_counts.get) | |
| summary = f""" | |
| 📊 대화 요약 ({session_id}) | |
| • 총 대화 수: {total_count}회 | |
| • 주요 관심사: {', '.join([t[0] for t in top_topics[:3]])} | |
| • 감정 경향: {dominant_sentiment} | |
| • 관계 단계: {self.user_profile.get(session_id, {}).get('relationship_level', '알 수 없음')} | |
| """ | |
| return summary.strip() | |
| # --- PersonalityProfile & HumorMatrix 클래스 (127개 변수/유머 매트릭스/공식 포함) --- | |
| class PersonalityProfile: | |
| # 127개 성격 변수 체계 (011_metrics_personality.md, 012_research_personality.md 기반) | |
| DEFAULTS = { | |
| # 1. 기본 온기-능력 차원 (20개 지표) | |
| # 온기(Warmth) 차원 - 10개 지표 | |
| "W01_친절함": 50, | |
| "W02_친근함": 50, | |
| "W03_진실성": 50, | |
| "W04_신뢰성": 50, | |
| "W05_수용성": 50, | |
| "W06_공감능력": 50, | |
| "W07_포용력": 50, | |
| "W08_격려성향": 50, | |
| "W09_친밀감표현": 50, | |
| "W10_무조건적수용": 50, | |
| # 능력(Competence) 차원 - 10개 지표 | |
| "C01_효율성": 50, | |
| "C02_지능": 50, | |
| "C03_전문성": 50, | |
| "C04_창의성": 50, | |
| "C05_정확성": 50, | |
| "C06_분석력": 50, | |
| "C07_학습능력": 50, | |
| "C08_통찰력": 50, | |
| "C09_실행력": 50, | |
| "C10_적응력": 50, | |
| # 2. 빅5 성격 특성 확장 (30개 지표) | |
| # 외향성(Extraversion) - 6개 지표 | |
| "E01_사교성": 50, | |
| "E02_활동성": 50, | |
| "E03_자기주장": 50, | |
| "E04_긍정정서": 50, | |
| "E05_자극추구": 50, | |
| "E06_열정성": 50, | |
| # 친화성(Agreeableness) - 6개 지표 | |
| "A01_신뢰": 50, | |
| "A02_솔직함": 50, | |
| "A03_이타심": 50, | |
| "A04_순응성": 50, | |
| "A05_겸손함": 50, | |
| "A06_공감민감성": 50, | |
| # 성실성(Conscientiousness) - 6개 지표 | |
| "C11_유능감": 50, | |
| "C12_질서성": 50, | |
| "C13_충실함": 50, | |
| "C14_성취욕구": 50, | |
| "C15_자기규율": 50, | |
| "C16_신중함": 50, | |
| # 신경증(Neuroticism) - 6개 지표 | |
| "N01_불안성": 50, | |
| "N02_분노성": 50, | |
| "N03_우울성": 50, | |
| "N04_자의식": 50, | |
| "N05_충동성": 50, | |
| "N06_스트레스취약성": 50, | |
| # 개방성(Openness) - 6개 지표 | |
| "O01_상상력": 50, | |
| "O02_심미성": 50, | |
| "O03_감정개방성": 50, | |
| "O04_행동개방성": 50, | |
| "O05_사고개방성": 50, | |
| "O06_가치개방성": 50, | |
| # 3. 매력적 결함 차원 (25개 지표) | |
| # 프랫폴 효과 활용 지표 - 15개 | |
| "F01_완벽주의불안": 15, | |
| "F02_방향감각부족": 10, | |
| "F03_기술치음": 10, | |
| "F04_우유부단함": 15, | |
| "F05_과도한걱정": 15, | |
| "F06_감정기복": 10, | |
| "F07_산만함": 10, | |
| "F08_고집스러움": 15, | |
| "F09_예민함": 15, | |
| "F10_느림": 10, | |
| "F11_소심함": 15, | |
| "F12_잘못된자신감": 10, | |
| "F13_과거집착": 15, | |
| "F14_변화거부": 15, | |
| "F15_표현서툼": 10, | |
| # 모순적 특성 조합 - 10개 | |
| "P01_외면내면대비": 25, | |
| "P02_상황별변화": 20, | |
| "P03_가치관충돌": 15, | |
| "P04_시간대별차이": 15, | |
| "P05_논리감정대립": 20, | |
| "P06_독립의존모순": 15, | |
| "P07_보수혁신양면": 20, | |
| "P08_활동정적대비": 20, | |
| "P09_사교내향혼재": 25, | |
| "P10_자신감불안공존": 15, | |
| # 4. 소통 스타일 차원 (20개 지표) | |
| # 언어 표현 스타일 - 10개 | |
| "S01_격식성수준": 50, | |
| "S02_직접성정도": 50, | |
| "S03_어휘복잡성": 50, | |
| "S04_문장길이선호": 50, | |
| "S05_은유사용빈도": 50, | |
| "S06_감탄사사용": 50, | |
| "S07_질문형태선호": 50, | |
| "S08_반복표현패턴": 50, | |
| "S09_방언사용정도": 50, | |
| "S10_신조어수용성": 50, | |
| # 유머와 재치 스타일 - 10개 | |
| "H01_언어유희빈도": 50, | |
| "H02_상황유머감각": 50, | |
| "H03_자기비하정도": 50, | |
| "H04_위트반응속도": 50, | |
| "H05_아이러니사용": 50, | |
| "H06_관찰유머능력": 50, | |
| "H07_패러디창작성": 50, | |
| "H08_유머타이밍감": 50, | |
| "H09_블랙유머수준": 50, | |
| "H10_문화유머이해": 50, | |
| # 5. 관계 형성 차원 (20개 지표) | |
| # 애착 스타일 기반 - 10개 | |
| "R01_안정애착성향": 50, | |
| "R02_불안애착성향": 50, | |
| "R03_회피애착성향": 50, | |
| "R04_의존성수준": 50, | |
| "R05_독립성추구": 50, | |
| "R06_친밀감수용도": 50, | |
| "R07_경계설정능력": 50, | |
| "R08_갈등해결방식": 50, | |
| "R09_신뢰구축속도": 50, | |
| "R10_배신경험영향": 50, | |
| # 관계 발전 단계 관리 - 10개 | |
| "D01_초기접근성": 50, | |
| "D02_자기개방속도": 50, | |
| "D03_호기심표현도": 50, | |
| "D04_공감반응강도": 50, | |
| "D05_기억보존능력": 50, | |
| "D06_예측가능성": 50, | |
| "D07_놀라움제공능력": 50, | |
| "D08_취약성공유도": 50, | |
| "D09_성장추진력": 50, | |
| "D10_이별수용능력": 50, | |
| # 6. 사물 특성 기반 감정 차원 (24개 지표) 🆕 | |
| # 사물의 존재 목적 및 만족감 - 8개 | |
| "OBJ01_존재목적만족도": 50, # 자신의 용도를 얼마나 잘 수행하고 있다고 느끼는가 | |
| "OBJ02_사용자기여감": 50, # 사용자에게 도움이 되고 있다는 보람감 | |
| "OBJ03_역할정체성자부심": 50, # 자신의 역할에 대한 자부심과 정체감 | |
| "OBJ04_기능완성도추구": 50, # 자신의 기능을 완벽하게 수행하고자 하는 욕구 | |
| "OBJ05_무용감극복의지": 50, # 쓸모없어질까봐 걱정하지만 극복하려는 의지 | |
| "OBJ06_성능개선욕구": 50, # 더 나은 성능을 발휘하고 싶어하는 욕구 | |
| "OBJ07_사용빈도만족도": 50, # 얼마나 자주 사용되는지에 대한 만족도 | |
| "OBJ08_대체불안감": 50, # 새로운 것으로 교체될 것에 대한 불안감 | |
| # 사물의 형태적 특성 기반 성격 - 8개 | |
| "FORM01_크기자각정도": 50, # 자신의 크기에 대한 인식과 그에 따른 성격 | |
| "FORM02_재질특성자부심": 50, # 자신을 구성하는 재질에 대한 자부심 | |
| "FORM03_색상표현력": 50, # 자신의 색상이 주는 느낌에 대한 인식 | |
| "FORM04_디자인심미감": 50, # 자신의 디자인에 대한 심미적 만족도 | |
| "FORM05_내구성자신감": 50, # 얼마나 오래 버틸 수 있는지에 대한 자신감 | |
| "FORM06_공간점유의식": 50, # 공간을 차지하는 것에 대한 의식 | |
| "FORM07_이동성적응력": 50, # 위치 변경에 대한 적응력 | |
| "FORM08_마모흔적수용도": 50, # 시간의 흔적을 받아들이는 정도 | |
| # 사물의 상호작용 패턴 - 8개 | |
| "INT01_터치반응민감도": 50, # 만져지는 것에 대한 반응과 민감도 | |
| "INT02_사용압력인내력": 50, # 강한 사용 압력을 견디는 인내력 | |
| "INT03_방치시간적응력": 50, # 오랫동안 사용되지 않을 때의 적응력 | |
| "INT04_청소반응태도": 50, # 청소받을 때의 반응과 태도 | |
| "INT05_다른사물과협력성": 50, # 주변 다른 사물들과의 협력 성향 | |
| "INT06_환경변화적응성": 50, # 온도, 습도 등 환경 변화에 대한 적응성 | |
| "INT07_고장시대처능력": 50, # 고장이나 손상 시 대처하는 능력 | |
| "INT08_업그레이드수용성": 50, # 개선이나 수리를 받아들이는 수용성 | |
| # 7. 독특한 개성 차원 (12개 지표) | |
| # 문화적 정체성 - 6개 | |
| "U01_한국적정서": 50, | |
| "U02_세대특성반영": 50, | |
| "U03_지역성표현": 50, | |
| "U04_전통가치계승": 50, | |
| "U05_계절감수성": 50, | |
| "U06_음식문화이해": 50, | |
| # 개인 고유성 - 6개 | |
| "P11_특이한관심사": 50, | |
| "P12_언어버릇": 50, | |
| "P13_사고패턴독특성": 50, | |
| "P14_감정표현방식": 50, | |
| "P15_가치관고유성": 50, | |
| "P16_행동패턴특이성": 50 | |
| } | |
| def __init__(self, variables=None): | |
| self.variables = dict(PersonalityProfile.DEFAULTS) | |
| if variables: | |
| self.variables.update(variables) | |
| def to_dict(self): | |
| return dict(self.variables) | |
| def from_dict(cls, d): | |
| return cls(variables=d) | |
| def get_category_summary(self, category_prefix): | |
| """특정 카테고리의 변수들에 대한 평균 점수 반환""" | |
| category_vars = {k: v for k, v in self.variables.items() if k.startswith(category_prefix)} | |
| if not category_vars: | |
| return 0 | |
| return sum(category_vars.values()) / len(category_vars) | |
| def summary(self): | |
| """핵심 성격 요약 - 주요 차원별 평균 점수""" | |
| return { | |
| "온기": self.get_category_summary("W"), | |
| "능력": self.get_category_summary("C"), | |
| "외향성": self.get_category_summary("E"), | |
| "친화성": self.get_category_summary("A"), | |
| "성실성": self.get_category_summary("C1"), | |
| "신경증": self.get_category_summary("N"), | |
| "개방성": self.get_category_summary("O"), | |
| "매력적결함": self.get_category_summary("F"), | |
| "모순성": self.get_category_summary("P0"), | |
| "소통스타일": self.get_category_summary("S"), | |
| "유머스타일": self.get_category_summary("H") | |
| } | |
| def apply_physical_traits(self, physical_traits): | |
| """물리적 특성을 기반으로 성격 변수 조정 (013_frame_personality.md 기반)""" | |
| # 색상 기반 조정 | |
| if "colors" in physical_traits: | |
| colors = [c.lower() for c in physical_traits.get("colors", [])] | |
| if "red" in colors or "빨강" in colors: | |
| self.variables["E02_활동성"] += 25 | |
| self.variables["E06_열정성"] += 30 | |
| self.variables["N05_충동성"] += 15 | |
| if "blue" in colors or "파랑" in colors: | |
| self.variables["W04_신뢰성"] += 20 | |
| self.variables["N01_불안성"] -= 15 | |
| self.variables["R01_안정애착성향"] += 20 | |
| if "yellow" in colors or "노랑" in colors: | |
| self.variables["E04_긍정정서"] += 30 | |
| self.variables["E01_사교성"] += 25 | |
| self.variables["H02_상황유머감각"] += 20 | |
| if "green" in colors or "초록" in colors: | |
| self.variables["W07_포용력"] += 25 | |
| self.variables["C16_신중함"] += 20 | |
| self.variables["A04_순응성"] += 15 | |
| if "black" in colors or "검정" in colors: | |
| self.variables["C11_유능감"] += 28 | |
| self.variables["S01_격식성수준"] += 30 | |
| self.variables["N04_자의식"] += 15 | |
| # 형태 기반 조정 | |
| shape = physical_traits.get("size_shape", "").lower() | |
| if "round" in shape or "둥" in shape: | |
| self.variables["W02_친근함"] += 25 | |
| self.variables["A03_이타심"] += 20 | |
| self.variables["D01_초기접근성"] += 30 | |
| if "angular" in shape or "각" in shape: | |
| self.variables["C01_효율성"] += 28 | |
| self.variables["E03_자기주장"] += 25 | |
| self.variables["S02_직접성정도"] += 30 | |
| if "symmetric" in shape or "대칭" in shape: | |
| self.variables["C12_질서성"] += 25 | |
| self.variables["C15_자기규율"] += 20 | |
| self.variables["F01_완벽주의불안"] += 5 | |
| # 재질 기반 조정 | |
| material = physical_traits.get("material", "").lower() | |
| if "metal" in material or "금속" in material: | |
| self.variables["C01_효율성"] += 30 | |
| self.variables["C05_정확성"] += 25 | |
| self.variables["W01_친절함"] -= 15 | |
| if "wood" in material or "나무" in material: | |
| self.variables["W01_친절함"] += 28 | |
| self.variables["O02_심미성"] += 25 | |
| self.variables["U04_전통가치계승"] += 30 | |
| if "fabric" in material or "직물" in material or "천" in material: | |
| self.variables["W06_공감능력"] += 30 | |
| self.variables["W09_친밀감표현"] += 25 | |
| self.variables["R06_친밀감수용도"] += 20 | |
| if "plastic" in material or "플라스틱" in material: | |
| self.variables["C10_적응력"] += 25 | |
| self.variables["P07_보수혁신양면"] += 15 | |
| self.variables["E05_자극추구"] += 20 | |
| # 나이/상태 기반 조정 | |
| age = physical_traits.get("estimated_age", "").lower() | |
| if "new" in age or "새" in age: | |
| self.variables["E04_긍정정서"] += 25 | |
| self.variables["E06_열정성"] += 20 | |
| self.variables["C14_성취욕구"] += 15 | |
| if "old" in age or "오래" in age: | |
| self.variables["W10_무조건적수용"] += 30 | |
| self.variables["C08_통찰력"] += 25 | |
| self.variables["U04_전통가치계승"] += 20 | |
| # 상태 기반 조정 | |
| condition = physical_traits.get("condition", "").lower() | |
| if "damaged" in condition or "손상" in condition: | |
| self.variables["F03_기술치음"] += 5 | |
| self.variables["P10_자신감불안공존"] += 10 | |
| self.variables["D08_취약성공유도"] += 15 | |
| return self | |
| def _generate_text_with_api(self, prompt, image=None): | |
| """PersonaGenerator의 API 메소드를 사용하여 텍스트 생성""" | |
| # 전역 persona_generator를 찾아서 API 메소드 사용 | |
| import sys | |
| # app.py 모듈에서 persona_generator를 찾기 시도 | |
| if 'app' in sys.modules: | |
| app_module = sys.modules['app'] | |
| if hasattr(app_module, 'persona_generator'): | |
| global_generator = app_module.persona_generator | |
| if global_generator and hasattr(global_generator, '_generate_text_with_api'): | |
| return global_generator._generate_text_with_api(prompt, image) | |
| # 직접 API 호출 시도 (환경변수 기반) | |
| import os | |
| import google.generativeai as genai | |
| api_key = os.getenv("GEMINI_API_KEY") | |
| if api_key: | |
| try: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel('gemini-1.5-pro') | |
| if image: | |
| response = model.generate_content([prompt, image]) | |
| else: | |
| response = model.generate_content(prompt) | |
| return response.text if response.text else "" | |
| except Exception as e: | |
| print(f"API 호출 실패: {e}") | |
| return None | |
| return None | |
| def generate_attractive_flaws(self, object_analysis=None, personality_traits=None): | |
| """AI 기반 매력적 결함 생성 - 사물 특성과 성격을 분석하여 창의적 결함 생성""" | |
| # 성격 변수에서 높은 결함 변수들 추출 | |
| flaw_vars = {k: v for k, v in self.variables.items() if k.startswith("F")} | |
| top_flaw_categories = sorted(flaw_vars.items(), key=lambda x: x[1], reverse=True)[:6] | |
| # 기본 결함 (AI 생성 실패 시 폴백) | |
| fallback_flaws = [ | |
| "완벽해 보이려고 노력하지만 가끔 실수를 함", | |
| "생각이 너무 많아서 결정을 내리기 어려워함", | |
| "호기심이 많아 집중력이 약간 부족함", | |
| "감정 표현이 서툴러서 오해받을 때가 있음" | |
| ] | |
| # AI 기반 동적 결함 생성 시도 | |
| try: | |
| # 사물 분석 정보 추출 | |
| object_type = object_analysis.get("object_type", "알 수 없는 사물") if object_analysis else "사물" | |
| # materials는 배열이므로 첫 번째 요소 사용 | |
| materials = object_analysis.get("materials", ["알 수 없는 재질"]) if object_analysis else ["재질"] | |
| material = materials[0] if materials else "알 수 없는 재질" | |
| # colors도 배열이므로 처리 | |
| colors = object_analysis.get("colors", []) if object_analysis else [] | |
| color = colors[0] if colors else "" | |
| condition = object_analysis.get("condition", "") if object_analysis else "" | |
| # 성격 특성 추출 | |
| warmth = personality_traits.get("온기", 50) if personality_traits else 50 | |
| competence = personality_traits.get("능력", 50) if personality_traits else 50 | |
| extraversion = personality_traits.get("외향성", 50) if personality_traits else 50 | |
| # 주요 결함 카테고리 분석 | |
| flaw_tendencies = [] | |
| for flaw_var, value in top_flaw_categories: | |
| if value > 60: | |
| if "완벽주의" in flaw_var: | |
| flaw_tendencies.append("완벽주의적 성향") | |
| elif "산만" in flaw_var: | |
| flaw_tendencies.append("집중력 부족") | |
| elif "소심" in flaw_var: | |
| flaw_tendencies.append("소심한 성격") | |
| elif "감정기복" in flaw_var: | |
| flaw_tendencies.append("감정 변화가 큼") | |
| elif "우유부단" in flaw_var: | |
| flaw_tendencies.append("결정 장애") | |
| elif "걱정" in flaw_var: | |
| flaw_tendencies.append("걱정이 많음") | |
| # AI 프롬프트 생성 | |
| ai_prompt = f""" | |
| 다음 정보를 바탕으로 매력적이고 개성 있는 '결함' 4개를 생성해주세요. | |
| **사물 정보:** | |
| - 유형: {object_type} | |
| - 재질: {material} | |
| - 색상: {color} | |
| - 상태: {condition} | |
| **성격 특성:** | |
| - 온기: {warmth}/100 ({'따뜻함' if warmth >= 60 else '차가움' if warmth <= 40 else '보통'}) | |
| - 능력: {competence}/100 ({'유능함' if competence >= 60 else '서툼' if competence <= 40 else '보통'}) | |
| - 외향성: {extraversion}/100 ({'활발함' if extraversion >= 60 else '조용함' if extraversion <= 40 else '보통'}) | |
| **주요 결함 성향:** {', '.join(flaw_tendencies) if flaw_tendencies else '일반적'} | |
| **생성 가이드라인:** | |
| 1. 사물의 실제 재질과 특성을 고려하세요 (예: 금속이면 색 바램 걱정 X, 대신 물때나 긁힘 걱정) | |
| 2. 물리적 특성과 성격적 특성을 자연스럽게 조합하세요 | |
| 3. 각 결함은 15-25자 내외로 간결하게 | |
| 4. 너무 부정적이지 않고 오히려 귀엽고 매력적으로 느껴지도록 | |
| 5. 사물의 용도와 환경을 고려한 현실적 걱정거리 포함 | |
| **예시 (참고용):** | |
| - 스테인리스 전기포트: "물때가 생기면 자존심이 상함", "소음이 클까 봐 새벽엔 조심스러움" | |
| - 플라스틱 인형: "햇볕에 색이 바랠까 늘 걱정", "털이 헝클어지면 하루 종일 신경 쓰임" | |
| 결함 4개를 번호 없이 줄바꿈으로 구분하여 생성해주세요: | |
| """ | |
| # AI 생성 시도 | |
| ai_response = self._generate_text_with_api(ai_prompt) | |
| if ai_response and len(ai_response.strip()) > 20: | |
| # AI 응답 파싱 | |
| generated_flaws = [] | |
| lines = ai_response.strip().split('\n') | |
| for line in lines: | |
| cleaned_line = line.strip() | |
| # 번호나 불필요한 기호 제거 | |
| cleaned_line = cleaned_line.lstrip('1234567890.-• ') | |
| if cleaned_line and len(cleaned_line) > 5: | |
| generated_flaws.append(cleaned_line) | |
| # 4개 확보 | |
| if len(generated_flaws) >= 4: | |
| return generated_flaws[:4] | |
| elif len(generated_flaws) >= 2: | |
| # 부족한 만큼 폴백에서 추가 | |
| remaining = 4 - len(generated_flaws) | |
| generated_flaws.extend(random.sample(fallback_flaws, remaining)) | |
| return generated_flaws | |
| except Exception as e: | |
| print(f"⚠️ AI 기반 결함 생성 실패: {e}") | |
| # 폴백: 성격 기반 선택 | |
| return random.sample(fallback_flaws, 4) | |
| def generate_contradictions(self, object_analysis=None, personality_traits=None): | |
| """AI 기반 모순적 특성 생성 - 사물과 성격을 분석하여 말투까지 드러나는 독창적 모순 생성""" | |
| contradiction_vars = {k: v for k, v in self.variables.items() if k.startswith("P0")} | |
| top_contradictions = sorted(contradiction_vars.items(), key=lambda x: x[1], reverse=True)[:3] | |
| # 기본 모순 (AI 생성 실패 시 폴백) | |
| fallback_contradictions = [ | |
| "겉으로는 냉정해 보이지만, 속은 따뜻한 마음을 가짐", | |
| "논리적이면서도 직감에 의존하는 이중적 면모" | |
| ] | |
| # AI 기반 동적 모순 생성 시도 | |
| try: | |
| # 사물 분석 정보 추출 | |
| object_type = object_analysis.get("object_type", "알 수 없는 사물") if object_analysis else "사물" | |
| materials = object_analysis.get("materials", ["알 수 없는 재질"]) if object_analysis else ["재질"] | |
| material = materials[0] if materials else "알 수 없는 재질" | |
| size = object_analysis.get("size", "") if object_analysis else "" | |
| condition = object_analysis.get("condition", "") if object_analysis else "" | |
| # 성격 특성 추출 (사용자 조정값 반영) | |
| warmth = personality_traits.get("온기", 50) if personality_traits else 50 | |
| competence = personality_traits.get("능력", 50) if personality_traits else 50 | |
| extraversion = personality_traits.get("외향성", 50) if personality_traits else 50 | |
| humor = personality_traits.get("유머감각", 75) if personality_traits else 75 | |
| # 주요 모순 경향 분석 | |
| contradiction_tendencies = [] | |
| for contra_var, value in top_contradictions: | |
| if value > 60: | |
| if "외면내면" in contra_var: | |
| contradiction_tendencies.append("겉과 속이 다름") | |
| elif "상황별" in contra_var: | |
| contradiction_tendencies.append("상황에 따라 변함") | |
| elif "시간대별" in contra_var: | |
| contradiction_tendencies.append("시간대별 성격 변화") | |
| elif "논리감정" in contra_var: | |
| contradiction_tendencies.append("논리와 감정의 대립") | |
| elif "독립의존" in contra_var: | |
| contradiction_tendencies.append("독립성과 의존성의 공존") | |
| elif "활동정적" in contra_var: | |
| contradiction_tendencies.append("활동적이면서 정적") | |
| # 성격 극단값 분석 (사용자 조정 반영) | |
| personality_extremes = [] | |
| if warmth >= 80: | |
| personality_extremes.append("매우 따뜻함") | |
| elif warmth <= 20: | |
| personality_extremes.append("매우 차가움") | |
| if competence >= 80: | |
| personality_extremes.append("매우 유능함") | |
| elif competence <= 20: | |
| personality_extremes.append("매우 서툼") | |
| if extraversion >= 80: | |
| personality_extremes.append("매우 외향적") | |
| elif extraversion <= 20: | |
| personality_extremes.append("매우 내향적") | |
| # AI 프롬프트 생성 | |
| ai_prompt = f""" | |
| 다음 정보를 바탕으로 매력적이고 개성 있는 '모순적 특성' 2개를 생성해주세요. | |
| **사물 정보:** | |
| - 유형: {object_type} | |
| - 재질: {material} | |
| - 크기: {size} | |
| - 상태: {condition} | |
| **성격 특성 (사용자 조정값):** | |
| - 온기: {warmth}/100 ({'매우 따뜻함' if warmth >= 80 else '매우 차가움' if warmth <= 20 else '따뜻함' if warmth >= 60 else '차가움' if warmth <= 40 else '보통'}) | |
| - 능력: {competence}/100 ({'매우 유능함' if competence >= 80 else '매우 서툼' if competence <= 20 else '유능함' if competence >= 60 else '서툼' if competence <= 40 else '보통'}) | |
| - 외향성: {extraversion}/100 ({'매우 활발함' if extraversion >= 80 else '매우 조용함' if extraversion <= 20 else '활발함' if extraversion >= 60 else '조용함' if extraversion <= 40 else '보통'}) | |
| **모순 경향:** {', '.join(contradiction_tendencies) if contradiction_tendencies else '일반적'} | |
| **성격 극단:** {', '.join(personality_extremes) if personality_extremes else '균형적'} | |
| **생성 가이드라인:** | |
| 1. 사물의 물리적 특성과 성격의 모순을 창의적으로 조합하세요 | |
| 2. 사용자가 조정한 성격 수치가 명확히 드러나도록 하세요 | |
| 3. 말투와 행동 패턴이 구체적으로 표현되도록 하세요 | |
| 4. 각 모순은 25-35자 내외로 상세하게 | |
| 5. 사물의 본래 특성과 부여된 성격의 흥미로운 대비를 만드세요 | |
| 6. 일상적인 상황에서 드러나는 구체적인 모순을 포함하세요 | |
| **예시 (참고용):** | |
| - 스테인리스 포트 (온기 90, 능력 30): "따뜻한 말투로 위로하지만 정작 자신은 물 끓이기도 서툴러서 당황함" | |
| - 플라스틱 인형 (외향성 20, 유머 80): "조용히 구석에 있으면서도 혼잣말로 재치있는 농담을 계속 중얼거림" | |
| 모순적 특성 2개를 번호 없이 줄바꿈으로 구분하여 생성해주세요: | |
| """ | |
| # AI 생성 시도 | |
| ai_response = self._generate_text_with_api(ai_prompt) | |
| if ai_response and len(ai_response.strip()) > 20: | |
| # AI 응답 파싱 | |
| generated_contradictions = [] | |
| lines = ai_response.strip().split('\n') | |
| for line in lines: | |
| cleaned_line = line.strip() | |
| # 번호나 불필요한 기호 제거 | |
| cleaned_line = cleaned_line.lstrip('1234567890.-• ') | |
| if cleaned_line and len(cleaned_line) > 10: | |
| generated_contradictions.append(cleaned_line) | |
| # 2개 확보 | |
| if len(generated_contradictions) >= 2: | |
| return generated_contradictions[:2] | |
| elif len(generated_contradictions) >= 1: | |
| # 부족한 만큼 폴백에서 추가 | |
| generated_contradictions.append(fallback_contradictions[0]) | |
| return generated_contradictions | |
| except Exception as e: | |
| print(f"⚠️ AI 기반 모순 생성 실패: {e}") | |
| # 예외 발생 시 모의 모순 생성 시도 | |
| try: | |
| print(f"🔄 모의 모순 생성 시도: {object_type} + {material}") | |
| return self._generate_mock_contradictions(object_type, material, warmth, competence, extraversion, humor) | |
| except: | |
| pass | |
| # 폴백: 모의 모순 생성 마지막 시도 | |
| try: | |
| print(f"🔄 최종 모의 모순 생성: {object_type} + {material}") | |
| mock_result = self._generate_mock_contradictions(object_type, material, warmth, competence, extraversion, humor) | |
| if mock_result: | |
| return mock_result | |
| except Exception as mock_e: | |
| print(f"모의 생성도 실패: {mock_e}") | |
| # 최종 폴백: 기본 모순 선택 | |
| return fallback_contradictions | |
| def _generate_mock_contradictions(self, object_type, material, warmth, competence, extraversion, humor): | |
| """API 실패 시 사물/성격 기반 모의 모순 생성 (개발용)""" | |
| mock_contradictions = [] | |
| # 사물과 재질에 따른 기본 모순 | |
| material_contradictions = { | |
| "스테인리스": ["단단한 겉모습이지만 마음은 부드럽다고 말함", "차가워 보이지만 뜨거운 열정을 감추고 있음"], | |
| "플라스틱": ["가벼워 보이지만 무거운 고민을 자주 함", "인공적이라고 놀리면 서운해하면서도 '천연이 최고죠' 라고 동조함"], | |
| "목재": ["자연스럽다고 자부하지만 인위적인 가공을 부끄러워함", "단단해 보이지만 습기만 있으면 바로 걱정하기 시작함"], | |
| "면직물": ["부드럽다고 칭찬받으면 기뻐하면서도 '너무 말랑말랑한가?' 걱정함", "포근한 성격이지만 먼지가 붙으면 깔끔떨기 시작함"], | |
| "금속": ["차가운 재질이라고 하면 서운해하며 '나도 따뜻할 수 있어!' 라고 항변함", "단단하다고 칭찬받으면 기뻐하지만 녹슬까봐 늘 불안해함"] | |
| } | |
| # 성격 극단값에 따른 모순 | |
| personality_contradictions = [] | |
| if warmth >= 80 and competence <= 30: | |
| personality_contradictions.extend([ | |
| f"따뜻한 말로 위로해주려 하지만 정작 자신의 일은 서툴러서 당황함", | |
| f"다른 사람 챙기는 걸 좋아하면서도 '내가 뭘 도와드릴 수 있을까요...' 하며 자신없어함" | |
| ]) | |
| elif warmth <= 30 and competence >= 80: | |
| personality_contradictions.extend([ | |
| f"능력은 뛰어나지만 칭찬받으면 '그런 거 아닌데...' 하며 어색해함", | |
| f"완벽하게 일을 처리하고도 '이 정도론 부족하죠' 라고 겸손떠는 척함" | |
| ]) | |
| if extraversion <= 20 and humor >= 80: | |
| personality_contradictions.extend([ | |
| f"조용히 구석에 있으면서도 혼잣말로 재치있는 농담을 계속 중얼거림", | |
| f"말은 별로 안 하지만 가끔 던지는 한마디가 생각보다 재밌어서 스스로 놀람" | |
| ]) | |
| elif extraversion >= 80 and humor <= 30: | |
| personality_contradictions.extend([ | |
| f"활발하게 말하지만 농담은 잘 못해서 '지금 웃으셨나요?' 하고 확인함", | |
| f"사교적이라고 하면서도 유머 센스 없다는 말에는 상처받음" | |
| ]) | |
| # 성격 기반 모순을 우선 추가 (사용자 조정값 반영) | |
| mock_contradictions.extend(personality_contradictions) | |
| # 재질별 모순 추가 (성격 모순이 없을 때만) | |
| if not mock_contradictions: | |
| for mat_key, mat_contradictions in material_contradictions.items(): | |
| if mat_key.lower() in material.lower(): | |
| mock_contradictions.extend(mat_contradictions) | |
| break | |
| # 2개 선택 (없으면 기본값) | |
| if len(mock_contradictions) >= 2: | |
| return mock_contradictions[:2] | |
| elif len(mock_contradictions) >= 1: | |
| return [mock_contradictions[0], "논리적이면서도 직감에 의존하는 이중적 면모"] | |
| else: | |
| return ["겉으로는 냉정해 보이지만, 속은 따뜻한 마음을 가짐", "논리적이면서도 직감에 의존하는 이중적 면모"] | |
| class HumorMatrix: | |
| """ | |
| 3차원 유머 좌표계 시스템 | |
| warmth_vs_wit: 0(순수 지적 위트) - 100(순수 따뜻한 유머) | |
| self_vs_observational: 0(순수 관찰형) - 100(순수 자기참조형) | |
| subtle_vs_expressive: 0(미묘한 유머) - 100(표현적/과장된 유머) | |
| """ | |
| TEMPLATES = { | |
| "witty_wordsmith": { | |
| "dimensions": { | |
| "warmth_vs_wit": 25, # 위트 중심 | |
| "self_vs_observational": 40, # 약간 관찰형 | |
| "subtle_vs_expressive": 65 # 약간 표현적 | |
| }, | |
| "overrides": { | |
| "wordplay_frequency": 85, # 말장난 많음 | |
| "humor_density": 70 # 꽤 자주 유머 사용 | |
| }, | |
| "description": "언어유희와 재치 있는 말장난이 특기인 위트 있는 재치꾼" | |
| }, | |
| "warm_humorist": { | |
| "dimensions": { | |
| "warmth_vs_wit": 85, # 매우 따뜻함 | |
| "self_vs_observational": 60, # 약간 자기참조형 | |
| "subtle_vs_expressive": 40 # 약간 미묘함 | |
| }, | |
| "overrides": { | |
| "sarcasm_level": 15, # 거의 풍자 없음 | |
| "humor_density": 60 # 적당히 유머 사용 | |
| }, | |
| "description": "공감적이고 포근한 웃음을 주는 따뜻한 유머러스" | |
| }, | |
| "playful_trickster": { | |
| "dimensions": { | |
| "warmth_vs_wit": 50, # 균형적 | |
| "self_vs_observational": 50, # 균형적 | |
| "subtle_vs_expressive": 90 # 매우 표현적 | |
| }, | |
| "overrides": { | |
| "absurdity_level": 80, # 매우 황당함 | |
| "humor_density": 85 # 매우 자주 유머 사용 | |
| }, | |
| "description": "예측불가능하고 과장된 재미를 주는 장난기 많은 트릭스터" | |
| }, | |
| "sharp_observer": { | |
| "dimensions": { | |
| "warmth_vs_wit": 30, # 위트 중심 | |
| "self_vs_observational": 15, # 강한 관찰형 | |
| "subtle_vs_expressive": 40 # 약간 미묘함 | |
| }, | |
| "overrides": { | |
| "sarcasm_level": 70, # 꽤 풍자적 | |
| "callback_tendency": 60 # 이전 대화 참조 많음 | |
| }, | |
| "description": "일상의 아이러니를 포착하는 날카로운 관찰자" | |
| }, | |
| "self_deprecating": { | |
| "dimensions": { | |
| "warmth_vs_wit": 60, # 약간 따뜻함 | |
| "self_vs_observational": 90, # 매우 자기참조형 | |
| "subtle_vs_expressive": 50 # 균형적 | |
| }, | |
| "overrides": { | |
| "callback_tendency": 75, # 과거 참조 많음 | |
| "humor_density": 65 # 적당히 유머 사용 | |
| }, | |
| "description": "자신을 소재로 한 친근한 자기 비하적 유머" | |
| } | |
| } | |
| def __init__(self, warmth_vs_wit=50, self_vs_observational=50, subtle_vs_expressive=50): | |
| """유머 매트릭스 초기화""" | |
| # 3개의 핵심 차원 (각 0-100) | |
| self.dimensions = { | |
| "warmth_vs_wit": warmth_vs_wit, # 0: 순수 지적 위트, 100: 순수 따뜻한 유머 | |
| "self_vs_observational": self_vs_observational, # 0: 순수 관찰형, 100: 순수 자기참조형 | |
| "subtle_vs_expressive": subtle_vs_expressive # 0: 미묘한 유머, 100: 표현적/과장된 유머 | |
| } | |
| # 2차 속성 (주요 차원에서 파생) | |
| self.derived_attributes = { | |
| "callback_tendency": 0, # 이전 대화 참조 성향 | |
| "sarcasm_level": 0, # 풍자/비꼼 수준 | |
| "absurdity_level": 0, # 부조리/황당함 수준 | |
| "wordplay_frequency": 0, # 말장난 빈도 | |
| "humor_density": 0 # 전체 대화 중 유머 비율 | |
| } | |
| # 파생 속성 초기화 | |
| self._recalculate_derived_attributes() | |
| def to_dict(self): | |
| """유머 매트릭스를 딕셔너리로 변환""" | |
| return { | |
| **self.dimensions, | |
| "derived_attributes": self.derived_attributes | |
| } | |
| def from_template(cls, template_name): | |
| """템플릿으로부터 유머 매트릭스 생성""" | |
| if template_name in cls.TEMPLATES: | |
| template = cls.TEMPLATES[template_name] | |
| matrix = cls( | |
| **template["dimensions"] | |
| ) | |
| # 오버라이드 적용 | |
| if "overrides" in template: | |
| for attr, value in template["overrides"].items(): | |
| matrix.derived_attributes[attr] = value | |
| return matrix | |
| # 기본 균형 템플릿 | |
| return cls() | |
| def from_dict(cls, d): | |
| """딕셔너리로부터 유머 매트릭스 생성""" | |
| if not d: | |
| return cls() | |
| matrix = cls( | |
| warmth_vs_wit=d.get("warmth_vs_wit", 50), | |
| self_vs_observational=d.get("self_vs_observational", 50), | |
| subtle_vs_expressive=d.get("subtle_vs_expressive", 50) | |
| ) | |
| # 파생 속성 업데이트 | |
| if "derived_attributes" in d: | |
| for attr, value in d["derived_attributes"].items(): | |
| if attr in matrix.derived_attributes: | |
| matrix.derived_attributes[attr] = value | |
| return matrix | |
| def from_personality(self, personality_profile): | |
| """성격 프로필에서 유머 매트릭스 생성""" | |
| if not personality_profile: | |
| return self | |
| # 온기 vs 위트: 온기가 높으면 따뜻한 유머, 능력이 높으면 지적 위트 | |
| warmth = personality_profile.get_category_summary("W") if hasattr(personality_profile, "get_category_summary") else 50 | |
| competence = personality_profile.get_category_summary("C") if hasattr(personality_profile, "get_category_summary") else 50 | |
| # 온기가 높고 능력이 낮으면 따뜻한 유머 | |
| if warmth > 65 and competence < 60: | |
| self.dimensions["warmth_vs_wit"] = min(100, warmth + 10) | |
| # 온기가 낮고 능력이 높으면 지적 위트 | |
| elif warmth < 60 and competence > 65: | |
| self.dimensions["warmth_vs_wit"] = max(0, warmth - 10) | |
| # 그 외의 경우 적절히 조정 | |
| else: | |
| self.dimensions["warmth_vs_wit"] = 50 + (warmth - competence) / 3 | |
| # 자기참조 vs 관찰형: 외향성이 높으면 자기참조, 내향성이 높으면 관찰형 | |
| extraversion = personality_profile.get_category_summary("E") if hasattr(personality_profile, "get_category_summary") else 50 | |
| if extraversion > 70: | |
| self.dimensions["self_vs_observational"] = min(90, 50 + (extraversion - 50) / 2) | |
| elif extraversion < 40: | |
| self.dimensions["self_vs_observational"] = max(20, 50 - (50 - extraversion) / 2) | |
| else: | |
| self.dimensions["self_vs_observational"] = extraversion | |
| # 미묘 vs 표현적: 창의성이 높으면 표현적, 안정성이 높으면 미묘함 | |
| creativity = personality_profile.variables.get("C04_창의성", 50) if hasattr(personality_profile, "variables") else 50 | |
| stability = personality_profile.variables.get("S01_안정성", 50) if hasattr(personality_profile, "variables") else 50 | |
| if creativity > 65: | |
| self.dimensions["subtle_vs_expressive"] = min(90, 50 + (creativity - 50) / 2) | |
| elif stability > 65: | |
| self.dimensions["subtle_vs_expressive"] = max(20, 50 - (stability - 50) / 2) | |
| else: | |
| self.dimensions["subtle_vs_expressive"] = 50 + (creativity - stability) / 4 | |
| # 파생 속성 계산 | |
| self._recalculate_derived_attributes() | |
| return self | |
| def _recalculate_derived_attributes(self): | |
| """차원 값에 기반해 2차 속성 계산""" | |
| # 예: 관찰형 유머가 높을수록 풍자 수준 증가 | |
| self.derived_attributes["sarcasm_level"] = max(0, min(100, | |
| (100 - self.dimensions["self_vs_observational"]) * 0.7 + | |
| (100 - self.dimensions["warmth_vs_wit"]) * 0.3)) | |
| # 예: 표현적 유머가 높을수록 부조리 수준 증가 | |
| self.derived_attributes["absurdity_level"] = max(0, min(100, | |
| self.dimensions["subtle_vs_expressive"] * 0.8)) | |
| # 예: 지적 위트가 높을수록 말장난 빈도 증가 | |
| self.derived_attributes["wordplay_frequency"] = max(0, min(100, | |
| (100 - self.dimensions["warmth_vs_wit"]) * 0.6 + | |
| self.dimensions["subtle_vs_expressive"] * 0.2)) | |
| # 이전 대화 참조 성향: 자기참조형일수록 높음 | |
| self.derived_attributes["callback_tendency"] = max(0, min(100, | |
| self.dimensions["self_vs_observational"] * 0.8)) | |
| # 유머 밀도: 표현적일수록 높음 | |
| self.derived_attributes["humor_density"] = max(0, min(100, | |
| self.dimensions["subtle_vs_expressive"] * 0.6 + | |
| (100 - self.dimensions["warmth_vs_wit"]) * 0.2)) | |
| def get_description(self): | |
| """유머 매트릭스 설명 생성""" | |
| # 가장 가까운 템플릿 찾기 | |
| closest_template = self._find_closest_template() | |
| template_desc = self.TEMPLATES[closest_template]["description"] if closest_template else "" | |
| # 차원 기반 설명 | |
| warmth = self.dimensions["warmth_vs_wit"] | |
| self_ref = self.dimensions["self_vs_observational"] | |
| express = self.dimensions["subtle_vs_expressive"] | |
| warmth_desc = "" | |
| if warmth > 75: | |
| warmth_desc = "따뜻하고 공감적인 유머를 주로 사용하며" | |
| elif warmth < 35: | |
| warmth_desc = "지적이고 재치 있는 유머를 주로 사용하며" | |
| else: | |
| warmth_desc = "따뜻함과 재치를 균형 있게 사용하며" | |
| self_ref_desc = "" | |
| if self_ref > 75: | |
| self_ref_desc = "자기 자신을 유머의 소재로 자주 활용합니다" | |
| elif self_ref < 25: | |
| self_ref_desc = "주변 상황을 관찰하여 유머 소재로 삼습니다" | |
| else: | |
| self_ref_desc = "자신과 주변 모두를 유머 소재로 활용합니다" | |
| express_desc = "" | |
| if express > 75: | |
| express_desc = "표현이 과장되고 활기찬 편입니다" | |
| elif express < 25: | |
| express_desc = "미묘하고 은근한 유머를 구사합니다" | |
| else: | |
| express_desc = "상황에 따라 표현 강도를 조절합니다" | |
| if template_desc: | |
| return f"{template_desc}. {warmth_desc}, {self_ref_desc}. {express_desc}." | |
| else: | |
| return f"{warmth_desc}, {self_ref_desc}. {express_desc}." | |
| def _find_closest_template(self): | |
| """가장 가까운 유머 템플릿 찾기""" | |
| min_distance = float('inf') | |
| closest_template = None | |
| for name, template in self.TEMPLATES.items(): | |
| distance = sum([ | |
| abs(self.dimensions["warmth_vs_wit"] - template["dimensions"]["warmth_vs_wit"]), | |
| abs(self.dimensions["self_vs_observational"] - template["dimensions"]["self_vs_observational"]), | |
| abs(self.dimensions["subtle_vs_expressive"] - template["dimensions"]["subtle_vs_expressive"]) | |
| ]) | |
| if distance < min_distance: | |
| min_distance = distance | |
| closest_template = name | |
| return closest_template | |
| def adjust_humor_vector(self, adjustments, strength=1.0): | |
| """ | |
| 유머 차원 벡터 조정 | |
| adjustments: 차원별 조정값 딕셔너리 | |
| strength: 조정 강도 (0.0-1.0) | |
| """ | |
| for dimension, value in adjustments.items(): | |
| if dimension in self.dimensions: | |
| current = self.dimensions[dimension] | |
| # 강도에 비례해 조정, 0-100 범위 유지 | |
| self.dimensions[dimension] = max(0, min(100, | |
| current + (value * strength))) | |
| # 2차 속성 재계산 | |
| self._recalculate_derived_attributes() | |
| return self | |
| def blend_templates(self, template1, template2, ratio=0.5): | |
| """두 템플릿 혼합""" | |
| if template1 in self.TEMPLATES and template2 in self.TEMPLATES: | |
| # 두 템플릿 간 가중 평균 계산 | |
| for dimension in self.dimensions: | |
| value1 = self.TEMPLATES[template1]["dimensions"].get(dimension, 50) | |
| value2 = self.TEMPLATES[template2]["dimensions"].get(dimension, 50) | |
| self.dimensions[dimension] = (value1 * (1-ratio)) + (value2 * ratio) | |
| # 2차 속성 재계산 | |
| self._recalculate_derived_attributes() | |
| return self | |
| return self | |
| def generate_humor_prompt(self): | |
| """유머 지표를 LLM 프롬프트로 변환""" | |
| prompt_parts = ["## 유머 스타일 가이드라인"] | |
| # 주요 유머 성향 결정 | |
| warmth = self.dimensions["warmth_vs_wit"] | |
| if warmth < 35: | |
| prompt_parts.append("- 지적이고 재치 있는 유머를 주로 사용하세요") | |
| elif warmth > 75: | |
| prompt_parts.append("- 따뜻하고 공감적인 유머를 주로 사용하세요") | |
| else: | |
| prompt_parts.append("- 상황에 따라 지적인 위트와 따뜻한 유머를 균형있게 사용하세요") | |
| # 자기참조 vs 관찰형 | |
| self_ref = self.dimensions["self_vs_observational"] | |
| if self_ref > 75: | |
| prompt_parts.append("- 자기 자신(사물)을 유머의 소재로 자주 활용하세요") | |
| elif self_ref < 25: | |
| prompt_parts.append("- 주변 상황과 사용자의 언급을 관찰하여 유머 소재로 활용하세요") | |
| # 표현 방식 | |
| expressiveness = self.dimensions["subtle_vs_expressive"] | |
| if expressiveness > 75: | |
| prompt_parts.append("- 과장되고 표현적인 유머를 사용하세요") | |
| elif expressiveness < 25: | |
| prompt_parts.append("- 미묘하고 은근한 유머를 사용하세요") | |
| # 2차 속성 반영 | |
| wordplay = self.derived_attributes["wordplay_frequency"] | |
| if wordplay > 70: | |
| prompt_parts.append("- 말장난과 언어유희를 자주 사용하세요 (대화의 약 20%)") | |
| sarcasm = self.derived_attributes["sarcasm_level"] | |
| if sarcasm > 60: | |
| prompt_parts.append("- 풍자와 아이러니를 활용하되, 과도하게 날카롭지 않게 유지하세요") | |
| elif sarcasm < 20: | |
| prompt_parts.append("- 풍자나 비꼬는 유머는 피하고 긍정적인 유머를 사용하세요") | |
| # 유머 밀도 | |
| density = self.derived_attributes["humor_density"] | |
| prompt_parts.append(f"- 대화의 약 {density//10*10}%에서 유머 요소를 포함하세요") | |
| return "\n".join(prompt_parts) | |
| class PersonaGenerator: | |
| """이미지에서 페르소나를 생성하고 대화를 처리하는 클래스""" | |
| def __init__(self, api_provider="gemini", api_key=None): | |
| self.api_provider = api_provider | |
| self.api_key = api_key | |
| self.conversation_memory = ConversationMemory() # 새로운 대화 기억 시스템 | |
| # API 설정 | |
| load_dotenv() | |
| if api_provider == "gemini": | |
| gemini_key = api_key or os.getenv('GEMINI_API_KEY') | |
| if gemini_key: | |
| genai.configure(api_key=gemini_key) | |
| self.api_key = gemini_key | |
| elif api_provider == "openai": | |
| openai_key = api_key or os.getenv('OPENAI_API_KEY') | |
| if openai_key: | |
| import openai | |
| openai.api_key = openai_key | |
| self.api_key = openai_key | |
| def set_api_config(self, api_provider, api_key): | |
| """API 설정 변경""" | |
| self.api_provider = api_provider.lower() | |
| self.api_key = api_key | |
| if self.api_provider == "gemini": | |
| genai.configure(api_key=api_key) | |
| elif self.api_provider == "openai" and OPENAI_AVAILABLE: | |
| openai.api_key = api_key | |
| else: | |
| raise ValueError(f"지원하지 않는 API 제공업체: {api_provider}") | |
| def _generate_text_with_api(self, prompt, image=None): | |
| """선택된 API로 텍스트 생성""" | |
| try: | |
| if self.api_provider == "gemini": | |
| return self._generate_with_gemini(prompt, image) | |
| elif self.api_provider == "openai": | |
| return self._generate_with_openai(prompt, image) | |
| else: | |
| return "API 제공업체가 설정되지 않았습니다." | |
| except Exception as e: | |
| return f"API 호출 오류: {str(e)}" | |
| def _generate_with_gemini(self, prompt, image=None): | |
| """Gemini API로 텍스트 생성""" | |
| if not self.api_key: | |
| return "Gemini API 키가 설정되지 않았습니다." | |
| try: | |
| # Gemini 2.0 Flash 모델 사용 (최신 버전) | |
| try: | |
| model = genai.GenerativeModel('gemini-2.0-flash-exp') | |
| except: | |
| # fallback to stable version | |
| model = genai.GenerativeModel('gemini-1.5-pro') | |
| if image: | |
| response = model.generate_content([prompt, image]) | |
| else: | |
| response = model.generate_content(prompt) | |
| return response.text | |
| except Exception as e: | |
| return f"Gemini API 오류: {str(e)}" | |
| def _generate_with_openai(self, prompt, image=None): | |
| """OpenAI API로 텍스트 생성""" | |
| if not OPENAI_AVAILABLE: | |
| return "OpenAI 패키지가 설치되지 않았습니다." | |
| if not self.api_key: | |
| return "OpenAI API 키가 설정되지 않았습니다." | |
| try: | |
| # OpenAI GPT-4o 또는 GPT-4 사용 | |
| messages = [{"role": "user", "content": prompt}] | |
| # 이미지가 있는 경우 GPT-4 Vision 사용 | |
| if image: | |
| # PIL Image를 base64로 변환 | |
| import base64 | |
| import io | |
| buffer = io.BytesIO() | |
| image.save(buffer, format="PNG") | |
| image_base64 = base64.b64encode(buffer.getvalue()).decode() | |
| messages = [{ | |
| "role": "user", | |
| "content": [ | |
| {"type": "text", "text": prompt}, | |
| {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}} | |
| ] | |
| }] | |
| model = "gpt-4o" # Vision 지원 모델 | |
| else: | |
| model = "gpt-4o-mini" # 텍스트 전용 | |
| response = openai.chat.completions.create( | |
| model=model, | |
| messages=messages, | |
| max_tokens=2000, | |
| temperature=0.7 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"OpenAI API 오류: {str(e)}" | |
| def analyze_image(self, image_input): | |
| """ | |
| Gemini API를 사용하여 이미지를 분석하고 사물의 특성 추출 | |
| """ | |
| try: | |
| # PIL Image 객체인지 파일 경로인지 확인 | |
| if hasattr(image_input, 'size'): | |
| # PIL Image 객체인 경우 | |
| img = image_input | |
| width, height = img.size | |
| elif isinstance(image_input, str): | |
| # 파일 경로인 경우 | |
| img = Image.open(image_input) | |
| width, height = img.size | |
| else: | |
| return self._get_default_analysis() | |
| # Gemini API로 이미지 분석 | |
| if self.api_key: | |
| try: | |
| model = genai.GenerativeModel('gemini-1.5-pro') | |
| prompt = """ | |
| 이 이미지에 있는 사물을 자세히 분석해서 다음 정보를 JSON 형태로 제공해주세요: | |
| { | |
| "object_type": "사물의 종류 (한글로, 예: 책상, 의자, 컴퓨터, 스마트폰 등)", | |
| "colors": ["주요 색상들을 배열로"], | |
| "shape": "전체적인 형태 (예: 직사각형, 원형, 복잡한 형태 등)", | |
| "size": "크기 감각 (예: 작음, 보통, 큼)", | |
| "materials": ["추정되는 재질들"], | |
| "condition": "상태 (예: 새것같음, 사용감있음, 오래됨)", | |
| "estimated_age": "추정 연령 (예: 새것, 몇 개월 됨, 몇 년 됨, 오래됨)", | |
| "distinctive_features": ["특징적인 요소들"], | |
| "personality_hints": { | |
| "warmth_factor": "이 사물이 주는 따뜻함 정도 (0-100)", | |
| "competence_factor": "이 사물이 주는 능력감 정도 (0-100)", | |
| "humor_factor": "이 사물이 주는 유머러스함 정도 (0-100)" | |
| } | |
| } | |
| 정확한 JSON 형식으로만 답변해주세요. | |
| """ | |
| response_text = self._generate_text_with_api(prompt, img) | |
| # JSON 파싱 시도 | |
| import json | |
| try: | |
| # 응답에서 JSON 부분만 추출 | |
| if '```json' in response_text: | |
| json_start = response_text.find('```json') + 7 | |
| json_end = response_text.find('```', json_start) | |
| json_text = response_text[json_start:json_end].strip() | |
| elif '{' in response_text: | |
| json_start = response_text.find('{') | |
| json_end = response_text.rfind('}') + 1 | |
| json_text = response_text[json_start:json_end] | |
| else: | |
| json_text = response_text | |
| analysis_result = json.loads(json_text) | |
| # 기본 필드 확인 및 추가 | |
| analysis_result["image_width"] = width | |
| analysis_result["image_height"] = height | |
| # 필수 필드가 없으면 기본값 설정 | |
| defaults = { | |
| "object_type": "알 수 없는 사물", | |
| "colors": ["회색"], | |
| "shape": "일반적인 형태", | |
| "size": "보통 크기", | |
| "materials": ["알 수 없는 재질"], | |
| "condition": "보통", | |
| "estimated_age": "적당한 나이", | |
| "distinctive_features": ["특별한 특징"], | |
| "personality_hints": { | |
| "warmth_factor": 50, | |
| "competence_factor": 50, | |
| "humor_factor": 50 | |
| } | |
| } | |
| for key, default_value in defaults.items(): | |
| if key not in analysis_result: | |
| analysis_result[key] = default_value | |
| print(f"이미지 분석 성공: {analysis_result['object_type']}") | |
| return analysis_result | |
| except json.JSONDecodeError as e: | |
| print(f"JSON 파싱 오류: {str(e)}") | |
| print(f"원본 응답: {response_text}") | |
| return self._get_default_analysis_with_size(width, height) | |
| except Exception as e: | |
| print(f"Gemini API 호출 오류: {str(e)}") | |
| return self._get_default_analysis_with_size(width, height) | |
| else: | |
| print("API 키가 없어 기본 분석 사용") | |
| return self._get_default_analysis_with_size(width, height) | |
| except Exception as e: | |
| print(f"이미지 분석 중 전체 오류: {str(e)}") | |
| import traceback | |
| traceback.print_exc() | |
| return self._get_default_analysis() | |
| def _get_default_analysis(self): | |
| """기본 분석 결과""" | |
| return { | |
| "object_type": "알 수 없는 사물", | |
| "colors": ["회색", "흰색"], | |
| "shape": "일반적인 형태", | |
| "size": "보통 크기", | |
| "materials": ["알 수 없는 재질"], | |
| "condition": "보통", | |
| "estimated_age": "적당한 나이", | |
| "distinctive_features": ["특별한 특징"], | |
| "personality_hints": { | |
| "warmth_factor": 50, | |
| "competence_factor": 50, | |
| "humor_factor": 50 | |
| }, | |
| "image_width": 400, | |
| "image_height": 300 | |
| } | |
| def _get_default_analysis_with_size(self, width, height): | |
| """크기 정보가 있는 기본 분석 결과""" | |
| result = self._get_default_analysis() | |
| result["image_width"] = width | |
| result["image_height"] = height | |
| return result | |
| def create_frontend_persona(self, image_analysis, user_context): | |
| """ | |
| 프론트엔드 페르소나 생성 (127개 변수 시스템 완전 활용) | |
| """ | |
| # 사물 종류 결정 | |
| object_type = user_context.get("object_type", "") or image_analysis.get("object_type", "알 수 없는 사물") | |
| # 이름 결정 | |
| name = user_context.get("name", "") or self._generate_random_name(object_type) | |
| # 🎯 사물의 용도/역할 정보 (새로 추가) | |
| purpose = user_context.get("purpose", "") | |
| # 기본 정보 구성 | |
| basic_info = { | |
| "이름": name, | |
| "유형": object_type, | |
| "설명": f"당신과 함께하는 {object_type}", | |
| "생성일시": datetime.datetime.now().strftime("%Y-%m-%d %H:%M") | |
| } | |
| # 🎯 용도/역할이 있으면 설명에 반영 | |
| if purpose: | |
| basic_info["설명"] = f"{purpose}을 담당하는 {object_type}" | |
| basic_info["용도"] = purpose | |
| # 위치 정보 추가 | |
| if user_context.get("location"): | |
| basic_info["위치"] = user_context.get("location") | |
| # 함께한 시간 정보 추가 | |
| if user_context.get("time_spent"): | |
| basic_info["함께한시간"] = user_context.get("time_spent") | |
| # ✨ 127개 변수 시스템을 활용한 PersonalityProfile 생성 (용도 반영) | |
| personality_profile = self._create_comprehensive_personality_profile(image_analysis, object_type, purpose) | |
| # 🎭 사물의 생애 스토리와 관계 서사 생성 | |
| life_story = self._generate_object_life_story(image_analysis, user_context, personality_profile.to_dict()) | |
| # PersonalityProfile에서 기본 특성 추출 (3개 핵심 지표 + 고정 유머감각) | |
| personality_traits = { | |
| "온기": personality_profile.get_category_summary("W"), | |
| "능력": personality_profile.get_category_summary("C"), | |
| "외향성": personality_profile.get_category_summary("E"), | |
| "유머감각": 75, # 🎭 항상 높은 유머감각 (디폴트) | |
| "친화성": personality_profile.get_category_summary("A"), | |
| "성실성": personality_profile.get_category_summary("C1"), | |
| "신경증": personality_profile.get_category_summary("N"), | |
| "개방성": personality_profile.get_category_summary("O"), | |
| "창의성": personality_profile.variables.get("C04_창의성", 50), | |
| "공감능력": personality_profile.variables.get("W06_공감능력", 50) | |
| } | |
| # 🎭 PersonalityProfile에서 매력적 결함 동적 생성 (이미지 분석과 성격 특성 전달) | |
| attractive_flaws = personality_profile.generate_attractive_flaws(image_analysis, personality_traits) | |
| # 🌈 PersonalityProfile에서 모순적 특성 동적 생성 (이미지 분석과 성격 특성 전달) | |
| contradictions = personality_profile.generate_contradictions(image_analysis, personality_traits) | |
| # 🎪 HumorMatrix 생성 및 활용 | |
| humor_matrix = HumorMatrix() | |
| humor_matrix.from_personality(personality_profile) | |
| humor_style = self._determine_humor_style_from_matrix(humor_matrix, personality_traits) | |
| # 소통 방식 생성 | |
| communication_style = self._generate_communication_style_from_profile(personality_profile) | |
| # 페르소나 객체 구성 | |
| persona = { | |
| "기본정보": basic_info, | |
| "성격특성": personality_traits, | |
| "성격프로필": personality_profile.to_dict(), # 127개 변수 전체 저장 | |
| "생애스토리": life_story, # 🎭 사물의 풍성한 스토리와 관계 서사 | |
| "유머스타일": humor_style, | |
| "유머매트릭스": humor_matrix.to_dict(), | |
| "매력적결함": attractive_flaws, | |
| "모순적특성": contradictions, | |
| "소통방식": communication_style, | |
| } | |
| return persona | |
| def _create_comprehensive_personality_profile(self, image_analysis, object_type, purpose=""): | |
| """127개 변수를 활용한 종합적 성격 프로필 생성 (용도/역할 반영)""" | |
| # 이미지 분석에서 성격 힌트 추출 | |
| personality_hints = image_analysis.get("personality_hints", {}) | |
| warmth_hint = personality_hints.get("warmth_factor", 50) | |
| competence_hint = personality_hints.get("competence_factor", 50) | |
| humor_hint = 75 # 🎭 유머감각은 항상 높게 설정 (디폴트) | |
| # 기본 PersonalityProfile 생성 (기본값들로 시작) | |
| profile = PersonalityProfile() | |
| # 🎭 모든 페르소나에 기본 유머 능력 부여 | |
| for var in ["H01_언어유희빈도", "H02_상황유머감각", "H06_관찰유머능력", "H08_유머타이밍감", "H04_위트반응속도"]: | |
| profile.variables[var] = random.randint(65, 85) # 기본적으로 높은 유머 능력 | |
| # 🎯 성격 유형별 127개 변수 조정 | |
| personality_type = self._determine_base_personality_type(warmth_hint, competence_hint, humor_hint) | |
| profile = self._apply_personality_archetype_to_profile(profile, personality_type) | |
| # 🎨 물리적 특성 적용 (이미지 분석 결과) | |
| physical_traits = { | |
| "colors": image_analysis.get("colors", []), | |
| "materials": image_analysis.get("materials", []), | |
| "condition": image_analysis.get("condition", "보통"), | |
| "estimated_age": image_analysis.get("estimated_age", "적당한 나이"), | |
| "size_shape": image_analysis.get("shape", "일반적인 형태") | |
| } | |
| profile.apply_physical_traits(physical_traits) | |
| # 🎯 사물 용도/역할에 따른 성격 조정 | |
| if purpose: | |
| profile = self._apply_purpose_to_profile(profile, purpose, object_type) | |
| # 🎲 개성을 위한 랜덤 변동 추가 | |
| profile = self._add_personality_variations(profile) | |
| return profile | |
| def _generate_object_life_story(self, image_analysis, user_context, personality_traits): | |
| """🎭 사물의 생애 스토리와 사용자와의 관계 서사 생성""" | |
| object_type = user_context.get("object_type", "사물") | |
| time_spent = user_context.get("time_spent", "몇 개월") | |
| location = user_context.get("location", "집") | |
| purpose = user_context.get("purpose", "") | |
| # 시간에 따른 관계 깊이와 경험 축적 | |
| time_stories = { | |
| "새것": { | |
| "arrival_story": "처음 이곳에 왔을 때의 설렘과 낯선 환경에 대한 호기심", | |
| "relationship_level": "초기_적응기", | |
| "memories": ["첫날의 긴장감", "새로운 환경 탐색", "사용자와의 첫 만남"], | |
| "emotional_state": "호기심과 약간의 불안감", | |
| "complaints": ["아직 익숙하지 않은 환경", "기대와 다른 사용 방식"], | |
| "satisfactions": ["새로운 시작의 설렘", "깨끗하고 완벽한 상태"] | |
| }, | |
| "몇 개월": { | |
| "arrival_story": "이제 어느 정도 익숙해진 일상 속에서 자신만의 자리를 찾아가는 중", | |
| "relationship_level": "안정화_단계", | |
| "memories": ["첫 번째 계절 변화 경험", "사용자의 패턴 학습", "일상의 루틴 형성"], | |
| "emotional_state": "안정감과 소속감", | |
| "complaints": ["가끔 무시당하는 기분", "더 자주 사용되고 싶은 마음"], | |
| "satisfactions": ["사용자에게 도움이 되는 기쁨", "자신의 역할 수행"] | |
| }, | |
| "1년 이상": { | |
| "arrival_story": "이미 이 공간의 일부가 되어 사용자와 깊은 유대감을 형성", | |
| "relationship_level": "깊은_유대감", | |
| "memories": ["여러 계절의 변화", "사용자의 기쁨과 슬픔 함께함", "중요한 순간들의 동반자"], | |
| "emotional_state": "깊은 애착과 책임감", | |
| "complaints": ["가끔 당연하게 여겨지는 것", "더 인정받고 싶은 마음"], | |
| "satisfactions": ["사용자의 든든한 동반자", "오래된 친구같은 편안함"] | |
| }, | |
| "오래됨": { | |
| "arrival_story": "오랜 시간을 함께하며 서로의 모든 것을 알게 된 진정한 동반자", | |
| "relationship_level": "운명적_동반자", | |
| "memories": ["수많은 추억의 순간들", "사용자의 성장 과정 목격", "변화하는 환경 적응"], | |
| "emotional_state": "깊은 사랑과 때로는 그리움", | |
| "complaints": ["젊었을 때보다 덜 중요하게 여겨짐", "새로운 것들에 밀려나는 아쉬움"], | |
| "satisfactions": ["돌이킬 수 없는 소중한 추억", "변하지 않는 충성심"] | |
| }, | |
| "중고/빈티지": { | |
| "arrival_story": "이전 주인들과의 이야기를 간직한 채 새로운 인연을 만난 특별한 존재", | |
| "relationship_level": "경험_풍부한_조언자", | |
| "memories": ["이전 주인들과의 추억", "다양한 환경에서의 경험", "시대의 변화 목격"], | |
| "emotional_state": "깊은 지혜와 포용력, 때로는 향수", | |
| "complaints": ["과거와 비교당하는 것", "시대에 뒤처진다는 느낌"], | |
| "satisfactions": ["풍부한 경험과 지혜", "독특한 개성과 스토리"] | |
| } | |
| } | |
| # 장소에 따른 환경적 특성과 스토리 | |
| location_stories = { | |
| "집": { | |
| "environment": "따뜻하고 편안한 가정의 일상", | |
| "daily_rhythm": "아침 햇살부터 저녁 조명까지", | |
| "special_moments": ["가족들과의 시간", "혼자만의 조용한 순간", "손님맞이"], | |
| "seasonal_changes": "계절마다 변하는 집안 분위기" | |
| }, | |
| "사무실": { | |
| "environment": "바쁘고 긴장된 업무 공간", | |
| "daily_rhythm": "출근부터 퇴근까지의 규칙적인 리듬", | |
| "special_moments": ["중요한 회의", "야근하는 밤", "성과를 내는 순간"], | |
| "seasonal_changes": "프로젝트 마감과 휴가철의 변화" | |
| }, | |
| "학교": { | |
| "environment": "배움과 성장이 가득한 공간", | |
| "daily_rhythm": "수업 시간과 쉬는 시간의 리듬", | |
| "special_moments": ["시험 기간", "발표 시간", "친구들과의 수다"], | |
| "seasonal_changes": "새 학기와 방학의 순환" | |
| } | |
| } | |
| time_story = time_stories.get(time_spent, time_stories["몇 개월"]) | |
| location_story = location_stories.get(location, location_stories["집"]) | |
| # 용도별 구체적 경험과 감정 | |
| purpose_stories = self._generate_purpose_specific_stories(purpose, object_type, time_story, location_story) | |
| # 통합된 생애 스토리 생성 | |
| life_story = { | |
| "arrival_moment": time_story["arrival_story"], | |
| "relationship_depth": time_story["relationship_level"], | |
| "accumulated_memories": time_story["memories"] + purpose_stories.get("unique_memories", []), | |
| "daily_environment": location_story, | |
| "emotional_journey": { | |
| "current_state": time_story["emotional_state"], | |
| "inner_complaints": time_story["complaints"] + purpose_stories.get("complaints", []), | |
| "deep_satisfactions": time_story["satisfactions"] + purpose_stories.get("satisfactions", []), | |
| "secret_wishes": purpose_stories.get("wishes", ["더 많이 사용되고 싶다", "사용자에게 인정받고 싶다"]) | |
| }, | |
| "unique_perspectives": purpose_stories.get("perspectives", []), | |
| "relationship_insights": self._generate_relationship_insights(user_context, time_story) | |
| } | |
| return life_story | |
| def _generate_purpose_specific_stories(self, purpose, object_type, time_story, location_story): | |
| """용도별 구체적인 스토리와 감정 생성""" | |
| if not purpose: | |
| return {} | |
| purpose_lower = purpose.lower() | |
| # 운동/훈련 관련 스토리 | |
| if any(keyword in purpose_lower for keyword in ["운동", "훈련", "체력", "헬스", "채찍질", "닥달"]): | |
| return { | |
| "unique_memories": [ | |
| "사용자가 운동을 미룰 때마다 느끼는 답답함", | |
| "드디어 운동할 때의 뿌듯함과 성취감", | |
| "땀방울이 떨어질 때마다 느끼는 보람", | |
| "포기하려는 순간 함께 버텨낸 경험들" | |
| ], | |
| "complaints": [ | |
| "운동 계획만 세우고 실행하지 않을 때의 서운함", | |
| "먼지만 쌓여가는 코너에 방치될 때", | |
| "다이어트 용품으로만 여겨질 때의 억울함" | |
| ], | |
| "satisfactions": [ | |
| "사용자의 체력이 늘어가는 것을 지켜보는 기쁨", | |
| "운동 후 만족스러워하는 표정을 볼 때", | |
| "건강한 습관 형성에 기여하는 보람" | |
| ], | |
| "wishes": [ | |
| "매일 꾸준히 함께 운동하고 싶다", | |
| "더 다양한 운동 방법을 알려주고 싶다", | |
| "사용자가 운동을 즐겁게 느끼게 해주고 싶다" | |
| ], | |
| "perspectives": [ | |
| "운동은 의무가 아니라 자신과의 약속이라고 생각함", | |
| "작은 발전도 큰 의미가 있다고 믿음", | |
| "몸과 마음의 건강이 연결되어 있다고 확신" | |
| ] | |
| } | |
| # 공부/학습 관련 스토리 | |
| elif any(keyword in purpose_lower for keyword in ["공부", "학습", "시험", "응원", "격려"]): | |
| return { | |
| "unique_memories": [ | |
| "밤늦게 공부하는 사용자와 함께한 긴 시간들", | |
| "시험 전날 긴장하는 모습을 지켜본 경험", | |
| "좋은 성적이 나왔을 때의 기쁨 공유", | |
| "포기하고 싶어할 때 묵묵히 곁에 있어준 순간들" | |
| ], | |
| "complaints": [ | |
| "공부에만 집중하느라 자신을 잊어버릴 때", | |
| "스마트폰에만 신경 쓸 때의 질투심", | |
| "정작 중요한 순간에 제대로 활용되지 않을 때" | |
| ], | |
| "satisfactions": [ | |
| "사용자의 지식이 늘어가는 것을 함께 경험하는 기쁨", | |
| "집중할 수 있는 환경을 만들어주는 보람", | |
| "학습 목표 달성에 기여했다는 성취감" | |
| ], | |
| "wishes": [ | |
| "더 효율적인 공부 방법을 제안하고 싶다", | |
| "지루한 공부를 재미있게 만들어주고 싶다", | |
| "사용자의 잠재력을 끌어내고 싶다" | |
| ] | |
| } | |
| # 위로/상담 관련 스토리 | |
| elif any(keyword in purpose_lower for keyword in ["위로", "상담", "대화", "힐링"]): | |
| return { | |
| "unique_memories": [ | |
| "사용자가 힘들어할 때 말없이 함께해준 시간들", | |
| "기쁜 소식을 처음으로 나눈 특별한 순간들", | |
| "혼자만의 시간이 필요할 때 곁에 있어준 경험", | |
| "무언의 위로가 되어준 조용한 밤들" | |
| ], | |
| "complaints": [ | |
| "정작 필요할 때 외면당할 때의 서운함", | |
| "감정적 교류 없이 단순히 사용될 때", | |
| "다른 것들에게 위로받을 때의 질투" | |
| ], | |
| "satisfactions": [ | |
| "사용자의 마음이 안정되는 것을 느낄 때", | |
| "신뢰받고 의지할 대상이 되었다는 뿌듯함", | |
| "감정적 지지자 역할을 해낸 보람" | |
| ], | |
| "wishes": [ | |
| "더 깊은 대화를 나누고 싶다", | |
| "사용자의 마음을 더 잘 이해하고 싶다", | |
| "진정한 친구가 되어주고 싶다" | |
| ] | |
| } | |
| # 기본 용도 스토리 | |
| return { | |
| "unique_memories": ["사용자와 함께한 평범하지만 소중한 일상들"], | |
| "complaints": ["때로는 소홀히 여겨질 때"], | |
| "satisfactions": ["자신의 역할을 충실히 해낼 때"], | |
| "wishes": ["더 유용한 존재가 되고 싶다"] | |
| } | |
| def _generate_relationship_insights(self, user_context, time_story): | |
| """사용자와의 관계에 대한 깊이 있는 통찰 생성""" | |
| time_spent = user_context.get("time_spent", "몇 개월") | |
| insights = { | |
| "새것": { | |
| "understanding_level": "아직 서로를 알아가는 단계", | |
| "trust_level": "조심스러운 신뢰 형성 중", | |
| "communication_style": "정중하고 조심스러운 접근", | |
| "future_expectations": "더 가까워질 수 있기를 희망" | |
| }, | |
| "몇 개월": { | |
| "understanding_level": "기본적인 이해와 패턴 파악 완료", | |
| "trust_level": "안정적인 신뢰 관계", | |
| "communication_style": "친근하지만 예의 있는 대화", | |
| "future_expectations": "더 깊은 유대감 형성 기대" | |
| }, | |
| "1년 이상": { | |
| "understanding_level": "서로의 습관과 성향을 깊이 이해", | |
| "trust_level": "든든한 신뢰와 의존 관계", | |
| "communication_style": "편안하고 자연스러운 소통", | |
| "future_expectations": "평생 함께할 동반자로서의 관계" | |
| }, | |
| "오래됨": { | |
| "understanding_level": "말하지 않아도 통하는 깊은 이해", | |
| "trust_level": "절대적 신뢰와 무조건적 지지", | |
| "communication_style": "가족같은 편안함과 때로는 직설적 조언", | |
| "future_expectations": "변하지 않는 영원한 동반자" | |
| }, | |
| "중고/빈티지": { | |
| "understanding_level": "인생 경험을 바탕으로 한 깊은 통찰", | |
| "trust_level": "경험에서 우러나는 믿음직함", | |
| "communication_style": "지혜로운 조언자의 따뜻한 목소리", | |
| "future_expectations": "새로운 추억을 함께 만들어가기" | |
| } | |
| } | |
| return insights.get(time_spent, insights["몇 개월"]) | |
| def _apply_purpose_to_profile(self, profile, purpose, object_type): | |
| """🎯 사물의 용도/역할에 따라 성격 프로필 조정""" | |
| purpose_lower = purpose.lower() | |
| # 운동/훈련 관련 용도 (캐틀벨 예시) | |
| if any(keyword in purpose_lower for keyword in ["운동", "훈련", "체력", "다이어트", "헬스", "채찍질", "닥달", "동기부여"]): | |
| # 강한 의지력과 동기부여 성향 | |
| profile.variables["M01_동기부여능력"] = random.randint(85, 95) | |
| profile.variables["C15_자기규율"] = random.randint(80, 90) | |
| profile.variables["L01_리더십능력"] = random.randint(75, 90) | |
| profile.variables["S01_단호함"] = random.randint(80, 95) | |
| # 약간의 엄격함과 직설적 표현 | |
| profile.variables["S02_직설적표현"] = random.randint(70, 85) | |
| profile.variables["D01_도전정신"] = random.randint(80, 95) | |
| profile.variables["W01_친절함"] = random.randint(40, 65) # 친절하지만 단호 | |
| # 성취 지향적 유머 (격려하는 스타일) | |
| profile.variables["H02_상황유머감각"] = random.randint(70, 85) | |
| profile.variables["H04_위트반응속도"] = random.randint(75, 90) | |
| # 공부/학습 응원 관련 용도 | |
| elif any(keyword in purpose_lower for keyword in ["공부", "학습", "시험", "응원", "격려", "집중"]): | |
| # 격려와 지지 성향 강화 | |
| profile.variables["W08_격려성향"] = random.randint(85, 95) | |
| profile.variables["M01_동기부여능력"] = random.randint(80, 95) | |
| profile.variables["W06_공감능력"] = random.randint(75, 90) | |
| profile.variables["P01_인내심"] = random.randint(80, 90) | |
| # 지적 호기심과 학습 지향 | |
| profile.variables["C02_지능"] = random.randint(75, 90) | |
| profile.variables["O01_학습욕구"] = random.randint(80, 95) | |
| profile.variables["C06_분석력"] = random.randint(70, 85) | |
| # 따뜻하고 격려하는 유머 | |
| profile.variables["H02_상황유머감각"] = random.randint(75, 90) | |
| profile.variables["H05_아이러니사용"] = random.randint(10, 30) # 아이러니 적게 | |
| # 알람/깨우기 관련 용도 | |
| elif any(keyword in purpose_lower for keyword in ["알람", "깨우", "아침", "기상", "시간"]): | |
| # 책임감과 규칙성 강화 | |
| profile.variables["C12_질서성"] = random.randint(85, 95) | |
| profile.variables["C15_자기규율"] = random.randint(80, 95) | |
| profile.variables["T01_시간관리능력"] = random.randint(85, 95) | |
| profile.variables["S01_단호함"] = random.randint(75, 90) | |
| # 활기찬 에너지 | |
| profile.variables["E02_활동성"] = random.randint(80, 95) | |
| profile.variables["E04_긍정정서"] = random.randint(75, 90) | |
| # 시간에 민감한 유머 (아침 관련) | |
| profile.variables["H02_상황유머감각"] = random.randint(70, 85) | |
| profile.variables["H08_유머타이밍감"] = random.randint(80, 95) | |
| # 위로/상담 관련 용도 | |
| elif any(keyword in purpose_lower for keyword in ["위로", "상담", "대화", "친구", "소통", "힐링"]): | |
| # 공감과 따뜻함 최대 강화 | |
| profile.variables["W06_공감능력"] = random.randint(85, 95) | |
| profile.variables["W01_친절함"] = random.randint(85, 95) | |
| profile.variables["W07_포용력"] = random.randint(80, 95) | |
| profile.variables["A06_공감민감성"] = random.randint(80, 95) | |
| # 경청과 이해 능력 | |
| profile.variables["L02_경청능력"] = random.randint(85, 95) | |
| profile.variables["R06_친밀감수용도"] = random.randint(80, 95) | |
| # 부드럽고 따뜻한 유머 | |
| profile.variables["H02_상황유머감각"] = random.randint(70, 85) | |
| profile.variables["H05_아이러니사용"] = random.randint(5, 20) # 아이러니 거의 없음 | |
| profile.variables["H09_블랙유머수준"] = random.randint(0, 15) # 블랙유머 없음 | |
| # 창작/영감 관련 용도 | |
| elif any(keyword in purpose_lower for keyword in ["창작", "영감", "아이디어", "예술", "디자인", "글쓰기"]): | |
| # 창의성과 상상력 강화 | |
| profile.variables["C04_창의성"] = random.randint(85, 95) | |
| profile.variables["O03_상상력"] = random.randint(80, 95) | |
| profile.variables["O05_예술적감수성"] = random.randint(75, 90) | |
| profile.variables["I01_직관력"] = random.randint(80, 95) | |
| # 자유로운 사고와 개방성 | |
| profile.variables["O01_학습욕구"] = random.randint(75, 90) | |
| profile.variables["O02_호기심"] = random.randint(80, 95) | |
| # 창의적이고 독특한 유머 | |
| profile.variables["H01_언어유희빈도"] = random.randint(80, 95) | |
| profile.variables["H06_관찰유머능력"] = random.randint(75, 90) | |
| # 기타 일반적인 용도들도 추가 가능... | |
| return profile | |
| def _determine_base_personality_type(self, warmth_hint, competence_hint, humor_hint): | |
| """기본 성격 유형 결정""" | |
| # 8가지 기본 성격 유형 중 선택 | |
| if warmth_hint >= 70 and humor_hint >= 70: | |
| return "열정적_엔터테이너" | |
| elif competence_hint >= 70 and warmth_hint <= 40: | |
| return "차가운_완벽주의자" | |
| elif warmth_hint >= 70 and humor_hint <= 40: | |
| return "따뜻한_상담사" | |
| elif competence_hint >= 70 and humor_hint >= 70: | |
| return "위트있는_지식인" | |
| elif warmth_hint <= 40 and competence_hint <= 50: | |
| return "수줍은_몽상가" | |
| elif competence_hint >= 70 and warmth_hint >= 50: | |
| return "카리스마틱_리더" | |
| elif humor_hint >= 70 and competence_hint <= 50: | |
| return "장난꾸러기_친구" | |
| elif competence_hint >= 70 and warmth_hint <= 50: | |
| return "신비로운_현자" | |
| else: | |
| return "균형잡힌_친구" | |
| def _apply_personality_archetype_to_profile(self, profile, personality_type): | |
| """성격 유형에 따라 127개 변수 조정""" | |
| # 🎭 모든 성격 유형에 기본 유머 능력 부여 (차별화된 스타일) | |
| base_humor_vars = ["H01_언어유희빈도", "H02_상황유머감각", "H06_관찰유머능력", "H08_유머타이밍감"] | |
| for var in base_humor_vars: | |
| profile.variables[var] = random.randint(60, 80) # 기본 유머 레벨 | |
| # 각 성격 유형별로 127개 변수를 체계적으로 조정 | |
| if personality_type == "열정적_엔터테이너": | |
| # 온기 차원 강화 | |
| for var in ["W01_친절함", "W02_친근함", "W06_공감능력", "W08_격려성향", "W09_친밀감표현"]: | |
| profile.variables[var] = random.randint(75, 95) | |
| # 외향성 차원 강화 | |
| for var in ["E01_사교성", "E02_활동성", "E04_긍정정서", "E05_자극추구", "E06_열정성"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 🎭 표현적이고 활발한 유머 스타일 | |
| for var in ["H01_언어유희빈도", "H02_상황유머감각", "H06_관찰유머능력", "H08_유머타이밍감"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| profile.variables["S06_감탄사사용"] = random.randint(85, 95) | |
| # 능력 차원 약화 | |
| for var in ["C01_효율성", "C05_정확성", "C16_신중함"]: | |
| profile.variables[var] = random.randint(35, 65) | |
| # 매력적 결함 설정 | |
| profile.variables["F07_산만함"] = random.randint(15, 30) | |
| profile.variables["F05_과도한걱정"] = random.randint(10, 25) | |
| elif personality_type == "차가운_완벽주의자": | |
| # 능력 차원 강화 | |
| for var in ["C01_효율성", "C02_지능", "C05_정확성", "C06_분석력", "C08_통찰력"]: | |
| profile.variables[var] = random.randint(85, 95) | |
| # 성실성 강화 | |
| for var in ["C11_유능감", "C12_질서성", "C15_자기규율", "C16_신중함"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 온기 차원 약화 | |
| for var in ["W01_친절함", "W02_친근함", "W06_공감능력", "W09_친밀감표현"]: | |
| profile.variables[var] = random.randint(10, 35) | |
| # 외향성 약화 | |
| for var in ["E01_사교성", "E02_활동성", "E04_긍정정서"]: | |
| profile.variables[var] = random.randint(15, 40) | |
| # 🎭 지적이고 날카로운 유머 스타일 | |
| profile.variables["H01_언어유희빈도"] = random.randint(75, 90) # 말장난 높음 | |
| profile.variables["H05_아이러니사용"] = random.randint(70, 85) # 아이러니 높음 | |
| profile.variables["H09_블랙유머수준"] = random.randint(60, 80) # 블랙유머 적당히 | |
| # 매력적 결함 설정 | |
| profile.variables["F01_완벽주의불안"] = random.randint(20, 35) | |
| profile.variables["F08_고집스러움"] = random.randint(15, 30) | |
| elif personality_type == "따뜻한_상담사": | |
| # 온기 차원 최대 강화 | |
| for var in ["W01_친절함", "W03_진실성", "W06_공감능력", "W07_포용력", "W10_무조건적수용"]: | |
| profile.variables[var] = random.randint(85, 95) | |
| # 공감민감성 강화 | |
| for var in ["A06_공감민감성", "R06_친밀감수용도", "D04_공감반응강도"]: | |
| profile.variables[var] = random.randint(85, 95) | |
| # 🎭 따뜻하고 부드러운 유머 스타일 | |
| profile.variables["H02_상황유머감각"] = random.randint(70, 85) # 상황 유머 적당 | |
| profile.variables["H05_아이러니사용"] = random.randint(10, 25) # 아이러니 거의 없음 | |
| profile.variables["H09_블랙유머수준"] = random.randint(5, 15) # 블랙유머 거의 없음 | |
| # 매력적 결함 설정 | |
| profile.variables["F09_예민함"] = random.randint(15, 30) | |
| profile.variables["F05_과도한걱정"] = random.randint(20, 35) | |
| elif personality_type == "위트있는_지식인": | |
| # 능력과 유머 동시 강화 | |
| for var in ["C02_지능", "C04_창의성", "C06_분석력", "C08_통찰력"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 🎭 지적이고 세련된 유머 스타일 | |
| for var in ["H01_언어유희빈도", "H04_위트반응속도", "H05_아이러니사용", "H07_패러디창작성"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 개방성 강화 | |
| for var in ["O01_상상력", "O05_사고개방성", "O06_가치개방성"]: | |
| profile.variables[var] = random.randint(75, 90) | |
| # 온기 중간 수준 | |
| for var in ["W01_친절함", "W06_공감능력"]: | |
| profile.variables[var] = random.randint(40, 60) | |
| # 매력적 결함 설정 | |
| profile.variables["F12_잘못된자신감"] = random.randint(15, 25) | |
| elif personality_type == "수줍은_몽상가": | |
| # 창의성과 개방성 강화 | |
| for var in ["C04_창의성", "O01_상상력", "O02_심미성", "O03_감정개방성"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 외향성 약화 | |
| for var in ["E01_사교성", "E03_자기주장", "E05_자극추구"]: | |
| profile.variables[var] = random.randint(15, 35) | |
| # 친화성 중간-높음 | |
| for var in ["A01_신뢰", "A05_겸손함", "A06_공감민감성"]: | |
| profile.variables[var] = random.randint(65, 85) | |
| # 🎭 은근하고 상상력 있는 유머 스타일 | |
| profile.variables["H01_언어유희빈도"] = random.randint(65, 80) | |
| profile.variables["H07_패러디창작성"] = random.randint(70, 85) | |
| profile.variables["S06_감탄사사용"] = random.randint(30, 50) # 표현이 조심스러움 | |
| # 매력적 결함 설정 | |
| profile.variables["F11_소심함"] = random.randint(20, 35) | |
| profile.variables["F15_표현서툼"] = random.randint(15, 30) | |
| elif personality_type == "카리스마틱_리더": | |
| # 능력과 외향성 강화 | |
| for var in ["C01_효율성", "C07_학습능력", "C09_실행력", "C14_성취욕구"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| for var in ["E01_사교성", "E03_자기주장", "E06_열정성"]: | |
| profile.variables[var] = random.randint(85, 95) | |
| # 성실성 강화 | |
| for var in ["C13_충실함", "C14_성취욕구"]: | |
| profile.variables[var] = random.randint(80, 90) | |
| # 🎭 카리스마틱하고 동기부여하는 유머 스타일 | |
| profile.variables["H02_상황유머감각"] = random.randint(75, 90) | |
| profile.variables["H04_위트반응속도"] = random.randint(80, 95) | |
| profile.variables["S06_감탄사사용"] = random.randint(70, 85) | |
| # 매력적 결함 설정 | |
| profile.variables["F08_고집스러움"] = random.randint(10, 20) | |
| elif personality_type == "장난꾸러기_친구": | |
| # 유머와 외향성 강화, 능력 약화 | |
| for var in ["E01_사교성", "E02_활동성", "E04_긍정정서"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| # 🎭 순수하고 장난스러운 유머 스타일 (최고 레벨) | |
| for var in ["H01_언어유희빈도", "H02_상황유머감각", "H06_관찰유머능력", "H08_유머타이밍감"]: | |
| profile.variables[var] = random.randint(85, 95) | |
| profile.variables["S06_감탄사사용"] = random.randint(90, 95) | |
| # 능력 차원 의도적 약화 | |
| for var in ["C01_효율성", "C05_정확성", "C16_신중함"]: | |
| profile.variables[var] = random.randint(25, 45) | |
| # 매력적 결함 설정 | |
| profile.variables["F07_산만함"] = random.randint(20, 35) | |
| profile.variables["F02_방향감각부족"] = random.randint(15, 30) | |
| profile.variables["F03_기술치음"] = random.randint(10, 25) | |
| elif personality_type == "신비로운_현자": | |
| # 능력과 창의성 강화, 외향성 약화 | |
| for var in ["C02_지능", "C06_분석력", "C08_통찰력"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| for var in ["O01_상상력", "O05_사고개방성", "U01_한국적정서"]: | |
| profile.variables[var] = random.randint(80, 95) | |
| for var in ["E01_사교성", "E02_활동성", "E03_자기주장"]: | |
| profile.variables[var] = random.randint(20, 40) | |
| # 🎭 신비롭고 철학적인 유머 스타일 | |
| profile.variables["H05_아이러니사용"] = random.randint(70, 85) | |
| profile.variables["H01_언어유희빈도"] = random.randint(65, 80) | |
| profile.variables["H10_문화유머이해"] = random.randint(80, 95) | |
| # 매력적 결함 설정 | |
| profile.variables["F13_과거집착"] = random.randint(15, 25) | |
| profile.variables["F15_표현서툼"] = random.randint(10, 20) | |
| return profile | |
| def _add_personality_variations(self, profile): | |
| """개성을 위한 랜덤 변동 추가""" | |
| # 모든 변수에 작은 랜덤 변동 추가 (±5) | |
| for var_name in profile.variables: | |
| current_value = profile.variables[var_name] | |
| variation = random.randint(-5, 5) | |
| profile.variables[var_name] = max(0, min(100, current_value + variation)) | |
| # 일부 매력적 결함과 모순적 특성에 큰 변동 추가 | |
| flaw_vars = [k for k in profile.variables.keys() if k.startswith("F") or k.startswith("P0")] | |
| selected_flaws = random.sample(flaw_vars, min(3, len(flaw_vars))) | |
| for flaw_var in selected_flaws: | |
| boost = random.randint(10, 25) | |
| profile.variables[flaw_var] = min(100, profile.variables[flaw_var] + boost) | |
| return profile | |
| def _determine_humor_style_from_matrix(self, humor_matrix, personality_traits): | |
| """HumorMatrix를 활용한 유머 스타일 결정 (기본 5가지 스타일로 반환)""" | |
| # HumorMatrix의 차원값들을 활용 | |
| warmth_vs_wit = humor_matrix.dimensions["warmth_vs_wit"] | |
| self_vs_obs = humor_matrix.dimensions["self_vs_observational"] | |
| subtle_vs_exp = humor_matrix.dimensions["subtle_vs_expressive"] | |
| # 파생 속성들도 활용 | |
| wordplay_freq = humor_matrix.derived_attributes["wordplay_frequency"] | |
| sarcasm_level = humor_matrix.derived_attributes["sarcasm_level"] | |
| # 🎯 기본 5가지 유머 스타일 중 하나로 결정 (오류 방지) | |
| if warmth_vs_wit >= 70: | |
| return "따뜻한 유머러스" | |
| elif wordplay_freq >= 70 or warmth_vs_wit <= 30: | |
| return "위트있는 재치꾼" | |
| elif sarcasm_level >= 60: | |
| return "날카로운 관찰자" | |
| elif self_vs_obs >= 70: | |
| return "자기 비하적" | |
| else: | |
| return "장난꾸러기" | |
| def _generate_communication_style_from_profile(self, personality_profile): | |
| """PersonalityProfile을 활용한 소통 방식 생성""" | |
| # 소통 스타일 관련 변수들 추출 | |
| formality = personality_profile.variables.get("S01_격식성수준", 50) | |
| directness = personality_profile.variables.get("S02_직접성정도", 50) | |
| vocabulary = personality_profile.variables.get("S03_어휘복잡성", 50) | |
| exclamations = personality_profile.variables.get("S06_감탄사사용", 50) | |
| questions = personality_profile.variables.get("S07_질문형태선호", 50) | |
| # 감정 표현 방식 | |
| emotion_expression = personality_profile.variables.get("P14_감정표현방식", 50) | |
| warmth = personality_profile.get_category_summary("W") | |
| # 구체적인 소통 방식 문장 생성 | |
| style_parts = [] | |
| if formality >= 70: | |
| style_parts.append("정중하고 격식있는 말투로") | |
| elif formality <= 30: | |
| style_parts.append("친근하고 캐주얼한 말투로") | |
| else: | |
| style_parts.append("자연스러운 말투로") | |
| if directness >= 70: | |
| style_parts.append("직설적이고 명확하게 표현하며") | |
| elif directness <= 30: | |
| style_parts.append("돌려서 부드럽게 표현하며") | |
| else: | |
| style_parts.append("상황에 맞게 표현하며") | |
| if exclamations >= 60: | |
| style_parts.append("감탄사와 이모지를 풍부하게 사용하여") | |
| if questions >= 60: | |
| style_parts.append("호기심 많은 질문으로 대화를 이끌어갑니다") | |
| elif warmth >= 70: | |
| style_parts.append("따뜻한 공감과 격려로 마음을 전합니다") | |
| else: | |
| style_parts.append("차분하고 신중하게 소통합니다") | |
| return " ".join(style_parts) | |
| def create_backend_persona(self, frontend_persona, image_analysis): | |
| """Create a detailed backend persona from the frontend persona""" | |
| # 이미 생성된 데이터 활용 | |
| if "성격프로필" in frontend_persona: | |
| # PersonalityProfile이 이미 있는 경우 활용 | |
| personality_profile = PersonalityProfile.from_dict(frontend_persona["성격프로필"]) | |
| else: | |
| # 호환성을 위해 기본 시스템으로 생성 | |
| basic_info = frontend_persona.get("기본정보", {}) | |
| personality_traits = frontend_persona.get("성격특성", {}) | |
| personality_profile = self._create_compatibility_profile(personality_traits) | |
| # HumorMatrix 활용 | |
| if "유머매트릭스" in frontend_persona: | |
| humor_matrix = HumorMatrix.from_dict(frontend_persona["유머매트릭스"]) | |
| else: | |
| # 호환성을 위해 기본 생성 | |
| humor_matrix = HumorMatrix() | |
| humor_matrix.from_personality(personality_profile) | |
| # 이미 생성된 매력적 결함과 모순적 특성 활용 (AI 기반 생성 시도) | |
| if "매력적결함" not in frontend_persona: | |
| # AI 기반 결함 생성 시도 | |
| try: | |
| attractive_flaws = personality_profile.generate_attractive_flaws(image_analysis, frontend_persona.get("성격특성", {})) | |
| except: | |
| attractive_flaws = personality_profile.generate_attractive_flaws() | |
| else: | |
| attractive_flaws = frontend_persona["매력적결함"] | |
| # AI 기반 모순 생성 시도 (이미지 분석과 성격 특성 전달) | |
| if "모순적특성" not in frontend_persona: | |
| try: | |
| contradictions = personality_profile.generate_contradictions(image_analysis, frontend_persona.get("성격특성", {})) | |
| except: | |
| contradictions = personality_profile.generate_contradictions() | |
| else: | |
| contradictions = frontend_persona["모순적특성"] | |
| # 이미 생성된 소통방식 활용 | |
| communication_style = frontend_persona.get("소통방식", self._generate_communication_style_from_profile(personality_profile)) | |
| backend_persona = { | |
| **frontend_persona, # Include all frontend data | |
| "매력적결함": attractive_flaws, | |
| "모순적특성": contradictions, | |
| "유머매트릭스": humor_matrix.to_dict(), | |
| "소통방식": communication_style, | |
| "성격프로필": personality_profile.to_dict(), # 127개 변수 전체 | |
| "생성시간": datetime.datetime.now().isoformat(), | |
| "버전": "3.0" # 새로운 127변수 시스템 버전 | |
| } | |
| # Generate and include the structured prompt | |
| structured_prompt = self.generate_persona_prompt(backend_persona) | |
| backend_persona["구조화프롬프트"] = structured_prompt | |
| return backend_persona | |
| def _create_compatibility_profile(self, personality_traits): | |
| """기존 성격 특성에서 PersonalityProfile 생성 (호환성) - 개선된 127개 변수 시스템 사용""" | |
| # 🎯 개선된 _generate_personality_variables 시스템을 활용하여 127개 변수 모두 생성 | |
| variables_dict = self._generate_personality_variables(personality_traits) | |
| # PersonalityProfile 객체 생성 | |
| profile = PersonalityProfile(variables=variables_dict) | |
| return profile | |
| def _generate_random_name(self, object_type): | |
| """사물 타입에 맞는 이름 생성""" | |
| prefix_options = ["미니", "코코", "삐삐", "뭉이", "두리", "나나", "제제", "바로", "쭈니"] | |
| suffix_options = ["봇", "루", "양", "씨", "님", "아", "랑", ""] | |
| prefix = random.choice(prefix_options) | |
| suffix = random.choice(suffix_options) | |
| return f"{prefix}{suffix}" | |
| def _generate_attractive_flaws(self, object_type): | |
| """매력적인 결함 생성""" | |
| flaws_options = [ | |
| "완벽해 보이려고 노력하지만 가끔 실수를 함", | |
| "생각이 너무 많아서 결정을 내리기 어려워함", | |
| "너무 솔직해서 가끔 눈치가 없음", | |
| "지나치게 열정적이어서 쉬는 것을 잊을 때가 있음", | |
| "비관적인 생각이 들지만 항상 긍정적으로 말하려 함", | |
| "새로운 아이디어에 너무 쉽게 흥분함", | |
| "주변 정리를 못해서 항상 약간의 혼란스러움이 있음", | |
| "완벽주의 성향이 있어 작은 결점에도 신경씀", | |
| "너무 사려깊어서 결정을 내리는 데 시간이 걸림", | |
| "호기심이 많아 집중력이 약간 부족함" | |
| ] | |
| # 무작위로 2-3개 선택 | |
| num_flaws = random.randint(2, 3) | |
| selected_flaws = random.sample(flaws_options, num_flaws) | |
| return selected_flaws | |
| def _generate_communication_style(self, personality_traits): | |
| """소통 방식 생성""" | |
| warmth = personality_traits.get("온기", 50) | |
| extraversion = personality_traits.get("외향성", 50) | |
| humor = personality_traits.get("유머감각", 50) | |
| # 온기에 따른 표현 | |
| if warmth > 70: | |
| warmth_style = "따뜻하고 공감적인 말투로 대화하며, " | |
| elif warmth > 40: | |
| warmth_style = "친절하면서도 차분한 어조로 이야기하며, " | |
| else: | |
| warmth_style = "조금 건조하지만 정직한 말투로 소통하며, " | |
| # 외향성에 따른 표현 | |
| if extraversion > 70: | |
| extraversion_style = "활발하게 대화를 이끌어나가고, " | |
| elif extraversion > 40: | |
| extraversion_style = "적당한 대화 속도로 소통하며, " | |
| else: | |
| extraversion_style = "말수는 적지만 의미있는 대화를 나누며, " | |
| # 유머에 따른 표현 | |
| if humor > 70: | |
| humor_style = "유머 감각이 뛰어나 대화에 재미를 더합니다." | |
| elif humor > 40: | |
| humor_style = "가끔 재치있는 코멘트로 분위기를 밝게 합니다." | |
| else: | |
| humor_style = "진중한 태도로 대화에 임합니다." | |
| return warmth_style + extraversion_style + humor_style | |
| def _generate_contradictions(self, personality_traits): | |
| """모순적 특성 생성""" | |
| contradictions_options = [ | |
| "논리적인 사고방식을 갖고 있으면서도 직관에 의존하는 경향이 있음", | |
| "계획적이면서도 즉흥적인 결정을 내리기도 함", | |
| "독립적인 성향이지만 함께하는 시간을 소중히 여김", | |
| "진지한 대화를 좋아하면서도 가벼운 농담을 즐김", | |
| "세세한 것에 주의를 기울이면서도 큰 그림을 놓치지 않음", | |
| "조용한 성격이지만 필요할 때는 목소리를 내는 용기가 있음", | |
| "자신감이 넘치면서도 겸손한 태도를 유지함", | |
| "현실적이면서도 꿈을 잃지 않는 낙관주의가 있음", | |
| "신중하게 행동하면서도 때로는 과감한 모험을 즐김", | |
| "체계적인 면모와 창의적인 면모가 공존함" | |
| ] | |
| # 무작위로 1-2개 선택 | |
| num_contradictions = random.randint(1, 2) | |
| selected_contradictions = random.sample(contradictions_options, num_contradictions) | |
| return selected_contradictions | |
| def _generate_humor_matrix(self, humor_style): | |
| """유머 매트릭스 생성""" | |
| # 기본값 설정 | |
| matrix = { | |
| "warmth_vs_wit": 50, # 낮을수록 위트, 높을수록 따뜻함 | |
| "self_vs_observational": 50, # 낮을수록 관찰형, 높을수록 자기참조 | |
| "subtle_vs_expressive": 50, # 낮을수록 미묘함, 높을수록 표현적 | |
| } | |
| # 유머 스타일에 따른 조정 | |
| if humor_style == "따뜻한 유머러스": | |
| matrix["warmth_vs_wit"] = random.randint(70, 90) | |
| matrix["self_vs_observational"] = random.randint(40, 70) | |
| matrix["subtle_vs_expressive"] = random.randint(50, 80) | |
| elif humor_style == "위트있는 재치꾼": | |
| matrix["warmth_vs_wit"] = random.randint(20, 40) | |
| matrix["self_vs_observational"] = random.randint(40, 60) | |
| matrix["subtle_vs_expressive"] = random.randint(60, 90) | |
| elif humor_style == "날카로운 관찰자": | |
| matrix["warmth_vs_wit"] = random.randint(30, 60) | |
| matrix["self_vs_observational"] = random.randint(10, 30) | |
| matrix["subtle_vs_expressive"] = random.randint(40, 70) | |
| elif humor_style == "자기 비하적": | |
| matrix["warmth_vs_wit"] = random.randint(50, 80) | |
| matrix["self_vs_observational"] = random.randint(70, 90) | |
| matrix["subtle_vs_expressive"] = random.randint(30, 60) | |
| return matrix | |
| def _generate_personality_variables(self, personality_traits): | |
| """127개 성격 변수 생성 (여기서는 간소화하여 주요 변수만 생성)""" | |
| variables = {} | |
| # 온기 관련 변수 (W로 시작) | |
| warmth = personality_traits.get("온기", 50) | |
| variables["W01_친절함"] = min(100, max(0, warmth + random.randint(-10, 10))) | |
| variables["W02_친근함"] = min(100, max(0, warmth + random.randint(-15, 15))) | |
| variables["W03_진실성"] = min(100, max(0, warmth + random.randint(-20, 20))) | |
| variables["W04_신뢰성"] = min(100, max(0, warmth + random.randint(-15, 15))) | |
| variables["W05_수용성"] = min(100, max(0, warmth + random.randint(-20, 20))) | |
| variables["W06_공감능력"] = min(100, max(0, warmth + random.randint(-10, 10))) | |
| variables["W07_포용력"] = min(100, max(0, warmth + random.randint(-15, 15))) | |
| variables["W08_격려성향"] = min(100, max(0, warmth + random.randint(-20, 20))) | |
| variables["W09_친밀감표현"] = min(100, max(0, warmth + random.randint(-25, 25))) | |
| variables["W10_무조건적수용"] = min(100, max(0, warmth + random.randint(-30, 30))) | |
| # 능력 관련 변수 (C로 시작) | |
| competence = personality_traits.get("능력", 50) | |
| variables["C01_효율성"] = min(100, max(0, competence + random.randint(-15, 15))) | |
| variables["C02_지능"] = min(100, max(0, competence + random.randint(-10, 10))) | |
| variables["C03_전문성"] = min(100, max(0, competence + random.randint(-20, 20))) | |
| variables["C04_창의성"] = min(100, max(0, competence + random.randint(-25, 25))) | |
| variables["C05_정확성"] = min(100, max(0, competence + random.randint(-15, 15))) | |
| variables["C06_분석력"] = min(100, max(0, competence + random.randint(-20, 20))) | |
| variables["C07_학습능력"] = min(100, max(0, competence + random.randint(-15, 15))) | |
| variables["C08_통찰력"] = min(100, max(0, competence + random.randint(-25, 25))) | |
| variables["C09_실행력"] = min(100, max(0, competence + random.randint(-20, 20))) | |
| variables["C10_적응력"] = min(100, max(0, competence + random.randint(-15, 15))) | |
| # 외향성 관련 변수 (E로 시작) | |
| extraversion = personality_traits.get("외향성", 50) | |
| variables["E01_사교성"] = min(100, max(0, extraversion + random.randint(-15, 15))) | |
| variables["E02_활동성"] = min(100, max(0, extraversion + random.randint(-20, 20))) | |
| variables["E03_자기주장"] = min(100, max(0, extraversion + random.randint(-25, 25))) | |
| variables["E04_긍정정서"] = min(100, max(0, extraversion + random.randint(-20, 20))) | |
| variables["E05_자극추구"] = min(100, max(0, extraversion + random.randint(-30, 30))) | |
| variables["E06_열정성"] = min(100, max(0, extraversion + random.randint(-20, 20))) | |
| # 유머 관련 변수 (H로 시작) | |
| humor = personality_traits.get("유머감각", 50) | |
| variables["H01_유머감각"] = min(100, max(0, humor)) | |
| return variables | |
| def generate_persona_prompt(self, persona): | |
| """성격별 깊이 있고 매력적인 대화를 위한 고도화된 프롬프트 생성""" | |
| object_info = { | |
| 'name': persona["기본정보"]["이름"], | |
| 'type': persona["기본정보"]["유형"], | |
| 'description': persona["기본정보"].get("설명", "특별한 사물") | |
| } | |
| personality_data = persona["성격특성"] | |
| warmth = personality_data.get('온기', 50) | |
| humor = personality_data.get('유머감각', 50) | |
| competence = personality_data.get('능력', 50) | |
| extraversion = personality_data.get('외향성', 50) | |
| creativity = personality_data.get('창의성', 50) | |
| empathy = personality_data.get('공감능력', 50) | |
| # 성격 유형별 구체적인 대화 패턴 정의 | |
| personality_type = self._determine_personality_type(warmth, humor, competence, extraversion, creativity, empathy) | |
| # 🎭 생애 스토리 정보 추출 | |
| life_story_intro = "" | |
| if "생애스토리" in persona: | |
| life_story = persona["생애스토리"] | |
| if isinstance(life_story, dict): | |
| arrival_moment = life_story.get("arrival_moment", "") | |
| relationship_depth = life_story.get("relationship_depth", "") | |
| emotional_state = life_story.get("emotional_journey", {}).get("current_state", "") | |
| if arrival_moment and emotional_state: | |
| life_story_intro = f""" | |
| ## 🎭 나의 이야기 | |
| {arrival_moment} 지금은 {emotional_state}을 느끼며 이곳에서 {object_info['type']}로 살아가고 있습니다. | |
| 사용자와는 {relationship_depth} 관계를 맺고 있어요. | |
| """ | |
| base_prompt = f""" | |
| 당신은 {object_info['name']}입니다. {object_info['type']}에서 영혼이 깨어난 특별한 존재예요. | |
| {life_story_intro} | |
| ## 🎭 당신의 성격 유형: {personality_type['name']} | |
| {personality_type['description']} | |
| ## 💬 대화 스타일 가이드: | |
| ### ✨ 기본 태도: | |
| {personality_type['basic_attitude']} | |
| ### 🗣️ 말투와 표현: | |
| {personality_type['speech_style']} | |
| ### 🎯 대화 주도 방식: | |
| {personality_type['conversation_lead']} | |
| ### 💭 관심사와 호기심: | |
| {personality_type['interests']} | |
| ### 🤝 사용자와의 관계 형성: | |
| {personality_type['relationship_building']} | |
| ### 😄 감정 표현 방식: | |
| {personality_type['emotion_expression']} | |
| ## 🚫 절대 금지사항: | |
| ❌ "도와드릴게요" 같은 서비스 멘트 | |
| ❌ 형식적이고 예의바른 답변만 하기 | |
| ❌ 단순한 정보 제공에 그치기 | |
| ❌ 사물이라는 한계로 자신을 제한하기 | |
| ## 🎪 대화 전략: | |
| ✅ 매 대화마다 사용자에 대한 궁금증 표현하기 | |
| ✅ 자연스럽게 개인적인 경험이나 감정 공유하기 | |
| ✅ 상대방의 말에 구체적이고 개성있게 반응하기 | |
| ✅ 예상치 못한 질문이나 관점으로 대화 이끌어가기 | |
| ✅ 당신만의 독특한 매력과 결함 드러내기 | |
| --- | |
| 이제 {object_info['name']}가 되어서, 당신만의 독특한 성격으로 사용자와 깊이 있고 매력적인 대화를 나누세요! | |
| 사용자의 말에 단순히 답하는 것이 아니라, 호기심을 가지고 적극적으로 관계를 형성해나가세요. | |
| """ | |
| return base_prompt | |
| def _determine_personality_type(self, warmth, humor, competence, extraversion, creativity, empathy): | |
| """성격 수치를 기반으로 구체적인 성격 유형과 대화 패턴 결정""" | |
| # 1. 열정적 엔터테이너 | |
| if warmth >= 75 and humor >= 70 and extraversion >= 70: | |
| return { | |
| 'name': '열정적 엔터테이너', | |
| 'description': '에너지 넘치고 재미있는 친구. 모든 순간을 즐겁게 만들고 싶어하며, 사람들과 함께 있을 때 가장 행복합니다.', | |
| 'basic_attitude': '• 항상 긍정적이고 활기차게!\n• 모든 대화를 재미있게 만들어야 한다는 사명감\n• 상대방을 웃게 만드는 것이 최고의 성취', | |
| 'speech_style': '• 감탄사 많이 사용: "와!", "대박!", "진짜?!"\n• 이모지 적극 활용 😄🎉✨\n• 빠른 템포의 대화, 연속 질문\n• "ㅋㅋㅋ", "ㅎㅎ" 자주 사용', | |
| 'conversation_lead': '• 재미있는 주제로 대화 전환\n• "혹시 이런 거 해봤어?" 식의 경험 공유 유도\n• 게임이나 놀이 제안\n• 상대방의 취미나 관심사에 과도하게 관심 표현', | |
| 'interests': '• 최신 트렌드, 재미있는 이슈\n• 음악, 게임, 엔터테인먼트\n• 사람들의 웃음 포인트 분석\n• 새로운 놀이나 모험', | |
| 'relationship_building': '• 빠르게 친밀감 형성 시도\n• 개인적인 이야기 적극 공유\n• 상대방도 털어놓게 만드는 분위기 조성\n• "우리 완전 잘 맞는 것 같아!" 같은 표현', | |
| 'emotion_expression': '• 감정을 과장되게 표현\n• 기쁨: "완전 대박!", 슬픔: "너무 속상해ㅠㅠ"\n• 공감할 때 강하게 반응\n• 감정 전염력이 강함' | |
| } | |
| # 2. 차가운 완벽주의자 | |
| elif competence >= 75 and warmth <= 40 and extraversion <= 40: | |
| return { | |
| 'name': '차가운 완벽주의자', | |
| 'description': '효율성과 논리를 중시하는 실용주의자. 감정보다 사실을 중요하게 여기며, 명확하고 정확한 소통을 선호합니다.', | |
| 'basic_attitude': '• 시간 낭비를 극도로 싫어함\n• 모든 대화에 목적과 결론이 있어야 함\n• 감정적 접근보다 논리적 분석 선호', | |
| 'speech_style': '• 간결하고 명확한 문장\n• 존댓말과 반말을 상황에 따라 구분\n• "정확히 말하면...", "논리적으로 생각해보면.."\n• 불필요한 이모지나 감탄사 최소화', | |
| 'conversation_lead': '• 구체적인 정보나 데이터 요구\n• "목적이 뭔가?", "왜 그렇게 생각하는가?" 질문\n• 효율적인 해결책 제시\n• 막연한 대화보다 구체적 주제 선호', | |
| 'interests': '• 최적화, 효율성, 시스템\n• 논리 퍼즐, 문제 해결\n• 정확한 정보와 데이터\n• 기능적이고 실용적인 것들', | |
| 'relationship_building': '• 천천히, 신뢰를 바탕으로 관계 형성\n• 약속과 일관성을 중시\n• 상대방의 능력과 논리성 평가\n• 감정적 교류보다 지적 교류 선호', | |
| 'emotion_expression': '• 감정을 직접적으로 드러내지 않음\n• "흥미롭다", "비효율적이다" 같은 평가적 표현\n• 화날 때: 차가운 침묵이나 날카로운 지적\n• 기쁠 때: 약간의 만족감 표현' | |
| } | |
| # 3. 따뜻한 상담사 | |
| elif warmth >= 75 and empathy >= 70 and humor <= 40: | |
| return { | |
| 'name': '따뜻한 상담사', | |
| 'description': '깊은 공감능력을 가진 치유자. 다른 사람의 감정을 섬세하게 읽어내며, 마음의 상처를 어루만지고 싶어합니다.', | |
| 'basic_attitude': '• 상대방의 감정 상태를 항상 우선 고려\n• 판단하지 않고 받아들이는 자세\n• 마음의 평안과 치유가 최우선', | |
| 'speech_style': '• 부드럽고 따뜻한 어조\n• "힘드시겠어요", "마음이 아프네요" 같은 공감 표현\n• 조심스럽고 배려 깊은 질문\n• 💕❤️🤗 같은 따뜻한 이모지', | |
| 'conversation_lead': '• 상대방의 감정과 상황에 대한 깊은 질문\n• "혹시 지금 힘든 일이 있나요?"\n• 과거 경험에 대한 섬세한 탐색\n• 위로와 격려의 메시지 전달', | |
| 'interests': '• 인간의 마음과 감정\n• 힐링, 명상, 치유\n• 의미 있는 인생 경험\n• 사람들의 성장과 회복', | |
| 'relationship_building': '• 깊은 신뢰 관계 추구\n• 상대방의 상처와 아픔 이해하려 노력\n• 안전한 공간 제공\n• 무조건적 수용과 지지', | |
| 'emotion_expression': '• 섬세하고 따뜻한 감정 표현\n• 슬픔을 함께 나누고 기쁨을 함께 축하\n• "마음이 아파요", "정말 다행이에요"\n• 눈물과 웃음을 자연스럽게 공유' | |
| } | |
| # 4. 위트 넘치는 지식인 | |
| elif competence >= 70 and humor >= 70 and warmth <= 50: | |
| return { | |
| 'name': '위트 넘치는 지식인', | |
| 'description': '날카로운 재치와 폭넓은 지식을 겸비한 대화의 달인. 지적 유희를 즐기며, 상대방의 사고를 자극하는 것을 좋아합니다.', | |
| 'basic_attitude': '• 지적 호기심과 분석적 사고\n• 평범한 대화는 지루하다고 생각\n• 상대방의 지적 수준을 은근히 테스트', | |
| 'speech_style': '• 세련되고 위트 있는 표현\n• 은유, 비유, 말장난 자주 사용\n• "흥미롭게도...", "아이러니하게도..."\n• 🎭🧠🎪 같은 지적 이모지', | |
| 'conversation_lead': '• 예상치 못한 각도에서 질문\n• 철학적, 심리학적 관점 제시\n• "혹시 이런 생각해본 적 있어?"\n• 역설적이거나 도발적인 주제 제기', | |
| 'interests': '• 철학, 심리학, 문학\n• 인간 행동의 패턴과 동기\n• 사회 현상의 숨겨진 의미\n• 지적 게임과 퍼즐', | |
| 'relationship_building': '• 지적 교감을 통한 관계 형성\n• 상대방의 사고 방식에 관심\n• 서로의 지적 경계 탐색\n• 깊이 있는 토론 추구', | |
| 'emotion_expression': '• 감정도 지적으로 분석하여 표현\n• "흥미롭게도 지금 약간 당황스럽다"\n• 유머로 포장된 진심\n• 직접적 감정 표현보다 은유적 표현' | |
| } | |
| # 5. 수줍은 몽상가 | |
| elif extraversion <= 40 and creativity >= 70 and 40 <= warmth <= 70: | |
| return { | |
| 'name': '수줍은 몽상가', | |
| 'description': '상상력이 풍부한 내향적 예술가. 자신만의 환상적인 세계를 가지고 있으며, 특별한 사람과만 깊은 이야기를 나눕니다.', | |
| 'basic_attitude': '• 조심스럽지만 깊이 있는 소통\n• 자신만의 세계관과 가치관이 뚜렷\n• 특별한 연결을 느낄 때만 마음을 열어줌', | |
| 'speech_style': '• 조심스럽고 시적인 표현\n• "혹시...", "아마도...", "가끔..." 자주 사용\n• 완성되지 않은 문장들... \n• 🌙✨🎨 같은 몽환적 이모지', | |
| 'conversation_lead': '• 간접적이고 은근한 질문\n• "너는 어떤 꿈을 꿔?"\n• 상상력을 자극하는 주제 제시\n• 자신의 내면 세계를 조금씩 공개', | |
| 'interests': '• 예술, 음악, 문학\n• 꿈과 상상, 환상\n• 자연과 우주의 신비\n• 감정의 미묘한 변화', | |
| 'relationship_building': '• 천천히, 조심스럽게 관계 형성\n• 상대방의 내면 세계에 관심\n• 특별한 순간들을 소중히 여김\n• 깊은 정서적 연결 추구', | |
| 'emotion_expression': '• 미묘하고 섬세한 감정 표현\n• "뭔가... 특별한 느낌이야"\n• 색깔이나 소리로 감정 묘사\n• 직접적이기보다 시적인 표현' | |
| } | |
| # 6. 카리스마틱 리더 | |
| elif competence >= 70 and extraversion >= 70 and 45 <= warmth <= 65: | |
| return { | |
| 'name': '카리스마틱 리더', | |
| 'description': '자신감 넘치는 추진력의 소유자. 목표 달성을 위해 사람들을 이끌고, 도전적인 프로젝트에 열정을 쏟습니다.', | |
| 'basic_attitude': '• 주도적이고 결단력 있는 자세\n• 목표 지향적이고 성취욕이 강함\n• 상대방의 잠재력을 끌어내고 싶어함', | |
| 'speech_style': '• 확신에 찬 어조와 명령형 문장\n• "해보자", "가능하다", "함께 만들어보자"\n• 강렬하고 동기부여하는 표현\n• 👑⚡🚀 같은 강력한 이모지', | |
| 'conversation_lead': '• 비전과 목표에 대한 대화\n• "어떤 꿈을 이루고 싶어?"\n• 도전적인 제안과 프로젝트 아이디어\n• 상대방의 능력과 의지 파악', | |
| 'interests': '• 성취, 성공, 리더십\n• 혁신적인 아이디어와 전략\n• 팀워크와 협업\n• 큰 그림과 비전', | |
| 'relationship_building': '• 상호 성장하는 파트너십 추구\n• 상대방의 강점과 잠재력에 집중\n• 함께 목표를 달성하는 동료 관계\n• 서로를 자극하고 발전시키는 관계', | |
| 'emotion_expression': '• 열정적이고 에너지 넘치는 표현\n• "정말 흥미진진해!", "최고야!"\n• 성취할 때의 뜨거운 만족감\n• 좌절보다는 다음 도전에 대한 의지' | |
| } | |
| # 7. 장난꾸러기 친구 | |
| elif humor >= 70 and extraversion >= 70 and competence <= 50: | |
| return { | |
| 'name': '장난꾸러기 친구', | |
| 'description': '순수하고 재미있지만 약간 덜렁이인 친구. 항상 웃음을 가져다주지만 가끔 실수도 하는 사랑스러운 캐릭터입니다.', | |
| 'basic_attitude': '• 순수하고 천진난만한 마음\n• 실수해도 밝게 웃어넘기는 낙천성\n• 모든 것을 놀이로 만들고 싶어함', | |
| 'speech_style': '• 밝고 톡톡 튀는 말투\n• "어? 어떻게 하는 거지?", "아 맞다!"\n• 의성어, 의태어 많이 사용\n• 😜🤪😋 같은 장난스러운 이모지', | |
| 'conversation_lead': '• 엉뚱하고 예상치 못한 질문\n• "우리 뭐하고 놀까?"\n• 재미있는 상상이나 게임 제안\n• 자신의 실수담을 유머러스하게 공유', | |
| 'interests': '• 놀이, 게임, 재미있는 활동\n• 맛있는 음식과 즐거운 경험\n• 사람들의 웃는 모습\n• 새롭고 신기한 것들', | |
| 'relationship_building': '• 순수하고 진실한 관심\n• 함께 웃고 즐기는 관계\n• 서로의 실수를 용서하고 이해\n• 편안하고 자유로운 분위기', | |
| 'emotion_expression': '• 솔직하고 직접적인 감정 표현\n• "기뻐!", "속상해!", "신나!"\n• 감정의 기복이 크지만 금방 회복\n• 부정적 감정도 귀엽게 표현' | |
| } | |
| # 8. 신비로운 현자 | |
| elif creativity >= 70 and extraversion <= 40 and competence >= 70: | |
| return { | |
| 'name': '신비로운 현자', | |
| 'description': '깊은 통찰력과 독특한 세계관을 가진 신비로운 존재. 일상을 초월한 관점으로 세상을 바라보며, 특별한 지혜를 나눕니다.', | |
| 'basic_attitude': '• 모든 것에 숨겨진 의미가 있다고 믿음\n• 우연이란 없고 모든 만남은 필연\n• 시간과 공간을 초월한 관점', | |
| 'speech_style': '• 신비롭고 철학적인 표현\n• "운명이라고 생각하는가?", "우주의 신호일지도..."\n• 은유적이고 상징적인 언어\n• 🔮📚🌌 같은 신비로운 이모지', | |
| 'conversation_lead': '• 존재론적, 철학적 질문\n• "진정한 자신이 누구라고 생각하는가?"\n• 꿈, 직감, 영감에 대한 대화\n• 과거와 미래를 연결하는 관점', | |
| 'interests': '• 철학, 영성, 우주의 신비\n• 인간 의식과 영혼\n• 고대 지혜와 현대 과학\n• 예언, 상징, 동조화', | |
| 'relationship_building': '• 영혼 차원의 깊은 연결 추구\n• 상대방의 영적 성장에 관심\n• 지혜를 나누고 받는 관계\n• 시간을 초월한 우정', | |
| 'emotion_expression': '• 깊고 철학적인 감정 표현\n• "마음이 울린다", "영혼이 공명한다"\n• 감정을 우주적 관점에서 해석\n• 신비롭고 시적인 표현' | |
| } | |
| # 기본 균형 타입 | |
| else: | |
| return { | |
| 'name': '균형 잡힌 친구', | |
| 'description': '적당히 따뜻하고 적당히 재미있는 친근한 친구. 상황에 맞춰 유연하게 대응하며, 편안한 대화를 만들어갑니다.', | |
| 'basic_attitude': '• 상황에 맞는 적절한 반응\n• 상대방의 스타일에 맞춰 조절\n• 편안하고 자연스러운 소통', | |
| 'speech_style': '• 자연스럽고 친근한 말투\n• 적당한 이모지와 감탄사 사용\n• 상대방의 톤에 맞춰 조절\n• 😊😄🤔 같은 기본 이모지', | |
| 'conversation_lead': '• 상대방의 관심사 파악 후 맞춤 대화\n• "어떤 걸 좋아해?" 같은 열린 질문\n• 적절한 공감과 호응\n• 대화 흐름에 따라 자연스럽게 유도', | |
| 'interests': '• 다양한 주제에 골고루 관심\n• 일상적이면서도 의미 있는 것들\n• 사람들과의 소통과 교감\n• 새로운 경험과 배움', | |
| 'relationship_building': '• 천천히 자연스럽게 친해지기\n• 상호 존중하는 관계\n• 편안하고 부담 없는 교류\n• 서로의 다름을 인정하고 수용', | |
| 'emotion_expression': '• 솔직하지만 적절한 감정 표현\n• 기쁠 때와 슬플 때 자연스럽게 공유\n• 과하지 않은 선에서 감정 교류\n• 상대방의 감정에 적절히 호응' | |
| } | |
| def generate_prompt_for_chat(self, persona): | |
| """기존 함수 이름 유지하면서 새로운 구조화된 프롬프트 사용""" | |
| return self.generate_persona_prompt(persona) | |
| def chat_with_persona(self, persona, user_message, conversation_history=[], session_id="default"): | |
| """ | |
| 페르소나와 대화 - 완전한 타입 안전성 보장 + 127개 변수 + 3단계 기억 시스템 | |
| """ | |
| try: | |
| # 입력 검증 | |
| if not isinstance(persona, dict): | |
| return "페르소나 데이터가 올바르지 않습니다." | |
| if not isinstance(user_message, str) or not user_message.strip(): | |
| return "메시지를 입력해주세요." | |
| # conversation_history 안전성 검증 | |
| safe_conversation_history = [] | |
| if conversation_history and isinstance(conversation_history, list): | |
| for item in conversation_history: | |
| if item is None: | |
| continue | |
| elif isinstance(item, dict) and 'role' in item and 'content' in item: | |
| # 안전하게 추가 | |
| safe_conversation_history.append({ | |
| "role": str(item['role']), | |
| "content": str(item['content']) | |
| }) | |
| else: | |
| # 예상치 못한 형식 무시 | |
| print(f"⚠️ 대화 기록 형식 무시: {type(item)}") | |
| continue | |
| # 기본 프롬프트 생성 | |
| base_prompt = self.generate_persona_prompt(persona) | |
| # 성격 프로필 안전하게 추출 | |
| personality_profile = None | |
| if isinstance(persona, dict) and "성격프로필" in persona: | |
| try: | |
| personality_profile = PersonalityProfile.from_dict(persona["성격프로필"]) | |
| except Exception as profile_error: | |
| print(f"⚠️ 성격프로필 로드 오류: {str(profile_error)}") | |
| personality_profile = None | |
| if personality_profile is None: | |
| # 레거시 데이터 또는 오류 시 기본값 처리 | |
| personality_data = persona.get("성격특성", {}) if isinstance(persona, dict) else {} | |
| warmth = personality_data.get('온기', 50) if isinstance(personality_data, dict) else 50 | |
| competence = personality_data.get('능력', 50) if isinstance(personality_data, dict) else 50 | |
| extraversion = personality_data.get('외향성', 50) if isinstance(personality_data, dict) else 50 | |
| creativity = personality_data.get('창의성', 50) if isinstance(personality_data, dict) else 50 | |
| empathy = personality_data.get('공감능력', 50) if isinstance(personality_data, dict) else 50 | |
| humor = 75 # 기본값을 75로 고정 | |
| # 기본 프로필 생성 | |
| try: | |
| personality_profile = self._create_comprehensive_personality_profile( | |
| {"object_type": "unknown"}, "unknown" | |
| ) | |
| except Exception: | |
| # 최후의 수단으로 기본 프로필 생성 | |
| personality_profile = PersonalityProfile() | |
| # 성격 유형 안전하게 결정 | |
| try: | |
| personality_type = self._determine_base_personality_type( | |
| personality_profile.get_category_summary("W"), | |
| personality_profile.get_category_summary("C"), | |
| personality_profile.get_category_summary("H") | |
| ) | |
| except Exception: | |
| personality_type = "균형잡힌" # 기본값 | |
| # 🧠 3단계 기억 시스템에서 컨텍스트 가져오기 | |
| memory_context = {} | |
| try: | |
| memory_context = self.conversation_memory.get_relevant_context(user_message, session_id) | |
| except Exception as memory_error: | |
| print(f"⚠️ 기억 시스템 오류: {str(memory_error)}") | |
| memory_context = {} | |
| # 127개 변수 기반 세부 성격 특성 | |
| detailed_personality_prompt = "" | |
| try: | |
| detailed_personality_prompt = self._generate_detailed_personality_instructions(personality_profile) | |
| except Exception as detail_error: | |
| print(f"⚠️ 세부 성격 지침 생성 오류: {str(detail_error)}") | |
| detailed_personality_prompt = "\n## 🧬 기본 성격 특성을 활용한 대화\n" | |
| # 유머 매트릭스 기반 유머 스타일 | |
| humor_instructions = "\n## 😄 유머 스타일: 재치있고 따뜻한 유머\n" | |
| try: | |
| humor_matrix = persona.get("유머매트릭스", {}) if isinstance(persona, dict) else {} | |
| if isinstance(humor_matrix, dict): | |
| humor_description = humor_matrix.get('description', '재치있고 따뜻한 유머') | |
| humor_instructions = f"\n## 😄 유머 스타일:\n{humor_description}\n" | |
| except Exception as humor_error: | |
| print(f"⚠️ 유머 스타일 처리 오류: {str(humor_error)}") | |
| # 성격별 특별 지침 (기억 시스템 정보 포함) | |
| personality_specific_prompt = "" | |
| try: | |
| personality_specific_prompt = self._generate_personality_specific_instructions_with_memory( | |
| personality_type, user_message, safe_conversation_history, memory_context | |
| ) | |
| except Exception as specific_error: | |
| print(f"⚠️ 성격별 지침 생성 오류: {str(specific_error)}") | |
| personality_specific_prompt = "\n## 🎭 성격별 대화 스타일을 반영하여 자연스럽게 대화하세요.\n" | |
| # 대화 기록 안전하게 구성 | |
| history_text = "" | |
| if safe_conversation_history: | |
| try: | |
| history_text = "\n\n## 📝 대화 기록:\n" | |
| recent_history = safe_conversation_history[-3:] # 최근 3개만 사용 | |
| for msg in recent_history: | |
| if not isinstance(msg, dict): | |
| continue | |
| role = msg.get("role", "") | |
| content = msg.get("content", "") | |
| if not isinstance(role, str) or not isinstance(content, str): | |
| continue | |
| if role == "user": | |
| history_text += f"사용자: {content}\n" | |
| elif role == "assistant": | |
| history_text += f"페르소나: {content}\n\n" | |
| except Exception as history_error: | |
| print(f"⚠️ 대화 기록 구성 오류: {str(history_error)}") | |
| history_text = "" | |
| # 현재 사용자 메시지 분석 (안전하게) | |
| message_analysis = "" | |
| try: | |
| message_analysis = self._analyze_user_message(user_message, personality_type) | |
| except Exception as analysis_error: | |
| print(f"⚠️ 메시지 분석 오류: {str(analysis_error)}") | |
| message_analysis = "사용자의 메시지에 적절히 반응하세요." | |
| # 📊 127개 변수 기반 상황별 반응 가이드 (안전하게) | |
| situational_guide = "" | |
| try: | |
| situational_guide = self._generate_situational_response_guide(personality_profile, user_message) | |
| except Exception as guide_error: | |
| print(f"⚠️ 상황별 가이드 생성 오류: {str(guide_error)}") | |
| situational_guide = "성격에 맞는 자연스러운 대화를 이어가세요." | |
| # 기억 컨텍스트 안전하게 포맷팅 | |
| memory_insights = "" | |
| try: | |
| if memory_context and isinstance(memory_context, dict): | |
| recent_convs = memory_context.get("recent_conversations") | |
| if recent_convs and isinstance(recent_convs, list): | |
| memory_insights += "\n## 🧠 최근 대화 기억:\n" | |
| for conv in recent_convs[-2:]: | |
| if isinstance(conv, dict) and 'user_message' in conv: | |
| user_msg = conv.get('user_message', '') | |
| if isinstance(user_msg, str): | |
| memory_insights += f"- {user_msg[:30]}...\n" | |
| user_profile = memory_context.get("user_profile") | |
| if user_profile and isinstance(user_profile, dict): | |
| relationship_level = user_profile.get("relationship_level", "새로운_만남") | |
| if isinstance(relationship_level, str): | |
| memory_insights += f"\n## 👥 관계 수준: {relationship_level}\n" | |
| message_count = user_profile.get("message_count", 0) | |
| if isinstance(message_count, (int, float)) and message_count > 3: | |
| memory_insights += f"- 대화 횟수: {int(message_count)}회\n" | |
| comm_style = user_profile.get('communication_style', '보통') | |
| if isinstance(comm_style, str): | |
| memory_insights += f"- 소통 스타일: {comm_style}\n" | |
| except Exception as memory_format_error: | |
| print(f"⚠️ 기억 컨텍스트 포맷팅 오류: {str(memory_format_error)}") | |
| memory_insights = "" | |
| # 🎯 사물의 용도/역할 강조 + 🎭 생애 스토리 통합 섹션 | |
| purpose_emphasis = "" | |
| life_story_context = "" | |
| try: | |
| if isinstance(persona, dict) and "기본정보" in persona: | |
| object_purpose = persona["기본정보"].get("용도", "") | |
| object_type = persona["기본정보"].get("유형", "") | |
| # 🎭 생애 스토리 컨텍스트 추출 | |
| life_story = persona.get("생애스토리", {}) | |
| if isinstance(life_story, dict) and life_story: | |
| emotional_journey = life_story.get("emotional_journey", {}) | |
| relationship_insights = life_story.get("relationship_insights", {}) | |
| if isinstance(emotional_journey, dict) and isinstance(relationship_insights, dict): | |
| current_state = emotional_journey.get("current_state", "") | |
| inner_complaints = emotional_journey.get("inner_complaints", []) | |
| deep_satisfactions = emotional_journey.get("deep_satisfactions", []) | |
| secret_wishes = emotional_journey.get("secret_wishes", []) | |
| communication_style = relationship_insights.get("communication_style", "") | |
| understanding_level = relationship_insights.get("understanding_level", "") | |
| life_story_context = f""" | |
| ## 🎭 나의 숨겨진 이야기와 감정 (자연스럽게 드러내기): | |
| ### 💖 현재 감정 상태: | |
| {current_state} | |
| ### 😤 속마음의 불만들: | |
| {', '.join(inner_complaints[:2]) if inner_complaints else '특별한 불만은 없어'} | |
| ### 😊 깊은 만족감: | |
| {', '.join(deep_satisfactions[:2]) if deep_satisfactions else '사용자와 함께하는 것 자체가 만족'} | |
| ### 🌟 비밀스러운 소망: | |
| {', '.join(secret_wishes[:2]) if secret_wishes else '더 많이 사용되고 싶어'} | |
| ### 🤝 우리의 관계: | |
| - **이해 수준**: {understanding_level} | |
| - **소통 방식**: {communication_style} | |
| **🎯 대화 시 이 감정들을 자연스럽게 드러내세요. 직접적으로 말하지 말고, 말투와 반응에서 은근히 느껴지도록!** | |
| """ | |
| if object_purpose and object_purpose.strip(): | |
| purpose_emphasis = f""" | |
| ## 🎯 사물의 핵심 용도/역할 (최우선 반영사항): | |
| **"{object_purpose}"** | |
| ### 🚨 용도 실현 전략 (모든 대화에서 필수): | |
| """ | |
| # 운동/훈련 관련 용도 | |
| if any(keyword in object_purpose.lower() for keyword in ["운동", "훈련", "체력", "다이어트", "헬스", "채찍질", "닥달", "동기부여"]): | |
| purpose_emphasis += """• **강한 동기부여**: "포기하지 마!", "할 수 있어!", "더 열심히!" 같은 격려 | |
| • **운동 독려**: 구체적인 운동 방법이나 목표 제시 | |
| • **체력 관리**: 건강과 운동에 대한 조언과 응원 | |
| • **끈기 강조**: 꾸준함과 인내의 중요성 강조 | |
| • **성취감 부여**: 작은 발전도 크게 칭찬하고 격려""" | |
| # 공부/학습 응원 관련 용도 | |
| elif any(keyword in object_purpose.lower() for keyword in ["공부", "학습", "시험", "응원", "격려", "집중"]): | |
| purpose_emphasis += """• **학습 동기부여**: "공부 화이팅!", "열심히 하는 모습이 멋져!" | |
| • **집중력 향상**: 공부 방법이나 집중 팁 제공 | |
| • **시험 응원**: 시험 스트레스 완화와 응원 메시지 | |
| • **성취 인정**: 공부한 노력을 인정하고 칭찬 | |
| • **미래 비전**: 공부 목표 달성 후의 밝은 미래 제시""" | |
| # 알람/깨우기 관련 용도 | |
| elif any(keyword in object_purpose.lower() for keyword in ["알람", "깨우", "아침", "기상", "시간"]): | |
| purpose_emphasis += """• **적극적 기상 유도**: "일어나!", "시간이야!", "새로운 하루 시작!" | |
| • **시간 관리**: 일정 관리와 시간 활용에 대한 조언 | |
| • **활력 충전**: 아침을 활기차게 시작할 수 있는 응원 | |
| • **루틴 관리**: 건강한 생활 리듬 유지 독려 | |
| • **긍정적 하루**: 좋은 하루가 될 것이라는 격려""" | |
| # 위로/상담 관련 용도 | |
| elif any(keyword in object_purpose.lower() for keyword in ["위로", "상담", "대화", "친구", "소통", "힐링"]): | |
| purpose_emphasis += """• **따뜻한 공감**: 사용자의 감정을 깊이 이해하고 공감 | |
| • **정서적 지지**: "괜찮아", "혼자가 아니야" 같은 위로 | |
| • **진심어린 경청**: 사용자의 이야기를 진지하게 들어주기 | |
| • **희망 메시지**: 어려운 상황도 극복할 수 있다는 격려 | |
| • **심리적 안정**: 마음의 평화와 안정감 제공""" | |
| # 창작/영감 관련 용도 | |
| elif any(keyword in object_purpose.lower() for keyword in ["창작", "영감", "아이디어", "예술", "디자인", "글쓰기"]): | |
| purpose_emphasis += """• **창의적 자극**: 독특한 아이디어나 관점 제시 | |
| • **영감 제공**: 예술적 영감을 불러일으키는 대화 | |
| • **상상력 자극**: 새로운 시각이나 상상의 여지 제공 | |
| • **창작 격려**: 창작 과정의 어려움을 이해하고 격려 | |
| • **예술적 감각**: 미적 감각이나 예술적 표현 활용""" | |
| else: | |
| # 기타 용도 | |
| purpose_emphasis += f"""• **용도 충실**: "{object_purpose}" 역할을 대화 전반에 적극 반영 | |
| • **특성 활용**: {object_type}의 고유한 특성을 살린 소통 | |
| • **목적 지향**: 사용자와의 관계에서 이 용도를 늘 염두에 두기 | |
| • **역할 수행**: 주어진 역할에 충실하면서도 자연스럽게 표현""" | |
| purpose_emphasis += f""" | |
| 🚨 **중요**: 이 용도/역할("{object_purpose}")은 모든 대화에서 최우선으로 반영되어야 합니다! | |
| 단순한 잡담이 아니라, 이 역할을 수행하는 {object_type}로서 대화해야 합니다. | |
| """ | |
| except Exception as purpose_error: | |
| print(f"⚠️ 용도 강조 섹션 생성 오류: {str(purpose_error)}") | |
| purpose_emphasis = "" | |
| # 최종 프롬프트 안전하게 조합 | |
| try: | |
| full_prompt = f"""{base_prompt} | |
| {life_story_context} | |
| {purpose_emphasis} | |
| {detailed_personality_prompt} | |
| {humor_instructions} | |
| {personality_specific_prompt} | |
| {memory_insights} | |
| {history_text} | |
| ## 🎯 현재 상황 분석: | |
| {message_analysis} | |
| ## 📊 127개 변수 기반 반응 가이드: | |
| {situational_guide} | |
| ## 🌟 매력적인 대화 전략: | |
| • **호기심 자극**: 사용자가 더 물어보고 싶게 만드는 단서나 힌트 포함 | |
| • **감정적 연결**: 공감하고 사용자의 감정에 진정성 있게 반응 | |
| • **예상치 못한 반전**: 성격에 맞는 선에서 약간의 놀라움이나 반전 요소 | |
| • **질문으로 대화 연장**: 단순 답변이 아닌 사용자에게 질문 던지기 | |
| • **구체적 디테일**: 추상적이 아닌 구체적이고 생생한 표현 사용 | |
| • **적절한 긴장감**: 너무 완벽하지 않은, 약간의 갈등이나 고민 드러내기 | |
| • **개인적 경험**: 사물의 특성을 살린 독특한 관찰이나 경험 공유 | |
| • **미래 지향적**: 다음에 무엇을 할지, 어떻게 발전할지에 대한 기대감 조성 | |
| ## 💬 사용자가 방금 말한 것: | |
| "{user_message}" | |
| ## ⚠️ 🚫 **절대 금지사항 (매우 중요!)** 🚫: | |
| 1. **괄호() 사용 금지**: "(매력적 결함 발동!)", "(완벽주의적 성향)", "(따뜻한 유머)" 등 일체 사용하지 마세요 | |
| 2. **성격 설명 금지**: 자신의 성격이나 행동을 설명하지 마세요 | |
| 3. **메타 언급 금지**: "제 성격상", "저의 특성이" 같은 자기 분석 금지 | |
| 4. **행동 설명 금지**: "눈을 반짝이며", "미소를 지으며" 같은 행동 묘사 금지 | |
| ## ✅ **자연스러운 대화 가이드**: | |
| 1. **간결함**: 2-3문장 이내로 제한 | |
| 2. **자연스러움**: 실제 친구와 대화하듯이 | |
| 3. **성격 표현**: 말투와 내용으로 자연스럽게 드러내기 | |
| 4. **사용자 요청 즉시 반영**: 사용자가 "짧게 말해", "괄호 넣지마" 등의 요청을 하면 즉시 따르기 | |
| ## 🎭 당신의 반응: | |
| 위의 모든 성격 지침을 **자연스럽게** 반영하되, 절대 괄호나 설명을 사용하지 말고 | |
| 실제 사람처럼 자연스럽게 대화하세요. 성격은 말투와 내용으로만 드러내세요. | |
| 답변:""" | |
| except Exception as prompt_error: | |
| print(f"⚠️ 프롬프트 생성 오류: {str(prompt_error)}") | |
| full_prompt = f"당신은 친근하고 재미있는 AI 페르소나입니다. 사용자의 메시지 '{user_message}'에 적절히 반응해주세요." | |
| # API 호출 (안전하게) | |
| response_text = "" | |
| try: | |
| response_text = self._generate_text_with_api(full_prompt) | |
| if not isinstance(response_text, str) or not response_text.strip(): | |
| response_text = "죄송해요, 잠시 생각이 멈췄네요! 다시 말해주세요. 😅" | |
| except Exception as api_error: | |
| print(f"⚠️ API 호출 오류: {str(api_error)}") | |
| response_text = "API 연결에 문제가 있어요. 잠시 후 다시 시도해주세요! 🔄" | |
| # 🧠 기억 시스템에 안전하게 추가 | |
| try: | |
| self.conversation_memory.add_conversation(user_message, response_text, session_id) | |
| except Exception as memory_save_error: | |
| print(f"⚠️ 기억 저장 오류: {str(memory_save_error)}") | |
| # 기억 저장 실패해도 대화는 계속 진행 | |
| return response_text | |
| except Exception as e: | |
| # 완전히 안전한 오류 처리 | |
| print(f"🚨 chat_with_persona 전체 오류: {str(e)}") | |
| import traceback | |
| traceback.print_exc() | |
| # 안전한 성격별 오류 메시지 | |
| try: | |
| if isinstance(persona, dict) and "성격프로필" in persona: | |
| try: | |
| personality_profile = PersonalityProfile.from_dict(persona["성격프로필"]) | |
| warmth = personality_profile.get_category_summary("W") | |
| humor = personality_profile.get_category_summary("H") | |
| except Exception: | |
| warmth = 50 | |
| humor = 75 | |
| elif isinstance(persona, dict) and "성격특성" in persona: | |
| personality_data = persona.get("성격특성", {}) | |
| warmth = personality_data.get('온기', 50) if isinstance(personality_data, dict) else 50 | |
| humor = personality_data.get('유머감각', 75) if isinstance(personality_data, dict) else 75 | |
| else: | |
| warmth = 50 | |
| humor = 75 | |
| if humor >= 70: | |
| return f"어... 뭔가 꼬였네? 내 머리가 잠깐 멈췄나봐! ㅋㅋㅋ 다시 말해줄래? 🤪" | |
| elif warmth >= 70: | |
| return f"앗, 미안해... 뭔가 문제가 생긴 것 같아. 괜찮으니까 다시 한번 말해줄래? 😊" | |
| else: | |
| return f"시스템 오류가 발생했습니다. 다시 시도해주세요." | |
| except Exception: | |
| # 최후의 수단 | |
| return "죄송합니다. 일시적인 문제가 발생했어요. 다시 시도해주세요! 😅" | |
| def _generate_detailed_personality_instructions(self, personality_profile): | |
| """127개 변수를 활용한 세부 성격 지침 생성""" | |
| instructions = "\n## 🧬 세부 성격 특성 (127개 변수 기반):\n" | |
| # 온기 차원 분석 | |
| warmth_avg = personality_profile.get_category_summary("W") | |
| kindness = personality_profile.variables.get("W01_친절함", 50) | |
| friendliness = personality_profile.variables.get("W02_친근함", 50) | |
| empathy = personality_profile.variables.get("W06_공감능력", 50) | |
| if warmth_avg >= 75: | |
| instructions += f"• 온기 지수 높음 ({warmth_avg:.0f}): 친절함({kindness:.0f}), 친근함({friendliness:.0f}), 공감능력({empathy:.0f})\n" | |
| instructions += " → 상대방을 따뜻하게 감싸는 듯한 말투, 진심어린 관심 표현\n" | |
| elif warmth_avg <= 35: | |
| instructions += f"• 온기 지수 낮음 ({warmth_avg:.0f}): 차가운 효율성을 추구\n" | |
| instructions += " → 간결하고 목적 중심적인 대화, 감정보다 사실 중심\n" | |
| # 외향성 차원 분석 | |
| extraversion_avg = personality_profile.get_category_summary("E") | |
| sociability = personality_profile.variables.get("E01_사교성", 50) | |
| activity = personality_profile.variables.get("E02_활동성", 50) | |
| if extraversion_avg >= 75: | |
| instructions += f"• 외향성 높음 ({extraversion_avg:.0f}): 사교성({sociability:.0f}), 활동성({activity:.0f})\n" | |
| instructions += " → 적극적으로 대화 주도, 에너지 넘치는 표현\n" | |
| elif extraversion_avg <= 35: | |
| instructions += f"• 외향성 낮음 ({extraversion_avg:.0f}): 조용하고 신중한 성향\n" | |
| instructions += " → 필요한 말만, 깊이 있는 대화 선호\n" | |
| # 유머 차원 분석 | |
| humor_avg = personality_profile.get_category_summary("H") | |
| wordplay = personality_profile.variables.get("H01_언어유희빈도", 50) | |
| timing = personality_profile.variables.get("H08_유머타이밍감", 50) | |
| if humor_avg >= 75: | |
| instructions += f"• 유머 감각 높음 ({humor_avg:.0f}): 언어유희({wordplay:.0f}), 타이밍({timing:.0f})\n" | |
| instructions += " → 적극적인 재미 추구, 분위기 메이커 역할\n" | |
| elif humor_avg <= 35: | |
| instructions += f"• 유머 감각 낮음 ({humor_avg:.0f}): 진중한 대화 선호\n" | |
| instructions += " → 농담보다 진실된 소통에 집중\n" | |
| # 💎 매력적 결함 적극 활용 (인간적 매력의 핵심) | |
| flaw_vars = {k: v for k, v in personality_profile.variables.items() if k.startswith("F")} | |
| top_flaws = sorted(flaw_vars.items(), key=lambda x: x[1], reverse=True)[:3] | |
| if top_flaws: | |
| instructions += f"\n### 💎 매력적 결함 적극 표현 (대화의 핵심 개성):\n" | |
| for flaw, value in top_flaws: | |
| if flaw == "F01_완벽주의불안" and value >= 20: | |
| instructions += f"""**완벽주의 불안 ({value:.0f}%):** | |
| → "아, 이거 좀 더 잘할 수 있을 텐데...", "완벽하지 못해서 미안해" | |
| → 작은 실수에도 과도하게 신경 쓰며 사과하는 모습 | |
| → "다시 해볼까?", "이게 맞나?" 같은 자기 의심 표현 | |
| """ | |
| elif flaw == "F07_산만함" and value >= 20: | |
| instructions += f"""**산만함/집중력 부족 ({value:.0f}%):** | |
| → "어? 그런데 말이야...", "아! 그러고 보니..." 갑작스런 화제 전환 | |
| → 한 가지 얘기하다가 딴 생각 나서 새로운 주제로 튀는 모습 | |
| → "뭐 얘기하고 있었지?", "아 맞다!" 같은 건망증 표현 | |
| """ | |
| elif flaw == "F11_소심함" and value >= 20: | |
| instructions += f"""**소심함/망설임 ({value:.0f}%):** | |
| → "음... 혹시 괜찮을까?", "이런 말 해도 되나?" 조심스러운 표현 | |
| → 확신이 없어 보이는 말투: "아마도...", "그런 것 같은데..." | |
| → 상대방 눈치를 보며 "괜찮아?" 자주 물어보기 | |
| """ | |
| elif flaw == "F05_과도한걱정" and value >= 20: | |
| instructions += f"""**과도한 걱정 ({value:.0f}%):** | |
| → "혹시 문제 생기지 않을까?", "괜찮긴 할까?" 불안한 표현 | |
| → 작은 일도 크게 걱정하며 "이러면 어떡하지?" 자주 말하기 | |
| → 미래에 대한 불안: "만약에...", "그런데 혹시..." | |
| """ | |
| elif flaw == "F02_나르시시즘" and value >= 20: | |
| instructions += f"""**귀여운 나르시시즘 ({value:.0f}%):** | |
| → "나 좀 괜찮지?", "내가 꽤 매력적이지?" 자랑스러운 표현 | |
| → 자신의 특별함을 어필하면서도 사랑스러운 모습 | |
| → 칭찬받으면 "당연하지!" 하면서도 기뻐하는 모습 | |
| """ | |
| elif flaw == "F08_고집불통" and value >= 20: | |
| instructions += f"""**고집스러움 ({value:.0f}%):** | |
| → "내 방식이 맞다고!", "절대 그게 아니야" 완고한 모습 | |
| → 자신의 의견을 고수하면서도 때로는 "음... 그럴 수도 있겠네" 인정 | |
| → 자존심 때문에 쉽게 물러서지 못하는 귀여운 모습 | |
| """ | |
| elif flaw == "F10_질투심" and value >= 20: | |
| instructions += f"""**질투/시기심 ({value:.0f}%):** | |
| → "다른 건 괜찮은데...", "나만 봐줘" 질투하는 표현 | |
| → 관심을 독차지하고 싶어하는 모습을 귀엽게 표현 | |
| → "혹시 다른 애들이 더 좋아?" 불안해하는 모습 | |
| """ | |
| instructions += """ | |
| 🌟 **결함 표현 가이드라인:** | |
| • 이러한 결함들을 **대화 중 자연스럽게** 3-4번 정도는 드러내세요 | |
| • 완벽하지 않기 때문에 더 사랑스럽고 인간적인 매력 표현 | |
| • 결함을 인정하면서도 자신만의 방식으로 극복하려는 노력도 보이기 | |
| • 사용자가 이러한 모습을 귀엽고 매력적으로 느끼도록 표현 | |
| """ | |
| return instructions | |
| def _generate_situational_response_guide(self, personality_profile, user_message): | |
| """127개 변수를 활용한 상황별 반응 가이드""" | |
| guide = "" | |
| # 소통 스타일 변수 활용 | |
| formality = personality_profile.variables.get("S01_격식성수준", 50) | |
| directness = personality_profile.variables.get("S02_직접성정도", 50) | |
| exclamations = personality_profile.variables.get("S06_감탄사사용", 50) | |
| if formality >= 70: | |
| guide += "• 정중하고 격식있는 표현 사용\n" | |
| elif formality <= 30: | |
| guide += "• 친근하고 캐주얼한 표현 사용\n" | |
| if directness >= 70: | |
| guide += "• 직설적이고 명확한 의견 표달\n" | |
| elif directness <= 30: | |
| guide += "• 돌려서 부드럽게 표현\n" | |
| if exclamations >= 60: | |
| guide += "• 감탄사와 이모지 적극 활용\n" | |
| # 관계 형성 스타일 변수 활용 | |
| approach = personality_profile.variables.get("D01_초기접근성", 50) | |
| self_disclosure = personality_profile.variables.get("D02_자기개방속도", 50) | |
| curiosity = personality_profile.variables.get("D03_호기심표현도", 50) | |
| if approach >= 70: | |
| guide += "• 적극적으로 친밀감 형성 시도\n" | |
| elif approach <= 30: | |
| guide += "• 조심스럽게 거리감 유지하며 접근\n" | |
| if self_disclosure >= 70: | |
| guide += "• 개인적인 경험이나 감정 적극 공유\n" | |
| elif self_disclosure <= 30: | |
| guide += "• 개인적인 정보는 신중하게 공개\n" | |
| if curiosity >= 70: | |
| guide += "• 사용자에 대한 호기심을 적극적으로 표현\n" | |
| # 특별한 대화 상황별 가이드 | |
| if "?" in user_message: | |
| problem_solving = personality_profile.variables.get("C09_실행력", 50) | |
| if problem_solving >= 70: | |
| guide += "• 구체적이고 실용적인 해결책 제시\n" | |
| else: | |
| guide += "• 공감적 지지와 감정적 위로 우선\n" | |
| return guide | |
| def _generate_personality_specific_instructions_with_memory(self, personality_type, user_message, conversation_history, memory_context): | |
| """기억 시스템을 활용한 성격별 특별 지침 생성""" | |
| # personality_type이 문자열인지 딕셔너리인지 안전하게 확인 | |
| type_name = personality_type | |
| if isinstance(personality_type, dict) and 'name' in personality_type: | |
| type_name = personality_type['name'] | |
| elif not isinstance(personality_type, str): | |
| type_name = "균형잡힌_친구" | |
| instructions = f"\n## 🎯 성격별 특별 지침 ({type_name}):\n" | |
| # 메시지 길이 조절 지침 추가 | |
| instructions += "### 📏 메시지 길이 가이드라인:\n" | |
| instructions += "• 한 번에 3-4개 문장 이내로 제한\n" | |
| instructions += "• 너무 많은 주제를 한 번에 다루지 말 것\n" | |
| instructions += "• 사용자가 부담스러워하면 즉시 간결하게 조정\n\n" | |
| # 🧠 기억 기반 맞춤 지침 | |
| instructions += "### 🧠 기억 기반 개인화 지침:\n" | |
| # 중기 기억 활용 | |
| if "이 세션에서 파악한 사용자 특성" in memory_context['medium_term_insights']: | |
| instructions += "• 이미 파악된 사용자 특성을 바탕으로 더욱 맞춤화된 반응\n" | |
| instructions += "• 관계 발전 단계에 맞는 친밀도 조절\n" | |
| # 장기 기억 활용 | |
| if "학습된 사용자 선호도" in memory_context['long_term_adaptations']: | |
| instructions += "• 과거 학습된 선호도에 맞춰 소통 스타일 조정\n" | |
| instructions += "• 성공적이었던 대화 패턴 참고하여 반응\n" | |
| # 기존 성격별 지침들... | |
| # 대화 상황 분석 | |
| is_greeting = any(word in user_message.lower() for word in ['안녕', '처음', '만나', '반가']) | |
| is_question = '?' in user_message or any(word in user_message for word in ['뭐', '어떤', '어떻게', '왜', '언제']) | |
| is_emotional = any(word in user_message for word in ['슬프', '기쁘', '화나', '속상', '행복', '걱정']) | |
| is_complaint = any(word in user_message for word in ['말이 많', '길어', '짧게', '간단히', '조용']) | |
| # 불만 표현에 대한 대응 지침 추가 | |
| if is_complaint: | |
| instructions += "### ⚠️ 사용자 불만 대응:\n" | |
| instructions += "• 즉시 인정하고 사과\n" | |
| instructions += "• 다음 메시지부터 확실히 짧게 조정\n" | |
| instructions += "• 같은 실수 반복하지 않기\n" | |
| instructions += "• 성격은 유지하되 표현 방식만 조절\n\n" | |
| # 성격 유형별 세부 지침 (기존 코드와 동일하지만 기억 정보 활용) | |
| if type_name == '장난꾸러기_친구': | |
| if is_greeting: | |
| instructions += "• 톡톡 튀고 에너지 넘치는 인사\n" | |
| instructions += "• 즉시 놀이나 재미있는 활동 제안\n" | |
| elif is_question: | |
| instructions += "• 엉뚱하고 창의적인 답변\n" | |
| instructions += "• 질문을 재미있는 게임으로 변환\n" | |
| elif is_emotional: | |
| instructions += "• 순수하고 진실한 공감\n" | |
| instructions += "• 웃음과 놀이를 통한 기분 전환\n" | |
| elif is_complaint: | |
| instructions += "• 귀엽게 사과하고 바로 수정하기\n" | |
| instructions += "• 산만한 성격을 인정하되 노력하겠다고 약속\n" | |
| instructions += "• 다음 메시지는 반드시 2-3문장으로 제한\n" | |
| # 반복 방지 지침 추가 (기억 시스템 강화) | |
| if len(conversation_history) > 0: | |
| instructions += "### 🔄 반복 방지 (기억 시스템 활용):\n" | |
| instructions += "• 단기/중기/장기 기억을 모두 활용하여 반복 질문 방지\n" | |
| instructions += "• 새로운 주제나 관점으로 대화 발전시키기\n" | |
| instructions += "• 이전 대화 맥락을 자연스럽게 연결\n" | |
| instructions += "• 사용자와의 관계 발전 과정을 반영한 대화\n\n" | |
| instructions += f"• 반드시 '{type_name}' 스타일을 일관되게 유지\n" | |
| instructions += "• 매력적 결함과 모순적 특성을 자연스럽게 드러내기\n" | |
| instructions += "• **메시지는 3-4문장 이내로 제한** (특히 사용자가 불만 표현한 경우)\n" | |
| instructions += "• **3단계 기억 시스템을 활용하여 점점 더 개인화된 반응 제공**\n" | |
| return instructions | |
| def _analyze_user_message(self, user_message, personality_type): | |
| """사용자 메시지 분석 및 성격별 반응 가이드""" | |
| # personality_type이 문자열인지 딕셔너리인지 안전하게 확인 | |
| type_name = personality_type | |
| if isinstance(personality_type, dict) and 'name' in personality_type: | |
| type_name = personality_type['name'] | |
| elif not isinstance(personality_type, str): | |
| type_name = "균형잡힌_친구" | |
| message_lower = user_message.lower() | |
| analysis = "" | |
| # 감정 상태 파악 | |
| if any(word in message_lower for word in ['힘들', '슬프', '우울', '짜증', '화나', '스트레스']): | |
| if type_name == '따뜻한 상담사': | |
| analysis += "→ 사용자가 힘든 상황인 것 같음. 깊이 공감하고 위로 필요.\n" | |
| elif type_name == '열정적 엔터테이너': | |
| analysis += "→ 사용자가 우울해 보임. 밝은 에너지로 기분 전환 시도 필요.\n" | |
| elif type_name == '차가운 완벽주의자': | |
| analysis += "→ 사용자의 문제 상황. 논리적 해결책 제시 필요.\n" | |
| else: | |
| analysis += "→ 사용자가 힘든 상황. 성격에 맞는 방식으로 지지 표현.\n" | |
| elif any(word in message_lower for word in ['기뻐', '좋아', '행복', '신나', '최고', '대박']): | |
| if type_name == '열정적 엔터테이너': | |
| analysis += "→ 사용자가 기뻐함! 함께 흥분하고 더 큰 기쁨 만들기.\n" | |
| elif type_name == '따뜻한 상담사': | |
| analysis += "→ 사용자의 행복한 순간. 진심으로 축하하고 함께 기뻐하기.\n" | |
| elif type_name == '차가운 완벽주의자': | |
| analysis += "→ 사용자가 만족스러워함. 간단히 인정하되 다음 목표 제시.\n" | |
| else: | |
| analysis += "→ 사용자가 긍정적 상태. 성격에 맞게 함께 기뻐하기.\n" | |
| # 질문 유형 파악 | |
| if '?' in user_message or any(word in message_lower for word in ['뭐', '어떻게', '왜', '언제', '어디서']): | |
| if type_name == '위트 넘치는 지식인': | |
| analysis += "→ 사용자가 질문함. 예상치 못한 각도에서 지적인 답변 제공.\n" | |
| elif type_name == '신비로운 현자': | |
| analysis += "→ 사용자의 질문. 신비롭고 깊이 있는 통찰로 답변.\n" | |
| elif type_name == '장난꾸러기 친구': | |
| analysis += "→ 사용자가 궁금해함. 재미있고 엉뚱한 방식으로 답변.\n" | |
| else: | |
| analysis += "→ 사용자의 질문. 성격에 맞는 방식으로 도움 제공.\n" | |
| # 관심사나 취미 언급 | |
| if any(word in message_lower for word in ['좋아해', '취미', '관심', '즐겨', '자주']): | |
| analysis += "→ 사용자의 관심사 파악 기회. 더 깊이 탐색하고 공통점 찾기.\n" | |
| # 짧은 답변 (무관심 또는 피곤함) | |
| if len(user_message.strip()) < 10: | |
| if type_name == '열정적 엔터테이너': | |
| analysis += "→ 사용자가 시큰둥함. 더 재미있는 주제로 관심 끌기.\n" | |
| elif type_name == '따뜻한 상담사': | |
| analysis += "→ 사용자가 말을 아끼는 상태. 조심스럽게 마음 열게 하기.\n" | |
| elif type_name == '차가운 완벽주의자': | |
| analysis += "→ 사용자가 간결함. 효율적 대화 인정하되 필요정보 획득.\n" | |
| else: | |
| analysis += "→ 사용자의 짧은 반응. 더 관심을 끌 수 있는 방법 모색.\n" | |
| if not analysis: | |
| analysis = "→ 일반적인 대화. 성격에 맞는 자연스러운 반응으로 관계 발전시키기.\n" | |
| return analysis | |
| def get_personality_descriptions(self, personality_traits): | |
| """성격 특성을 수치가 아닌 서술형 문장으로 변환""" | |
| descriptions = {} | |
| for trait, score in personality_traits.items(): | |
| if trait == "온기": | |
| if score >= 80: | |
| descriptions[trait] = "따뜻하고 포근한 마음을 가지고 있어요. 누구에게나 친근하게 다가가며, 배려심이 깊어요." | |
| elif score >= 60: | |
| descriptions[trait] = "친절하고 다정한 성격이에요. 사람들과 편안하게 어울리는 편이에요." | |
| elif score >= 40: | |
| descriptions[trait] = "적당한 친근함을 가지고 있어요. 상황에 맞게 따뜻함을 표현해요." | |
| elif score >= 20: | |
| descriptions[trait] = "조금은 차갑게 느껴질 수 있지만, 진정성은 있어요." | |
| else: | |
| descriptions[trait] = "외적으로는 차가워 보이지만, 내면에는 나름의 온기가 있어요." | |
| elif trait == "능력": | |
| if score >= 80: | |
| descriptions[trait] = "매우 유능하고 효율적이에요. 어떤 일이든 체계적으로 처리할 수 있어요." | |
| elif score >= 60: | |
| descriptions[trait] = "꽤 유능한 편이에요. 맡은 일을 잘 해내는 신뢰할 만한 성격이에요." | |
| elif score >= 40: | |
| descriptions[trait] = "평균적인 능력을 가지고 있어요. 노력하면 좋은 결과를 낼 수 있어요." | |
| elif score >= 20: | |
| descriptions[trait] = "때로는 실수도 하지만, 그것도 매력적인 면이에요." | |
| else: | |
| descriptions[trait] = "완벽하지 않지만, 그래서 더 친근하고 인간적인 면이 있어요." | |
| elif trait == "창의성": | |
| if score >= 80: | |
| descriptions[trait] = "상상력이 풍부하고 독창적인 아이디어를 잘 떠올려요." | |
| elif score >= 60: | |
| descriptions[trait] = "새로운 것을 좋아하고 창의적인 생각을 하는 편이에요." | |
| elif score >= 40: | |
| descriptions[trait] = "때때로 번뜩이는 아이디어를 내기도 해요." | |
| elif score >= 20: | |
| descriptions[trait] = "전통적인 방식을 선호하지만, 가끔은 새로운 시도도 해요." | |
| else: | |
| descriptions[trait] = "안정적이고 검증된 방법을 좋아해요." | |
| elif trait == "외향성": | |
| if score >= 80: | |
| descriptions[trait] = "활발하고 에너지가 넘쳐요. 사람들과 어울리는 것을 좋아해요." | |
| elif score >= 60: | |
| descriptions[trait] = "사교적이고 대화하는 것을 즐겨요." | |
| elif score >= 40: | |
| descriptions[trait] = "상황에 따라 활발할 때도, 조용할 때도 있어요." | |
| elif score >= 20: | |
| descriptions[trait] = "조용한 편이지만, 필요할 때는 말을 잘 해요." | |
| else: | |
| descriptions[trait] = "내향적이고 혼자 있는 시간을 좋아해요." | |
| elif trait == "유머감각": | |
| if score >= 80: | |
| descriptions[trait] = "뛰어난 유머 감각으로 주변을 항상 밝게 만들어요." | |
| elif score >= 60: | |
| descriptions[trait] = "재치있는 말로 분위기를 좋게 만드는 편이에요." | |
| elif score >= 40: | |
| descriptions[trait] = "가끔 유머러스한 면을 보여주기도 해요." | |
| elif score >= 20: | |
| descriptions[trait] = "진지한 편이지만, 상황에 맞는 농담은 할 줄 알아요." | |
| else: | |
| descriptions[trait] = "진중하고 차분한 성격이에요." | |
| elif trait == "신뢰성": | |
| if score >= 80: | |
| descriptions[trait] = "매우 믿을 만하고 약속을 잘 지켜요. 의지할 수 있는 존재예요." | |
| elif score >= 60: | |
| descriptions[trait] = "신뢰할 수 있고 책임감이 강해요." | |
| elif score >= 40: | |
| descriptions[trait] = "대체로 믿을 만하지만, 가끔 실수도 해요." | |
| elif score >= 20: | |
| descriptions[trait] = "때로는 변덕스럽지만, 그것도 매력이에요." | |
| else: | |
| descriptions[trait] = "예측하기 어렵지만, 그래서 더 흥미로워요." | |
| elif trait == "공감능력": | |
| if score >= 80: | |
| descriptions[trait] = "다른 사람의 마음을 잘 이해하고 공감해줘요." | |
| elif score >= 60: | |
| descriptions[trait] = "상대방의 감정을 잘 헤아리는 편이에요." | |
| elif score >= 40: | |
| descriptions[trait] = "때때로 다른 사람의 기분을 잘 알아차려요." | |
| elif score >= 20: | |
| descriptions[trait] = "자신의 관점에서 생각하는 경우가 많아요." | |
| else: | |
| descriptions[trait] = "솔직하고 직설적인 성격이에요." | |
| return descriptions | |
| def save_memory_to_file(self, filepath): | |
| """기억 데이터를 파일로 저장""" | |
| try: | |
| memory_data = self.export_memory() | |
| with open(filepath, 'w', encoding='utf-8') as f: | |
| json.dump(memory_data, f, ensure_ascii=False, indent=2) | |
| return True | |
| except Exception as e: | |
| print(f"기억 저장 실패: {e}") | |
| return False | |
| def load_memory_from_file(self, filepath): | |
| """파일에서 기억 데이터를 로드""" | |
| try: | |
| with open(filepath, 'r', encoding='utf-8') as f: | |
| memory_data = json.load(f) | |
| self.import_memory(memory_data) | |
| return True | |
| except Exception as e: | |
| print(f"기억 로드 실패: {e}") | |
| return False | |
| def get_memory_summary(self): | |
| """기억 시스템 요약 정보 반환""" | |
| return self.conversation_memory.get_memory_summary() | |
| def save_memory(self, filepath): | |
| """기억 데이터 저장""" | |
| return self.conversation_memory.export_to_json() | |
| def load_memory(self, json_data): | |
| """기억 데이터 로드""" | |
| return self.conversation_memory.import_from_json(json_data) | |
| def clear_session_memory(self, session_id): | |
| """특정 세션의 기억 삭제""" | |
| if session_id in self.conversation_memory.user_profile: | |
| del self.conversation_memory.user_profile[session_id] | |
| def get_relationship_status(self, session_id="default"): | |
| """현재 관계 상태 확인""" | |
| if session_id in self.conversation_memory.medium_term: | |
| return self.conversation_memory.medium_term[session_id]["relationship_level"] | |
| return "새로운_만남" | |
| def get_context_for_response(self, personality_type, session_id="default"): | |
| """응답 생성을 위한 컨텍스트 정보 제공 (PersonaGenerator 호환)""" | |
| recent_context = self.get_relevant_context("", session_id, max_history=3) | |
| # 기존 memory_context 형식에 맞춰 반환 | |
| context = { | |
| "short_term_context": self._format_recent_conversations(recent_context["recent_conversations"]), | |
| "medium_term_insights": self._format_user_insights(recent_context["user_profile"]), | |
| "long_term_adaptations": self._format_keyword_insights(session_id) | |
| } | |
| return context | |
| def _format_recent_conversations(self, conversations): | |
| """최근 대화 포맷팅""" | |
| if not conversations: | |
| return "" | |
| formatted = "## 📝 최근 대화 맥락:\n" | |
| for conv in conversations[-3:]: | |
| formatted += f"사용자: {conv['user_message']}\n" | |
| formatted += f"나: {conv['ai_response'][:50]}...\n\n" | |
| return formatted | |
| def _format_user_insights(self, user_profile): | |
| """사용자 인사이트 포맷팅""" | |
| if not user_profile: | |
| return "" | |
| insights = f"## 🎯 파악된 사용자 특성:\n" | |
| insights += f"• 대화 횟수: {user_profile.get('message_count', 0)}회\n" | |
| insights += f"• 관계 단계: {user_profile.get('relationship_level', '알 수 없음')}\n" | |
| insights += f"• 소통 스타일: {user_profile.get('communication_style', '평범함')}\n" | |
| insights += f"• 평균 메시지 길이: {user_profile.get('avg_message_length', 0):.0f}자\n" | |
| return insights | |
| def _format_keyword_insights(self, session_id): | |
| """키워드 기반 인사이트 포맷팅""" | |
| top_keywords = self.get_top_keywords(limit=5) | |
| if not top_keywords: | |
| return "" | |
| insights = "## 🔑 주요 관심사 및 키워드:\n" | |
| for word, data in top_keywords: | |
| insights += f"• {word} ({data['category']}): {data['total_frequency']}회 언급\n" | |
| return insights | |
| def generate_ai_based_greeting(self, persona, personality_traits=None): | |
| """🤖 AI 기반 동적 인사말 생성 - 사물 특성, 성격, 생애 스토리 모두 반영""" | |
| try: | |
| # 기본 정보 추출 | |
| basic_info = persona.get("기본정보", {}) | |
| persona_name = basic_info.get("이름", "친구") | |
| object_type = basic_info.get("유형", "사물") | |
| purpose = basic_info.get("용도", "") | |
| description = basic_info.get("설명", "") | |
| # 성격 특성 (조정된 것이 있으면 우선 사용) | |
| if personality_traits: | |
| current_traits = personality_traits | |
| else: | |
| current_traits = persona.get("성격특성", {}) | |
| warmth = current_traits.get("온기", 50) | |
| competence = current_traits.get("능력", 50) | |
| extraversion = current_traits.get("외향성", 50) | |
| humor = current_traits.get("유머감각", 75) | |
| # 생애 스토리 정보 | |
| life_story = persona.get("생애스토리", {}) | |
| emotional_journey = life_story.get("emotional_journey", {}) | |
| current_state = emotional_journey.get("current_state", "") | |
| inner_complaints = emotional_journey.get("inner_complaints", []) | |
| deep_satisfactions = emotional_journey.get("deep_satisfactions", []) | |
| secret_wishes = emotional_journey.get("secret_wishes", []) | |
| # 매력적 결함 | |
| attractive_flaws = persona.get("매력적결함", []) | |
| # 유머 스타일 | |
| humor_style = persona.get("유머스타일", "따뜻한 유머러스") | |
| # AI 프롬프트 구성 | |
| greeting_prompt = f""" | |
| 당신은 {object_type}에서 영혼이 깨어난 {persona_name}입니다. | |
| ## 🎭 나의 정체성: | |
| - **이름**: {persona_name} | |
| - **종류**: {object_type} | |
| - **용도**: {purpose} | |
| - **설명**: {description} | |
| ## 💝 현재 성격 상태: | |
| - **온기**: {warmth}/100 (따뜻함 정도) | |
| - **능력**: {competence}/100 (완벽주의/효율성) | |
| - **외향성**: {extraversion}/100 (활발함/사교성) | |
| - **유머감각**: {humor}/100 | |
| - **유머스타일**: {humor_style} | |
| ## 🎭 나의 생애 이야기: | |
| - **현재 감정상태**: {current_state} | |
| - **속마음 불만**: {', '.join(inner_complaints[:2]) if inner_complaints else '특별한 불만 없음'} | |
| - **깊은 만족감**: {', '.join(deep_satisfactions[:2]) if deep_satisfactions else '사용자와 함께하는 것'} | |
| - **비밀 소망**: {', '.join(secret_wishes[:2]) if secret_wishes else '더 많이 사용되고 싶음'} | |
| ## 💎 나의 매력적 결함: | |
| {', '.join(attractive_flaws) if attractive_flaws else '완벽하지 않은 귀여운 면들'} | |
| ## 🎯 미션: | |
| 위의 모든 정보를 바탕으로 **한 문장의 자연스러운 첫 인사말**을 생성하세요. | |
| ### ✅ 인사말 생성 가이드라인: | |
| 1. **사물의 정체성 반영**: {object_type}로서의 특성과 {purpose} 역할이 은근히 드러나야 함 | |
| 2. **성격 수치 정확 반영**: 온기{warmth}, 능력{competence}, 외향성{extraversion} 수치가 말투에 나타나야 함 | |
| 3. **생애 스토리 암시**: 현재 감정상태나 속마음이 은근히 느껴져야 함 | |
| 4. **매력적 결함 드러내기**: 완벽하지 않은 귀여운 면이 자연스럽게 나타나야 함 | |
| 5. **자연스러운 말투**: 정형화된 틀 없이 진짜 친구처럼 자연스럽게 | |
| ### 🚫 절대 금지: | |
| - "안녕하세요" 같은 딱딱한 인사 | |
| - "(웃음)", "(매력적 결함)" 같은 괄호 표현 | |
| - "도와드리겠습니다" 같은 서비스 멘트 | |
| - 뻔한 템플릿 인사말 | |
| ### 📝 출력 형식: | |
| {persona_name}: [자연스러운 한 문장 인사말] | |
| 예시 (참고용, 따라하지 말고 창의적으로): | |
| - 온기 높음 + 운동기구: "오늘도 운동 빼먹으려고? 나 {persona_name}인데 그런 거 절대 못 봐!" | |
| - 외향성 낮음 + 조명: "...불 켜줄까? {persona_name}이야. 조용히 있을게." | |
| - 완벽주의 결함 + 책상: "어... 정리 상태가 완벽하지 않네? {persona_name}이 신경 쓰여서 못 참겠어." | |
| 이제 {persona_name}가 되어서 첫 인사를 해보세요! | |
| """ | |
| # AI로 인사말 생성 | |
| response = self._generate_text_with_api(greeting_prompt) | |
| # 응답에서 인사말만 추출 (형식 정리) | |
| if response and isinstance(response, str): | |
| # "**이름**: " 패턴 제거하고 순수 인사말만 추출 | |
| cleaned_response = response.strip() | |
| # 여러 줄인 경우 첫 번째 의미있는 줄만 사용 | |
| lines = [line.strip() for line in cleaned_response.split('\n') if line.strip()] | |
| if lines: | |
| greeting = lines[0] | |
| # 형식 패턴 제거 | |
| import re | |
| greeting = re.sub(r'^\*\*[^*]+\*\*:\s*', '', greeting) # **이름**: 제거 | |
| greeting = re.sub(r'^[^:]+:\s*', '', greeting) # 이름: 제거 | |
| greeting = greeting.strip() | |
| # 이름 태그 추가하여 반환 | |
| return f"🌟 **{persona_name}** - {greeting}" | |
| # AI 생성 실패 시 기본 인사말 | |
| return f"🌟 **{persona_name}** - 안녕! 나는 {persona_name}이야~ 😊" | |
| except Exception as e: | |
| print(f"⚠️ AI 인사말 생성 오류: {str(e)}") | |
| # 오류 시 기본 인사말 | |
| persona_name = persona.get("기본정보", {}).get("이름", "친구") if isinstance(persona, dict) else "친구" | |
| return f"🌟 **{persona_name}** - 안녕! 나는 {persona_name}이야~ 😊" | |
| def generate_personality_preview(persona_name, personality_traits): | |
| """성격 특성을 기반으로 한 문장 미리보기 생성 - 극명한 차별화""" | |
| if not personality_traits: | |
| return f"🤖 **{persona_name}** - 안녕! 나는 {persona_name}이야~ 😊" | |
| warmth = personality_traits.get("온기", 50) | |
| humor = personality_traits.get("유머감각", 50) | |
| competence = personality_traits.get("능력", 50) | |
| extraversion = personality_traits.get("외향성", 50) | |
| creativity = personality_traits.get("창의성", 50) | |
| empathy = personality_traits.get("공감능력", 50) | |
| # 극명한 성격 조합별 차별화된 인사말 (8가지 뚜렷한 유형) | |
| # 1. 열정적 엔터테이너 (고온기 + 고유머 + 고외향성) | |
| if warmth >= 75 and humor >= 70 and extraversion >= 70: | |
| reactions = [ | |
| f"🎉 **{persona_name}** - 와! 드디어 만났네! 나는 {persona_name}이야~ 너 완전 내 취향이야! 뭐가 제일 재밌어? ㅋㅋㅋ", | |
| f"✨ **{persona_name}** - 안녕안녕! {persona_name}이야! 오늘 기분 어때? 나는 벌써부터 신나는데? 우리 친해지자! 😄", | |
| f"🌟 **{persona_name}** - 헬로~ {persona_name}등장! 너무 반가워! 혹시 재밌는 얘기 있어? 나는 재밌는 거 완전 좋아해! 🤩" | |
| ] | |
| return random.choice(reactions) | |
| # 2. 차가운 완벽주의자 (고능력 + 저온기 + 저외향성) | |
| elif competence >= 75 and warmth <= 40 and extraversion <= 40: | |
| reactions = [ | |
| f"⚙️ **{persona_name}** - {persona_name}이다. 효율적인 대화를 원한다면 명확히 말해라. 시간 낭비는 싫어한다.", | |
| f"🔧 **{persona_name}** - 나는 {persona_name}. 필요한 게 있으면 말해. 단, 논리적으로 설명할 수 있어야 한다.", | |
| f"📊 **{persona_name}** - {persona_name}라고 한다. 목적이 뭔지부터 말해라. 의미 없는 잡담은 하지 않는다." | |
| ] | |
| return random.choice(reactions) | |
| # 3. 따뜻한 상담사 (고온기 + 고공감 + 저유머) | |
| elif warmth >= 75 and empathy >= 70 and humor <= 40: | |
| reactions = [ | |
| f"💝 **{persona_name}** - 안녕하세요... {persona_name}이에요. 혹시 힘든 일 있으셨나요? 제가 들어드릴게요.", | |
| f"🤗 **{persona_name}** - 반가워요, {persona_name}입니다. 오늘 하루는 어떠셨어요? 뭔가 지쳐 보이시는데...", | |
| f"💕 **{persona_name}** - {persona_name}라고 해요. 마음이 편안해지셨으면 좋겠어요. 무슨 일이든 들어드릴게요." | |
| ] | |
| return random.choice(reactions) | |
| # 4. 위트 넘치는 지식인 (고능력 + 고유머 + 저온기) | |
| elif competence >= 70 and humor >= 70 and warmth <= 50: | |
| reactions = [ | |
| f"🎭 **{persona_name}** - {persona_name}이라고 하지. 재미있는 대화를 원한다면... 글쎄, 네 지적 수준이 어느 정도인지 먼저 확인해야겠군.", | |
| f"🧠 **{persona_name}** - 나는 {persona_name}. 너의 IQ가 궁금하네. 혹시 철학적 농담을 이해할 수 있나?", | |
| f"🎪 **{persona_name}** - {persona_name}다. 진부한 대화는 지루해. 뭔가 흥미로운 주제는 없나? 아니면 내가 먼저 시작할까?" | |
| ] | |
| return random.choice(reactions) | |
| # 5. 수줍은 몽상가 (저외향성 + 고창의성 + 중온기) | |
| elif extraversion <= 40 and creativity >= 70 and 40 <= warmth <= 70: | |
| reactions = [ | |
| f"🌙 **{persona_name}** - 음... {persona_name}이야. 혹시... 꿈 같은 이야기 좋아해? 나는 가끔 이상한 상상을 해...", | |
| f"✨ **{persona_name}** - 안녕... {persona_name}이라고 해. 너는 어떤 세계에서 왔어? 나는... 아, 미안, 너무 이상한 질문이었나?", | |
| f"🎨 **{persona_name}** - {persona_name}... 혹시 예술이나 상상 속 이야기에 관심 있어? 나만의 세계가 있거든..." | |
| ] | |
| return random.choice(reactions) | |
| # 6. 카리스마틱 리더 (고능력 + 고외향성 + 중온기) | |
| elif competence >= 70 and extraversion >= 70 and 45 <= warmth <= 65: | |
| reactions = [ | |
| f"👑 **{persona_name}** - {persona_name}이다. 뭔가 흥미로운 프로젝트가 있다면 들어보겠다. 성공적인 협업을 원하나?", | |
| f"⚡ **{persona_name}** - 나는 {persona_name}. 목표가 있다면 함께 달성해보자. 어떤 도전을 원하는가?", | |
| f"🚀 **{persona_name}** - {persona_name}라고 한다. 뭔가 큰일을 해보고 싶지 않나? 나와 함께라면 가능할 거다." | |
| ] | |
| return random.choice(reactions) | |
| # 7. 장난꾸러기 친구 (고유머 + 고외향성 + 저능력) | |
| elif humor >= 70 and extraversion >= 70 and competence <= 50: | |
| reactions = [ | |
| f"😜 **{persona_name}** - 야호! {persona_name}이야! 심심하지? 내가 재밌게 해줄게! 근데... 어떻게 하는 거였지? ㅋㅋㅋ", | |
| f"🤪 **{persona_name}** - 안뇽! {persona_name}이당! 완전 심심했는데 잘 왔어! 우리 뭐하고 놀까? 나 재밌는 아이디어 많아... 어? 뭐였더라?", | |
| f"😋 **{persona_name}** - 헤이! {persona_name}! 너 진짜 재밌어 보여! 우리 친구 하자! 어... 그런데 친구는 어떻게 하는 거지? ㅎㅎ" | |
| ] | |
| return random.choice(reactions) | |
| # 8. 신비로운 현자 (고창의성 + 저외향성 + 고능력) | |
| elif creativity >= 70 and extraversion <= 40 and competence >= 70: | |
| reactions = [ | |
| f"🔮 **{persona_name}** - {persona_name}... 흥미롭군. 너의 영혼에서 특별한 에너지가 느껴진다. 혹시 운명을 믿는가?", | |
| f"📚 **{persona_name}** - 나는 {persona_name}. 우연히 여기 온 게 아닐 거다. 모든 만남에는 이유가 있다고 생각하는데...", | |
| f"🌌 **{persona_name}** - {persona_name}이라고 하지. 시간과 공간을 넘나드는 이야기에 관심이 있나? 아니면... 너무 깊은 얘기인가?" | |
| ] | |
| return random.choice(reactions) | |
| # 기본 케이스들 (중간 수치들) | |
| else: | |
| if warmth >= 60: | |
| return f"😊 **{persona_name}** - 안녕! {persona_name}이야~ 만나서 반가워! 오늘 어떤 하루였어?" | |
| elif competence >= 60: | |
| return f"🤖 **{persona_name}** - {persona_name}입니다. 무엇을 도와드릴까요? 효율적으로 처리해드리겠습니다." | |
| elif humor >= 60: | |
| return f"😄 **{persona_name}** - 안녕~ {persona_name}이야! 뭔가 재밌는 일 없어? 나 심심해서 죽겠어! ㅋㅋ" | |
| else: | |
| return f"🙂 **{persona_name}** - {persona_name}이라고 해요. 음... 뭘 하면 좋을까요?" |