rorshi commited on
Commit
71fb1a6
Β·
1 Parent(s): 5a9f257

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 query_llm_for_response_with_context(npc_name, npc_job, emotion_state, recent_memories,
38
- personality, relationship_with_player):
39
  """
40
- LLM에 μ»¨ν…μŠ€νŠΈ 정보λ₯Ό μ „λ‹¬ν•˜μ—¬ μžμ—°μŠ€λŸ¬μš΄ λŒ€μ‚¬ 생성
41
  """
42
- memory_text = "\n".join([f"- {m.content}" for m in recent_memories])
43
- emotion_text = ", ".join([f"{emo}({score})" for emo, score in emotion_state])
 
 
44
 
45
- # 졜근 λŒ€ν™” λ‚΄μš©μ€ 기얡에 λ‹€ λ“€μ–΄κ°€λ‚˜?
46
- # 행동이 λ“€μ–΄κ°ˆ κ±°λ‹ˆκΉŒ, 기얡도 λ‹¨μˆœ λ¬Έμž₯ν˜•μ΄ μ•„λ‹ˆλΌ κ°€κ³΅ν•΄μ„œ κΈ°μ–΅ν•˜κ³ ,
47
- # λŒ€ν™” λ‚΄μš©μ€ κ°€μž₯ 졜근 λŒ€ν™” λ‚΄μš©μ„ λ„˜κ²¨μ£ΌλŠ” 방식이 쒋을거 같은데, μ‹œκ°„μ΄ μ§€λ‚˜λ©΄ μžŠν˜€μ§€κ³ 
48
- prompt = f"""
49
- NPC 이름: {npc_name}
50
- NPC 직업: {npc_job}
51
- ν˜„μž¬ 감정 μƒνƒœ: {emotion_text}
52
- 졜근 κΈ°μ–΅:
53
- {memory_text}
54
- ν˜„μž¬ ν”Œλ ˆμ΄μ–΄μ™€μ˜ 관계: {relationship_with_player}
55
- Personality: {personality}
56
 
57
- μœ„ 정보λ₯Ό μ°Έκ³ ν•˜μ—¬, ν•œκ΅­μ–΄λ‘œ μžμ—°μŠ€λŸ½κ³  상황에 λ§žλŠ” λŒ€ν™” λ¬Έμž₯을 μƒμ„±ν•˜μ„Έμš”.
58
  """
59
 
60
- # μž„μ‹œ 응닡 (LLM μ—°κ²° μ‹œ ꡐ체)
61
- return f"{npc_name}이(κ°€) λ§ν–ˆμŠ΅λ‹ˆλ‹€. 'μ˜€λŠ˜μ€ 기뢄이 μ’‹μ•„μš”!' "
 
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 query_llm_for_emotion, query_llm_for_response, query_llm_for_response_with_context
 
 
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
- # LLM 기반 generate
114
- npc_reply = query_llm_for_response_with_context(
115
- npc_name = self.name,
116
- npc_job = self.job,
117
- emotion_state = self.get_composite_emotion_state(),
118
- recent_memories = self.memory_store.get_recent_memories(limit=5),
119
- personality = self.personality,
120
- relationship_with_player = self.get_relationship_description("ν”Œλ ˆμ΄μ–΄"),
121
- )
 
 
122
 
123
  # Memory 기둝
124
  self.remember(
125
- content = f"[NPC: {self.name}] LLM λŒ€μ‚¬: '{npc_reply}'",
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, user_input:str, player_name:str="ν”Œλ ˆμ΄μ–΄"):
295
  """
296
  ν”Œλ ˆμ΄μ–΄ μž…λ ₯에 λ°˜μ‘ (LLM 기반 응닡 생성 + Memory/Emotion/관계 반영)
297
  """
298
- # 1. LLM 응닡 생성
299
- npc_reply = query_llm_for_response(self.name, self.job, user_input)
300
-
301
- # 2. LLM 감정 μΆ”μΆœ
302
- dominant_emotion = query_llm_for_emotion(user_input)
303
 
304
- # 3. Memory 기둝
305
- self.remember(
306
- content = f"[ν”Œλ ˆμ΄μ–΄:{player_name}] '{user_input}' β†’ [NPC:{self.name}] '{npc_reply}'",
307
- importance = 7, # ν”Œλ ˆμ΄μ–΄ μƒν˜Έμž‘μš©μ€ 높은 μ€‘μš”λ„λ‘œ μ €μž₯(λ‚˜μ€‘μ— λ‚΄μš©μ— 따라 λ³€κ²½ν•˜λ„λ‘ μˆ˜μ •)
308
- emotion = dominant_emotion
309
- )
310
 
311
- # Personality μ—…λ°μ΄νŠΈ
312
- self.update_personality()
313
 
314
- # 4. Emotion 반영
315
- if dominant_emotion:
316
- self.update_emotion(dominant_emotion, strength=2.0)
317
 
318
- # 5. 관계 반영 (μƒν˜Έμž‘μš© 기반 - μ§€κΈˆ μƒνƒœλ©΄ 긍정적인 κ΄€κ³„λ§Œ λ°˜μ˜λ˜λŠ”κ±° μ•„λ‹Œκ°€?)
319
- positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
320
- self.interact_with(player_name, dominant_emotion, positive)
 
 
 
321
 
322
- return npc_reply, dominant_emotion
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}")