Spaces:
Sleeping
Sleeping
humanda5
commited on
Commit
·
0482473
1
Parent(s):
5430701
감정 모델 리팩토링 테스트
Browse files
npc_social_network/npc/emotion_config.py
CHANGED
@@ -1,161 +1,138 @@
|
|
1 |
# portfolio/npc_social_network/npc/emotion_config.py
|
2 |
# 감정 초기 상태 및 decay 설정
|
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 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
52 |
EMOTION_DECAY_RATE = {
|
53 |
-
# 기본
|
54 |
-
"joy": 0.
|
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 |
# 개성 템플릿(0~1 범위)
|
89 |
PERSONALITY_TEMPLATE = {
|
90 |
-
"
|
91 |
-
"
|
92 |
-
"
|
93 |
-
"
|
94 |
-
"
|
95 |
-
|
96 |
-
|
97 |
-
# 긍정/부정 감정 전역 리스트
|
98 |
-
AFFECT_EMOTIONS = {
|
99 |
-
"positive": ["joy", "satisfaction", "gratitude", "calm", "anticipation", "pride", "connectedness"],
|
100 |
-
"negative": ["sadness", "anger", "anxiety", "disgust", "fear", "regret", "frustration"]
|
101 |
-
}
|
102 |
-
|
103 |
-
# 사회적 감정 전역 리스트
|
104 |
-
SOCIAL_EMOTIONS = [
|
105 |
-
"jealousy", "shame", "guilt", "compassion", "awe", "empathy"
|
106 |
-
]
|
107 |
-
|
108 |
-
# 인지적 감정 전역 리스트
|
109 |
-
COGNITIVE_STATES = [
|
110 |
-
"confusion", "nervousness", "apathy", "excitement", "immersion", "skepticism"
|
111 |
-
]
|
112 |
-
|
113 |
-
|
114 |
-
"""
|
115 |
-
🌟 균형 잡힌 복합 감정 모델 정의( 이쪽으로 변형하는게 더 고차원 일 것 같음, ComplexEmotions)
|
116 |
-
1️⃣ 기본 감정 (Core Emotions)
|
117 |
-
Joy (기쁨)
|
118 |
-
Sadness (슬픔)
|
119 |
-
Anger (분노)
|
120 |
-
Fear (공포)
|
121 |
-
Disgust (혐오)
|
122 |
-
Surprise (놀람)
|
123 |
-
|
124 |
-
2️⃣ 사회적 감정 (Social Emotions)
|
125 |
-
Gratitude (감사)
|
126 |
-
Shame (수치심)
|
127 |
-
Guilt (죄책감)
|
128 |
-
Pride (자부심)
|
129 |
-
Jealousy (질투)
|
130 |
-
Compassion (연민/동정)
|
131 |
-
Love (사랑/애정)
|
132 |
-
Admiration (존경/감탄)
|
133 |
-
Empathy (공감)
|
134 |
-
Awe (경외심)
|
135 |
-
Attachment (애착)
|
136 |
-
Comfort (위로 추구)
|
137 |
-
|
138 |
-
3️⃣ 인지적 상태 (Cognitive States)
|
139 |
-
Anticipation (기대)
|
140 |
-
Curiosity (호기심)
|
141 |
-
Confusion (혼란)
|
142 |
-
Interest (흥미)
|
143 |
-
Engagement (몰입)
|
144 |
-
Boredom (지루함)
|
145 |
-
Relief (안도)
|
146 |
-
Anxiety (불안 / 긴장)
|
147 |
-
Calm (평온)
|
148 |
-
|
149 |
-
Skepticism (회의)
|
150 |
-
|
151 |
-
4️⃣ 복합 감정 (Mixed / Complex Emotions)
|
152 |
-
Nostalgia (향수)
|
153 |
-
Bittersweet (달콤쌉싸름함)
|
154 |
-
Schadenfreude (남의 불행에서 느끼는 기쁨)
|
155 |
-
Hope (희망)
|
156 |
-
Resentment (분개)
|
157 |
-
Anticipatory joy (기대 속의 기쁨)
|
158 |
-
Regret (후회)
|
159 |
-
Rumination (반추)
|
160 |
-
Groundedness (안정감)
|
161 |
-
"""
|
|
|
1 |
# portfolio/npc_social_network/npc/emotion_config.py
|
2 |
# 감정 초기 상태 및 decay 설정
|
3 |
|
4 |
+
# 고차원 감정 상태 정의
|
5 |
+
# 균형 잡힌 복합 감정 모델 정의를 위해 GoEmotions 바탕으로 설계 (ComplexEmotions이라 명명)
|
6 |
+
EMOTION_LIST = [
|
7 |
+
# Core Emotions (기본 감정)
|
8 |
+
# 기쁨, 슬픔, 분노, 공포, 혐오,
|
9 |
+
# 놀람, 중립
|
10 |
+
"joy", "sadness", "anger", "fear", "disgust",
|
11 |
+
"surprise", "neutral",
|
12 |
+
|
13 |
+
# Social Emotions (사회적 감정)
|
14 |
+
# 감사, 수치심, 죄책감, 자부심, 질투, 연민/동정,
|
15 |
+
# 사랑/애정, 존경/감탄, 공감, 경외심, 애착, 위로 추구
|
16 |
+
"gratitude", "shame", "guilt", "pride", "jealousy", "compassion",
|
17 |
+
"love", "admiration", "empathy", "awe", "attachment", "comfort",
|
18 |
+
|
19 |
+
# Cognitive States (인지적 상태)
|
20 |
+
# 기대, 호기심, 혼란, 흥미, 몰입
|
21 |
+
# 지루함, 안도, 불안/긴장, 평온, 회의
|
22 |
+
"anticipation", "curiosity", "confusion", "interest", "engagement",
|
23 |
+
"boredom", "relief", "anxiety", "calm", "skepticism",
|
24 |
+
|
25 |
+
# Complex Emotions (복합 감정)
|
26 |
+
# 향수, 달콤쌉싸름함, 남의 불행에서 느끼는 행복, 희망, 분개
|
27 |
+
# 기대 속의 기쁨, 후회, 반추, 안정감
|
28 |
+
"nostalgia", "bittersweet", "schadenfreude", "hope", "resentment",
|
29 |
+
"anticipatory_joy", "regret", "rumination", "groundedness",
|
30 |
+
]
|
31 |
|
32 |
+
# 감정 : 카테고리 맵
|
33 |
+
EMOTION_CATEGORY_MAP = {
|
34 |
+
# Core
|
35 |
+
"joy": "core",
|
36 |
+
"sadness": "core",
|
37 |
+
"anger": "core",
|
38 |
+
"fear": "core",
|
39 |
+
"disgust": "core",
|
40 |
+
"surprise": "core",
|
41 |
+
"neutral": "core",
|
42 |
+
|
43 |
+
# Social
|
44 |
+
"gratitude": "social",
|
45 |
+
"shame": "social",
|
46 |
+
"guilt": "social",
|
47 |
+
"pride": "social",
|
48 |
+
"jealousy": "social",
|
49 |
+
"compassion": "social",
|
50 |
+
"love": "social",
|
51 |
+
"admiration": "social",
|
52 |
+
"empathy": "social",
|
53 |
+
"awe": "social",
|
54 |
+
"attachment": "social",
|
55 |
+
"comfort": "social",
|
56 |
+
|
57 |
+
# Cognitive
|
58 |
+
"anticipation": "cognitive",
|
59 |
+
"curiosity": "cognitive",
|
60 |
+
"confusion": "cognitive",
|
61 |
+
"interest": "cognitive",
|
62 |
+
"engagement": "cognitive",
|
63 |
+
"boredom": "cognitive",
|
64 |
+
"relief": "cognitive",
|
65 |
+
"anxiety": "cognitive",
|
66 |
+
"calm": "cognitive",
|
67 |
+
"skepticism": "cognitive",
|
68 |
+
|
69 |
+
# Complex
|
70 |
+
"nostalgia": "complex",
|
71 |
+
"bittersweet": "complex",
|
72 |
+
"schadenfreude": "complex",
|
73 |
+
"hope": "complex",
|
74 |
+
"resentment": "complex",
|
75 |
+
"anticipatory_joy": "complex",
|
76 |
+
"regret": "complex",
|
77 |
+
"rumination": "complex",
|
78 |
+
"groundedness": "complex",
|
79 |
}
|
80 |
|
81 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
82 |
EMOTION_DECAY_RATE = {
|
83 |
+
# Core (기본 감정)
|
84 |
+
"joy": 0.6, # 기쁨
|
85 |
+
"sadness": 0.3, # 슬픔
|
86 |
+
"anger": 0.4, # 분노
|
87 |
+
"fear": 0.4, # 공포
|
88 |
+
"disgust": 0.5, # 혐오
|
89 |
+
"surprise": 0.8, # 놀람
|
90 |
+
"neutral": 1.2, # 중립
|
91 |
+
|
92 |
+
# Social (사회적 감정)
|
93 |
+
"gratitude": 0.6, # 감사
|
94 |
+
"shame": 0.3, # 수치심
|
95 |
+
"guilt": 0.3, # 죄책감
|
96 |
+
"pride": 0.6, # 자부심
|
97 |
+
"jealousy": 0.4, # 질투
|
98 |
+
"compassion": 0.7, # 연민/동정
|
99 |
+
"love": 0.2, # 사랑/애정
|
100 |
+
"admiration": 0.7, # 존경/감탄
|
101 |
+
"empathy": 0.7, # 공감
|
102 |
+
"awe": 0.8, # 경외심
|
103 |
+
"attachment": 0.2, # 애착
|
104 |
+
"comfort": 0.6, # 위로 추구
|
105 |
+
|
106 |
+
# Cognitive (인지적 상태)
|
107 |
+
"anticipation": 0.6, # 기대
|
108 |
+
"curiosity": 0.8, # 호기심
|
109 |
+
"confusion": 0.5, # 혼란
|
110 |
+
"interest": 0.8, # 흥미
|
111 |
+
"engagement": 0.7, # 몰입
|
112 |
+
"boredom": 0.4, # 지루함
|
113 |
+
"relief": 0.8, # 안도
|
114 |
+
"anxiety": 0.5, # 불안/긴장
|
115 |
+
"calm": 0.7, # 평온
|
116 |
+
"skepticism": 0.5, # 회의
|
117 |
+
|
118 |
+
# Complex (복합 감정)
|
119 |
+
"nostalgia": 0.2, # 향수
|
120 |
+
"bittersweet": 0.3, # 달콤쌉싸름함
|
121 |
+
"schadenfreude": 0.5, # 남의 불행에서 느끼는 기쁨
|
122 |
+
"hope": 0.6, # 희망
|
123 |
+
"resentment": 0.4, # 분개
|
124 |
+
"anticipatory_joy": 0.6, # 기대 속의 기쁨
|
125 |
+
"regret": 0.3, # 후회
|
126 |
+
"rumination": 0.3, # 반추
|
127 |
+
"groundedness": 0.8 # 안정감
|
128 |
}
|
129 |
|
130 |
# 개성 템플릿(0~1 범위)
|
131 |
PERSONALITY_TEMPLATE = {
|
132 |
+
"affect_bias": 1.0, # Core emotion 영향
|
133 |
+
"social_bias": 1.0, # Social emotion 영향
|
134 |
+
"cognitive_bias": 1.0, # Cognitive state 영향
|
135 |
+
"complex_bias": 1.0, # Complex emotion 영향
|
136 |
+
"sensitive": 1.0, # 전체 감정 반응 강도
|
137 |
+
"stoic": 0.0 # 전체 감정 둔화 정도 (감쇠 가속도에 영향)
|
138 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/npc/npc_base.py
CHANGED
@@ -2,12 +2,21 @@
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
-
from .emotion_config import
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
|
8 |
-
#
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
# NPC 클래스 정의
|
13 |
class NPC:
|
@@ -26,12 +35,11 @@ class NPC:
|
|
26 |
# npc 기억
|
27 |
self.memory_store = MemoryStore()
|
28 |
# 감정 상태 등 감정 관리자 초기화
|
29 |
-
self.emotion_state = EMOTION_STATE_TEMPLATE
|
30 |
self.emotion_decay_rate = EMOTION_DECAY_RATE
|
31 |
self.personality = personality or PERSONALITY_TEMPLATE
|
32 |
self.relationships = RelationshipManager() # 관계 시스템 초기화
|
33 |
|
34 |
-
self.emotion = EmotionManager(self.
|
35 |
|
36 |
# 행동 관리자
|
37 |
self.behavior = BehaviorManager()
|
@@ -66,7 +74,18 @@ class NPC:
|
|
66 |
감정 상태와 직업에 따른 복합 행동 시퀀스 기반 대사 생성
|
67 |
"""
|
68 |
composite = self.get_composite_emotion_state(top_n=3) # 상위 3개 감정 사용
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0):
|
72 |
"""
|
@@ -132,11 +151,11 @@ class NPC:
|
|
132 |
base = 5.0 # 기본 영향력
|
133 |
|
134 |
# 감정 종류별 base 수정
|
135 |
-
if emotion in
|
136 |
base += 2.0
|
137 |
# 긍정 감정은 positive = True일 때만 +로 반영, positive = False일 경우 최소한으로 영향
|
138 |
return base if positive else 0.0
|
139 |
-
elif emotion in
|
140 |
base += 3.0
|
141 |
# 부정 감정은 positive = False일 때만 -로 반영, positive = True일 경우 최소한으로 영향
|
142 |
return -base if not positive else 0.0
|
@@ -163,23 +182,15 @@ class NPC:
|
|
163 |
for mem in memories[-5:]: # 최근 5개 기억 기준
|
164 |
if mem.importance >= 7: # 중요 기억만 반영
|
165 |
# 감정 성향에 따라 positive 결정 및 영향력 계산
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
delta = self._get_emotion_influence(mem.emotion, positive=False if mem.importance >= 4 else True)
|
170 |
-
else:
|
171 |
-
delta = 0.0
|
172 |
base_influence += delta
|
173 |
|
174 |
-
# 현재 감정 상태 영향 추가 반영
|
175 |
if dominant_emotion:
|
176 |
-
|
177 |
-
|
178 |
-
elif dominant_emotion in negative_emotions:
|
179 |
-
emo_influence = self._get_emotion_influence(dominant_emotion, positive=False)
|
180 |
-
else:
|
181 |
-
emo_influence = 0.0
|
182 |
-
|
183 |
base_influence += emo_influence * 0.5 # 현재 감정은 보조적 영향
|
184 |
|
185 |
# 관계 갱신
|
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
+
from .emotion_config import EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
|
8 |
+
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
9 |
+
POSITIVE_RELATION_EMOTIONS = [
|
10 |
+
"joy", "surprise", "gratitude", "pride", "love",
|
11 |
+
"admiration", "empathy", "comfort", "hope", "anticipatory_joy",
|
12 |
+
"calm", "engagement", "relief", "interest",
|
13 |
+
]
|
14 |
+
|
15 |
+
NEGATIVE_RELATION_EMOTIONS = [
|
16 |
+
"sadness", "anger", "fear", "disgust", "shame",
|
17 |
+
"guilt", "jealousy", "resentment", "regret", "schadenfreude",
|
18 |
+
"anxiety", "confusion", "skepticism", "boredom",
|
19 |
+
]
|
20 |
|
21 |
# NPC 클래스 정의
|
22 |
class NPC:
|
|
|
35 |
# npc 기억
|
36 |
self.memory_store = MemoryStore()
|
37 |
# 감정 상태 등 감정 관리자 초기화
|
|
|
38 |
self.emotion_decay_rate = EMOTION_DECAY_RATE
|
39 |
self.personality = personality or PERSONALITY_TEMPLATE
|
40 |
self.relationships = RelationshipManager() # 관계 시스템 초기화
|
41 |
|
42 |
+
self.emotion = EmotionManager(self.emotion_decay_rate, self.personality)
|
43 |
|
44 |
# 행동 관리자
|
45 |
self.behavior = BehaviorManager()
|
|
|
74 |
감정 상태와 직업에 따른 복합 행동 시퀀스 기반 대사 생성
|
75 |
"""
|
76 |
composite = self.get_composite_emotion_state(top_n=3) # 상위 3개 감정 사용
|
77 |
+
behavior_output, behavior_trace = self.behavior.perform_sequence(
|
78 |
+
self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace = True
|
79 |
+
)
|
80 |
+
# Behavior Trace를 Memory에 기록
|
81 |
+
memory_entry = Memory(
|
82 |
+
content=f"행동 수행: {behavior_trace}",
|
83 |
+
importance=5, # 기본 importance (튜닝 가능)
|
84 |
+
emotion = self.emotion.get_dominant_emotion(),
|
85 |
+
behavior_trace = behavior_trace
|
86 |
+
)
|
87 |
+
self.memory_store.add_memory(memory_entry)
|
88 |
+
return behavior_output
|
89 |
|
90 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0):
|
91 |
"""
|
|
|
151 |
base = 5.0 # 기본 영향력
|
152 |
|
153 |
# 감정 종류별 base 수정
|
154 |
+
if emotion in POSITIVE_RELATION_EMOTIONS:
|
155 |
base += 2.0
|
156 |
# 긍정 감정은 positive = True일 때만 +로 반영, positive = False일 경우 최소한으로 영향
|
157 |
return base if positive else 0.0
|
158 |
+
elif emotion in NEGATIVE_RELATION_EMOTIONS:
|
159 |
base += 3.0
|
160 |
# 부정 감정은 positive = False일 때만 -로 반영, positive = True일 경우 최소한으로 영향
|
161 |
return -base if not positive else 0.0
|
|
|
182 |
for mem in memories[-5:]: # 최근 5개 기억 기준
|
183 |
if mem.importance >= 7: # 중요 기억만 반영
|
184 |
# 감정 성향에 따라 positive 결정 및 영향력 계산
|
185 |
+
# 중요 기억만 반영
|
186 |
+
is_positive = mem.emotion in POSITIVE_RELATION_EMOTIONS
|
187 |
+
delta = self._get_emotion_influence(mem.emotion, positive=is_positive)
|
|
|
|
|
|
|
188 |
base_influence += delta
|
189 |
|
190 |
+
# 현재 감정 상태 영향 추가 반영 (보조적 반영)
|
191 |
if dominant_emotion:
|
192 |
+
is_positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
|
193 |
+
emo_influence = self._get_emotion_influence(dominant_emotion, positive=is_positive)
|
|
|
|
|
|
|
|
|
|
|
194 |
base_influence += emo_influence * 0.5 # 현재 감정은 보조적 영향
|
195 |
|
196 |
# 관계 갱신
|
npc_social_network/npc/npc_behavior.py
CHANGED
@@ -1,83 +1,168 @@
|
|
1 |
# 감정-행동 연동 매핑 및 행동 생성
|
2 |
# portfolio/npc_social_network/npc/npc_behavior.py
|
3 |
|
|
|
|
|
4 |
class BehaviorManager:
|
5 |
def __init__(self):
|
6 |
self.mapping = {
|
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 |
sequence = []
|
47 |
-
for
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
|
|
|
|
51 |
return sequence
|
52 |
|
53 |
-
def perform_sequence(self, name, job,
|
54 |
"""
|
55 |
-
|
56 |
"""
|
57 |
-
|
|
|
58 |
if not sequence:
|
59 |
-
|
|
|
|
|
|
|
|
|
60 |
|
61 |
# 행동 텍스트 템플릿
|
62 |
behavior_lines = {
|
63 |
-
|
64 |
-
"
|
65 |
-
"
|
66 |
-
"
|
67 |
-
"
|
68 |
-
"
|
69 |
-
"
|
70 |
-
"
|
71 |
-
|
|
|
|
|
|
|
|
|
72 |
"show_off": "자랑스러운 모습을 보여줍니다.",
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
}
|
75 |
|
76 |
# 행동 문장 생성
|
77 |
lines = []
|
|
|
78 |
for action, score in sequence:
|
79 |
msg = behavior_lines.get(action, f"{action} 행동을 합니다.")
|
80 |
lines.append(f"({round(score,1)}) {msg}")
|
|
|
81 |
|
82 |
# 직업 라인 추가
|
83 |
job_line = {
|
@@ -85,5 +170,11 @@ class BehaviorManager:
|
|
85 |
"blacksmith": "대장간 불도 지펴야죠.",
|
86 |
}.get(job, "오늘도 바쁜 하루네요.")
|
87 |
|
|
|
|
|
|
|
88 |
# 최종 출력 문장
|
89 |
-
|
|
|
|
|
|
|
|
1 |
# 감정-행동 연동 매핑 및 행동 생성
|
2 |
# portfolio/npc_social_network/npc/npc_behavior.py
|
3 |
|
4 |
+
from .emotion_config import EMOTION_CATEGORY_MAP
|
5 |
+
|
6 |
class BehaviorManager:
|
7 |
def __init__(self):
|
8 |
self.mapping = {
|
9 |
+
# Core (기본 감정)
|
10 |
+
"joy": "jump", # 기쁨 : 점프 뛰다
|
11 |
+
"sadness": "sit_down", # 슬픔 : 주저 앉다
|
12 |
+
"anger": "shout", # 분노 : 소리치다
|
13 |
+
"fear": "run_away", # 공포 : 도망치다
|
14 |
+
"disgust": "avoid", # 혐오 : 피하다
|
15 |
+
"surprise": "gasp", # 놀람 : 감탄하며 숨을 들이쉼
|
16 |
+
"neutral": "idle", # 중립 : 아무것도 하지 않음
|
17 |
|
18 |
+
# Social (사회적 감정)
|
19 |
+
"gratitude": "thank", # 감사 : 감사하다
|
20 |
+
"shame": "hide_face", # 수치심 : 얼굴을 감추다
|
21 |
+
"guilt": "apologize", # 죄책감 : 미안하다고 말함
|
22 |
+
"pride": "show_off", # 자부심 : 자랑하다
|
23 |
+
"jealousy": "glare", # 질투 : 노려보다
|
24 |
+
"compassion": "console", # 연민/동정 : 위로하다
|
25 |
+
"love": "embrace", # 사랑/애정 : 포옹하다
|
26 |
+
"admiration": "cheer", # 존경/감탄 : 환호하다
|
27 |
+
"empathy": "listen_carefully", # 공감 : 집중하다
|
28 |
+
"awe": "gasp", # 경외심 : 감탄하며 숨을 들이쉼
|
29 |
+
"attachment": "hold_hands", # 애착 : 소유하다
|
30 |
+
"comfort": "pat", # 위로 추구 : 토닥거리다
|
31 |
|
32 |
+
# Cognitive (인지적 상태)
|
33 |
+
"anticipation": "prepare", # 기대 : 준비하다
|
34 |
+
"curiosity": "inspect", # 호기심 : 살펴보다
|
35 |
+
"confusion": "scratch_head", # 혼란 : 머리를 긁다
|
36 |
+
"interest": "lean_forward", # 흥미 : 집중하다
|
37 |
+
"engagement": "focus", # 몰입 : 집중하다
|
38 |
+
"boredom": "idle", # 지루함 : 아무것도 하지 않음
|
39 |
+
"relief": "exhale", # 안도 : 숨을 내쉬다
|
40 |
+
"anxiety": "fidget", # 불안/긴장 : 안절부절 못하다
|
41 |
+
"calm": "relax", # 평온 : 진정하다
|
42 |
+
"skepticism": "raise_eyebrow", # 회의 : 눈썹을 올리다
|
43 |
|
44 |
+
# Complex (복합 감정)
|
45 |
+
"nostalgia": "stare_into_distance", # 향수
|
46 |
+
"bittersweet": "smile_then_look_down", # 달콤쌉싸름함
|
47 |
+
"schadenfreude": "smirk", # 남의 불행에서 느끼는 기쁨
|
48 |
+
"hope": "brighten_up", # 희망
|
49 |
+
"resentment": "scowl", # 분개
|
50 |
+
"anticipatory_joy": "rub_hands", # 기대 속의 기쁨
|
51 |
+
"regret": "look_down", # 후회
|
52 |
+
"rumination": "mutter_to_self", # 반추
|
53 |
+
"groundedness": "steady_posture", # 안정감
|
54 |
}
|
55 |
|
56 |
+
# Layer 우선 순위 / weight 설정 (튜닝 가능)
|
57 |
+
self.layer_priority = ["core", "social", "cognitive", "complex"]
|
58 |
+
self.layer_weights = {
|
59 |
+
"core" : 1.0,
|
60 |
+
"social" : 0.8,
|
61 |
+
"cognitive" : 0.6,
|
62 |
+
"complex" : 0.4,
|
63 |
+
}
|
64 |
+
|
65 |
+
def get_layer_dominant_emotions(self, buffer):
|
66 |
"""
|
67 |
+
layer별 dominant emotion 계산
|
68 |
+
"""
|
69 |
+
layer_emotions = {layer: [] for layer in self.layer_priority}
|
70 |
+
for emo, val in buffer.items():
|
71 |
+
if val > 0:
|
72 |
+
layer = EMOTION_CATEGORY_MAP.get(emo)
|
73 |
+
if layer:
|
74 |
+
layer_emotions[layer].append((emo, val))
|
75 |
+
|
76 |
+
# 각 layer에서 가장 강한 감정 추출
|
77 |
+
layer_dominant = {}
|
78 |
+
for layer, emos in layer_emotions.items():
|
79 |
+
if emos:
|
80 |
+
dominant_emo = max(emos, key=lambda x: x[1])
|
81 |
+
layer_dominant[layer] = dominant_emo
|
82 |
+
|
83 |
+
return layer_dominant
|
84 |
+
|
85 |
+
def decide_layered_sequence(self, layer_dominant_emotions):
|
86 |
+
"""
|
87 |
+
layer priority + weight 기반 시퀀스 생성
|
88 |
"""
|
89 |
sequence = []
|
90 |
+
for layer in self.layer_priority:
|
91 |
+
if layer in layer_dominant_emotions:
|
92 |
+
emo, score = layer_dominant_emotions[layer]
|
93 |
+
weighted_score = round(score * self.layer_weights[layer], 2)
|
94 |
+
action = self.mapping.get(emo)
|
95 |
+
if action:
|
96 |
+
sequence.append((action, weighted_score))
|
97 |
return sequence
|
98 |
|
99 |
+
def perform_sequence(self, name, job, emotion_buffer, return_trace=False):
|
100 |
"""
|
101 |
+
Layer-aware Behavior 시퀀스 구성 + Behavior Trace 반환
|
102 |
"""
|
103 |
+
layer_dominant_emotions = self.get_layer_dominant_emotions(emotion_buffer)
|
104 |
+
sequence = self.decide_layered_sequence(layer_dominant_emotions)
|
105 |
if not sequence:
|
106 |
+
behavior_output = f"{name}은(는) 특별한 행동을 하지 않습니다."
|
107 |
+
if return_trace:
|
108 |
+
return behavior_output, "특별한 행동 없음"
|
109 |
+
else:
|
110 |
+
return behavior_output
|
111 |
|
112 |
# 행동 텍스트 템플릿
|
113 |
behavior_lines = {
|
114 |
+
# Core
|
115 |
+
"jump": "기뻐서 깡충 뜁니다.",
|
116 |
+
"sit_down": "슬퍼서 주저앉습니다....",
|
117 |
+
"shout": "화가 나서 소리를 지릅니다!",
|
118 |
+
"run_away": "무서워 도망갑니다!",
|
119 |
+
"avoid": "뒤로 물러서 피하려 합니다.",
|
120 |
+
"gasp": "깜짝 놀랍니다.",
|
121 |
+
"idle": "가만히 있습니다.",
|
122 |
+
|
123 |
+
# Social
|
124 |
+
"thank": "감사한 마음을 표현합니다.",
|
125 |
+
"hide_face": "수치심에 얼굴을 가립니다.",
|
126 |
+
"apologize": "미안하다고 말합니다.",
|
127 |
"show_off": "자랑스러운 모습을 보여줍니다.",
|
128 |
+
"glare": "상대를 노려봅니다.",
|
129 |
+
"console": "상대방을 따뜻하게 위로합니다.",
|
130 |
+
"embrace": "상대를 포옹합니다.",
|
131 |
+
"cheer": "감탄하며 응원합니다.",
|
132 |
+
"listen_carefully": "상대의 말을 집중해서 듣습니다.",
|
133 |
+
"hold_hands": "손을 잡습니다.",
|
134 |
+
"pat": "가볍게 등을 두드려 위로합니다.",
|
135 |
+
|
136 |
+
# Cognitive
|
137 |
+
"prepare": "무언가를 준비합니다.",
|
138 |
+
"inspect": "유심히 관찰합니다.",
|
139 |
+
"scratch_head": "머리를 긁적입니다.",
|
140 |
+
"lean_forward": "앞으로 숙이며 집중합니다.",
|
141 |
+
"focus": "몰입하여 집중합니다.",
|
142 |
+
"exhale": "안도하며 숨을 쉽니다.",
|
143 |
+
"fidget": "불안하게 몸을 움직입니다.",
|
144 |
+
"relax": "평온하게 휴식합니다.",
|
145 |
+
"raise_eyebrow": "의심스럽게 눈썹을 치켜뜹니다.",
|
146 |
+
|
147 |
+
# Complex
|
148 |
+
"stare_into_distance": "먼 곳을 바라보며 회상에 잠깁니다.",
|
149 |
+
"smile_then_look_down": "미소를 짓다가 고개를 숙입니다.",
|
150 |
+
"smirk": "비웃는 표정을 짓습니다.",
|
151 |
+
"brighten_up": "기대감에 눈빛이 빛납니다.",
|
152 |
+
"scowl": "인상을 찌푸립니다.",
|
153 |
+
"rub_hands": "들뜬 마음에 손을 비빕니다.",
|
154 |
+
"look_down": "고개를 숙이며 후회합니다.",
|
155 |
+
"mutter_to_self": "혼잣말을 중얼거립니다.",
|
156 |
+
"steady_posture": "안정된 자세를 유지합니다.",
|
157 |
}
|
158 |
|
159 |
# 행동 문장 생성
|
160 |
lines = []
|
161 |
+
trace_lines = []
|
162 |
for action, score in sequence:
|
163 |
msg = behavior_lines.get(action, f"{action} 행동을 합니다.")
|
164 |
lines.append(f"({round(score,1)}) {msg}")
|
165 |
+
trace_lines.append(f"{action}({round(score, 1)})")
|
166 |
|
167 |
# 직업 라인 추가
|
168 |
job_line = {
|
|
|
170 |
"blacksmith": "대장간 불도 지펴야죠.",
|
171 |
}.get(job, "오늘도 바쁜 하루네요.")
|
172 |
|
173 |
+
behavior_output = f"{name} 행동 시퀀스: \n" + "\n".join(lines) + f"\n{job_line}"
|
174 |
+
behavior_trace = ", ".join(trace_lines)
|
175 |
+
|
176 |
# 최종 출력 문장
|
177 |
+
if return_trace:
|
178 |
+
return behavior_output, behavior_trace
|
179 |
+
else:
|
180 |
+
return behavior_output
|
npc_social_network/npc/npc_emotion.py
CHANGED
@@ -1,60 +1,83 @@
|
|
1 |
# 감정 상태, 감정 업데이트, decay 처리
|
2 |
# portfolio/npc_social_network/npc/npc_emotion.py
|
3 |
-
from .emotion_config import
|
4 |
|
5 |
class EmotionManager:
|
6 |
-
def __init__(self,
|
7 |
-
#
|
8 |
-
self.
|
9 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
10 |
self.decay_rate = decay_rate
|
11 |
-
# 내부: float 감정 수치 관리용 버퍼
|
12 |
-
self._emotion_buffer = {emo: 0.0 for cat in emotion_state.values() for emo in cat}
|
13 |
self.personality = personality or {}
|
14 |
|
15 |
-
|
16 |
-
|
|
|
|
|
17 |
if emotion not in self._emotion_buffer:
|
18 |
print(f"[경고] '{emotion}'은(는) 정의된 감정이 아닙니다.")
|
19 |
return
|
20 |
|
21 |
# 개성 계수 반영
|
22 |
-
multiplier = 1.0
|
23 |
-
if emotion in AFFECT_EMOTIONS["positive"]:
|
24 |
-
multiplier *= self.personality.get("positive_bias", 1)
|
25 |
-
elif emotion in AFFECT_EMOTIONS["negative"]:
|
26 |
-
multiplier *= self.personality.get("negative_bias", 1)
|
27 |
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
|
|
31 |
self._emotion_buffer[emotion] += strength * multiplier
|
32 |
-
for category in self.emotion_state:
|
33 |
-
if emotion in self.emotion_state[category]:
|
34 |
-
self.emotion_state[category][emotion] = int(self._emotion_buffer[emotion])
|
35 |
-
break
|
36 |
|
37 |
-
|
38 |
-
# 시간이 지남에 따라 모든 감정이 서서히 감소
|
39 |
def decay_emotion(self):
|
40 |
-
|
|
|
|
|
|
|
41 |
rate = self.decay_rate.get(emotion, 0.5)
|
42 |
-
modifier = 1.0 - self.personality.get("stoic", 0)
|
43 |
-
self._emotion_buffer[emotion] = max(0.0,
|
44 |
-
|
45 |
-
for category in self.emotion_state:
|
46 |
-
for emotion in self.emotion_state[category]:
|
47 |
-
self.emotion_state[category][emotion] = int(self._emotion_buffer[emotion])
|
48 |
|
49 |
-
# 정수 기반 출력용 감정 상태 반환
|
50 |
def get_state(self):
|
51 |
-
|
|
|
|
|
|
|
52 |
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
def get_buffer(self):
|
|
|
|
|
|
|
60 |
return self._emotion_buffer.copy()
|
|
|
1 |
# 감정 상태, 감정 업데이트, decay 처리
|
2 |
# portfolio/npc_social_network/npc/npc_emotion.py
|
3 |
+
from .emotion_config import EMOTION_LIST, EMOTION_DECAY_RATE, EMOTION_CATEGORY_MAP
|
4 |
|
5 |
class EmotionManager:
|
6 |
+
def __init__(self, decay_rate, personality = None):
|
7 |
+
# 내부: float 감정 수치 관리용 버퍼
|
8 |
+
self._emotion_buffer = {emo: 0.0 for emo in EMOTION_LIST}
|
9 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
10 |
self.decay_rate = decay_rate
|
|
|
|
|
11 |
self.personality = personality or {}
|
12 |
|
13 |
+
def update_emotion(self, emotion: str, strength: float = 1.0, context: str = "general"):
|
14 |
+
"""
|
15 |
+
특정 감정을 감정 상태에 반영 (카테고리 기반)
|
16 |
+
"""
|
17 |
if emotion not in self._emotion_buffer:
|
18 |
print(f"[경고] '{emotion}'은(는) 정의된 감정이 아닙니다.")
|
19 |
return
|
20 |
|
21 |
# 개성 계수 반영
|
22 |
+
multiplier = self.personality.get("sensitive", 1.0)
|
|
|
|
|
|
|
|
|
23 |
|
24 |
+
category = EMOTION_CATEGORY_MAP.get(emotion, None)
|
25 |
+
if category == "core":
|
26 |
+
multiplier *= self.personality.get("affect_bias", 1.0)
|
27 |
+
elif category == "social":
|
28 |
+
# 사회적 감정은 social context에서만 반영
|
29 |
+
if context != "social":
|
30 |
+
return # 무시
|
31 |
+
multiplier *= self.personality.get("social_bias", 1.0)
|
32 |
+
elif category == "cognitive":
|
33 |
+
multiplier *= self.personality.get("cognitive_bias", 1.0)
|
34 |
+
elif category == "complex":
|
35 |
+
multiplier *= self.personality.get("complex_bias", 1.0)
|
36 |
|
37 |
+
# 감정 반영
|
38 |
self._emotion_buffer[emotion] += strength * multiplier
|
|
|
|
|
|
|
|
|
39 |
|
|
|
|
|
40 |
def decay_emotion(self):
|
41 |
+
"""
|
42 |
+
시간이 지남에 따라 모든 감정이 서서히 감소
|
43 |
+
"""
|
44 |
+
for emotion in EMOTION_LIST:
|
45 |
rate = self.decay_rate.get(emotion, 0.5)
|
46 |
+
modifier = 1.0 - self.personality.get("stoic", 0.0)
|
47 |
+
self._emotion_buffer[emotion] = max(0.0, self._emotion_buffer[emotion] - rate * modifier)
|
|
|
|
|
|
|
|
|
48 |
|
|
|
49 |
def get_state(self):
|
50 |
+
"""
|
51 |
+
감정 상태 반환 (출력용, 정수로 변환)
|
52 |
+
"""
|
53 |
+
return {emo: int(self._emotion_buffer[emo]) for emo in EMOTION_LIST}
|
54 |
|
55 |
+
def get_dominant_emotion(self, layer: str = None):
|
56 |
+
"""
|
57 |
+
현재 가장 강한 감정 반환 (전체 감정 중 최대값 1개)
|
58 |
+
"""
|
59 |
+
if layer:
|
60 |
+
# 특정 layer 대상
|
61 |
+
layer_emotions = [emo for emo, cat in EMOTION_CATEGORY_MAP.items() if cat == layer]
|
62 |
+
if not layer_emotions:
|
63 |
+
return None
|
64 |
+
dominant_emo, score = max(
|
65 |
+
((emo, self._emotion_buffer[emo]) for emo in layer_emotions),
|
66 |
+
key=lambda x: x[1],
|
67 |
+
default=(None, 0.0)
|
68 |
+
)
|
69 |
+
if score == 0.0:
|
70 |
+
return None
|
71 |
+
return dominant_emo
|
72 |
+
else:
|
73 |
+
# 전체 대상
|
74 |
+
dominant_emo, score = max(self._emotion_buffer.items(), key=lambda x:x[1])
|
75 |
+
if score == 0.0:
|
76 |
+
return None
|
77 |
+
return dominant_emo
|
78 |
|
79 |
def get_buffer(self):
|
80 |
+
"""
|
81 |
+
emotion_buffer 복사본 반환
|
82 |
+
"""
|
83 |
return self._emotion_buffer.copy()
|
npc_social_network/npc/npc_memory.py
CHANGED
@@ -10,13 +10,16 @@ class Memory:
|
|
10 |
- importance: 중요도 (1~10)
|
11 |
- emotion: 감정 태그 (기쁨, 슬픔, 분노, 중립 등)
|
12 |
- is_long_term: 장기 기억 여부
|
|
|
13 |
"""
|
14 |
-
def __init__(self, content: str, timestamp: datetime = None,
|
|
|
15 |
self.content = content
|
16 |
self.timestamp = timestamp or datetime.now()
|
17 |
self.importance = importance
|
18 |
self.emotion = emotion
|
19 |
self.is_long_term = False
|
|
|
20 |
|
21 |
|
22 |
class MemoryStore:
|
|
|
10 |
- importance: 중요도 (1~10)
|
11 |
- emotion: 감정 태그 (기쁨, 슬픔, 분노, 중립 등)
|
12 |
- is_long_term: 장기 기억 여부
|
13 |
+
- behavior_trace: 기억을 통한 행동 추적
|
14 |
"""
|
15 |
+
def __init__(self, content: str, timestamp: datetime = None,
|
16 |
+
importance: int = 1, emotion: str = "neutral", behavior_trace: str = None):
|
17 |
self.content = content
|
18 |
self.timestamp = timestamp or datetime.now()
|
19 |
self.importance = importance
|
20 |
self.emotion = emotion
|
21 |
self.is_long_term = False
|
22 |
+
self.behavior_trace = behavior_trace
|
23 |
|
24 |
|
25 |
class MemoryStore:
|