File size: 6,801 Bytes
1a3c349
 
 
e80a3bc
2cd8c90
 
e80a3bc
 
 
d57920f
e80a3bc
 
 
 
 
 
9009e60
e80a3bc
 
 
1a3c349
e80a3bc
 
 
 
ce3d575
e80a3bc
 
 
 
 
 
1a3c349
e80a3bc
146c098
9df6a7e
 
 
e80a3bc
9df6a7e
 
 
e80a3bc
9df6a7e
 
e80a3bc
9df6a7e
e80a3bc
 
9df6a7e
e80a3bc
 
1a3c349
e80a3bc
 
 
 
 
146c098
 
 
b25a43f
e80a3bc
 
9009e60
 
 
 
e80a3bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1a3c349
2cd8c90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c08ff6f
e80a3bc
 
c08ff6f
 
e80a3bc
 
 
 
 
c08ff6f
d57920f
 
e80a3bc
 
 
d28f7eb
e80a3bc
d57920f
e80a3bc
 
c08ff6f
e80a3bc
 
 
 
 
 
c08ff6f
e80a3bc
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# 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}")