Spaces:
Sleeping
Sleeping
오류: LLM 연결 테스트
Browse files
npc_social_network/models/llm_helper.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# npc_social_network/models/llm_helper.py
|
2 |
+
|
3 |
+
from npc_social_network.npc.emotion_config import EMOTION_LIST
|
4 |
+
|
5 |
+
# 임시 LLM 호출 함수 예시 (Gemini / GPT 연결 가능)
|
6 |
+
def query_llm_for_response(npc_name, npc_job, user_input):
|
7 |
+
"""
|
8 |
+
LLM을 통해 NPC 응답 생성
|
9 |
+
"""
|
10 |
+
|
11 |
+
# 실제 구현시 Gemini API 호출 코드 사용 가능
|
12 |
+
prompt = f"""
|
13 |
+
NPC 이름: {npc_name}
|
14 |
+
NPC 직업: {npc_job}
|
15 |
+
플레이어가 다음과 같이 말했습니다. "{user_input}"
|
16 |
+
|
17 |
+
이에 대해 자연스럽고 친근한 한국어로 응답을 작성하세요.
|
18 |
+
"""
|
19 |
+
# 예시 응답 (추후 LLM 연결 시 교체)
|
20 |
+
return f"{npc_name}이(가) 대답했습니다: '네, 정말 흥미롭네요!'"
|
21 |
+
|
22 |
+
def query_llm_for_emotion(user_input):
|
23 |
+
"""
|
24 |
+
LLM을 통해 플레이어 입력에서 감정 추출
|
25 |
+
"""
|
26 |
+
prompt = f"""
|
27 |
+
플레이어 입력: "{user_input}"
|
28 |
+
|
29 |
+
아래 감정 리스트 중 가장 잘 해당되는 감정을 한 개만 골라 반환하세요.
|
30 |
+
감정 리스트: {', '.join(EMOTION_LIST)}
|
31 |
+
|
32 |
+
반드시 감정 이름을 한 개만 출력하세요. (예: joy)
|
33 |
+
"""
|
34 |
+
# 예시 결과 (추후 LLM 연결 시 교체)
|
35 |
+
return "joy"
|
npc_social_network/npc/emotion_config.py
CHANGED
@@ -135,4 +135,17 @@ PERSONALITY_TEMPLATE = {
|
|
135 |
"complex_bias": 1.0, # Complex emotion 영향
|
136 |
"sensitive": 1.0, # 전체 감정 반응 강도
|
137 |
"stoic": 0.0 # 전체 감정 둔화 정도 (감쇠 가속도에 영향)
|
138 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
"complex_bias": 1.0, # Complex emotion 영향
|
136 |
"sensitive": 1.0, # 전체 감정 반응 강도
|
137 |
"stoic": 0.0 # 전체 감정 둔화 정도 (감쇠 가속도에 영향)
|
138 |
+
}
|
139 |
+
|
140 |
+
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
141 |
+
POSITIVE_RELATION_EMOTIONS = [
|
142 |
+
"joy", "surprise", "gratitude", "pride", "love",
|
143 |
+
"admiration", "empathy", "comfort", "hope", "anticipatory_joy",
|
144 |
+
"calm", "engagement", "relief", "interest",
|
145 |
+
]
|
146 |
+
|
147 |
+
NEGATIVE_RELATION_EMOTIONS = [
|
148 |
+
"sadness", "anger", "fear", "disgust", "shame",
|
149 |
+
"guilt", "jealousy", "resentment", "regret", "schadenfreude",
|
150 |
+
"anxiety", "confusion", "skepticism", "boredom",
|
151 |
+
]
|
npc_social_network/npc/npc_base.py
CHANGED
@@ -2,22 +2,9 @@
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
-
from .emotion_config import EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
-
import
|
8 |
-
|
9 |
-
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
10 |
-
POSITIVE_RELATION_EMOTIONS = [
|
11 |
-
"joy", "surprise", "gratitude", "pride", "love",
|
12 |
-
"admiration", "empathy", "comfort", "hope", "anticipatory_joy",
|
13 |
-
"calm", "engagement", "relief", "interest",
|
14 |
-
]
|
15 |
-
|
16 |
-
NEGATIVE_RELATION_EMOTIONS = [
|
17 |
-
"sadness", "anger", "fear", "disgust", "shame",
|
18 |
-
"guilt", "jealousy", "resentment", "regret", "schadenfreude",
|
19 |
-
"anxiety", "confusion", "skepticism", "boredom",
|
20 |
-
]
|
21 |
|
22 |
# NPC 클래스 정의
|
23 |
class NPC:
|
@@ -249,4 +236,31 @@ class NPC:
|
|
249 |
"""
|
250 |
현재 NPC 감정 상태 상위 N개 반환
|
251 |
"""
|
252 |
-
return self.emotion.get_top_emotions(top_n=top_n)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
+
from .emotion_config import EMOTION_LIST, EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE, POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
+
from ..models.llm_helper import query_llm_for_emotion, query_llm_for_response
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
# NPC 클래스 정의
|
10 |
class NPC:
|
|
|
236 |
"""
|
237 |
현재 NPC 감정 상태 상위 N개 반환
|
238 |
"""
|
239 |
+
return self.emotion.get_top_emotions(top_n=top_n)
|
240 |
+
|
241 |
+
def interact_with_player(self, user_input:str, player_name:str="플레이어"):
|
242 |
+
"""
|
243 |
+
플레이어 입력에 반응 (LLM 기반 응답 생성 + Memory/Emotion/관계 반영)
|
244 |
+
"""
|
245 |
+
# 1. LLM 응답 생성
|
246 |
+
npc_reply = query_llm_for_response(self.name, self.job, user_input)
|
247 |
+
|
248 |
+
# 2. LLM 감정 추출
|
249 |
+
dominant_emotion = query_llm_for_emotion(user_input)
|
250 |
+
|
251 |
+
# 3. Memory 기록
|
252 |
+
self.remember(
|
253 |
+
content = f"[플레이어:{player_name}] '{user_input}' → [NPC:{self.name}] '{npc_reply}'",
|
254 |
+
importance = 7, # 플레이어 상호작용은 높은 중요도로 저장(나중에 내용에 따라 변경하도록 수정)
|
255 |
+
emotion = dominant_emotion
|
256 |
+
)
|
257 |
+
|
258 |
+
# 4. Emotion 반영
|
259 |
+
if dominant_emotion:
|
260 |
+
self.update_emotion(dominant_emotion, strength=2.0)
|
261 |
+
|
262 |
+
# 5. 관계 반영 (상호작용 기반 - 지금 상태면 긍정적인 관계만 반영되는거 아닌가?)
|
263 |
+
positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
|
264 |
+
self.interact_with(player_name, dominant_emotion, positive)
|
265 |
+
|
266 |
+
return npc_reply, dominant_emotion
|
npc_social_network/routes/npc_route.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1 |
# portfolio/npc_social_network/routes/npc_route.py
|
2 |
from flask import Blueprint, render_template, request, jsonify
|
3 |
from npc_social_network.models.npc_manager import npc_list
|
4 |
-
from npc_social_network.models.deepseek_setup import load_deepseek
|
5 |
-
from npc_social_network.models.openkollm_setup import load_openkollm
|
6 |
from npc_social_network.models.gemini_setup import load_gemini
|
7 |
|
8 |
npc_bp = Blueprint(
|
@@ -22,45 +20,59 @@ def home():
|
|
22 |
@npc_bp.route("/chat", methods=['POST'])
|
23 |
def chat():
|
24 |
user_input = request.json.get("message")
|
25 |
-
npc_name = request.json.get(
|
26 |
npc = npc_list[npc_name]
|
27 |
|
28 |
-
#
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
#
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
|
65 |
# npc 상태 정보 API
|
66 |
@npc_bp.route('/npc_info', methods=['GET'])
|
|
|
1 |
# portfolio/npc_social_network/routes/npc_route.py
|
2 |
from flask import Blueprint, render_template, request, jsonify
|
3 |
from npc_social_network.models.npc_manager import npc_list
|
|
|
|
|
4 |
from npc_social_network.models.gemini_setup import load_gemini
|
5 |
|
6 |
npc_bp = Blueprint(
|
|
|
20 |
@npc_bp.route("/chat", methods=['POST'])
|
21 |
def chat():
|
22 |
user_input = request.json.get("message")
|
23 |
+
npc_name = request.json.get("npc")
|
24 |
npc = npc_list[npc_name]
|
25 |
|
26 |
+
# 플레이어 상호 작용 진행
|
27 |
+
npc_reply, emotion = npc.interact_with_player(user_input, player_name = "플레이어")
|
28 |
+
|
29 |
+
return jsonify({
|
30 |
+
"npc_reply": npc_reply,
|
31 |
+
"dominant_emotion": emotion,
|
32 |
+
"memory_summary": npc.summarize_emotional_state(),
|
33 |
+
"relationship_with_player": npc.get_relationship_description("플레이어")
|
34 |
+
})
|
35 |
+
|
36 |
+
# user_input = request.json.get("message")
|
37 |
+
# npc_name = request.json.get('npc')
|
38 |
+
# npc = npc_list[npc_name]
|
39 |
+
|
40 |
+
# # 최근 기억과 성격, 관계 점수를 포함한 프롬프트 생성
|
41 |
+
# recent_memory = npc.get_latest_memory()
|
42 |
+
# memory_str = '\n'.join(
|
43 |
+
# [f"User: {m['user']}\n{npc_name}: {m['npc']}" for m in recent_memory]
|
44 |
+
# )
|
45 |
+
|
46 |
+
# # 프롬프트 구성
|
47 |
+
# prompt = f"""
|
48 |
+
# 당신은 이름이 {npc.name}이고, 성격은 "{npc.personality}"인 가상의 캐릭터입니다.
|
49 |
+
|
50 |
+
# 다음은 유저와 나눈 최근 대화입니다:
|
51 |
+
# {memory_str if memory_str else "(대화 기록 없음)"}
|
52 |
+
|
53 |
+
# 지금 유저가 이렇게 말했습니다: "{user_input}"
|
54 |
+
# 이 말에 대해 당신은 {npc.name}으로서 자연스럽게 한국어로 답변하세요.
|
55 |
+
# """
|
56 |
+
|
57 |
+
# try:
|
58 |
+
# response = llm.generate_content(prompt)
|
59 |
+
# npc_reply = response.text.strip()
|
60 |
+
# except Exception as e:
|
61 |
+
# npc_reply = f"[에러 발생: {str(e)}]"
|
62 |
+
|
63 |
+
# # NPC 기억에 대화 추가 및 관계 점수 업데이트
|
64 |
+
# npc.remember_conversation(user_input, npc_reply)
|
65 |
+
# npc.update_user_relationship(0.05) # 임시로 0.05 상승 (추후 감정 분석 등으로 조절)
|
66 |
+
|
67 |
+
# # 다른 NPC에게 유저에 대한 정보 전달
|
68 |
+
# for other_name, other_npc in npc_list.items():
|
69 |
+
# if other_name != npc.name:
|
70 |
+
# relation_score = npc.relationship_with_npcs.get(other_name, 0.0)
|
71 |
+
# if relation_score > 0.5:
|
72 |
+
# # 유저에 대한 인상이 전달됨
|
73 |
+
# other_npc.update_user_relationship(0.02 * relation_score)
|
74 |
+
|
75 |
+
# return jsonify({'response': npc_reply})
|
76 |
|
77 |
# npc 상태 정보 API
|
78 |
@npc_bp.route('/npc_info', methods=['GET'])
|
requirements.txt
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
accelerate==1.7.0
|
2 |
annotated-types==0.7.0
|
3 |
-
asttokens==3.0.0
|
4 |
bitsandbytes==0.45.5
|
5 |
blinker==1.9.0
|
6 |
cachetools==5.5.2
|
@@ -8,82 +7,51 @@ certifi==2025.4.26
|
|
8 |
charset-normalizer==3.4.2
|
9 |
click==8.2.0
|
10 |
colorama==0.4.6
|
11 |
-
comm==0.2.2
|
12 |
-
debugpy==1.8.13
|
13 |
-
decorator==5.2.1
|
14 |
-
exceptiongroup==1.3.0
|
15 |
-
executing==2.2.0
|
16 |
filelock==3.18.0
|
17 |
Flask==3.1.1
|
18 |
fsspec==2025.5.0
|
19 |
google-ai-generativelanguage==0.6.15
|
20 |
-
google-api-core==2.25.
|
21 |
-
google-api-python-client==2.
|
22 |
-
google-auth==2.40.
|
23 |
google-auth-httplib2==0.2.0
|
24 |
google-generativeai==0.8.5
|
25 |
googleapis-common-protos==1.70.0
|
26 |
-
grpcio==1.
|
27 |
grpcio-status==1.71.0
|
28 |
httplib2==0.22.0
|
29 |
huggingface-hub==0.32.0
|
30 |
idna==3.10
|
31 |
-
importlib_metadata==8.7.0
|
32 |
-
ipykernel==6.29.5
|
33 |
-
ipython==9.1.0
|
34 |
-
ipython_pygments_lexers==1.1.1
|
35 |
itsdangerous==2.2.0
|
36 |
-
jedi==0.19.2
|
37 |
Jinja2==3.1.6
|
38 |
-
jupyter_client==8.6.3
|
39 |
-
jupyter_core==5.7.2
|
40 |
MarkupSafe==3.0.2
|
41 |
-
matplotlib-inline==0.1.7
|
42 |
mpmath==1.3.0
|
43 |
-
nest-asyncio==1.6.0
|
44 |
networkx==3.4.2
|
45 |
numpy==2.2.6
|
46 |
-
packaging==24.2
|
47 |
-
parso==0.8.4
|
48 |
-
pickleshare==0.7.5
|
49 |
pillow==11.0.0
|
50 |
-
platformdirs==4.3.7
|
51 |
-
prompt_toolkit==3.0.50
|
52 |
proto-plus==1.26.1
|
53 |
-
protobuf==5.29.
|
54 |
-
psutil==7.0.0
|
55 |
-
pure_eval==0.2.3
|
56 |
pyasn1==0.6.1
|
57 |
pyasn1_modules==0.4.2
|
58 |
pydantic==2.11.5
|
59 |
pydantic_core==2.33.2
|
60 |
pygame==2.6.1
|
61 |
-
Pygments==2.19.1
|
62 |
pyparsing==3.2.3
|
63 |
-
python-dateutil==2.9.0.post0
|
64 |
python-dotenv==1.1.0
|
65 |
pywin32==304
|
66 |
PyYAML==6.0.2
|
67 |
-
pyzmq==26.4.0
|
68 |
regex==2024.11.6
|
69 |
requests==2.32.3
|
70 |
rsa==4.9.1
|
71 |
safetensors==0.5.3
|
72 |
-
six==1.17.0
|
73 |
-
stack-data==0.6.3
|
74 |
sympy==1.14.0
|
75 |
tokenizers==0.21.1
|
76 |
torch==2.7.0+cu118
|
77 |
torchaudio==2.7.0+cu118
|
78 |
torchvision==0.22.0+cu118
|
79 |
-
tornado==6.4.2
|
80 |
tqdm==4.67.1
|
81 |
-
traitlets==5.14.3
|
82 |
transformers==4.52.3
|
83 |
typing-inspection==0.4.1
|
84 |
-
|
85 |
-
uritemplate==4.1.1
|
86 |
urllib3==2.4.0
|
87 |
-
wcwidth==0.2.13
|
88 |
Werkzeug==3.1.3
|
89 |
-
zipp==3.21.0
|
|
|
1 |
accelerate==1.7.0
|
2 |
annotated-types==0.7.0
|
|
|
3 |
bitsandbytes==0.45.5
|
4 |
blinker==1.9.0
|
5 |
cachetools==5.5.2
|
|
|
7 |
charset-normalizer==3.4.2
|
8 |
click==8.2.0
|
9 |
colorama==0.4.6
|
|
|
|
|
|
|
|
|
|
|
10 |
filelock==3.18.0
|
11 |
Flask==3.1.1
|
12 |
fsspec==2025.5.0
|
13 |
google-ai-generativelanguage==0.6.15
|
14 |
+
google-api-core==2.25.0
|
15 |
+
google-api-python-client==2.171.0
|
16 |
+
google-auth==2.40.3
|
17 |
google-auth-httplib2==0.2.0
|
18 |
google-generativeai==0.8.5
|
19 |
googleapis-common-protos==1.70.0
|
20 |
+
grpcio==1.73.0
|
21 |
grpcio-status==1.71.0
|
22 |
httplib2==0.22.0
|
23 |
huggingface-hub==0.32.0
|
24 |
idna==3.10
|
|
|
|
|
|
|
|
|
25 |
itsdangerous==2.2.0
|
|
|
26 |
Jinja2==3.1.6
|
|
|
|
|
27 |
MarkupSafe==3.0.2
|
|
|
28 |
mpmath==1.3.0
|
|
|
29 |
networkx==3.4.2
|
30 |
numpy==2.2.6
|
|
|
|
|
|
|
31 |
pillow==11.0.0
|
|
|
|
|
32 |
proto-plus==1.26.1
|
33 |
+
protobuf==5.29.5
|
|
|
|
|
34 |
pyasn1==0.6.1
|
35 |
pyasn1_modules==0.4.2
|
36 |
pydantic==2.11.5
|
37 |
pydantic_core==2.33.2
|
38 |
pygame==2.6.1
|
|
|
39 |
pyparsing==3.2.3
|
|
|
40 |
python-dotenv==1.1.0
|
41 |
pywin32==304
|
42 |
PyYAML==6.0.2
|
|
|
43 |
regex==2024.11.6
|
44 |
requests==2.32.3
|
45 |
rsa==4.9.1
|
46 |
safetensors==0.5.3
|
|
|
|
|
47 |
sympy==1.14.0
|
48 |
tokenizers==0.21.1
|
49 |
torch==2.7.0+cu118
|
50 |
torchaudio==2.7.0+cu118
|
51 |
torchvision==0.22.0+cu118
|
|
|
52 |
tqdm==4.67.1
|
|
|
53 |
transformers==4.52.3
|
54 |
typing-inspection==0.4.1
|
55 |
+
uritemplate==4.2.0
|
|
|
56 |
urllib3==2.4.0
|
|
|
57 |
Werkzeug==3.1.3
|
|