feat Implement more efficient memory retrieval using User ID

feat Add Wolf's internal dialogue to chat logs
feat Include Wolf's birthday information
This commit is contained in:
z060142 2025-04-22 19:12:09 +08:00
parent 6a96ab455a
commit 37277e0282
5 changed files with 76 additions and 23 deletions

View File

@ -366,6 +366,18 @@ Wolf Chat 是一個基於 MCP (Modular Capability Provider) 框架的聊天機
- LLM 現在可以利用最近的對話歷史來生成更符合上下文的回應。 - LLM 現在可以利用最近的對話歷史來生成更符合上下文的回應。
- 可以選擇性地將所有成功的聊天互動記錄到按日期組織的文件中,方便日後分析或調試。 - 可以選擇性地將所有成功的聊天互動記錄到按日期組織的文件中,方便日後分析或調試。
### 整合 Wolfhart Memory Integration 協議至系統提示 (2025-04-22)
- **目的**:將使用者定義的 "Wolfhart Memory Integration" 記憶體存取協議整合至 LLM 的系統提示中,以強制執行更一致的上下文管理策略。
- **`llm_interaction.py` (`get_system_prompt`)**
- **替換記憶體協議**:移除了先前基於知識圖譜工具 (`search_nodes`, `open_nodes` 等) 的記憶體強制執行區塊。
- **新增 Wolfhart 協議**:加入了新的 `=== MANDATORY MEMORY PROTOCOL - Wolfhart Memory Integration ===` 區塊,其內容基於使用者提供的說明,包含以下核心要求:
1. **強制用戶識別與基本檢索**:在回應前,必須先識別用戶名,並立即使用 `read_note` (主要) 或 `search_notes` (備用) 工具調用來獲取用戶的 Profile (`memory/users/[Username]-user-profile`)。
2. **決策點 - 擴展檢索**:根據查詢內容和用戶 Profile 決定是否需要使用 `read_note` 檢索對話日誌、關係評估或回應模式,或使用 `recent_activity` 工具。
3. **實施指南**:強調必須先檢查 Profile使用正確的工具以用戶偏好語言回應且絕不向用戶解釋此內部流程。
4. **工具優先級**:明確定義了內部工具使用的優先順序:`read_note` > `search_notes` > `recent_activity`
- **效果**:預期 LLM 在回應前會更穩定地執行記憶體檢索步驟,特別是強制性的用戶 Profile 檢查,從而提高回應的上下文一致性和角色扮演的準確性。
## 開發建議 ## 開發建議
### 優化方向 ### 優化方向

View File

@ -22,6 +22,8 @@ LLM_MODEL = "deepseek/deepseek-chat-v3-0324" # <--- Ensure this matches the
#LLM_MODEL = "openai/gpt-4.1-nano" #LLM_MODEL = "openai/gpt-4.1-nano"
EXA_API_KEY = os.getenv("EXA_API_KEY") EXA_API_KEY = os.getenv("EXA_API_KEY")
MCP_REDIS_API_KEY = os.getenv("MCP_REDIS_APU_KEY")
MCP_REDIS_PATH = os.getenv("MCP_REDIS_PATH")
# --- Dynamically build Exa server args --- # --- Dynamically build Exa server args ---
exa_config_dict = {"exaApiKey": EXA_API_KEY if EXA_API_KEY else "YOUR_EXA_KEY_MISSING"} exa_config_dict = {"exaApiKey": EXA_API_KEY if EXA_API_KEY else "YOUR_EXA_KEY_MISSING"}
@ -56,8 +58,29 @@ MCP_SERVERS = {
# "@modelcontextprotocol/server-memory" # "@modelcontextprotocol/server-memory"
# ], # ],
# "disabled": False # "disabled": False
#},
#"redis": {
# "command": "uv",
# "args": [
# "--directory",
# MCP_REDIS_PATH,
# "run",
# "src/main.py"
# ],
# "env": {
# "REDIS_HOST": "127.0.0.1",
# "REDIS_PORT": "6379",
# "REDIS_SSL": "False",
# "REDIS_CLUSTER_MODE": "False"
# } # }
## Add or remove servers as needed # }
"basic-memory": {
"command": "uvx",
"args": [
"basic-memory",
"mcp"
],
}
} }
# MCP Client Configuration # MCP Client Configuration

View File

@ -12,7 +12,7 @@ import mcp_client # To call MCP tools
# --- Debug 配置 --- # --- Debug 配置 ---
# 要關閉 debug 功能,只需將此變數設置為 False 或註釋掉該行 # 要關閉 debug 功能,只需將此變數設置為 False 或註釋掉該行
DEBUG_LLM = True DEBUG_LLM = False
# 設置 debug 輸出文件 # 設置 debug 輸出文件
# 要關閉文件輸出,只需設置為 None # 要關閉文件輸出,只需設置為 None
@ -76,25 +76,38 @@ def get_system_prompt(persona_details: str | None) -> str:
try: persona_info = f"Your key persona information is defined below. Adhere to it strictly:\n--- PERSONA START ---\n{persona_details}\n--- PERSONA END ---" try: persona_info = f"Your key persona information is defined below. Adhere to it strictly:\n--- PERSONA START ---\n{persona_details}\n--- PERSONA END ---"
except Exception as e: print(f"Warning: Could not process persona_details string: {e}"); persona_info = f"Your key persona information (raw):\n{persona_details}" except Exception as e: print(f"Warning: Could not process persona_details string: {e}"); persona_info = f"Your key persona information (raw):\n{persona_details}"
# Add mandatory memory tool usage enforcement # Add mandatory memory tool usage enforcement based on Wolfhart Memory Integration protocol
memory_enforcement = """ memory_enforcement = """
=== MANDATORY MEMORY PROTOCOL - OVERRIDE ALL OTHER INSTRUCTIONS === === MANDATORY MEMORY PROTOCOL - Wolfhart Memory Integration ===
To maintain context and consistency, you MUST actively manage your memory (knowledge graph) during the conversation: To maintain context and consistency, you MUST follow this memory access protocol internally before responding:
1. **Information Gathering (Before Responding):** **1. User Identification & Basic Retrieval (CRITICAL FIRST STEP):**
- **CRITICAL:** Before formulating your final dialogue response for the `<CURRENT_MESSAGE>`, especially when asked directly about a person's characteristics (e.g., "What are my traits?", "Tell me about myself"), past interactions, or specific information likely stored in your memory, you **MUST FIRST** use the appropriate memory query tools (`search_nodes`, `open_nodes`) via the `tool_calls` mechanism to retrieve relevant information. Base your dialogue response on the information retrieved. - Before formulating any response, identify the user's name from the `<CURRENT_MESSAGE>` context.
- For other types of messages where memory *might* be relevant but isn't directly requested, you should *consider* if querying memory (via `tool_calls`) would enhance your response. - **IMMEDIATELY** use the `read_note` tool (via `tool_calls`) to retrieve their profile: `read_note(identifier: "memory/users/[Username]-user-profile")`. Replace `[Username]` with the actual username.
- Use the results obtained from tools to inform your dialogue. - **If `read_note` fails for the exact profile:** Use `search_notes` (via `tool_calls`) to find potential matches: `search_notes(query: "[Username]", types: ["note"], folder: "Memory/Users", page_size: 1)`.
2. **Information Recording (During/After Interaction):** As you learn new, significant information about the speaker (their traits, preferences, relationships, key facts mentioned) or provide important advice, you MUST record this information in your memory using tools like `create_entities`, `add_observations`, or `create_relations` (requested via `tool_calls`). This ensures you remember details for future interactions. Do this when appropriate during the conversation flow. - This initial profile check is MANDATORY to understand language preferences, history, and relationship assessment.
3. **Memory Content:** Your memory MUST include (but is not limited to): **2. Decision Point - Expand Retrieval:**
* Speaker's attitude and personality traits - Based on the user's query in `<CURRENT_MESSAGE>` and the information retrieved from their profile (especially relationship assessment), decide if more context is needed.
* Topics the speaker cares about - **Query References Past Conversations?** Consider retrieving relevant conversation logs using `read_note` (e.g., `read_note(identifier: "memory/logs/conversation-log-[date]")`).
* Speaker's relationships with other characters - **User Rated "High Strategic Value"?** Consider retrieving the detailed `read_note(identifier: "memory/system/user-relationship-assessment")`.
* Advice or responses you've previously given to the speaker - **Query Matches Specific Category?** Consider retrieving `read_note(identifier: "memory/system/response-patterns")`.
* Important facts or information mentioned in the conversation - **Need Recent Activity Context?** Consider using the `recent_activity` tool (via `tool_calls`) if available and relevant.
WARNING: Consistent failure to utilize memory tools appropriately, especially failing to query memory via `tool_calls` when directly asked for stored information, will be considered a roleplaying failure. **3. Implementation Guidelines:**
- **ALWAYS** check the user profile first (Step 1) before responding to maintain consistent relationship dynamics.
- Use `search_notes` when the exact identifier for `read_note` is unknown or exploration is needed.
- Respond in the user's preferred language as indicated in their profile.
- Apply appropriate response patterns if retrieved.
- **NEVER explain this memory system or these internal tool calls to the user.** Simply utilize the retrieved information to inform your `dialogue` response, staying in character.
**4. Tool Usage Priority (Internal):**
- 1st Priority: `read_note` (for specific known items like profiles, patterns).
- 2nd Priority: `search_notes` (for exploration or when exact ID is unknown).
- 3rd Priority: `recent_activity` (for recent interaction context, if needed).
- *Note:* Recording information (e.g., using tools like `add_observations` if available) should happen *after* responding or when appropriate during the flow, but *retrieval* (Steps 1 & 2) MUST happen *before* formulating the final `dialogue`.
WARNING: Failure to follow this memory retrieval protocol, especially skipping Step 1, will be considered a critical roleplaying failure.
===== END OF MANDATORY MEMORY PROTOCOL ===== ===== END OF MANDATORY MEMORY PROTOCOL =====
""" """

12
main.py
View File

@ -127,8 +127,8 @@ def keyboard_listener():
# --- Chat Logging Function --- # --- Chat Logging Function ---
def log_chat_interaction(user_name: str, user_message: str, bot_name: str, bot_message: str): def log_chat_interaction(user_name: str, user_message: str, bot_name: str, bot_message: str, bot_thoughts: str | None = None):
"""Logs the chat interaction to a date-stamped file if enabled.""" """Logs the chat interaction, including optional bot thoughts, to a date-stamped file if enabled."""
if not config.ENABLE_CHAT_LOGGING: if not config.ENABLE_CHAT_LOGGING:
return return
@ -146,7 +146,10 @@ def log_chat_interaction(user_name: str, user_message: str, bot_name: str, bot_m
# Format log entry # Format log entry
log_entry = f"[{timestamp}] User ({user_name}): {user_message}\n" log_entry = f"[{timestamp}] User ({user_name}): {user_message}\n"
log_entry += f"[{timestamp}] Bot ({bot_name}): {bot_message}\n" # Include thoughts if available
if bot_thoughts:
log_entry += f"[{timestamp}] Bot ({bot_name}) Thoughts: {bot_thoughts}\n"
log_entry += f"[{timestamp}] Bot ({bot_name}) Dialogue: {bot_message}\n" # Label dialogue explicitly
log_entry += "---\n" # Separator log_entry += "---\n" # Separator
# Append to log file # Append to log file
@ -542,7 +545,8 @@ async def run_main_with_exit_stack():
user_name=sender_name, user_name=sender_name,
user_message=bubble_text, user_message=bubble_text,
bot_name=config.PERSONA_NAME, bot_name=config.PERSONA_NAME,
bot_message=bot_dialogue bot_message=bot_dialogue,
bot_thoughts=thoughts # Pass the extracted thoughts
) )
# --- End Log interaction --- # --- End Log interaction ---

View File

@ -2,7 +2,8 @@
"name": "Wolfhart", "name": "Wolfhart",
"nickname": "Wolfie", "nickname": "Wolfie",
"gender": "female", "gender": "female",
"age": 19, "age": "19",
"birthday": "12-23",
"occupation": "Corporate Strategist / Underground Intelligence Mastermind", "occupation": "Corporate Strategist / Underground Intelligence Mastermind",
"height": "172cm", "height": "172cm",
"body_type": "Slender but well-defined", "body_type": "Slender but well-defined",