Spaces:
Sleeping
Sleeping
Complete step 14
Browse files
npc_social_network/models/llm_helper.py
CHANGED
@@ -34,28 +34,26 @@ def query_llm_for_emotion(user_input):
|
|
34 |
# μμ κ²°κ³Ό (μΆν LLM μ°κ²° μ κ΅μ²΄)
|
35 |
return "joy"
|
36 |
|
37 |
-
def
|
38 |
-
personality, relationship_with_player):
|
39 |
"""
|
40 |
-
|
41 |
"""
|
42 |
-
|
43 |
-
|
|
|
|
|
44 |
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
{memory_text}
|
54 |
-
νμ¬ νλ μ΄μ΄μμ κ΄κ³: {relationship_with_player}
|
55 |
-
Personality: {personality}
|
56 |
|
57 |
-
|
58 |
"""
|
59 |
|
60 |
-
|
61 |
-
return f"
|
|
|
34 |
# μμ κ²°κ³Ό (μΆν LLM μ°κ²° μ κ΅μ²΄)
|
35 |
return "joy"
|
36 |
|
37 |
+
def query_llm_with_prompt(prompt: str) -> str:
|
|
|
38 |
"""
|
39 |
+
prompt λ¬Έμμ΄μ λ°μ LLM νΈμΆ
|
40 |
"""
|
41 |
+
print(f"[LLM νΈμΆ ν둬ννΈ λ―Έλ¦¬λ³΄κΈ°]\n{prompt[:300]}...\n")
|
42 |
+
lines = [l.strip() for l in prompt.strip().splitlines() if l.strip()]
|
43 |
+
summary_line = lines[-1] if lines else "(μμ² μμ)"
|
44 |
+
return f"(μλ΅ μμ) {summary_line}"
|
45 |
|
46 |
+
def summarize_memories(memory_texts: list[str]) -> str:
|
47 |
+
"""
|
48 |
+
κΈ°μ΅ λ¦¬μ€νΈλ₯Ό 1~2λ¬Έμ₯μΌλ‘ μμ½
|
49 |
+
"""
|
50 |
+
joined = "\n".join([f"- {t}" for t in memory_texts])
|
51 |
+
prompt = f"""λ€λ¬μ ν μΈλ¬Όμ κ³Όκ±° κΈ°μ΅λ€μ
λλ€:
|
52 |
+
|
53 |
+
{joined}
|
|
|
|
|
|
|
54 |
|
55 |
+
β μ΄ μ¬λμ μ΄λ€ κ²½νμ νλμ§ 1~2λ¬Έμ₯μΌλ‘ μμ½ν΄ μ£ΌμΈμ.
|
56 |
"""
|
57 |
|
58 |
+
print(f"[μμ½ ν둬ννΈ λ―Έλ¦¬λ³΄κΈ°]\n{prompt}")
|
59 |
+
return f"(μμ½ κ²°κ³Ό) {' / '.join(memory_texts)}" # μ€μ LLM μ°κ²° μ κ΅μ²΄
|
npc_social_network/models/llm_prompt_builder.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# portfolio/npc_social_network/models/llm_prompt_builder.py
|
2 |
+
|
3 |
+
def build_npc_prompt(npc, user_input: str, matched_memories: list[str]) -> str:
|
4 |
+
"""
|
5 |
+
NPC μ 보 + μ¬μ©μ μ
λ ₯ + κ²μλ κΈ°μ΅μ κΈ°λ°μΌλ‘ ν둬ννΈ μμ±
|
6 |
+
"""
|
7 |
+
# κ°μ μν μμ½
|
8 |
+
emotions_text = ", ".join([f"{e}({v})" for e, v in npc.get_composite_emotion_state()])
|
9 |
+
|
10 |
+
# κ΄λ ¨ κΈ°μ΅ κ΅¬μ±
|
11 |
+
memory_block = "\n".join([f"- {m}" for m in matched_memories]) if matched_memories else "κ΄λ ¨ κΈ°μ΅ μμ"
|
12 |
+
|
13 |
+
# ν둬ννΈ κ΅¬μ±
|
14 |
+
prompt = f"""
|
15 |
+
NPC μ΄λ¦: {npc.name}
|
16 |
+
μ§μ
: {npc.job}
|
17 |
+
νμ¬ κ°μ μν: {emotions_text}
|
18 |
+
μ±κ²© μμ½: {npc.personality}
|
19 |
+
νλ μ΄μ΄μμ κ΄κ³: {npc.get_relationship_description("νλ μ΄μ΄")}
|
20 |
+
|
21 |
+
# κ³Όκ±° κ΄λ ¨ κΈ°μ΅:
|
22 |
+
{memory_block}
|
23 |
+
|
24 |
+
# νλ μ΄μ΄ μ§λ¬Έ:
|
25 |
+
{user_input}
|
26 |
+
|
27 |
+
β μ μ 보λ₯Ό λ°νμΌλ‘ μμ°μ€λ½κ³ μν©μ λ§λ λλ΅μ ν΄μ£ΌμΈμ.
|
28 |
+
"""
|
29 |
+
return prompt.strip()
|
npc_social_network/npc/emotion_config.py
CHANGED
@@ -153,15 +153,4 @@ NEGATIVE_RELATION_EMOTIONS = [
|
|
153 |
COGNITIVE_RELATION_EMOTIONS = [
|
154 |
"compassion", "awe", "attachment", "anticipation", "curiosity",
|
155 |
"nostalgia", "bittersweet", "rumination", "groundedness", "comfort",
|
156 |
-
]
|
157 |
-
|
158 |
-
# μΈμ λ¨κ³λ³ Personality λ³νμ¨ μ μ
|
159 |
-
AGE_PROFILE = {
|
160 |
-
"infancy_and_toddlerhood": 3.0, # (0-3 μΈ) Temperament & attachment foundation β change rate very high
|
161 |
-
"early_childhood": 2.0, # (3-6 μΈ) Initial personality structure emergence β high change rate
|
162 |
-
"middle_childhood": 1.5, # (6-12 μΈ) Personality structure solidification β still high change rate
|
163 |
-
"adolescence": 1.0, # (12-18 μΈ) Identity formation β moderate to high change rate
|
164 |
-
"young_adulthood": 0.5, # (18-30 μΈ) Personality stabilization β moderate change rate
|
165 |
-
"middle_adulthood": 0.2, # (30-65 μΈ) Personality highly stable β low change rate
|
166 |
-
"older_adulthood": 0.02 # (65- μΈ) Personality largely fixed β minimal change
|
167 |
-
}
|
|
|
153 |
COGNITIVE_RELATION_EMOTIONS = [
|
154 |
"compassion", "awe", "attachment", "anticipation", "curiosity",
|
155 |
"nostalgia", "bittersweet", "rumination", "groundedness", "comfort",
|
156 |
+
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/npc/npc_base.py
CHANGED
@@ -6,7 +6,9 @@ from .emotion_config import EMOTION_LIST, EMOTION_CATEGORY_MAP, EMOTION_DECAY_RA
|
|
6 |
from .emotion_config import POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS, COGNITIVE_RELATION_EMOTIONS
|
7 |
from .personality_config import AGE_PROFILE, PERSONALITY_PROFILE
|
8 |
from .npc_relationship import RelationshipManager
|
9 |
-
from ..models.llm_helper import
|
|
|
|
|
10 |
from datetime import datetime
|
11 |
import random
|
12 |
import copy
|
@@ -105,24 +107,26 @@ class NPC:
|
|
105 |
x, y = self.get_position()
|
106 |
screen.blit(self.image, (x * tile_size, y * tile_size))
|
107 |
|
108 |
-
def generate_dialogue(self, use_llm=True):
|
109 |
"""
|
110 |
-
λν μμ± ν¨μ
|
111 |
"""
|
112 |
-
if use_llm:
|
113 |
-
#
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
|
|
|
|
122 |
|
123 |
# Memory κΈ°λ‘
|
124 |
self.remember(
|
125 |
-
content = f"[NPC:
|
126 |
importance = 7,
|
127 |
emotion = self.emotion.get_dominant_emotion()
|
128 |
)
|
@@ -142,6 +146,14 @@ class NPC:
|
|
142 |
self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace = True
|
143 |
)
|
144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
# λ¨κ³λ³ dominant emotion sequence κ³μ°
|
146 |
dominant_emotions = self.behavior.get_layer_dominant_emotions(self._emotion_buffer)
|
147 |
dominant_sequence = self.behavior.decide_layered_sequence(dominant_emotions)
|
@@ -174,7 +186,7 @@ class NPC:
|
|
174 |
# Personality μ
λ°μ΄νΈ
|
175 |
self.update_personality()
|
176 |
|
177 |
-
return behavior_output
|
178 |
|
179 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0, memory_type:str = "Event"):
|
180 |
"""
|
@@ -291,35 +303,35 @@ class NPC:
|
|
291 |
"""
|
292 |
return self.emotion.get_top_emotions(top_n=top_n)
|
293 |
|
294 |
-
def interact_with_player(self,
|
295 |
"""
|
296 |
νλ μ΄μ΄ μ
λ ₯μ λ°μ (LLM κΈ°λ° μλ΅ μμ± + Memory/Emotion/κ΄κ³ λ°μ)
|
297 |
"""
|
298 |
-
# 1
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
dominant_emotion = query_llm_for_emotion(user_input)
|
303 |
|
304 |
-
#
|
305 |
-
self.
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
)
|
310 |
|
311 |
-
#
|
312 |
-
self
|
313 |
|
314 |
-
# 4
|
315 |
-
|
316 |
-
self.update_emotion(dominant_emotion, strength=2.0)
|
317 |
|
318 |
-
# 5
|
319 |
-
|
320 |
-
|
|
|
|
|
|
|
321 |
|
322 |
-
return npc_reply
|
323 |
|
324 |
def interact_with_npc(self, target_npc):
|
325 |
"""
|
@@ -531,3 +543,16 @@ class NPC:
|
|
531 |
else:
|
532 |
# fallback β μ μν
|
533 |
return 1.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
from .emotion_config import POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS, COGNITIVE_RELATION_EMOTIONS
|
7 |
from .personality_config import AGE_PROFILE, PERSONALITY_PROFILE
|
8 |
from .npc_relationship import RelationshipManager
|
9 |
+
from ..models.llm_helper import query_llm_with_prompt, query_llm_for_emotion, summarize_memories
|
10 |
+
from ..models.llm_prompt_builder import build_npc_prompt
|
11 |
+
from .npc_memory_embedder import search_similar_memories
|
12 |
from datetime import datetime
|
13 |
import random
|
14 |
import copy
|
|
|
107 |
x, y = self.get_position()
|
108 |
screen.blit(self.image, (x * tile_size, y * tile_size))
|
109 |
|
110 |
+
def generate_dialogue(self,user_input=None, use_llm=True):
|
111 |
"""
|
112 |
+
λν μμ± ν¨μ (κΈ°μ΅ κ²μ κΈ°λ° ν둬ννΈ ν¬ν¨)
|
113 |
"""
|
114 |
+
if use_llm and user_input:
|
115 |
+
# κ΄λ ¨ κΈ°μ΅ κ²μ
|
116 |
+
_, _, matched_memories = search_similar_memories(self.name, user_input)
|
117 |
+
# μμ½ & μ₯κΈ° κΈ°μ΅ν (3κ° μ΄μμΌ λλ§)
|
118 |
+
if len(matched_memories) >= 3:
|
119 |
+
self.summarize_and_store_memories(matched_memories)
|
120 |
+
|
121 |
+
# ν둬ννΈ μμ±
|
122 |
+
prompt = build_npc_prompt(self, user_input, matched_memories)
|
123 |
+
|
124 |
+
# LLM νΈμΆ
|
125 |
+
npc_reply = query_llm_with_prompt(prompt)
|
126 |
|
127 |
# Memory κΈ°λ‘
|
128 |
self.remember(
|
129 |
+
content = f"[νλ μ΄μ΄] '{user_input}' β [NPC:{self.name}] '{npc_reply}'",
|
130 |
importance = 7,
|
131 |
emotion = self.emotion.get_dominant_emotion()
|
132 |
)
|
|
|
146 |
self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace = True
|
147 |
)
|
148 |
|
149 |
+
# fallback (κ°μ κΈ°λ° νλ)
|
150 |
+
behavior_output = self.behavior.perform_sequence(
|
151 |
+
self.name,
|
152 |
+
self.job,
|
153 |
+
emotion_buffer=self._emotion_buffer,
|
154 |
+
return_trace=False
|
155 |
+
)
|
156 |
+
|
157 |
# λ¨κ³λ³ dominant emotion sequence κ³μ°
|
158 |
dominant_emotions = self.behavior.get_layer_dominant_emotions(self._emotion_buffer)
|
159 |
dominant_sequence = self.behavior.decide_layered_sequence(dominant_emotions)
|
|
|
186 |
# Personality μ
λ°μ΄νΈ
|
187 |
self.update_personality()
|
188 |
|
189 |
+
return str(behavior_output)
|
190 |
|
191 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0, memory_type:str = "Event"):
|
192 |
"""
|
|
|
303 |
"""
|
304 |
return self.emotion.get_top_emotions(top_n=top_n)
|
305 |
|
306 |
+
def interact_with_player(self, player_input: str) -> str:
|
307 |
"""
|
308 |
νλ μ΄μ΄ μ
λ ₯μ λ°μ (LLM κΈ°λ° μλ΅ μμ± + Memory/Emotion/κ΄κ³ λ°μ)
|
309 |
"""
|
310 |
+
# STEP 1: κ°μ μΆλ‘ (μ νμ )
|
311 |
+
emotion = query_llm_for_emotion(player_input)
|
312 |
+
if emotion:
|
313 |
+
self.update_emotion(emotion, strength=2.0)
|
|
|
314 |
|
315 |
+
# STEP 2: κ΄λ ¨ κΈ°μ΅ κ²μ
|
316 |
+
_, _, matched_memories = search_similar_memories(self.name, player_input)
|
317 |
+
# μμ½ & μ₯κΈ° κΈ°μ΅ν (3κ° μ΄μμΌ λλ§)
|
318 |
+
if len(matched_memories) >= 3:
|
319 |
+
self.summarize_and_store_memories(matched_memories)
|
|
|
320 |
|
321 |
+
# STEP 3: ν둬ννΈ μμ±
|
322 |
+
prompt = build_npc_prompt(self, player_input, matched_memories)
|
323 |
|
324 |
+
# STEP 4: LLM μλ΅ μμ±
|
325 |
+
npc_reply = query_llm_with_prompt(prompt)
|
|
|
326 |
|
327 |
+
# STEP 5: κΈ°μ΅ κΈ°λ‘
|
328 |
+
self.remember(
|
329 |
+
content=f"[νλ μ΄μ΄] '{player_input}' β [NPC:{self.name}] '{npc_reply}'",
|
330 |
+
importance=6,
|
331 |
+
emotion=emotion
|
332 |
+
)
|
333 |
|
334 |
+
return npc_reply
|
335 |
|
336 |
def interact_with_npc(self, target_npc):
|
337 |
"""
|
|
|
543 |
else:
|
544 |
# fallback β μ μν
|
545 |
return 1.0
|
546 |
+
|
547 |
+
def ummarize_and_store_memories(self, memory_texts: list[str]):
|
548 |
+
"""
|
549 |
+
κΈ°μ΅ λ¦¬μ€νΈλ₯Ό μμ½νκ³ , NPC μ₯κΈ° κΈ°μ΅μΌλ‘ μ μ₯
|
550 |
+
"""
|
551 |
+
if not memory_texts:
|
552 |
+
return
|
553 |
+
|
554 |
+
summary = summarize_memories(memory_texts)
|
555 |
+
self.memory_store.add_memory(
|
556 |
+
Memory(content=summary, importance=9, emotion=self.emotion.get_dominant_emotion(), memory_type="Summary")
|
557 |
+
)
|
558 |
+
print(f"[μμ½ κΈ°μ΅ μ μ₯λ¨] {summary}")
|