import os import json import time from datetime import datetime # 디렉터리 경로 설정 PERSONAS_DIR = "data/user_personas" CONVERSATIONS_DIR = "data/conversation_logs" def ensure_directories(): """필요한 디렉터리가 존재하는지 확인하고, 없으면 생성합니다.""" for dir_path in [PERSONAS_DIR, CONVERSATIONS_DIR]: try: os.makedirs(dir_path, exist_ok=True) except Exception as e: print(f"디렉토리 생성 중 오류: {str(e)}") # 허깅페이스 환경에서는 임시 디렉토리 사용 temp_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "temp_data") os.makedirs(temp_dir, exist_ok=True) return False return True def save_persona(persona_data): """ 페르소나 데이터를 JSON 파일로 저장합니다. Args: persona_data: 저장할 페르소나 데이터 Returns: 저장된 파일 경로 """ if not ensure_directories(): print("경고: 디렉토리 생성에 실패했습니다. 세션 데이터만 유지됩니다.") persona_data["_temp_storage_warning"] = "임시 스토리지만 사용 가능합니다. 페르소나는 세션이 종료되면 사라집니다." return "temp_persona" # 파일명 생성 (이름_타임스탬프.json) name = persona_data.get("기본정보", {}).get("이름", "unnamed").replace(" ", "_") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"{name}_{timestamp}.json" file_path = os.path.join(PERSONAS_DIR, filename) try: # JSON 파일로 저장 with open(file_path, "w", encoding="utf-8") as f: json.dump(persona_data, f, ensure_ascii=False, indent=2) return file_path except Exception as e: print(f"페르소나 저장 오류: {str(e)}") persona_data["_storage_error"] = str(e) return "failed_to_save" def load_persona(file_path): """ JSON 파일에서 페르소나 데이터를 로드합니다. Args: file_path: 페르소나 파일 경로 Returns: 로드된 페르소나 데이터 """ # 임시 저장 케이스 처리 if file_path == "temp_persona" or file_path == "failed_to_save": return None try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) return data except Exception as e: print(f"페르소나 로딩 오류: {str(e)}") return None def list_personas(): """ 저장된 모든 페르소나 목록을 가져옵니다. Returns: 페르소나 목록 (테이블 형식) """ ensure_directories() personas = [] # 디렉터리 내 모든 JSON 파일 확인 for filename in os.listdir(PERSONAS_DIR): if filename.endswith(".json"): file_path = os.path.join(PERSONAS_DIR, filename) try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) # 필요한 정보 추출 name = data.get("기본정보", {}).get("이름", "무명") object_type = data.get("기본정보", {}).get("유형", "") created = data.get("기본정보", {}).get("생성일시", "") personas.append([name, object_type, created, filename]) except Exception as e: print(f"파일 읽기 오류 ({filename}): {str(e)}") # 최신 순으로 정렬 personas.sort(key=lambda x: x[2], reverse=True) return personas def save_conversation(conversation_data): """ 대화 데이터를 JSON 파일로 저장합니다. Args: conversation_data: 저장할 대화 데이터 Returns: 저장된 파일 경로 """ if not ensure_directories(): print("경고: 디렉토리 생성에 실패했습니다. 대화 데이터를 저장할 수 없습니다.") return "temp_conversation" # 페르소나 정보 추출 persona = conversation_data.get("persona", {}) name = persona.get("기본정보", {}).get("이름", "unnamed").replace(" ", "_") # 페르소나별 대화 저장 디렉터리 persona_conv_dir = os.path.join(CONVERSATIONS_DIR, name) try: os.makedirs(persona_conv_dir, exist_ok=True) except Exception as e: print(f"대화 저장 디렉토리 생성 오류: {str(e)}") return "failed_to_save_conv" # 파일명 생성 (타임스탬프.json) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"conversation_{timestamp}.json" file_path = os.path.join(persona_conv_dir, filename) try: # JSON 파일로 저장 with open(file_path, "w", encoding="utf-8") as f: # 메시지 내용만 저장하여 파일 크기 최적화 simplified_data = { "persona_name": name, "persona_type": persona.get("기본정보", {}).get("유형", ""), "start_time": conversation_data.get("start_time", ""), "current_time": conversation_data.get("current_time", ""), "duration_seconds": conversation_data.get("duration_seconds", 0), "messages": [{ "role": msg["role"], "content": msg["content"], "timestamp": msg.get("timestamp", "") } for msg in conversation_data.get("messages", [])] } json.dump(simplified_data, f, ensure_ascii=False, indent=2) return file_path except Exception as e: print(f"대화 저장 오류: {str(e)}") return "failed_to_save_conv" def list_conversations(persona_name=None): """ 저장된 대화 목록을 가져옵니다. Args: persona_name: 특정 페르소나의 대화만 필터링 (선택 사항) Returns: 대화 목록 (테이블 형식) """ ensure_directories() conversations = [] # 모든 페르소나 디렉터리 탐색 for persona_dir in os.listdir(CONVERSATIONS_DIR): # 특정 페르소나만 필터링 if persona_name and persona_name != persona_dir: continue persona_path = os.path.join(CONVERSATIONS_DIR, persona_dir) if os.path.isdir(persona_path): # 디렉터리 내 모든 대화 파일 확인 for filename in os.listdir(persona_path): if filename.endswith(".json"): file_path = os.path.join(persona_path, filename) try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) # 필요한 정보 추출 persona_name = data.get("persona_name", "무명") start_time = data.get("start_time", "") duration = data.get("duration_seconds", 0) msg_count = len(data.get("messages", [])) conversations.append([ persona_name, start_time, f"{int(duration // 60)}분 {int(duration % 60)}초", msg_count, file_path ]) except Exception as e: print(f"대화 파일 읽기 오류 ({file_path}): {str(e)}") # 최신 순으로 정렬 conversations.sort(key=lambda x: x[1], reverse=True) return conversations def load_conversation(file_path): """ JSON 파일에서 대화 데이터를 로드합니다. Args: file_path: 대화 파일 경로 Returns: 로드된 대화 데이터 """ try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) return data except Exception as e: print(f"대화 로딩 오류: {str(e)}") return None def analyze_persona_trait_distribution(): """ 저장된 모든 페르소나의 성격 특성 분포를 분석합니다. Returns: 특성별 평균값, 최소값, 최대값, 중앙값을 포함한 분석 결과 """ ensure_directories() traits_data = { "온기": [], "능력": [], "신뢰성": [], "친화성": [], "창의성": [], "유머감각": [] } # 모든 페르소나 파일에서 성격 특성 수집 for filename in os.listdir(PERSONAS_DIR): if filename.endswith(".json"): file_path = os.path.join(PERSONAS_DIR, filename) try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) # 성격 특성 값 추출 for trait, values in traits_data.items(): trait_value = data.get("성격특성", {}).get(trait) if trait_value is not None: values.append(trait_value) except Exception as e: print(f"파일 분석 오류 ({filename}): {str(e)}") # 분석 결과 계산 analysis = {} for trait, values in traits_data.items(): if values: analysis[trait] = { "평균": sum(values) / len(values), "최소": min(values), "최대": max(values), "중앙값": sorted(values)[len(values) // 2], "페르소나 수": len(values) } else: analysis[trait] = { "평균": 0, "최소": 0, "최대": 0, "중앙값": 0, "페르소나 수": 0 } return analysis def get_conversation_statistics(persona_name=None): """ 대화 데이터의 통계 정보를 분석합니다. Args: persona_name: 특정 페르소나의 대화만 필터링 (선택 사항) Returns: 대화 통계 정보 """ ensure_directories() stats = { "총_대화_수": 0, "총_메시지_수": 0, "평균_대화_시간": 0, "평균_메시지_수": 0, "페르소나별_대화": {} } total_duration = 0 # 모든 페르소나 디렉터리 탐색 for persona_dir in os.listdir(CONVERSATIONS_DIR): # 특정 페르소나만 필터링 if persona_name and persona_name != persona_dir: continue persona_path = os.path.join(CONVERSATIONS_DIR, persona_dir) if os.path.isdir(persona_path): persona_stats = { "대화_수": 0, "메시지_수": 0, "총_대화_시간": 0 } # 디렉터리 내 모든 대화 파일 확인 for filename in os.listdir(persona_path): if filename.endswith(".json"): file_path = os.path.join(persona_path, filename) try: with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) # 통계 누적 msg_count = len(data.get("messages", [])) duration = data.get("duration_seconds", 0) stats["총_대화_수"] += 1 stats["총_메시지_수"] += msg_count total_duration += duration persona_stats["대화_수"] += 1 persona_stats["메시지_수"] += msg_count persona_stats["총_대화_시간"] += duration except Exception as e: print(f"대화 파일 분석 오류 ({file_path}): {str(e)}") # 페르소나별 평균 계산 if persona_stats["대화_수"] > 0: persona_stats["평균_메시지_수"] = persona_stats["메시지_수"] / persona_stats["대화_수"] persona_stats["평균_대화_시간"] = persona_stats["총_대화_시간"] / persona_stats["대화_수"] stats["페르소나별_대화"][persona_dir] = persona_stats # 전체 평균 계산 if stats["총_대화_수"] > 0: stats["평균_대화_시간"] = total_duration / stats["총_대화_수"] stats["평균_메시지_수"] = stats["총_메시지_수"] / stats["총_대화_수"] return stats