Skip to content

Commit a795a42

Browse files
authored
Merge pull request #25 from kkkjz/main
update
2 parents b15223e + 9c45e45 commit a795a42

23 files changed

Lines changed: 1121 additions & 652 deletions

memoryos-mcp/config.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
"openai_base_url": "",
55
"data_storage_path": "./memoryos_data",
66
"assistant_id": "memoryos_assistant",
7-
"short_term_capacity": 10,
7+
"short_term_capacity": 2,
88
"mid_term_capacity": 2000,
9+
"embedding_model_name": "BAAI/bge-m3",
910
"long_term_knowledge_capacity": 100,
1011
"retrieval_queue_capacity": 7,
11-
"mid_term_heat_threshold": 5.0,
12+
"mid_term_heat_threshold": 7.0,
13+
"mid_term_similarity_threshold": 0.6,
1214
"llm_model": "gpt-4o-mini"
1315
}

memoryos-mcp/memoryos/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# Import the main class for easy access
21
from .memoryos import Memoryos
32

43
__all__ = ['Memoryos']

memoryos-mcp/memoryos/long_term.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@
22
import numpy as np
33
import faiss
44
from collections import deque
5-
from utils import get_timestamp, get_embedding, normalize_vector, ensure_directory_exists
5+
try:
6+
from .utils import get_timestamp, get_embedding, normalize_vector, ensure_directory_exists
7+
except ImportError:
8+
from utils import get_timestamp, get_embedding, normalize_vector, ensure_directory_exists
69

710
class LongTermMemory:
8-
def __init__(self, file_path, knowledge_capacity=100):
11+
def __init__(self, file_path, knowledge_capacity=100, embedding_model_name: str = "all-MiniLM-L6-v2", embedding_model_kwargs: dict = None):
912
self.file_path = file_path
1013
ensure_directory_exists(self.file_path)
1114
self.knowledge_capacity = knowledge_capacity
1215
self.user_profiles = {} # {user_id: {data: "profile_string", "last_updated": "timestamp"}}
1316
# Use deques for knowledge bases to easily manage capacity
1417
self.knowledge_base = deque(maxlen=self.knowledge_capacity) # For general/user private knowledge
1518
self.assistant_knowledge = deque(maxlen=self.knowledge_capacity) # For assistant specific knowledge
19+
20+
self.embedding_model_name = embedding_model_name
21+
self.embedding_model_kwargs = embedding_model_kwargs if embedding_model_kwargs is not None else {}
1622
self.load()
1723

1824
def update_user_profile(self, user_id, new_data, merge=True):
@@ -45,7 +51,11 @@ def add_knowledge_entry(self, knowledge_text, knowledge_deque: deque, type_name=
4551
return
4652

4753
# If deque is full, the oldest item is automatically removed when appending.
48-
vec = get_embedding(knowledge_text)
54+
vec = get_embedding(
55+
knowledge_text,
56+
model_name=self.embedding_model_name,
57+
**self.embedding_model_kwargs
58+
)
4959
vec = normalize_vector(vec).tolist()
5060
entry = {
5161
"knowledge": knowledge_text,
@@ -72,7 +82,11 @@ def _search_knowledge_deque(self, query, knowledge_deque: deque, threshold=0.1,
7282
if not knowledge_deque:
7383
return []
7484

75-
query_vec = get_embedding(query)
85+
query_vec = get_embedding(
86+
query,
87+
model_name=self.embedding_model_name,
88+
**self.embedding_model_kwargs
89+
)
7690
query_vec = normalize_vector(query_vec)
7791

7892
embeddings = []

memoryos-mcp/memoryos/memoryos.py

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import os
22
import json
3-
from utils import OpenAIClient, get_timestamp, generate_id, gpt_user_profile_analysis, gpt_knowledge_extraction, gpt_update_profile, ensure_directory_exists
4-
5-
import prompts
6-
from short_term import ShortTermMemory
7-
from mid_term import MidTermMemory, compute_segment_heat # For H_THRESHOLD logic
8-
from long_term import LongTermMemory
9-
from updater import Updater
10-
from retriever import Retriever
3+
from concurrent.futures import ThreadPoolExecutor, as_completed
4+
5+
# 修改为绝对导入
6+
try:
7+
# 尝试相对导入(当作为包使用时)
8+
from .utils import OpenAIClient, get_timestamp, generate_id, gpt_user_profile_analysis, gpt_knowledge_extraction, ensure_directory_exists
9+
from . import prompts
10+
from .short_term import ShortTermMemory
11+
from .mid_term import MidTermMemory, compute_segment_heat # For H_THRESHOLD logic
12+
from .long_term import LongTermMemory
13+
from .updater import Updater
14+
from .retriever import Retriever
15+
except ImportError:
16+
# 回退到绝对导入(当作为独立模块使用时)
17+
from utils import OpenAIClient, get_timestamp, generate_id, gpt_user_profile_analysis, gpt_knowledge_extraction, ensure_directory_exists
18+
import prompts
19+
from short_term import ShortTermMemory
20+
from mid_term import MidTermMemory, compute_segment_heat # For H_THRESHOLD logic
21+
from long_term import LongTermMemory
22+
from updater import Updater
23+
from retriever import Retriever
1124

1225
# Heat threshold for triggering profile/knowledge update from mid-term memory
1326
H_PROFILE_UPDATE_THRESHOLD = 5.0
@@ -24,15 +37,32 @@ def __init__(self, user_id: str,
2437
long_term_knowledge_capacity=100,
2538
retrieval_queue_capacity=7,
2639
mid_term_heat_threshold=H_PROFILE_UPDATE_THRESHOLD,
27-
llm_model="gpt-4o-mini" # Unified model for all LLM operations
40+
mid_term_similarity_threshold=0.6,
41+
llm_model="gpt-4o-mini",
42+
embedding_model_name: str = "all-MiniLM-L6-v2",
43+
embedding_model_kwargs: dict = None
2844
):
2945
self.user_id = user_id
3046
self.assistant_id = assistant_id
3147
self.data_storage_path = os.path.abspath(data_storage_path)
3248
self.llm_model = llm_model
49+
self.mid_term_similarity_threshold = mid_term_similarity_threshold
50+
self.embedding_model_name = embedding_model_name
51+
52+
# Smart defaults for embedding_model_kwargs
53+
if embedding_model_kwargs is None:
54+
if 'bge-m3' in self.embedding_model_name.lower():
55+
print("INFO: Detected bge-m3 model, defaulting embedding_model_kwargs to {'use_fp16': True}")
56+
self.embedding_model_kwargs = {'use_fp16': True}
57+
else:
58+
self.embedding_model_kwargs = {}
59+
else:
60+
self.embedding_model_kwargs = embedding_model_kwargs
61+
3362

3463
print(f"Initializing Memoryos for user '{self.user_id}' and assistant '{self.assistant_id}'. Data path: {self.data_storage_path}")
3564
print(f"Using unified LLM model: {self.llm_model}")
65+
print(f"Using embedding model: {self.embedding_model_name} with kwargs: {self.embedding_model_kwargs}")
3666

3767
# Initialize OpenAI Client
3868
self.client = OpenAIClient(api_key=openai_api_key, base_url=openai_base_url)
@@ -55,17 +85,34 @@ def __init__(self, user_id: str,
5585

5686
# Initialize Memory Modules for User
5787
self.short_term_memory = ShortTermMemory(file_path=user_short_term_path, max_capacity=short_term_capacity)
58-
self.mid_term_memory = MidTermMemory(file_path=user_mid_term_path, client=self.client, max_capacity=mid_term_capacity)
59-
self.user_long_term_memory = LongTermMemory(file_path=user_long_term_path, knowledge_capacity=long_term_knowledge_capacity)
88+
self.mid_term_memory = MidTermMemory(
89+
file_path=user_mid_term_path,
90+
client=self.client,
91+
max_capacity=mid_term_capacity,
92+
embedding_model_name=self.embedding_model_name,
93+
embedding_model_kwargs=self.embedding_model_kwargs
94+
)
95+
self.user_long_term_memory = LongTermMemory(
96+
file_path=user_long_term_path,
97+
knowledge_capacity=long_term_knowledge_capacity,
98+
embedding_model_name=self.embedding_model_name,
99+
embedding_model_kwargs=self.embedding_model_kwargs
100+
)
60101

61102
# Initialize Memory Module for Assistant Knowledge
62-
self.assistant_long_term_memory = LongTermMemory(file_path=assistant_long_term_path, knowledge_capacity=long_term_knowledge_capacity)
103+
self.assistant_long_term_memory = LongTermMemory(
104+
file_path=assistant_long_term_path,
105+
knowledge_capacity=long_term_knowledge_capacity,
106+
embedding_model_name=self.embedding_model_name,
107+
embedding_model_kwargs=self.embedding_model_kwargs
108+
)
63109

64110
# Initialize Orchestration Modules
65111
self.updater = Updater(short_term_memory=self.short_term_memory,
66112
mid_term_memory=self.mid_term_memory,
67113
long_term_memory=self.user_long_term_memory, # Updater primarily updates user's LTM profile/knowledge
68114
client=self.client,
115+
topic_similarity_threshold=mid_term_similarity_threshold, # 传递中期记忆相似度阈值
69116
llm_model=self.llm_model)
70117
self.retriever = Retriever(
71118
mid_term_memory=self.mid_term_memory,
@@ -80,6 +127,7 @@ def _trigger_profile_and_knowledge_update_if_needed(self):
80127
"""
81128
Checks mid-term memory for hot segments and triggers profile/knowledge update if threshold is met.
82129
Adapted from main_memoybank.py's update_user_profile_from_top_segment.
130+
Enhanced with parallel LLM processing for better performance.
83131
"""
84132
if not self.mid_term_memory.heap:
85133
return
@@ -102,23 +150,42 @@ def _trigger_profile_and_knowledge_update_if_needed(self):
102150
if unanalyzed_pages:
103151
print(f"Memoryos: Mid-term session {sid} heat ({current_heat:.2f}) exceeded threshold. Analyzing {len(unanalyzed_pages)} pages for profile/knowledge update.")
104152

105-
# Perform user profile analysis and knowledge extraction separately
106-
# First call: User profile analysis
107-
new_user_profile_text = gpt_user_profile_analysis(unanalyzed_pages, self.client, model=self.llm_model)
153+
# 并行执行两个LLM任务:用户画像分析(已包含更新)、知识提取
154+
def task_user_profile_analysis():
155+
print("Memoryos: Starting parallel user profile analysis and update...")
156+
# 获取现有用户画像
157+
existing_profile = self.user_long_term_memory.get_raw_user_profile(self.user_id)
158+
if not existing_profile or existing_profile.lower() == "none":
159+
existing_profile = "No existing profile data."
160+
161+
# 直接输出更新后的完整画像
162+
return gpt_user_profile_analysis(unanalyzed_pages, self.client, model=self.llm_model, existing_user_profile=existing_profile)
163+
164+
def task_knowledge_extraction():
165+
print("Memoryos: Starting parallel knowledge extraction...")
166+
return gpt_knowledge_extraction(unanalyzed_pages, self.client, model=self.llm_model)
167+
168+
# 使用并行任务执行
169+
with ThreadPoolExecutor(max_workers=2) as executor:
170+
# 提交两个主要任务
171+
future_profile = executor.submit(task_user_profile_analysis)
172+
future_knowledge = executor.submit(task_knowledge_extraction)
173+
174+
# 等待结果
175+
try:
176+
updated_user_profile = future_profile.result() # 直接是更新后的完整画像
177+
knowledge_result = future_knowledge.result()
178+
except Exception as e:
179+
print(f"Error in parallel LLM processing: {e}")
180+
return
108181

109-
# Second call: Knowledge extraction (user private data and assistant knowledge)
110-
knowledge_result = gpt_knowledge_extraction(unanalyzed_pages, self.client, model=self.llm_model)
111182
new_user_private_knowledge = knowledge_result.get("private")
112183
new_assistant_knowledge = knowledge_result.get("assistant_knowledge")
113184

114-
# Update User Profile in user's LTM
115-
if new_user_profile_text and new_user_profile_text.lower() != "none":
116-
old_profile = self.user_long_term_memory.get_raw_user_profile(self.user_id)
117-
if old_profile and old_profile.lower() != "none":
118-
updated_profile = gpt_update_profile(old_profile, new_user_profile_text, self.client, model=self.llm_model)
119-
else:
120-
updated_profile = new_user_profile_text
121-
self.user_long_term_memory.update_user_profile(self.user_id, updated_profile, merge=False) # Don't merge, replace with latest
185+
# 直接使用更新后的完整用户画像
186+
if updated_user_profile and updated_user_profile.lower() != "none":
187+
print("Memoryos: Updating user profile with integrated analysis...")
188+
self.user_long_term_memory.update_user_profile(self.user_id, updated_user_profile, merge=False) # 直接替换为新的完整画像
122189

123190
# Add User Private Knowledge to user's LTM
124191
if new_user_private_knowledge and new_user_private_knowledge.lower() != "none":

0 commit comments

Comments
 (0)