fix: remove duplicate message(CR comments)

This commit is contained in:
AL76
2025-03-10 11:46:59 +08:00
parent 2f2be5b3ad
commit a43f9495ea
8 changed files with 171 additions and 167 deletions

View File

@@ -5,6 +5,9 @@ import threading
import time import time
from datetime import datetime from datetime import datetime
from typing import Dict, List from typing import Dict, List
from loguru import logger
from typing import Optional
from pymongo import MongoClient
import customtkinter as ctk import customtkinter as ctk
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -17,23 +20,20 @@ root_dir = os.path.abspath(os.path.join(current_dir, '..', '..'))
# 加载环境变量 # 加载环境变量
if os.path.exists(os.path.join(root_dir, '.env.dev')): if os.path.exists(os.path.join(root_dir, '.env.dev')):
load_dotenv(os.path.join(root_dir, '.env.dev')) load_dotenv(os.path.join(root_dir, '.env.dev'))
print("成功加载开发环境配置") logger.info("成功加载开发环境配置")
elif os.path.exists(os.path.join(root_dir, '.env.prod')): elif os.path.exists(os.path.join(root_dir, '.env.prod')):
load_dotenv(os.path.join(root_dir, '.env.prod')) load_dotenv(os.path.join(root_dir, '.env.prod'))
print("成功加载生产环境配置") logger.info("成功加载生产环境配置")
else: else:
print("未找到环境配置文件") logger.error("未找到环境配置文件")
sys.exit(1) sys.exit(1)
from typing import Optional
from pymongo import MongoClient
class Database: class Database:
_instance: Optional["Database"] = None _instance: Optional["Database"] = None
def __init__(self, host: str, port: int, db_name: str, username: str = None, password: str = None, auth_source: str = None): def __init__(self, host: str, port: int, db_name: str, username: str = None, password: str = None,
auth_source: str = None):
if username and password: if username and password:
self.client = MongoClient( self.client = MongoClient(
host=host, host=host,
@@ -47,7 +47,8 @@ class Database:
self.db = self.client[db_name] self.db = self.client[db_name]
@classmethod @classmethod
def initialize(cls, host: str, port: int, db_name: str, username: str = None, password: str = None, auth_source: str = None) -> "Database": def initialize(cls, host: str, port: int, db_name: str, username: str = None, password: str = None,
auth_source: str = None) -> "Database":
if cls._instance is None: if cls._instance is None:
cls._instance = cls(host, port, db_name, username, password, auth_source) cls._instance = cls(host, port, db_name, username, password, auth_source)
return cls._instance return cls._instance
@@ -59,12 +60,11 @@ class Database:
return cls._instance return cls._instance
class ReasoningGUI: class ReasoningGUI:
def __init__(self): def __init__(self):
# 记录启动时间戳转换为Unix时间戳 # 记录启动时间戳转换为Unix时间戳
self.start_timestamp = datetime.now().timestamp() self.start_timestamp = datetime.now().timestamp()
print(f"程序启动时间戳: {self.start_timestamp}") logger.info(f"程序启动时间戳: {self.start_timestamp}")
# 设置主题 # 设置主题
ctk.set_appearance_mode("dark") ctk.set_appearance_mode("dark")
@@ -79,15 +79,15 @@ class ReasoningGUI:
# 初始化数据库连接 # 初始化数据库连接
try: try:
self.db = Database.get_instance().db self.db = Database.get_instance().db
print("数据库连接成功") logger.success("数据库连接成功")
except RuntimeError: except RuntimeError:
print("数据库未初始化,正在尝试初始化...") logger.warning("数据库未初始化,正在尝试初始化...")
try: try:
Database.initialize("127.0.0.1", 27017, "maimai_bot") Database.initialize("127.0.0.1", 27017, "maimai_bot")
self.db = Database.get_instance().db self.db = Database.get_instance().db
print("数据库初始化成功") logger.success("数据库初始化成功")
except Exception as e: except Exception:
print(f"数据库初始化失败: {e}") logger.exception(f"数据库初始化失败")
sys.exit(1) sys.exit(1)
# 存储群组数据 # 存储群组数据
@@ -285,12 +285,12 @@ class ReasoningGUI:
try: try:
# 从数据库获取最新数据,只获取启动时间之后的记录 # 从数据库获取最新数据,只获取启动时间之后的记录
query = {"time": {"$gt": self.start_timestamp}} query = {"time": {"$gt": self.start_timestamp}}
print(f"查询条件: {query}") logger.debug(f"查询条件: {query}")
# 先获取一条记录检查时间格式 # 先获取一条记录检查时间格式
sample = self.db.reasoning_logs.find_one() sample = self.db.reasoning_logs.find_one()
if sample: if sample:
print(f"样本记录时间格式: {type(sample['time'])} 值: {sample['time']}") logger.debug(f"样本记录时间格式: {type(sample['time'])} 值: {sample['time']}")
cursor = self.db.reasoning_logs.find(query).sort("time", -1) cursor = self.db.reasoning_logs.find(query).sort("time", -1)
new_data = {} new_data = {}
@@ -299,7 +299,7 @@ class ReasoningGUI:
for item in cursor: for item in cursor:
# 调试输出 # 调试输出
if total_count == 0: if total_count == 0:
print(f"记录时间: {item['time']}, 类型: {type(item['time'])}") logger.debug(f"记录时间: {item['time']}, 类型: {type(item['time'])}")
total_count += 1 total_count += 1
group_id = str(item.get('group_id', 'unknown')) group_id = str(item.get('group_id', 'unknown'))
@@ -312,7 +312,7 @@ class ReasoningGUI:
elif isinstance(item['time'], datetime): elif isinstance(item['time'], datetime):
time_obj = item['time'] time_obj = item['time']
else: else:
print(f"未知的时间格式: {type(item['time'])}") logger.warning(f"未知的时间格式: {type(item['time'])}")
time_obj = datetime.now() # 使用当前时间作为后备 time_obj = datetime.now() # 使用当前时间作为后备
new_data[group_id].append({ new_data[group_id].append({
@@ -325,12 +325,12 @@ class ReasoningGUI:
'prompt': item.get('prompt', '') # 添加prompt字段 'prompt': item.get('prompt', '') # 添加prompt字段
}) })
print(f"从数据库加载了 {total_count} 条记录,分布在 {len(new_data)} 个群组中") logger.info(f"从数据库加载了 {total_count} 条记录,分布在 {len(new_data)} 个群组中")
# 更新数据 # 更新数据
if new_data != self.group_data: if new_data != self.group_data:
self.group_data = new_data self.group_data = new_data
print("数据已更新,正在刷新显示...") logger.info("数据已更新,正在刷新显示...")
# 将更新任务添加到队列 # 将更新任务添加到队列
self.update_queue.put({'type': 'update_group_list'}) self.update_queue.put({'type': 'update_group_list'})
if self.group_data: if self.group_data:
@@ -341,8 +341,8 @@ class ReasoningGUI:
'type': 'update_display', 'type': 'update_display',
'group_id': self.selected_group_id 'group_id': self.selected_group_id
}) })
except Exception as e: except Exception:
print(f"自动更新出错: {e}") logger.exception(f"自动更新出错")
# 每5秒更新一次 # 每5秒更新一次
time.sleep(5) time.sleep(5)
@@ -371,6 +371,5 @@ def main():
app.run() app.run()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -135,7 +135,7 @@ class BotConfig:
try: try:
config_version: str = toml["inner"]["version"] config_version: str = toml["inner"]["version"]
except KeyError as e: except KeyError as e:
logger.error(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") logger.error(f"配置文件中 inner 段 不存在, 这是错误的配置文件")
raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件")
else: else:
toml["inner"] = {"version": "0.0.0"} toml["inner"] = {"version": "0.0.0"}
@@ -246,11 +246,11 @@ class BotConfig:
try: try:
cfg_target[i] = cfg_item[i] cfg_target[i] = cfg_item[i]
except KeyError as e: except KeyError as e:
logger.error(f"{item} 中的必要字段 {e} 不存在,请检查") logger.error(f"{item} 中的必要字段不存在,请检查")
raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查") raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查")
provider = cfg_item.get("provider") provider = cfg_item.get("provider")
if provider == None: if provider is None:
logger.error(f"provider 字段在模型配置 {item} 中不存在,请检查") logger.error(f"provider 字段在模型配置 {item} 中不存在,请检查")
raise KeyError(f"provider 字段在模型配置 {item} 中不存在,请检查") raise KeyError(f"provider 字段在模型配置 {item} 中不存在,请检查")

View File

@@ -93,8 +93,8 @@ class ResponseGenerator:
# 生成回复 # 生成回复
try: try:
content, reasoning_content = await model.generate_response(prompt) content, reasoning_content = await model.generate_response(prompt)
except Exception as e: except Exception:
logger.exception(f"生成回复时出错: {e}") logger.exception(f"生成回复时出错")
return None return None
# 保存到数据库 # 保存到数据库
@@ -145,8 +145,8 @@ class ResponseGenerator:
else: else:
return ["neutral"] return ["neutral"]
except Exception as e: except Exception:
logger.exception(f"获取情感标签时出错: {e}") logger.exception(f"获取情感标签时出错")
return ["neutral"] return ["neutral"]
async def _process_response(self, content: str) -> Tuple[List[str], List[str]]: async def _process_response(self, content: str) -> Tuple[List[str], List[str]]:

View File

@@ -119,8 +119,8 @@ class MessageContainer:
self.messages.remove(message) self.messages.remove(message)
return True return True
return False return False
except Exception as e: except Exception:
logger.exception(f"移除消息时发生错误: {e}") logger.exception(f"移除消息时发生错误")
return False return False
def has_messages(self) -> bool: def has_messages(self) -> bool:
@@ -213,8 +213,8 @@ class MessageManager:
# 安全地移除消息 # 安全地移除消息
if not container.remove_message(msg): if not container.remove_message(msg):
logger.warning("尝试删除不存在的消息") logger.warning("尝试删除不存在的消息")
except Exception as e: except Exception:
logger.exception(f"处理超时消息时发生错误: {e}") logger.exception(f"处理超时消息时发生错误")
continue continue
async def start_processor(self): async def start_processor(self):

View File

@@ -2,6 +2,7 @@ from typing import Optional
from ...common.database import Database from ...common.database import Database
from .message import Message from .message import Message
from loguru import logger
class MessageStorage: class MessageStorage:
@@ -43,7 +44,7 @@ class MessageStorage:
} }
self.db.db.messages.insert_one(message_data) self.db.db.messages.insert_one(message_data)
except Exception as e: except Exception:
print(f"\033[1;31m[错误]\033[0m 存储消息失败: {e}") logger.exception(f"存储消息失败")
# 如果需要其他存储相关的函数,可以在这里添加 # 如果需要其他存储相关的函数,可以在这里添加

View File

@@ -7,6 +7,7 @@ import jieba
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import networkx as nx import networkx as nx
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger
sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径 sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径
from src.common.database import Database # 使用正确的导入语法 from src.common.database import Database # 使用正确的导入语法
@@ -99,18 +100,20 @@ class Memory_graph:
# 返回所有节点对应的 Memory_dot 对象 # 返回所有节点对应的 Memory_dot 对象
return [self.get_dot(node) for node in self.G.nodes()] return [self.get_dot(node) for node in self.G.nodes()]
def get_random_chat_from_db(self, length: int, timestamp: str): def get_random_chat_from_db(self, length: int, timestamp: str):
# 从数据库中根据时间戳获取离其最近的聊天记录 # 从数据库中根据时间戳获取离其最近的聊天记录
chat_text = '' chat_text = ''
closest_record = self.db.db.messages.find_one({"time": {"$lte": timestamp}}, sort=[('time', -1)]) # 调试输出 closest_record = self.db.db.messages.find_one({"time": {"$lte": timestamp}}, sort=[('time', -1)]) # 调试输出
print(f"距离time最近的消息时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(closest_record['time'])))}") logger.info(
f"距离time最近的消息时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(closest_record['time'])))}")
if closest_record: if closest_record:
closest_time = closest_record['time'] closest_time = closest_record['time']
group_id = closest_record['group_id'] # 获取groupid group_id = closest_record['group_id'] # 获取groupid
# 获取该时间戳之后的length条消息且groupid相同 # 获取该时间戳之后的length条消息且groupid相同
chat_record = list(self.db.db.messages.find({"time": {"$gt": closest_time}, "group_id": group_id}).sort('time', 1).limit(length)) chat_record = list(
self.db.db.messages.find({"time": {"$gt": closest_time}, "group_id": group_id}).sort('time', 1).limit(
length))
for record in chat_record: for record in chat_record:
time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(record['time']))) time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(int(record['time'])))
try: try:
@@ -179,30 +182,31 @@ def main():
break break
first_layer_items, second_layer_items = memory_graph.get_related_item(query) first_layer_items, second_layer_items = memory_graph.get_related_item(query)
if first_layer_items or second_layer_items: if first_layer_items or second_layer_items:
print("\n第一层记忆:") logger.debug("第一层记忆:")
for item in first_layer_items: for item in first_layer_items:
print(item) logger.debug(item)
print("\n第二层记忆:") logger.debug("第二层记忆:")
for item in second_layer_items: for item in second_layer_items:
print(item) logger.debug(item)
else: else:
print("未找到相关记忆。") logger.debug("未找到相关记忆。")
def segment_text(text): def segment_text(text):
seg_text = list(jieba.cut(text)) seg_text = list(jieba.cut(text))
return seg_text return seg_text
def find_topic(text, topic_num): def find_topic(text, topic_num):
prompt = f'这是一段文字:{text}。请你从这段话中总结出{topic_num}个话题,帮我列出来,用逗号隔开,尽可能精简。只需要列举{topic_num}个话题就好,不要告诉我其他内容。' prompt = f'这是一段文字:{text}。请你从这段话中总结出{topic_num}个话题,帮我列出来,用逗号隔开,尽可能精简。只需要列举{topic_num}个话题就好,不要告诉我其他内容。'
return prompt return prompt
def topic_what(text, topic): def topic_what(text, topic):
prompt = f'这是一段文字:{text}。我想知道这记忆里有什么关于{topic}的话题,帮我总结成一句自然的话,可以包含时间和人物。只输出这句话就好' prompt = f'这是一段文字:{text}。我想知道这记忆里有什么关于{topic}的话题,帮我总结成一句自然的话,可以包含时间和人物。只输出这句话就好'
return prompt return prompt
def visualize_graph_lite(memory_graph: Memory_graph, color_by_memory: bool = False): def visualize_graph_lite(memory_graph: Memory_graph, color_by_memory: bool = False):
# 设置中文字体 # 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
@@ -226,7 +230,7 @@ def visualize_graph_lite(memory_graph: Memory_graph, color_by_memory: bool = Fal
# 如果过滤后没有节点,则返回 # 如果过滤后没有节点,则返回
if len(H.nodes()) == 0: if len(H.nodes()) == 0:
print("过滤后没有符合条件的节点可显示") logger.debug("过滤后没有符合条件的节点可显示")
return return
# 保存图到本地 # 保存图到本地
@@ -287,6 +291,5 @@ def visualize_graph_lite(memory_graph: Memory_graph, color_by_memory: bool = Fal
plt.show() plt.show()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -45,7 +45,7 @@ class LLM_request:
self.db.db.llm_usage.create_index([("user_id", 1)]) self.db.db.llm_usage.create_index([("user_id", 1)])
self.db.db.llm_usage.create_index([("request_type", 1)]) self.db.db.llm_usage.create_index([("request_type", 1)])
except Exception as e: except Exception as e:
logger.error(f"创建数据库索引失败: {e}") logger.error(f"创建数据库索引失败")
def _record_usage(self, prompt_tokens: int, completion_tokens: int, total_tokens: int, def _record_usage(self, prompt_tokens: int, completion_tokens: int, total_tokens: int,
user_id: str = "system", request_type: str = "chat", user_id: str = "system", request_type: str = "chat",
@@ -79,8 +79,8 @@ class LLM_request:
f"提示词: {prompt_tokens}, 完成: {completion_tokens}, " f"提示词: {prompt_tokens}, 完成: {completion_tokens}, "
f"总计: {total_tokens}" f"总计: {total_tokens}"
) )
except Exception as e: except Exception:
logger.error(f"记录token使用情况失败: {e}") logger.error(f"记录token使用情况失败")
def _calculate_cost(self, prompt_tokens: int, completion_tokens: int) -> float: def _calculate_cost(self, prompt_tokens: int, completion_tokens: int) -> float:
"""计算API调用成本 """计算API调用成本
@@ -226,8 +226,8 @@ class LLM_request:
if delta_content is None: if delta_content is None:
delta_content = "" delta_content = ""
accumulated_content += delta_content accumulated_content += delta_content
except Exception as e: except Exception:
logger.error(f"解析流式输出错误: {e}") logger.exception(f"解析流式输出错")
content = accumulated_content content = accumulated_content
reasoning_content = "" reasoning_content = ""
think_match = re.search(r'<think>(.*?)</think>', content, re.DOTALL) think_match = re.search(r'<think>(.*?)</think>', content, re.DOTALL)

View File

@@ -3,6 +3,7 @@ import time
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Dict from typing import Any, Dict
from loguru import logger
from ...common.database import Database from ...common.database import Database
@@ -153,8 +154,8 @@ class LLMStatistics:
try: try:
all_stats = self._collect_all_statistics() all_stats = self._collect_all_statistics()
self._save_statistics(all_stats) self._save_statistics(all_stats)
except Exception as e: except Exception:
print(f"\033[1;31m[错误]\033[0m 统计数据处理失败: {e}") logger.exception(f"统计数据处理失败")
# 等待1分钟 # 等待1分钟
for _ in range(60): for _ in range(60):