humanda5 commited on
Commit
0b572d3
·
1 Parent(s): b5ff045

멀티턴 대화 시스템 구현 - 진행중

Browse files
npc_social_network/data/saves/simulation_state.pkl CHANGED
Binary files a/npc_social_network/data/saves/simulation_state.pkl and b/npc_social_network/data/saves/simulation_state.pkl differ
 
npc_social_network/data/vectorstores/diana.faiss CHANGED
Binary files a/npc_social_network/data/vectorstores/diana.faiss and b/npc_social_network/data/vectorstores/diana.faiss differ
 
npc_social_network/manager/conversation_manager.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # portfolio/npc_social_network/manager/conversation_manager.py
2
+ # 대화의 시작, 진행, 종료를 총괄하는 역할
3
+ from .. import simulation_core
4
+ from typing import List, Optional, TYPE_CHECKING
5
+ if TYPE_CHECKING:
6
+ from ..npc.npc_base import NPC
7
+
8
+ class Conversation:
9
+ """단일 대화의 상태를 저장하는 데이터 클래스"""
10
+ def __init__(self, initiator: "NPC", target: "NPC", topic: str):
11
+ self.participants: List["NPC"] = [initiator, target]
12
+ self.conversation_history: List[str] = []
13
+ self.topic: str = topic
14
+ self.turn_index: int = 0 # 0: initiator, 1: target
15
+
16
+ def add_utterance(self, speaker: "NPC", utterance: str):
17
+ """대화 내역을 추가"""
18
+ self.conversation_history.append(f"[{speaker.korean_name}]: {utterance}")
19
+
20
+ def get_current_speaker(self) -> "NPC":
21
+ """이번에 말할 차례인 NPC를 반환"""
22
+ return self.participants[self.turn_index]
23
+
24
+ def switch_turn(self):
25
+ """대화 턴을 상대방에게 넘김"""
26
+ self.turn_index = 1 - self.turn_index
27
+
28
+ class ConversationManager:
29
+ """활성화된 대화를 관리하는 중앙 관리자"""
30
+ def __init__(self):
31
+ self.active_conversation: Optional[Conversation] = None
32
+
33
+ def is_conversation_active(self) -> bool:
34
+ """현재 진행 중인 대화가 있는지 확인"""
35
+ return self.active_conversation is not None
36
+
37
+ def start_conversation(self, initiator: "NPC", target: "NPC",
38
+ topic: Optional[str] = None):
39
+ """새로운 대화를 시작"""
40
+ if self.is_conversation_active():
41
+ return # 이미 다른 대화가 진행중이면 시작 x
42
+
43
+ conv_topic = topic or "가벼운 인사"
44
+ self.active_conversation = Conversation(initiator, target, conv_topic)
45
+ print(f"--[대화 시작]--")
46
+ print(f"주제: {conv_topic} / 참여자: {initiator.korean_name}, {target.korean_name}")
47
+
48
+ # 1. 첫 마디 생성
49
+ first_utterance = initiator.generate_first_utterance(conv_topic, target)
50
+ if "..." in first_utterance:
51
+ self.end_conversation("첫 마디 생성 실패")
52
+ return
53
+
54
+ # 2. 첫 마디를 기록에 추가
55
+ self.active_conversation.add_utterance(initiator, first_utterance)
56
+
57
+ # 3. 턴을 상대방에게 넘김 (이제 상대방이 대답할 차례)
58
+ self.active_conversation.switch_turn()
59
+
60
+ def end_conversation(self, reason: str = "자연스럽게"):
61
+ """현재 대화를 종료"""
62
+ if self.is_conversation_active():
63
+ # 대화가 끝나면, 전체 대화 내용을 바탕으로 기억을 생성하고 관계를 업데이트
64
+ conv = self.active_conversation
65
+ initiator = conv.participants[0]
66
+ target = conv.participants[1]
67
+ full_conversation = "\n".join(conv.conversation_history)
68
+
69
+ with simulation_core.simulation_lock:
70
+ # 양쪽 모두에게 대화 기억을 저장
71
+ initiator.remember(content=f"{target.korean_name}와 '{conv.topic}'에 대해 대화했다:\n{full_conversation}", importance=7, memory_type="Conversation")
72
+ target.remember(content=f"{initiator.korean_name}와 '{conv.topic}'에 대해 대화했다:\n{full_conversation}", importance=7, memory_type="Conversation")
73
+
74
+ print(f"---[대화 종료: {reason}]---\n")
75
+ self.active_conversation = None
76
+
77
+ def next_turn(self):
78
+ """대화의 다음 턴을 진행"""
79
+ if not self.is_conversation_active():
80
+ return
81
+
82
+ conv = self.active_conversation
83
+ speaker = conv.get_current_speaker()
84
+
85
+ # 대답 생성 로직 (다음 단계에서 수정)
86
+ utterance, action = speaker.generate_dialogue_turn(conv)
87
+
88
+ if "[LLM Error]" in utterance:
89
+ self.end_conversation("오류 발생")
90
+ return
91
+
92
+ conv.add_utterance(speaker, utterance)
93
+
94
+ if action == "END" or len(conv.conversation_history) >= 10: #대화가 너무 길어지면 강제 종료
95
+ self.end_conversation()
96
+ else:
97
+ conv.switch_turn()
npc_social_network/models/llm_helper.py CHANGED
@@ -114,7 +114,7 @@ Action Types can be "TALK" (for conversing with others) or "SOLO_ACTION" (for ac
114
  json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
115
 
116
  if json_match:
117
- return json.load(json_match.group())
118
 
119
  # 분석 실패 시 혼자 하는 행동으로 간주
120
  return {"action_type": "SOLO_ACTION", "target": None, "topic": plan_step}
@@ -173,4 +173,36 @@ Your response MUST be a valid JSON array of strings, where each string is a step
173
  result = _query_llm_for_json_robustly(prompt)
174
  if isinstance(result, list) and all(isinstance(s, str) for s in result):
175
  return result
176
- return ["[계획 생성에 실패했습니다]"] # 최종 실패 시 안전한 값 반환
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
115
 
116
  if json_match:
117
+ return json.loads(json_match.group())
118
 
119
  # 분석 실패 시 혼자 하는 행동으로 간주
120
  return {"action_type": "SOLO_ACTION", "target": None, "topic": plan_step}
 
173
  result = _query_llm_for_json_robustly(prompt)
174
  if isinstance(result, list) and all(isinstance(s, str) for s in result):
175
  return result
176
+ return ["[계획 생성에 실패했습니다]"] # 최종 실패 시 안전한 값 반환
177
+
178
+ def generate_dialogue_action(speaker_name: str, conversation_history: List[str], topic: str) -> dict:
179
+ """대화 기록을 바탕으로 다음 대사와 행동(계속/종료)을 생성"""
180
+ history_str = "\n".join(conversation_history)
181
+
182
+ prompt = f"""
183
+ # Persona
184
+ 당신은 "{speaker_name}"입니다.
185
+
186
+ # Context
187
+ - 현재 대화 주제: "{topic}"
188
+ - 지금까지의 대화 내용:
189
+ {history_str}
190
+
191
+ # Instruction
192
+ - 위 대화의 흐름과 주제에 맞춰, 당신이 이번 턴에 할 자연스러운 대사를 생성하셍.
193
+ - 그리고, 이 대화를 계속 이어갈지("CONTINUE"), 아니면 이 대사를 끝으로 대화를 마무리할지("END") 결정하세요.
194
+ - 당신의 응답은 반드시 아래 JSON 형식이어야 합니다.
195
+ - 대사에는 이름, 행동 묘사, 따옴표를 절대 포함하지 마세요.
196
+
197
+ # Response (JSON format only)
198
+ {{
199
+ "dialogue": "실제 말할 대사 내용",
200
+ "action": "CONTINUE or END"
201
+ }}
202
+ """
203
+ result = _query_llm_for_json_robustly(prompt) # 3중 방어 시스템 재활용
204
+
205
+ # 결과 포맷 검증 및 기본값 처리
206
+ if isinstance(result, dict) and "dialogue" in result and "action" in result:
207
+ return result
208
+ return {"dialogue": "...", "action": "END"} # 최종 실패 시 안전하게 대화 종료
npc_social_network/npc/npc_base.py CHANGED
@@ -11,7 +11,8 @@ from .emotion_config import EMOTION_LIST, EMOTION_CATEGORY_MAP, EMOTION_DECAY_RA
11
  from .emotion_config import POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS, COGNITIVE_RELATION_EMOTIONS
12
  from .personality_config import AGE_PROFILE, PERSONALITY_PROFILE
13
  from .npc_relationship import RelationshipManager
14
- from ..models.llm_helper import query_llm_with_prompt, query_llm_for_emotion, summarize_memories, analyze_gossip, classify_and_extract_plan_details
 
15
  from ..models.llm_prompt_builder import build_npc_prompt
16
  from .npc_memory_embedder import search_similar_memories, add_memory_to_index
17
  from .npc_planner import PlannerManager
@@ -141,91 +142,133 @@ class NPC:
141
  self.personality_stage = None
142
  self.update_personality_stage()
143
 
144
- def generate_dialogue(self, user_input: str, time_context: str
145
- , target_npc: Optional["NPC"]=None, use_llm: bool=True
146
- , create_memory: bool = True) -> str:
147
- """
148
- 플레이어 또는 NPC와 상호작용 시 사용되는 통합 대사 생성 함수
149
- - user_input: 입력 문장 (플레이어나 NPC로부터)
150
- - target_npc: NPC 간 대화일 경우 상대 NPC
151
- """
152
- if use_llm and user_input:
153
- # 1. 생각하기
154
- # 1-1. 유사 기억 검색
155
- _, _, matched_memories = search_similar_memories(self, user_input)
156
-
157
- # 1-2. LLM 프롬프트 생성 및 호출
158
- prompt = build_npc_prompt(self, user_input, matched_memories, time_context, target_npc)
159
- npc_reply = query_llm_with_prompt(prompt)
160
-
161
- if not npc_reply or "[LLM Error]" in npc_reply:
162
- return "[LLM Error]..." # 에러 시 기본 응답
163
-
164
- # 1-3. 자신의 응답에서 감정 추론 및 상태 업데이트
165
- emotion_from_reply = query_llm_for_emotion(npc_reply)
166
- # 1-4. 상대방의 말에서 감정 추론 (NPC간 대화일 경우)
167
- emotion_from_input = query_llm_for_emotion(user_input) if target_npc else None
168
-
169
- # 2. 행동하기
170
- if create_memory:
171
- with simulation_core.simulation_lock:
172
- # 2-1. 기억 요약 및 장기 기억화
173
- if len(matched_memories) >= 3:
174
- self.summarize_and_store_memories(matched_memories)
175
-
176
- # 2-2. 추론한 감정들을 상태에 업데이트
177
- if emotion_from_reply and emotion_from_reply in EMOTION_LIST:
178
- self.update_emotion(emotion_from_reply , strength=2.0)
179
-
180
- # 2-3. 상호작용 기억 저장
181
- interlocutor_name = target_npc.name if target_npc else "player"
182
- interlocutor_korean_name = target_npc.korean_name if target_npc else "플레이어"
183
- memory_content = f"[{interlocutor_korean_name}의 말] '{user_input}' → [나의 응답] '{npc_reply}'"
184
- memory = self.remember(content=memory_content, importance=7, emotion=emotion_from_reply , context_tags=[time_context])
185
-
186
- # 2-4. 관계 상태 업데이트
187
- if emotion_from_reply and emotion_from_reply in EMOTION_RELATION_IMPACT:
188
- self.relationships.update_relationship(interlocutor_name, emotion_from_reply , strength=2.0, memory=memory)
189
- if target_npc and emotion_from_input and emotion_from_input in EMOTION_RELATION_IMPACT:
190
- target_npc.relationships.update_relationship(self.name, emotion_from_input, strength=2.0, memory=memory)
191
-
192
- # 2-5. 성격 변화 반영
193
- self.update_personality()
194
-
195
- return npc_reply
196
 
197
- else:
198
- # LLM 미사용 시: 감정 기반 행동 생성
199
- behavior_output, behavior_trace = self.behavior.perform_sequence(
200
- self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace=True
201
- )
202
 
203
- # 행동 기반 감정 추론 및 반영
204
- for action, score in behavior_trace:
205
- emotion_name = self.behavior.action_to_emotion.get(action)
206
- if emotion_name:
207
- self.update_emotion(emotion_name, strength=score)
208
-
209
- # dominant_score 기반 중요도 추정
210
- dominant_emotions = self.behavior.get_layer_dominant_emotions(self._emotion_buffer)
211
- dominant_sequence = self.behavior.decide_layered_sequence(dominant_emotions)
212
- dominant_score = max([score for _, score in dominant_sequence]) if dominant_sequence else 5
213
- importance = min(int(dominant_score), 10)
214
-
215
- # Memory 저장
216
- memory_entry = Memory(
217
- content=f"행동 수행: {behavior_trace}",
218
- importance=importance,
219
- emotion=self.emotion.get_dominant_emotion(),
220
- behavior_trace=behavior_trace,
221
- context_tags=[time_context]
222
- )
223
- self.memory_store.add_memory(memory_entry)
224
 
225
- # 성격 업데이트
226
- self.update_personality()
 
 
227
 
228
- return str(behavior_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
 
230
  def remember(self, content: str, importance: int = 5, emotion: str = None,
231
  strength: float = 1.0, memory_type:str = "Event",
@@ -451,7 +494,7 @@ class NPC:
451
  target_npc = self.manager.get_npc_by_korean_name(target_korean_name)
452
  if target_npc and target_npc != self:
453
  # 주제가 있는 대화 시작
454
- self.manager.initiate_npc_to_npc_interaction(self, target_npc, time_context, topic=topic)
455
  else:
456
  print(f"[{self.korean_name}] '{target_korean_name}'를 찾을 수 없어 대화 계획을 실행하지 못했습니다.")
457
  else:
@@ -520,7 +563,7 @@ class NPC:
520
  target_npc = random.choices(potential_targets, weights=weights, k=1)[0]
521
 
522
  # 주제가 없는 가벼운 대화 시작
523
- self.manager.initiate_npc_to_npc_interaction(self, target_npc, time_context, topic=None)
524
 
525
  def create_symbolic_memory(self):
526
  """
 
11
  from .emotion_config import POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS, COGNITIVE_RELATION_EMOTIONS
12
  from .personality_config import AGE_PROFILE, PERSONALITY_PROFILE
13
  from .npc_relationship import RelationshipManager
14
+ from ..models.llm_helper import query_llm_with_prompt, query_llm_for_emotion, summarize_memories
15
+ from ..models.llm_helper import analyze_gossip, classify_and_extract_plan_details, generate_dialogue_action
16
  from ..models.llm_prompt_builder import build_npc_prompt
17
  from .npc_memory_embedder import search_similar_memories, add_memory_to_index
18
  from .npc_planner import PlannerManager
 
142
  self.personality_stage = None
143
  self.update_personality_stage()
144
 
145
+ def generate_first_utterance(self, topic: str, target_npc: "NPC") -> str:
146
+ """대화의 마디를 생성. (대화 기록이 필요 없음)"""
147
+ prompt = f"""
148
+ # Persona
149
+ 당신은 "{self.korean_name}"입니다.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ # Context
152
+ - 당신은 지금 "{target_npc.korean_name}"에게 말을 걸려고 합니다.
153
+ - 당신과 상대의 관계: {self.relationships.get_relationship_summary(target_npc.name)}
154
+ - 대화 주제: "{topic}"
 
155
 
156
+ # Instruction
157
+ - 주어진 상황과 대화 주제에 맞춰, 상대방에게 건넬 자연스러운 첫 대사 **한 문장만** 생성하세요.
158
+ - **절대로** 당신의 이름, 행동 묘사, 주석, 따옴표를 포함하지 마세요.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
 
160
+ {self.korean_name}:
161
+ """
162
+ utterance = query_llm_with_prompt(prompt).strip().strip('"')
163
+ return utterance if utterance and "[LLM Error]" not in utterance else "..."
164
 
165
+ def generate_dialogue_turn(self, conversation: "Conversation") -> (str, str):
166
+ """대화의 현재 턴에 대한 응답과 행동을 생성"""
167
+
168
+ # 생각하기
169
+ result = generate_dialogue_action(
170
+ self.korean_name,
171
+ conversation.conversation_history,
172
+ conversation.topic
173
+ )
174
+ dialogue = result.get("dialogue", "...")
175
+ action = result.get("action", "END")
176
+
177
+ # 행동하기 (잠금과 함께)
178
+ # 이 부분은 대화가 끝난 후, 전체 대화 내용을 바탕으로 기억/관계를 한 번에 업데이트 하도록 향후 수정.
179
+ # 지금은 매 턴 감정만 업데이트
180
+ with simulation_core.simulation_lock:
181
+ emotion = query_llm_for_emotion(dialogue)
182
+ if emotion and emotion in EMOTION_LIST:
183
+ self.update_emotion(emotion, 1.0)
184
+
185
+ return dialogue, action
186
+
187
+ # def generate_dialogue(self, user_input: str, time_context: str
188
+ # , target_npc: Optional["NPC"]=None, use_llm: bool=True
189
+ # , create_memory: bool = True) -> str:
190
+ # """
191
+ # 플레이어 또는 NPC와 상호작용 시 사용되는 통합 대사 생성 함수
192
+ # - user_input: 입력 문장 (플레이어나 NPC로부터)
193
+ # - target_npc: NPC 간 대화일 경우 상대 NPC
194
+ # """
195
+ # if use_llm and user_input:
196
+ # # 1. 생각하기
197
+ # # 1-1. 유사 기억 검색
198
+ # _, _, matched_memories = search_similar_memories(self, user_input)
199
+
200
+ # # 1-2. LLM 프롬프트 생성 및 호출
201
+ # prompt = build_npc_prompt(self, user_input, matched_memories, time_context, target_npc)
202
+ # npc_reply = query_llm_with_prompt(prompt)
203
+
204
+ # if not npc_reply or "[LLM Error]" in npc_reply:
205
+ # return "[LLM Error]..." # 에러 시 기본 응답
206
+
207
+ # # 1-3. 자신의 응답에서 감정 추론 및 상태 업데이트
208
+ # emotion_from_reply = query_llm_for_emotion(npc_reply)
209
+ # # 1-4. 상대방의 말에서 감정 추론 (NPC간 대화일 경우)
210
+ # emotion_from_input = query_llm_for_emotion(user_input) if target_npc else None
211
+
212
+ # # 2. 행동하기
213
+ # if create_memory:
214
+ # with simulation_core.simulation_lock:
215
+ # # 2-1. 기억 요약 및 장기 기억화
216
+ # if len(matched_memories) >= 3:
217
+ # self.summarize_and_store_memories(matched_memories)
218
+
219
+ # # 2-2. 추론한 감정들을 상태에 업데이트
220
+ # if emotion_from_reply and emotion_from_reply in EMOTION_LIST:
221
+ # self.update_emotion(emotion_from_reply , strength=2.0)
222
+
223
+ # # 2-3. 상호작용 기억 저장
224
+ # interlocutor_name = target_npc.name if target_npc else "player"
225
+ # interlocutor_korean_name = target_npc.korean_name if target_npc else "플레이어"
226
+ # memory_content = f"[{interlocutor_korean_name}의 말] '{user_input}' → [나의 응답] '{npc_reply}'"
227
+ # memory = self.remember(content=memory_content, importance=7, emotion=emotion_from_reply , context_tags=[time_context])
228
+
229
+ # # 2-4. 관계 상태 업데이트
230
+ # if emotion_from_reply and emotion_from_reply in EMOTION_RELATION_IMPACT:
231
+ # self.relationships.update_relationship(interlocutor_name, emotion_from_reply , strength=2.0, memory=memory)
232
+ # if target_npc and emotion_from_input and emotion_from_input in EMOTION_RELATION_IMPACT:
233
+ # target_npc.relationships.update_relationship(self.name, emotion_from_input, strength=2.0, memory=memory)
234
+
235
+ # # 2-5. 성격 변화 반영
236
+ # self.update_personality()
237
+
238
+ # return npc_reply
239
+
240
+ # else:
241
+ # # LLM 미사용 시: 감정 기반 행동 생성
242
+ # behavior_output, behavior_trace = self.behavior.perform_sequence(
243
+ # self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace=True
244
+ # )
245
+
246
+ # # 행동 기반 감정 추론 및 반영
247
+ # for action, score in behavior_trace:
248
+ # emotion_name = self.behavior.action_to_emotion.get(action)
249
+ # if emotion_name:
250
+ # self.update_emotion(emotion_name, strength=score)
251
+
252
+ # # dominant_score 기반 중요도 추정
253
+ # dominant_emotions = self.behavior.get_layer_dominant_emotions(self._emotion_buffer)
254
+ # dominant_sequence = self.behavior.decide_layered_sequence(dominant_emotions)
255
+ # dominant_score = max([score for _, score in dominant_sequence]) if dominant_sequence else 5
256
+ # importance = min(int(dominant_score), 10)
257
+
258
+ # # Memory 저장
259
+ # memory_entry = Memory(
260
+ # content=f"행동 수행: {behavior_trace}",
261
+ # importance=importance,
262
+ # emotion=self.emotion.get_dominant_emotion(),
263
+ # behavior_trace=behavior_trace,
264
+ # context_tags=[time_context]
265
+ # )
266
+ # self.memory_store.add_memory(memory_entry)
267
+
268
+ # # 성격 업데이트
269
+ # self.update_personality()
270
+
271
+ # return str(behavior_output)
272
 
273
  def remember(self, content: str, importance: int = 5, emotion: str = None,
274
  strength: float = 1.0, memory_type:str = "Event",
 
494
  target_npc = self.manager.get_npc_by_korean_name(target_korean_name)
495
  if target_npc and target_npc != self:
496
  # 주제가 있는 대화 시작
497
+ simulation_core.conversation_manager.start_conversation(self, target_npc, topic=topic)
498
  else:
499
  print(f"[{self.korean_name}] '{target_korean_name}'를 찾을 수 없어 대화 계획을 실행하지 못했습니다.")
500
  else:
 
563
  target_npc = random.choices(potential_targets, weights=weights, k=1)[0]
564
 
565
  # 주제가 없는 가벼운 대화 시작
566
+ simulation_core.conversation_manager.start_conversation(self, target_npc)
567
 
568
  def create_symbolic_memory(self):
569
  """
npc_social_network/simulation_core.py CHANGED
@@ -7,6 +7,9 @@ import random
7
  from .npc.npc_manager import NPCManager
8
  from .scenarios.scenario_setup import setup_initial_scenario
9
  from .manager.simulation_manager import save_simulation, load_simulation
 
 
 
10
 
11
  #------------------------------------------
12
  # 1. 시뮬레이션 상태 (전역 변수)
@@ -14,8 +17,9 @@ from .manager.simulation_manager import save_simulation, load_simulation
14
  #------------------------------------------
15
  npc_manager: NPCManager = None
16
  simulation_paused = True
17
- event_log = []
18
  simulation_lock = threading.Lock() # 데이터 동시 접근을 막는 잠금장치
 
19
 
20
  #------------------------------------------
21
  # 2. 시뮬레이션 로직 함수
@@ -45,7 +49,7 @@ def tick_simulation():
45
  with simulation_lock:
46
  npc.decay_emotions()
47
  npc.decay_memories()
48
- if random.random() < 0.2:
49
  npc.update_autonomous_behavior("자율 행동 시간")
50
 
51
  def simulation_loop():
@@ -57,7 +61,11 @@ def simulation_loop():
57
  tick_counter = 0
58
 
59
  while True:
60
- if not simulation_paused:
 
 
 
 
61
  tick_simulation()
62
 
63
  # 틱이 발생할 때마다 카운터 증가
@@ -67,12 +75,13 @@ def simulation_loop():
67
  save_simulation(npc_manager)
68
  tick_counter = 0
69
 
70
- time.sleep(5) # 5초에 한 번씩 틱 발생
71
 
72
  def initialize_simulation():
73
  """서버 시작 시 시뮬레이션을 초기화합니다."""
74
- global npc_manager
75
  npc_manager = load_simulation()
 
76
  if npc_manager is None:
77
  npc_manager = setup_initial_scenario()
78
  save_simulation(npc_manager)
 
7
  from .npc.npc_manager import NPCManager
8
  from .scenarios.scenario_setup import setup_initial_scenario
9
  from .manager.simulation_manager import save_simulation, load_simulation
10
+ from .manager.conversation_manager import ConversationManager
11
+
12
+ from typing import List, Optional
13
 
14
  #------------------------------------------
15
  # 1. 시뮬레이션 상태 (전역 변수)
 
17
  #------------------------------------------
18
  npc_manager: NPCManager = None
19
  simulation_paused = True
20
+ event_log:List[str] = []
21
  simulation_lock = threading.Lock() # 데이터 동시 접근을 막는 잠금장치
22
+ conversation_manager: Optional[ConversationManager] = None
23
 
24
  #------------------------------------------
25
  # 2. 시뮬레이션 로직 함수
 
49
  with simulation_lock:
50
  npc.decay_emotions()
51
  npc.decay_memories()
52
+ if random.random() < 0.1:
53
  npc.update_autonomous_behavior("자율 행동 시간")
54
 
55
  def simulation_loop():
 
61
  tick_counter = 0
62
 
63
  while True:
64
+ # 대화 중일 때는 대화만 진행
65
+ if conversation_manager and conversation_manager.is_conversation_active():
66
+ conversation_manager.next_turn()
67
+
68
+ elif not simulation_paused:
69
  tick_simulation()
70
 
71
  # 틱이 발생할 때마다 카운터 증가
 
75
  save_simulation(npc_manager)
76
  tick_counter = 0
77
 
78
+ time.sleep(5) # 5초에 한 번씩 틱 발생 (이것보다 줄이면 Gemini API 사용량 초과 발생 가능)
79
 
80
  def initialize_simulation():
81
  """서버 시작 시 시뮬레이션을 초기화합니다."""
82
+ global npc_manager, conversation_manager
83
  npc_manager = load_simulation()
84
+ conversation_manager = ConversationManager()
85
  if npc_manager is None:
86
  npc_manager = setup_initial_scenario()
87
  save_simulation(npc_manager)