# NPC 간의 관계를 감정 상태와 상호작용을 기반으로 동적 형성 및 변화 # portfolio/npc_social_network/npc/npc_relationship.py from typing import List, Optional, TYPE_CHECKING from .npc_manager import get_korean_postposition if TYPE_CHECKING: from .npc_memory import Memory from .npc_base import NPC from .npc_manager import NPCManager class SocialProfile: """특정 대상과의 사회적 관계를 종합적으로 관리하는 클래스""" def __init__(self, target_name:str): self.target_name = target_name self.score: float = 0.0 # 관계 점수 (-100 ~ 100) self.type: str = "stranger" # 관계 유형 (e.g., "friend", "rival", "colleague") self.summary: str = f"{target_name}에 대해 아직 잘 모릅니다." # LLM이 생성한 관계 요약 self.key_memories: List["Memory"] = [] # 관계에 영향을 준 핵심 기억 class RelationshipManager: """SocialProfile 객체를 사용하여 모든 관계를 관리""" def __init__(self, owner_npc: "NPC"): self.owner_npc = owner_npc # 관계 사전: {상대방 이름: SocialProfile 객체} self.relationships: dict[str, SocialProfile] = {} def _get_or_create_profile(self, target_name: str) -> SocialProfile: """대상의 프로필이 없으면 새로 생성하고 반환""" if target_name not in self.relationships: self.relationships[target_name] = SocialProfile(target_name) return self.relationships[target_name] def update_relationship(self, target_name: str, emotion: str, strength: float=1.0, memory: Optional["Memory"]=None): """ 특정 감정 기반으로 관계 수치 조정 - 긍정/부정만이 아니라 감정 유형에 따른 영향 차별화 - 감정, 강도, 그리고 관련 기억을 바탕으로 관계 점수 및 프로필 업데이트 """ from .emotion_config import EMOTION_RELATION_IMPACT profile = self._get_or_create_profile(target_name) impact = EMOTION_RELATION_IMPACT.get(emotion, 0.0) delta = impact * strength # 관계 점수 업데이트 및 클리핑 profile.score = max(-100, min(100, profile.score + delta)) # 관계 유형 업데이트 self.update_relationship_type(profile) # 핵심 기억 추가 if memory and memory.importance >= 6: if memory not in profile.key_memories: profile.key_memories.append(memory) profile.key_memories = profile.key_memories[-5:] # 최근 5개만 유지 target_npc = self.owner_npc.manager.get_npc_by_name(target_name) target_korean_name = target_npc.korean_name if target_npc else target_name print(f"[{self.owner_npc.korean_name}] '{target_korean_name}'와의 새로운 핵심 기억 추가: {memory.content[:30]}...") def update_relationship_type(self, profile:SocialProfile): """ 점수에 따라 관계 유형을 업데이트 - 관계 유형 정의 """ if profile.score > 70: profile.type = "best friend" elif profile.score > 30: profile.type = "friend" elif profile.score > 5: profile.type = "acquaintance" elif profile.score < -5: profile.type = "nuisance" elif profile.score < -30: profile.type = "rival" elif profile.score < -70: profile.type = "enemy" else: profile.type = "stranger" def get_relationship_score(self, target_name:str) -> float: """현재 관계 점수를 반환""" profile = self._get_or_create_profile(target_name) return profile.score def get_relationship_summary(self, target_name: str) -> str: """LLM이 생성한 관계 요약 반환""" profile = self._get_or_create_profile(target_name) return profile.summary def set_relationship(self, target_name: str, relationship_type: str): """플레이어 개입 등으로 관계 유형과 점수를 직접 설정""" from .. import simulation_core profile = self._get_or_create_profile(target_name) # 설정된 타입에 따라 점수를 부여 (값 조절 가능) score_map = { "best friend": 80.0, "friend": 50.0, "acquaintance": 10.0, "stranger": 0.0, "nuisance": -10.0, "rival": -50.0, "enemy": -80.0 } profile.type = relationship_type profile.score = score_map.get(relationship_type, 0.0) target_npc = self.owner_npc.manager.get_npc_by_korean_name(target_name) target_korean_name = target_npc.korean_name if target_npc else target_name self_postposition = get_korean_postposition(self.owner_npc.korean_name, "은", "는") target_postposition = get_korean_postposition(target_korean_name, "과", "와") # 관계 요약도 간단하게 업데이트 profile.summary = f"{target_korean_name}{target_postposition} {self.owner_npc.korean_name}{self_postposition} {relationship_type} 관계이다." simulation_core.add_log(f"[관계 설정] {self.owner_npc.korean_name} -> {target_korean_name} 관계가 '{relationship_type}'(으)로 설정되었습니다.") def summarize_relationship(self, target_name: str, npc_manager: "NPCManager"): """ LLM을 사용하여 특정 대상과의 관계를 주기적으로 요약하고 업데이트""" from ..models.llm_helper import query_llm_with_prompt from .. import simulation_core profile = self._get_or_create_profile(target_name) if not profile.key_memories: return # 요약할 기억이 없으면 실행 안함 target_npc = npc_manager.get_npc_by_name(target_name) target_korean_name = target_npc.korean_name if target_npc else target_name memory_details = "\n".join([f"- {mem.content} (감정: {mem.emotion})" for mem in profile.key_memories]) prompt = f""" # 지시사항 나은 '{self.owner_npc.korean_name}'입니다. 나의 기억을 바탕으로 '{target_korean_name}'에 대한 나의 생각과 감정을 한두 문장으로 솔직하게 요약해주세요. # '{target_korean_name}'와(과)의 핵심 기억들 {memory_details} → '{target_korean_name}'에 대한 {self.owner_npc.korean_name}의 생각: """ summary = query_llm_with_prompt(prompt).replace("'", "").strip() if summary and "[LLM Error]" not in summary: profile.summary = summary simulation_core.add_log(f"[{self.owner_npc.korean_name}의 관계 요약 업데이트] {profile.summary}")