Spaces:
Sleeping
Sleeping
2. 정적 인덱싱 문제 수정: step 1
Browse files
npc_social_network/npc/npc_memory_embedder.py
CHANGED
@@ -8,6 +8,7 @@ import re
|
|
8 |
from typing import TYPE_CHECKING
|
9 |
if TYPE_CHECKING:
|
10 |
from .npc_base import NPC
|
|
|
11 |
|
12 |
# 사전 훈련된 문장 임베딩 모델
|
13 |
model = SentenceTransformer("all-MiniLM-L6-v2")
|
@@ -15,8 +16,9 @@ model = SentenceTransformer("all-MiniLM-L6-v2")
|
|
15 |
# 문장 임베딩 저장 장소
|
16 |
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
17 |
VECTOR_DIR = os.path.join(BASE_DIR, "..", "data", "vectorstores")
|
|
|
18 |
|
19 |
-
def memory_to_text(memory):
|
20 |
"""
|
21 |
Memory 객체를 임베딩 가능한 텍스트 형태로 변환
|
22 |
"""
|
@@ -39,43 +41,70 @@ def embed_memory(memory):
|
|
39 |
|
40 |
def embed_npc_memories(npc: "NPC"):
|
41 |
"""
|
42 |
-
특정 NPC의 기억 전체를 임베딩하고 FAISS index로 저장
|
|
|
43 |
"""
|
44 |
if not os.path.exists(VECTOR_DIR):
|
45 |
os.makedirs(VECTOR_DIR)
|
46 |
|
47 |
memories = npc.memory_store.get_all_memories()
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
|
|
|
|
|
|
53 |
|
54 |
safe_filename = sanitize_filename(npc.name)
|
55 |
save_path = os.path.join(VECTOR_DIR, f"{safe_filename}.faiss")
|
56 |
faiss.write_index(index, save_path)
|
57 |
|
58 |
-
print(f"[임베딩 완료] {npc.
|
59 |
-
print(f"
|
60 |
|
61 |
-
def
|
62 |
"""
|
63 |
저장된 FAISS 인덱스를 불러옴
|
|
|
64 |
"""
|
65 |
safe_filename = sanitize_filename(npc_name)
|
66 |
-
index_path = f"{
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
def search_similar_memories(npc: "NPC", query: str, top_k=3):
|
73 |
"""
|
74 |
질의 문장을 벡터로 변환하고 FAISS에서 유사한 기억 검색
|
75 |
"""
|
76 |
-
index =
|
|
|
|
|
|
|
77 |
query_vec = model.encode([query])
|
78 |
-
distances, indices = index.search(np.array(query_vec, dtype=np.float32), top_k)
|
79 |
|
80 |
all_memories = npc.memory_store.get_all_memories()
|
81 |
|
|
|
8 |
from typing import TYPE_CHECKING
|
9 |
if TYPE_CHECKING:
|
10 |
from .npc_base import NPC
|
11 |
+
from .npc_memory import Memory
|
12 |
|
13 |
# 사전 훈련된 문장 임베딩 모델
|
14 |
model = SentenceTransformer("all-MiniLM-L6-v2")
|
|
|
16 |
# 문장 임베딩 저장 장소
|
17 |
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
18 |
VECTOR_DIR = os.path.join(BASE_DIR, "..", "data", "vectorstores")
|
19 |
+
DIMENSION = model.get_sentence_embedding_dimension()
|
20 |
|
21 |
+
def memory_to_text(memory: "Memory") -> str:
|
22 |
"""
|
23 |
Memory 객체를 임베딩 가능한 텍스트 형태로 변환
|
24 |
"""
|
|
|
41 |
|
42 |
def embed_npc_memories(npc: "NPC"):
|
43 |
"""
|
44 |
+
특정 NPC의 초기 기억 전체를 임베딩하고 FAISS index로 처음 저장
|
45 |
+
- scenario_setup에서만 사용
|
46 |
"""
|
47 |
if not os.path.exists(VECTOR_DIR):
|
48 |
os.makedirs(VECTOR_DIR)
|
49 |
|
50 |
memories = npc.memory_store.get_all_memories()
|
51 |
+
if not memories:
|
52 |
+
# 기억이 없으면 빈 인덱스 생성
|
53 |
+
index = faiss.IndexFlatL2(DIMENSION)
|
54 |
+
else:
|
55 |
+
texts = [memory_to_text(mem) for mem in memories]
|
56 |
+
embeddings = model.encode(texts)
|
57 |
+
index = faiss.IndexFlatL2(DIMENSION)
|
58 |
+
index.add(np.arry(embeddings, dtype=np.float32))
|
59 |
|
60 |
safe_filename = sanitize_filename(npc.name)
|
61 |
save_path = os.path.join(VECTOR_DIR, f"{safe_filename}.faiss")
|
62 |
faiss.write_index(index, save_path)
|
63 |
|
64 |
+
print(f"[초기 임베딩 완료] {npc.korean_name}의 기억 {len(memories)}개를 벡터화하여 저장했습니다.")
|
65 |
+
print(f"저장 위치: {save_path}")
|
66 |
|
67 |
+
def load_or_create_faiss_index(npc_name:str) -> faiss.Index:
|
68 |
"""
|
69 |
저장된 FAISS 인덱스를 불러옴
|
70 |
+
- 없으면 새로 생성
|
71 |
"""
|
72 |
safe_filename = sanitize_filename(npc_name)
|
73 |
+
index_path = os.path.join(VECTOR_DIR, f"{safe_filename}.faiss")
|
74 |
+
|
75 |
+
if os.path.exists(index_path):
|
76 |
+
return faiss.read_index(index_path)
|
77 |
+
else:
|
78 |
+
# 파일이 없으면 새로운 빈 인덱스를 생성하여 반환
|
79 |
+
return faiss.IndexFlatL2(DIMENSION)
|
80 |
+
|
81 |
+
def add_memory_to_index(npc: "NPC", memory: "Memory"):
|
82 |
+
"""단일 기억을 기존 FAISS 인덱스에 추가하고 다시 저장"""
|
83 |
+
# 1. 기존 인덱스를 불러오거나 새로 생성
|
84 |
+
index = load_or_create_faiss_index(npc.name)
|
85 |
+
|
86 |
+
# 2. 새로운 기억을 텍스트로 변환하고 임베딩
|
87 |
+
text = memory_to_text(memory)
|
88 |
+
new_embedding = model.encode([text])
|
89 |
+
|
90 |
+
# 3. 새로운 벡터를 인덱스에 추가
|
91 |
+
index.add(np.array(new_embedding, dtype=np.float32))
|
92 |
+
|
93 |
+
# 4. 업데이트된 인덱스를 다시 저장
|
94 |
+
safe_filename = sanitize_filename(npc.name)
|
95 |
+
save_path = os.path.join(VECTOR_DIR, f"{safe_filename}.faiss")
|
96 |
+
faiss.write_index(index, save_path)
|
97 |
|
98 |
def search_similar_memories(npc: "NPC", query: str, top_k=3):
|
99 |
"""
|
100 |
질의 문장을 벡터로 변환하고 FAISS에서 유사한 기억 검색
|
101 |
"""
|
102 |
+
index = load_or_create_faiss_index(npc.name)
|
103 |
+
if index.ntotal == 0:
|
104 |
+
return [], [], []
|
105 |
+
|
106 |
query_vec = model.encode([query])
|
107 |
+
distances, indices = index.search(np.array(query_vec, dtype=np.float32), min(top_k, index.ntotal))
|
108 |
|
109 |
all_memories = npc.memory_store.get_all_memories()
|
110 |
|
npc_social_network/npc/npc_relationship.py
CHANGED
@@ -30,7 +30,7 @@ class RelationshipManager:
|
|
30 |
return self.relationships[target_name]
|
31 |
|
32 |
def update_relationship(self, target_name: str, emotion: str, strength: float=1.0,
|
33 |
-
memory: Optional["Memory"]=None):
|
34 |
"""
|
35 |
특정 감정 기반으로 관계 수치 조정
|
36 |
- 긍정/부정만이 아니라 감정 유형에 따른 영향 차별화
|
@@ -40,6 +40,9 @@ class RelationshipManager:
|
|
40 |
|
41 |
profile = self._get_or_create_profile(target_name)
|
42 |
|
|
|
|
|
|
|
43 |
impact = EMOTION_RELATION_IMPACT.get(emotion, 0.0)
|
44 |
delta = impact * strength
|
45 |
|
@@ -54,7 +57,7 @@ class RelationshipManager:
|
|
54 |
if memory not in profile.key_memories:
|
55 |
profile.key_memories.append(memory)
|
56 |
profile.key_memories = profile.key_memories[-5:] # 최근 5개만 유지
|
57 |
-
print(f"[{self.owner_npc.
|
58 |
|
59 |
def update_relationship_type(self, profile:SocialProfile):
|
60 |
"""
|
|
|
30 |
return self.relationships[target_name]
|
31 |
|
32 |
def update_relationship(self, target_name: str, emotion: str, strength: float=1.0,
|
33 |
+
memory: Optional["Memory"]=None, npc_manager: "NPCManager"=None):
|
34 |
"""
|
35 |
특정 감정 기반으로 관계 수치 조정
|
36 |
- 긍정/부정만이 아니라 감정 유형에 따른 영향 차별화
|
|
|
40 |
|
41 |
profile = self._get_or_create_profile(target_name)
|
42 |
|
43 |
+
target_npc = npc_manager.get_npc_by_korean_name(target_name)
|
44 |
+
target_korean_name = target_npc.korean_name if target_npc else target_name
|
45 |
+
|
46 |
impact = EMOTION_RELATION_IMPACT.get(emotion, 0.0)
|
47 |
delta = impact * strength
|
48 |
|
|
|
57 |
if memory not in profile.key_memories:
|
58 |
profile.key_memories.append(memory)
|
59 |
profile.key_memories = profile.key_memories[-5:] # 최근 5개만 유지
|
60 |
+
print(f"[{self.owner_npc.korean_name}] '{target_korean_name}'와의 새로운 핵심 기억 추가: {memory.content[:30]}...")
|
61 |
|
62 |
def update_relationship_type(self, profile:SocialProfile):
|
63 |
"""
|