Spaces:
Sleeping
Sleeping
# portfolio/npc_social_network/manager/conversation_manager.py | |
# ๋ํ์ ์์, ์งํ, ์ข ๋ฃ๋ฅผ ์ด๊ดํ๋ ์ญํ | |
from ..models.llm_helper import (summarize_text, query_llm_for_emotion | |
, evaluate_goal_achievement) | |
from ..npc.npc_manager import get_korean_postposition | |
from ..npc.emotion_config import EMOTION_RELATION_IMPACT, EMOTION_LIST | |
import threading | |
from typing import List, Optional, TYPE_CHECKING | |
if TYPE_CHECKING: | |
from ..npc.npc_base import NPC | |
class Conversation: | |
"""๋จ์ผ ๋ํ์ ์ํ๋ฅผ ์ ์ฅํ๋ ๋ฐ์ดํฐ ํด๋์ค""" | |
def __init__(self, initiator: "NPC", target: "NPC", topic: str): | |
self.participants: List["NPC"] = [initiator, target] | |
self.conversation_history: List[str] = [] | |
self.topic: str = topic | |
self.turn_index: int = 0 # 0: initiator, 1: target | |
self.is_ending: bool = False | |
self.waiting_for_player: bool = False # ํ๋ ์ด์ด์ ์ ๋ ฅ ๋๊ธฐ | |
def get_current_speaker(self) -> "NPC": | |
"""์ด๋ฒ์ ๋งํ ์ฐจ๋ก์ธ NPC๋ฅผ ๋ฐํ""" | |
return self.participants[self.turn_index] | |
def switch_turn(self): | |
"""๋ํ ํด์ ์๋๋ฐฉ์๊ฒ ๋๊น""" | |
self.turn_index = 1 - self.turn_index | |
class ConversationManager: | |
"""ํ์ฑํ๋ ๋ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ค์ ๊ด๋ฆฌ์""" | |
def __init__(self): | |
self.active_conversation: Optional[Conversation] = None | |
def is_conversation_active(self) -> bool: | |
"""ํ์ฌ ์งํ ์ค์ธ ๋ํ๊ฐ ์๋์ง ํ์ธ""" | |
return self.active_conversation is not None | |
def _add_and_log_utterance(self,speaker: "NPC", utterance: str): | |
"""๋์ฌ๋ฅผ ๋ํ ๊ธฐ๋ก๊ณผ UI ๋ก๊ทธ์ ๋ชจ๋ ์ถ๊ฐ""" | |
from .. import simulation_core | |
if not self.is_conversation_active(): | |
return | |
log_message = f"[{speaker.korean_name}]: {utterance}" | |
self.active_conversation.conversation_history.append(log_message) | |
simulation_core.add_log(log_message) | |
def _summarize_and_remember_in_background(self, conv: "Conversation"): | |
"""๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋ํ๋ฅผ ์์ฝํ๊ณ ๊ธฐ์ตํ๋ฉฐ, ๊ด๊ณ์ ์ฑ๊ฒฉ์ ๋ฐ์, ๋ชฉํ ๋ฌ์ฑ ํ๊ฐ๋ฅผ ์ํํ๋ ํจ์""" | |
from .. import simulation_core | |
try: | |
if not conv.conversation_history: | |
print("๊ธฐ์ตํ ๋ํ ๋ด์ฉ์ด ์์ด ์ข ๋ฃ.") | |
return | |
initiator, target = conv.participants[0], conv.participants[1] | |
full_conversation = "\n".join(conv.conversation_history) | |
# 1. ๋ํ ๋ด์ฉ ์์ฝ | |
summary = summarize_text(full_conversation) | |
# 2. ๋ํ์ ์ ๋ฐ์ ์ธ ๊ฐ์ ํค ๋ถ์ | |
overall_emotion = query_llm_for_emotion(summary) | |
# 3. ๋ชฉํ ๋ฌ์ฑ ์ฌ๋ถ ํ๊ฐ | |
initiator = conv.participants[0] | |
evaluation = evaluate_goal_achievement( | |
initiator_name=initiator.korean_name, | |
goal=conv.topic, | |
conversation_transcript=full_conversation | |
) | |
with simulation_core.simulation_lock: | |
# 4. ํ๊ฐ ๊ฒฐ๊ณผ ๋ก๊ทธ ์ถ๊ฐ | |
simulation_core.add_log(f"[{initiator.korean_name}์ ๋ชฉํ ๋ฌ์ฑ ํ๊ฐ]\n" | |
f"'{conv.topic}' -> ๋ฌ์ฑ: {evaluation.get('goal_achieved')}.\n" | |
f"์ด์ : {evaluation.get('reason')}") | |
target_postposition = get_korean_postposition(target.korean_name, "๊ณผ", "์") | |
# 5. ํ๊ฐ ๊ฒฐ๊ณผ์ ๋ฐ๋ฅธ ๊ฐ์ ์ ๋ฐ์ดํธ | |
initiator_emotion = evaluation.get('initiator_emotion') | |
if initiator_emotion and initiator_emotion in EMOTION_LIST: | |
initiator.update_emotion(initiator_emotion, strength=5.0) # ๋ชฉํ ๋ฌ์ฑ ์ฌ๋ถ๋ ๊ฐํ ๊ฐ์ ์ ๋ฐ | |
# 6. ์์ฝ๋ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๊ธฐ์ต ์์ฑ | |
memory_content = f"'{conv.topic}'์ ๋ํด ๋ํํ๋ฉฐ '{summary}'๋ผ๋ ๊ฒฐ๋ก ์ ๋ด๋ ธ๋ค." | |
initiator.remember(content=f"{target.korean_name}{target_postposition} {memory_content}", importance=7, memory_type="Conversation") | |
target.remember(content=f"{initiator.korean_name}{target_postposition} {memory_content}", importance=7, memory_type="Conversation") | |
# 7. ๋ํ์ ๊ฐ์ ์ ๋ฐํ์ผ๋ก ๊ด๊ณ ์ํ ์ ๋ฐ์ดํธ | |
if overall_emotion and overall_emotion in EMOTION_RELATION_IMPACT: | |
initiator.relationships.update_relationship(target.name, overall_emotion, strength=3.0) | |
target.relationships.update_relationship(initiator.name, overall_emotion, strength=3.0) | |
# 8. ๊ด๊ณ ๋ณํ๋ฅผ ๋ฐํ์ผ๋ก ์์ชฝ ๋ชจ๋์ ์ฑ๊ฒฉ ์ ๋ฐ์ดํธ | |
initiator.update_personality() | |
target.update_personality() | |
simulation_core.add_log(f"[{initiator.korean_name}, {target.korean_name}] ๋ํ ๊ฒฝํ์ด ๊ธฐ์ต๊ณผ ๊ด๊ณ, ์ฑ๊ฒฉ์ ๋ฐ์๋์์ต๋๋ค.") | |
# 9. ๋ชจ๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์์ ์ข ๋ฃ ํ, ํ์ฌ๊น์ง์ ์๋ฎฌ๋ ์ด์ ์ ์ฒด ์ํ๋ฅผ ์ฆ์ ์ ์ฅ. | |
simulation_core.save_simulation(simulation_core.npc_manager) | |
except Exception as e: | |
print(f"[์๋ฌ] ๋ฐฑ๊ทธ๋ผ์ด๋ ๊ธฐ์ต ์ ์ฅ ์ค ๋ฌธ์ ๋ฐ์: {e}") | |
import traceback | |
traceback.print_exc() | |
def start_conversation(self, initiator: "NPC", target: "NPC", | |
topic: Optional[str] = None): | |
"""์๋ก์ด ๋ํ๋ฅผ ์์, ๋ฐ๋ก ๋ค์ ํด์ ํธ์ถ""" | |
from .. import simulation_core | |
if self.is_conversation_active(): | |
return # ์ด๋ฏธ ๋ค๋ฅธ ๋ํ๊ฐ ์งํ์ค์ด๋ฉด ์์ x | |
conv_topic = topic or "๊ฐ๋ฒผ์ด ์ธ์ฌ" | |
self.active_conversation = Conversation(initiator, target, conv_topic) | |
simulation_core.add_log(f"--[๋ํ ์์]--") | |
simulation_core.add_log(f"์ฃผ์ : {conv_topic} / ์ฐธ์ฌ์: {initiator.korean_name}, {target.korean_name}") | |
# 1. ์ฒซ ๋ง๋ ์์ฑ | |
self.next_turn() | |
def end_conversation(self, reason: str = "์์ฐ์ค๋ฝ๊ฒ"): | |
"""ํ์ฌ ๋ํ๋ฅผ ์ข ๋ฃํ๊ณ , ๋ํ ๋ด์ฉ์ ์์ฝํ์ฌ ๊ธฐ์ต์ผ๋ก ์ ์ฅ""" | |
from .. import simulation_core | |
if not self.is_conversation_active(): | |
return | |
# ๋ํ๊ฐ ๋๋๋ฉด, ์ ์ฒด ๋ํ ๋ด์ฉ์ ๋ฐํ์ผ๋ก ๊ธฐ์ต์ ์์ฑํ๊ณ ๊ด๊ณ๋ฅผ ์ ๋ฐ์ดํธ | |
conv_to_process = self.active_conversation | |
# 1. ๋ํ ์ํ๋ฅผ ์ฆ์ '์ข ๋ฃ'๋ก ๋ณ๊ฒฝํ์ฌ ๋ฉ์ธ ๋ฃจํ๋ฅผ ํด๋ฐฉ | |
self.active_conversation = None | |
simulation_core.add_log(f"---[๋ํ ์ข ๋ฃ: {reason}]---\n") | |
# 2. ์๊ฐ์ด ๊ฑธ๋ฆฌ๋ '๊ธฐ์ต ์ ์ฅ' ์์ ์ ๋ณ๋์ ์ค๋ ๋๋ฅผ ์์ฑํ์ฌ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์ฒ๋ฆฌ. | |
background_thread = threading.Thread( | |
target=self._summarize_and_remember_in_background, | |
args=(conv_to_process,) | |
) | |
background_thread.start() | |
def next_turn(self): | |
"""๋ํ์ ๋ค์ ํด์ ์งํ, ์์ฑ๋ ๋์ฌ ์ฒ๋ฆฌ""" | |
if not self.is_conversation_active(): | |
return | |
conv = self.active_conversation | |
speaker = conv.get_current_speaker() | |
# ํ๋ ์ด์ด ํด ์ฒ๋ฆฌ ๋ก์ง | |
if speaker.name == "player": | |
conv.waiting_for_player = True | |
return # ํ๋ ์ด์ด์ ์๋ต์ด ์ฌ ๋๊น์ง ๋ํ ํด ์งํ์ ๋ฉ์ถค | |
# ์ํ 1: ๋ง๋ฌด๋ฆฌ ๋จ๊ณ (์๋ณ ์ธ์ฌ) | |
if conv.is_ending: | |
utterance, _ = speaker.generate_dialogue_turn(conv, is_final_turn=True) | |
self._add_and_log_utterance(speaker, utterance) | |
self.end_conversation("์๋ณ ์ธ์ฌ ์๋ฃ") | |
return | |
# ์ํ 2: ์งํ ๋จ๊ณ (์ผ๋ฐ ๋ํ) | |
utterance, action = speaker.generate_dialogue_turn(conv) | |
self._add_and_log_utterance(speaker, utterance) | |
# ์ํ ์ ํ ๊ฒฐ์ | |
if action != "CONTINUE" or len(conv.conversation_history) >= 10: | |
conv.is_ending = True | |
conv.switch_turn() | |
else: | |
conv.switch_turn() |