File size: 5,251 Bytes
0a47069
 
 
6be7d0b
e80a3bc
 
 
 
0a47069
 
 
 
 
 
 
 
 
0482473
0a47069
24c2c08
6be7d0b
 
0a47069
 
e80a3bc
0a47069
 
0482473
24c2c08
6be7d0b
ec95760
0a47069
 
 
 
 
 
 
9009e60
 
0a47069
 
 
 
9009e60
0a47069
9009e60
 
 
 
 
0a47069
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3eb99bc
 
 
 
 
 
 
 
24c2c08
e80a3bc
24c2c08
 
 
 
 
 
e80a3bc
24c2c08
e80a3bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24c2c08
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
# portfolio/npc_social_network/npc/npc_memory.py

from datetime import datetime
from typing import TYPE_CHECKING, List, Optional

# ์ˆœํ™˜ ์ฐธ์กฐ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด TYPE_CHECKING์„ ์‚ฌ์šฉ
if TYPE_CHECKING:
    from .npc_base import NPC

class Memory:
    """
    ํ•˜๋‚˜์˜ ๊ธฐ์–ต ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ํด๋ž˜์Šค
    - content: ๊ธฐ์–ต ๋‚ด์šฉ
    - timestamp: ๊ธฐ์–ตํ•œ ์‹œ๊ฐ„ (๊ธฐ๋ณธ๊ฐ’์€ ํ˜„์žฌ ์‹œ๊ฐ„)
    - importance: ์ค‘์š”๋„ (1~10)
    - emotion: ๊ฐ์ • ํƒœ๊ทธ (๊ธฐ์จ, ์Šฌํ””, ๋ถ„๋…ธ, ์ค‘๋ฆฝ ๋“ฑ)
    - is_long_term: ์žฅ๊ธฐ ๊ธฐ์–ต ์—ฌ๋ถ€
    - behavior_trace: ๊ธฐ์–ต์„ ํ†ตํ•œ ํ–‰๋™ ์ถ”์ 
    """
    def __init__(self, content: str, timestamp: datetime = None, importance: int = 1,
                 emotion: str = "neutral", behavior_trace: str = None, memory_type: str = "Event",
                 context_tags: Optional[List[str]] = None):
        self.content = content
        self.timestamp = timestamp or datetime.now()
        self.importance = float(importance)
        self.emotion = emotion 
        self.is_long_term = False
        self.behavior_trace = behavior_trace
        self.memory_type = memory_type
        self.context_tags = context_tags if context_tags is not None else []
        self.is_shared = False


class MemoryStore:
    """
    ๊ธฐ์–ต ์ €์žฅ์†Œ: ๋‹จ๊ธฐ/์žฅ๊ธฐ ๊ธฐ์–ต์„ ๊ตฌ๋ถ„ํ•˜์—ฌ ์ €์žฅ
    """
    def __init__(self):
        self.short_term: List[Memory] = []    # ๋‹จ๊ธฐ ๊ธฐ์–ต
        self.long_term: List[Memory] = []     # ์žฅ๊ธฐ ๊ธฐ์–ต

    def add_memory(self, memory: Memory):
        """
        ์ค‘์š”๋„๊ฐ€ ๋†’์€ ๊ธฐ์–ต์€ ์žฅ๊ธฐ ๊ธฐ์–ต์œผ๋กœ, ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ๋‹จ๊ธฐ ๊ธฐ์–ต์œผ๋กœ ์ €์žฅ
        - ์ƒ์ง• ๊ธฐ์–ต์€ ํ•ญ์ƒ ์ตœ์ƒ์œ„ ์ค‘์š”๋„๋ฅผ ๊ฐ€์ง€๋ฉฐ ์žฅ๊ธฐ ๊ธฐ์–ต์œผ๋กœ ์ €์žฅ
        """
        if memory.memory_type == "Symbolic":
            memory.importance = 10.0
            memory.is_long_term = True

        if memory.importance >=7 or memory.memory_type == "Symbolic":
            memory.is_long_term = True
            self.long_term.append(memory)
        else:
            self.short_term.append(memory)

    def promote_memories(self, time_threshold_minutes: int = 60):
        """
        ์‹œ๊ฐ„์ด ์ง€๋‚œ ๋‹จ๊ธฐ ๊ธฐ์–ต ์ค‘ ์ค‘์š”๋„๊ฐ€ ์ผ์ • ์ด์ƒ์ธ ๊ฒƒ๋“ค์„ ์žฅ๊ธฐ ๊ธฐ์–ต์œผ๋กœ ์Šน๊ฒฉ
        """
        now = datetime.now()
        promoted = []

        for mem in self.short_term:
            elapsed = (now - mem.timestamp).total_seconds() / 60
            if elapsed > time_threshold_minutes and mem.importance >= 5:
                mem.is_long_term = True
                self.long_term.append(mem)
                promoted.append(mem)
        
        # ๋‹จ๊ธฐ ๊ธฐ์–ต์—์„œ ์ œ๊ฑฐ
        self.short_term = [m for m in self.short_term if m not in promoted]

    def get_all_memories(self):
        """
        ๋‹จ๊ธฐ + ์žฅ๊ธฐ ๊ธฐ์–ต ์ „์ฒด ๋ฐ˜ํ™˜
        """
        return self.long_term + self.short_term
    
    def get_recent_memories(self, limit=10):
        """
        ์ตœ์‹  Memory n๊ฐœ ๋ฐ˜ํ™˜ (๊ธฐ๋ณธ 10๊ฐœ)
        """
        all_memories = self.get_all_memories()
        all_memories.sort(key=lambda m: m.timestamp, reverse=True)
        return all_memories[:limit]
    
    def decay_memories(self, npc: "NPC", min_importance=0.1):
        """
        MemoryType์— ๋”ฐ๋ผ decay ์ ์šฉ
        - ๋‹จ๊ธฐ ๊ธฐ์–ต์€ decay_rate_short ๋งŒํผ importance ๊ฐ์†Œ (๊ธฐ๋ณธ 5% ๊ฐ์†Œ)
        - ์žฅ๊ธฐ ๊ธฐ์–ต์€ decay_rate_long ๋งŒํผ importance ๊ฐ์†Œ (๊ธฐ๋ณธ 1% ๊ฐ์†Œ)
        - ๊ธฐ์–ต ํƒ€์ž…์— ๋”ฐ๋ฅธ importance ์ฐจ๋“ฑ ๊ฐ์†Œ (๋Œ€ํ™”, ํ–‰๋™, ์‚ฌ๊ฑด, ๊ด€๊ณ„)
        """
        from .emotion_config import EMOTION_RELATION_IMPACT

        # ์ฒ˜๋ฆฌํ•  ๋ชจ๋“  ๊ธฐ์–ต์„ ์ž„์‹œ ๋ฆฌ์ŠคํŠธ์— ๋ณต์‚ฌ
        all_memorise = self.get_all_memories()

        for mem in all_memorise:
            # 1. ๊ธฐ๋ณธ ๊ฐ์‡ ์œจ ์„ค์ •
            base_decay = 0.02 if mem.is_long_term else 0.05

            # 2. ๊ฐ์ • ๊ฐ•๋„์— ๋”ฐ๋ฅธ ์กฐ์ ˆ (๊ฐ์ •์ด ๊ฐ•ํ• ์ˆ˜๋ก ๋œ ์žŠ์Œ)
            # ๊ฐ์ • ์˜ํ–ฅ๋„ ์ ˆ๋Œ€๊ฐ’์„ ์‚ฌ์šฉ (๊ธ์ •/๋ถ€์ • ๋ชจ๋‘ ๊ฐ•ํ•œ ๊ฐ์ •์œผ๋กœ ์ทจ๊ธ‰)
            emotion_impact = abs(EMOTION_RELATION_IMPACT.get(mem.emotion, 0.0))
            # ๊ฐ์ • ๊ฐ•๋„๊ฐ€ ๋†’์„์ˆ˜๋ก ๊ฐ์‡ ์œจ์ด ๋‚ฎ์•„์ง (0.5 ~ 1.5 ์‚ฌ์ด๋กœ ์กฐ์ ˆ)
            emotion_modifier = 1.0 - (emotion_impact / 4.0) # IMPACT ์ตœ๋Œ€๊ฐ’์ด 2.0์ด๋ฏ€๋กœ 4๋กœ ๋‚˜๋ˆ ์„œ ์˜ํ–ฅ๋ ฅ ์กฐ์ ˆ

            # 3. ์„ฑ๊ฒฉ์— ๋”ฐ๋ฅธ ์กฐ์ ˆ
            personality = npc.personality
            # ๋‚ด์„ฑ์ (stoic)์ผ์ˆ˜๋ก ๊ฐ์ •์„ ๊ณฑ์”น์œผ๋ฉฐ ๋” ์˜ค๋ž˜ ๊ธฐ์–ต (๊ฐ์‡ ์œจ ๊ฐ์†Œ)
            # ๋ฏผ๊ฐํ• (sensitive)์ˆ˜๋ก ๊ฐ์ •์  ๊ธฐ์–ต์„ ๋” ์˜ค๋ž˜ ๊ฐ„์งํ•จ (๊ฐ์‡ ์œจ ๊ฐ์†Œ)
            personality_modifier = 1.0 - personality.get("stoic", 0.5) * 0.5 -personality.get("sensitive", 0.5) * 0.5
            personality_modifier = max(0.1, personality_modifier)   # 0 ๋˜๋Š” ์Œ์ˆ˜๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ๋ฐฉ์ง€

            # ์ตœ์ข… ๊ฐ์‡ ์œจ ๊ณ„์‚ฐ
            final_decay_rate = base_decay * emotion_modifier * personality_modifier
            final_decay_rate = max(0.005, min(final_decay_rate, 0.1))

            # ์ค‘์š”๋„ ์—…๋ฐ์ดํŠธ
            mem.importance -= final_decay_rate

            if mem.importance < min_importance:
                mem.importance = 0