Spaces:
Sleeping
Sleeping
# portfolio/npc_social_network/npc/npc_manager.py | |
import random | |
from typing import TYPE_CHECKING, List, Optional | |
from .. import simulation_core | |
if TYPE_CHECKING: | |
from .npc_base import NPC | |
def get_korean_postposition(name, first_char, second_char): | |
"""์ด๋ฆ์ ๋ง์ง๋ง ๊ธ์ ๋ฐ์นจ ์ ๋ฌด์ ๋ฐ๋ผ ์ฌ๋ฐ๋ฅธ ์กฐ์ฌ๋ฅผ ๋ฐํ""" | |
if (ord(name[-1]) - 0xAC00) % 28 > 0: | |
return first_char | |
else: | |
return second_char | |
class NPCManager: | |
"""๋ชจ๋ NPC๋ฅผ ๊ด๋ฆฌํ๋ ์ค์ ๊ด๋ฆฌ์""" | |
def __init__(self): | |
self.npcs: list['NPC'] = [] | |
self.npc_dict: dict[str, 'NPC'] = {} | |
self.korean_name_to_npc: dict[str, 'NPC'] = {} # ํ๊ธ ์ด๋ฆ ๊ฒ์์ฉ ๋์ ๋๋ฆฌ | |
self.player_is_active = True # ํ๋ ์ด์ด ํ์ฑํ ์ํ ํ๋๊ทธ | |
def add_npc(self, npc: 'NPC'): | |
"""NPC๋ฅผ ๋งค๋์ ์ ์ถ๊ฐํฉ๋๋ค.""" | |
if npc.name not in self.npc_dict: | |
self.npcs.append(npc) | |
self.npc_dict[npc.name] = npc | |
self.korean_name_to_npc[npc.korean_name] = npc | |
npc.manager = self | |
def set_player_active(self, is_active: bool): | |
"""ํ๋ ์ด์ด์ ํ์ฑํ ์ํ๋ฅผ ์ค์ """ | |
self.player_is_active = is_active | |
def get_all_npcs_except_player(self) -> List['NPC']: | |
"""ํ๋ ์ด์ด๋ฅผ ์ ์ธํ ์์ NPC ๋ชฉ๋ก์ ๋ฐํ""" | |
return [npc for npc in self.npcs if npc.name != "player"] | |
def get_interactive_npcs(self) -> List['NPC']: | |
"""ํ์ฌ ์ํ์์ฉ์ด ๊ฐ๋ฅํ NPC ๋ชฉ๋ก์ ๋ฐํ""" | |
if self.player_is_active: | |
return self.npcs # ํ๋ ์ด์ด๊ฐ ํ์ฑํ ์ํ์ด๋ฉด ๋ชจ๋ ๋ฐํ | |
else: | |
return self.get_all_npcs_except_player() # ๋นํ์ฑํ ์ํ์ด๋ฉด ํ๋ ์ด์ด ์ ์ธ | |
def get_npc_by_name(self, name: str) -> Optional['NPC']: | |
""" | |
NPC ์์ด ID๋ฅผ ํตํด์ NPC์ ์ ๋ณด๋ฅผ ๋ฐํ | |
""" | |
return self.npc_dict.get(name) | |
def get_npc_by_korean_name(self, korean_name: str) -> Optional['NPC']: | |
"""NPC ํ๊ธ ์ด๋ฆ์ ํตํด์ NPC์ ์ ๋ณด๋ฅผ ๋ฐํ""" | |
return self.korean_name_to_npc.get(korean_name) | |
def get_random_npc(self, exclude: Optional['NPC']=None) -> Optional['NPC']: | |
""" | |
ํน์ NPC๋ฅผ ์ ์ธํ๊ณ ๋๋คํ NPC๋ฅผ ์ ํ | |
์์ ํ์: ์๋ NPC๊ฐ ๋๋ค์ด ์๋๋ผ, ์ํธ์์ฉํ ๋งํ ๊ทผ๊ฑฐ๊ฐ ์์ด์ผํ๋ค. | |
์) ๊ทผ์ฒ์ ์ฐ๋ค, ํน๋ณํ ์ด๋ฒคํธ๊ฐ ์์๋ค ๋ฑ ๊ณผ ๊ฐ์ ์ด์ | |
""" | |
possible_targets = [n for n in self.npcs if n != exclude] | |
if not possible_targets: | |
return None | |
return random.choice(possible_targets) | |
def all(self) -> List['NPC']: | |
"""๊ด๋ฆฌ ์ค์ธ ๋ชจ๋ NPC์ ๋ฆฌ์คํธ๋ฅผ ๋ฐํํฉ๋๋ค.""" | |
return self.npcs | |
def initiate_npc_to_npc_interaction(self, initiator: 'NPC', target: 'NPC', | |
time_context: str, topic: Optional[str] = None): | |
""" | |
NPC ๊ฐ์ ์ํธ์์ฉ์ ์์์ํค๋ ํจ์ | |
- ์ฐธ์ฌํ NPC๋ฅผ ๋ฐํํด์ ์๋ฌธ์ ํผํธ๋ฆด '๋ชฉ๊ฒฉ์' ์์ฑ | |
- ์ฃผ์ ๊ฐ ์์ ๊ฒฝ์ฐ, ๋ชฉํ ์งํฅ์ ๋ํ๋ฅผ ์์ฑ | |
""" | |
from ..models.llm_helper import query_llm_with_prompt | |
if len(self.npcs) < 2: | |
return None, None # ์ํธ์์ฉํ NPC๊ฐ ์ต์ 2๋ช ํ์ | |
initiator_postposition = get_korean_postposition(initiator.korean_name, "์ด", "๊ฐ") | |
target_postposition = get_korean_postposition(target.korean_name, "์๊ฒ", "์๊ฒ") | |
simulation_core.add_log(f"\n---[NPC ์ํธ์์ฉ ์ด๋ฒคํธ]---\n{initiator.korean_name}{initiator_postposition} {target.korean_name}{target_postposition} ์ํธ์์ฉ์ ์๋ํฉ๋๋ค.") | |
if topic: | |
# ์ฃผ์ ๊ฐ ์๋ ๊ฒฝ์ฐ | |
prompt = f""" | |
# Persona | |
๋น์ ์ '{initiator.korean_name}'์ ๋๋ค. | |
# Context | |
- ๋น์ ์ ์ง๊ธ '{target.korean_name}'์ ๋ง์ฃผ์ณค์ต๋๋ค. | |
- ๋น์ ๊ณผ ์๋์ ๊ด๊ณ: {initiator.relationships.get_relationship_summary(target.name)} | |
- ๋ํ ์ฃผ์ : "{topic}" | |
# Instruction | |
- ์ฃผ์ด์ง ์ํฉ๊ณผ ๋ํ ์ฃผ์ ์ ๋ง์ถฐ, ์๋๋ฐฉ์๊ฒ ๊ฑด๋ฌ ์์ฐ์ค๋ฌ์ด ์ฒซ ๋์ฌ **ํ ๋ฌธ์ฅ๋ง** ์์ฑํ์ธ์. | |
- **์ ๋๋ก** ๋ค๋ฅธ ๋ถ๊ฐ ์ค๋ช , ์ด์ , ์ฃผ์ ๋ฑ์ ํฌํจํ์ง ๋ง์ธ์. | |
- ๋น์ ์ ์๋ต์ ์ค์ง ์๋๋ฐฉ์๊ฒ ๋งํ ๋์ฌ ๋ด์ฉ ๊ทธ ์์ฒด์ฌ์ผ ํฉ๋๋ค. | |
{initiator.korean_name}: | |
""" | |
else: | |
# ์ฃผ์ ๊ฐ ์๋ ๊ฒฝ์ฐ (๊ธฐ์กด์ ๊ฐ๋ฒผ์ด ์ธ์ฌ) | |
prompt = f""" | |
# Persona | |
๋น์ ์ "{initiator.korean_name}"์ ๋๋ค. | |
# Context | |
- ๋น์ ์ ์ง๊ธ "{target.korean_name}"์ ๋ง์ฃผ์ณค์ต๋๋ค. | |
- ๋น์ ๊ณผ ์๋์ ๊ด๊ณ: {initiator.relationships.get_relationship_summary(target.name)} | |
# Instruction | |
- ์ฃผ์ด์ง ์ํฉ์ ๋ง์ถฐ, ์๋๋ฐฉ์๊ฒ ๊ฑด๋ฌ ์์ฐ์ค๋ฝ๊ณ ๊ฐ๋ฒผ์ด ์ฒซ ์ธ์ฌ ๋์ฌ **ํ ๋ฌธ์ฅ๋ง** ์์ฑํ์ธ์. | |
- **์ ๋๋ก** ๋ค๋ฅธ ๋ถ๊ฐ ์ค๋ช , ์ด์ , ์ฃผ์ ๋ฑ์ ํฌํจํ์ง ๋ง์ธ์. | |
- ๋น์ ์ ์๋ต์ ์ค์ง ์๋๋ฐฉ์๊ฒ ๋งํ ๋์ฌ ๋ด์ฉ ๊ทธ ์์ฒด์ฌ์ผ ํฉ๋๋ค. | |
{initiator.korean_name}: | |
""" | |
initial_utterance = query_llm_with_prompt(prompt).strip() | |
# ๋ฐ์ดํ๋ ์ ๊ฑฐ | |
clean_utterance = initial_utterance.strip('"') | |
# ์ด๋ฆ ์ ๋์ฌ๊ฐ ์๋์ง ํ์ธํ๊ณ ์ ๊ฑฐ | |
name_prefix = f"{initiator.korean_name}:" | |
if clean_utterance.startswith(name_prefix): | |
clean_utterance = clean_utterance[len(name_prefix):].strip() | |
# ๋ฐ์ดํ๋ ์ ๊ฑฐ | |
final_utterance = clean_utterance.strip().strip('"') | |
if "[LLM Error]" in final_utterance or not final_utterance: | |
simulation_core.add_log("[Error: {initiator.korean_name} ๋ํ ์์ ์คํจ]") | |
print(f"[{initiator.korean_name}] ๋ํ ์์์ ์คํจํ์ต๋๋ค.") | |
return None, None, None | |
simulation_core.add_log(f"[{initiator.korean_name}]: {final_utterance}") | |
# 2. Target์ด Initiator์ ๋ง์ ๋ฃ๊ณ ์๋ต ์์ฑ | |
# generate_dialogue ํจ์๋ฅผ ์ฌ์ฌ์ฉํ๋, target_npc ์ธ์๋ฅผ ์ ๋ฌ | |
response_utterance = target.generate_dialogue( | |
user_input=final_utterance, | |
time_context = time_context, | |
target_npc=initiator | |
) | |
simulation_core.add_log(f"[{target.korean_name}]: {response_utterance}") | |
# 3. ๋ํ๊ฐ ๋๋ ํ, ๋ชฉ๊ฒฉ์๋ฅผ ์ ์ ํ์ฌ ์๋ฌธ์ ์์ฑํฉ๋๋ค. | |
potential_witnesses = [npc for npc in self.npcs if npc not in [initiator, target]] | |
if potential_witnesses and random.random() < 0.25: # 25% ํ๋ฅ ๋ก ๋ชฉ๊ฒฉ์ ๋ฐ์ | |
witness = random.choice(potential_witnesses) | |
initiator_postposition = get_korean_postposition(initiator.korean_name, "์ด", "๊ฐ") | |
target_postposition = get_korean_postposition(target.korean_name, "์๊ฒ", "์๊ฒ") | |
witness_postposition = get_korean_postposition(witness.korean_name, "์ด", "๊ฐ") | |
gossip_content = f"'{initiator.korean_name}'{initiator_postposition} '{target.korean_name}'{target_postposition} '{initial_utterance[:100]}...'๋ผ๊ณ ๋งํ๋ ๊ฒ์ ๋ดค๋ค." | |
# ๋ชฉ๊ฒฉ์๊ฐ '์๋ฌธ'์ ๊ธฐ์ตํ๋๋ก ํจ | |
witness.remember( | |
content=gossip_content, | |
importance=4, | |
emotion="curiosity", | |
memory_type="Gossip", | |
) | |
# ๋ก๊ทธ๋ simulation_core์ add_log๋ฅผ ์ง์ ํธ์ถํ ์ ์์ผ๋ฏ๋ก print๋ก ๋์ฒด | |
simulation_core.add_log(f"[๋ชฉ๊ฒฉ] {witness.korean_name}{witness_postposition} {initiator.korean_name}์ {target.korean_name}์ ๋ํ๋ฅผ ๋ชฉ๊ฒฉํจ.") | |
return initiator, target, final_utterance | |