diff --git a/scripts/test_hfc_performance.py b/scripts/test_hfc_performance.py index be3f229d2..939aa93ec 100644 --- a/scripts/test_hfc_performance.py +++ b/scripts/test_hfc_performance.py @@ -16,149 +16,126 @@ from src.chat.focus_chat.hfc_version_manager import set_hfc_version, get_hfc_ver def test_performance_logger(): """测试性能记录器功能""" - + # 设置测试版本号 test_version = "v1.2.3_test" set_hfc_version(test_version) print(f"设置测试版本号: {test_version}") print(f"当前版本号: {get_hfc_version()}") - + # 创建测试用的性能记录器 test_chat_id = "test_chat_123" logger = HFCPerformanceLogger(test_chat_id, test_version) - + print(f"测试 HFC 性能记录器 - Chat ID: {test_chat_id}, Version: {logger.version}") - + # 模拟记录几个循环的数据 test_cycles = [ { "cycle_id": 1, "action_type": "reply", "total_time": 2.5, - "step_times": { - "观察": 0.1, - "并行调整动作、处理": 1.2, - "规划器": 0.8, - "执行动作": 0.4 - }, + "step_times": {"观察": 0.1, "并行调整动作、处理": 1.2, "规划器": 0.8, "执行动作": 0.4}, "reasoning": "用户询问天气,需要回复", - "success": True + "success": True, }, { "cycle_id": 2, "action_type": "no_reply", "total_time": 1.8, - "step_times": { - "观察": 0.08, - "并行调整动作、处理": 0.9, - "规划器": 0.6, - "执行动作": 0.22 - }, + "step_times": {"观察": 0.08, "并行调整动作、处理": 0.9, "规划器": 0.6, "执行动作": 0.22}, "reasoning": "无需回复的日常对话", - "success": True + "success": True, }, { "cycle_id": 3, "action_type": "reply", "total_time": 3.2, - "step_times": { - "观察": 0.12, - "并行调整动作、处理": 1.5, - "规划器": 1.1, - "执行动作": 0.48 - }, + "step_times": {"观察": 0.12, "并行调整动作、处理": 1.5, "规划器": 1.1, "执行动作": 0.48}, "reasoning": "用户提出复杂问题,需要详细回复", - "success": True + "success": True, }, { "cycle_id": 4, "action_type": "no_reply", "total_time": 1.5, - "step_times": { - "观察": 0.07, - "并行调整动作、处理": 0.8, - "规划器": 0.5, - "执行动作": 0.13 - }, + "step_times": {"观察": 0.07, "并行调整动作、处理": 0.8, "规划器": 0.5, "执行动作": 0.13}, "reasoning": "群聊中的无关对话", - "success": True + "success": True, }, { "cycle_id": 5, "action_type": "error", "total_time": 0.5, - "step_times": { - "观察": 0.05, - "并行调整动作、处理": 0.2, - "规划器": 0.15, - "执行动作": 0.1 - }, + "step_times": {"观察": 0.05, "并行调整动作、处理": 0.2, "规划器": 0.15, "执行动作": 0.1}, "reasoning": "处理过程中出现错误", - "success": False - } + "success": False, + }, ] - + # 记录测试数据 for cycle_data in test_cycles: logger.record_cycle(cycle_data) print(f"已记录循环 {cycle_data['cycle_id']}: {cycle_data['action_type']} ({cycle_data['total_time']:.1f}s)") - + # 获取当前会话统计 current_stats = logger.get_current_session_stats() print("\n=== 当前会话统计 ===") print(json.dumps(current_stats, ensure_ascii=False, indent=2)) - + # 完成会话 logger.finalize_session() - print(f"\n=== 会话已完成 ===") + print("\n=== 会话已完成 ===") print(f"日志文件: {logger.session_file}") print(f"统计文件: {logger.stats_file}") - + # 检查生成的文件 if logger.session_file.exists(): print(f"\n会话文件大小: {logger.session_file.stat().st_size} 字节") - + if logger.stats_file.exists(): print(f"统计文件大小: {logger.stats_file.stat().st_size} 字节") - + # 读取并显示统计数据 - with open(logger.stats_file, 'r', encoding='utf-8') as f: + with open(logger.stats_file, "r", encoding="utf-8") as f: stats_data = json.load(f) - - print(f"\n=== 最终统计数据 ===") + + print("\n=== 最终统计数据 ===") if test_chat_id in stats_data: chat_stats = stats_data[test_chat_id] print(f"Chat ID: {test_chat_id}") print(f"最后更新: {chat_stats['last_updated']}") print(f"总记录数: {chat_stats['overall']['total_records']}") print(f"平均总时间: {chat_stats['overall']['avg_total_time']:.2f}秒") - - print(f"\n各步骤平均时间:") - for step, avg_time in chat_stats['overall']['avg_step_times'].items(): + + print("\n各步骤平均时间:") + for step, avg_time in chat_stats["overall"]["avg_step_times"].items(): print(f" {step}: {avg_time:.3f}秒") - - print(f"\n按动作类型统计:") - for action, action_stats in chat_stats['by_action'].items(): - print(f" {action}: {action_stats['count']}次 ({action_stats['percentage']:.1f}%), 平均{action_stats['avg_total_time']:.2f}秒") + + print("\n按动作类型统计:") + for action, action_stats in chat_stats["by_action"].items(): + print( + f" {action}: {action_stats['count']}次 ({action_stats['percentage']:.1f}%), 平均{action_stats['avg_total_time']:.2f}秒" + ) def test_version_manager(): """测试版本号管理功能""" print("\n=== 测试版本号管理器 ===") - + # 测试默认版本 print(f"默认版本: {get_hfc_version()}") - + # 测试设置版本 test_versions = ["v2.0.0", "1.5.0", "v1.0.0.beta", "v1.0.build123"] for version in test_versions: success = set_hfc_version(version) print(f"设置版本 '{version}': {'成功' if success else '失败'} -> {get_hfc_version()}") - + # 测试自动生成版本 auto_version = auto_generate_hfc_version() print(f"自动生成版本: {auto_version}") - + # 测试基于现有版本的自动生成 auto_version2 = auto_generate_hfc_version("v2.1.0") print(f"基于v2.1.0自动生成: {auto_version2}") @@ -166,4 +143,4 @@ def test_version_manager(): if __name__ == "__main__": test_version_manager() - test_performance_logger() \ No newline at end of file + test_performance_logger() diff --git a/scripts/view_hfc_stats.py b/scripts/view_hfc_stats.py index 65e47a4ab..75e792e25 100644 --- a/scripts/view_hfc_stats.py +++ b/scripts/view_hfc_stats.py @@ -7,7 +7,6 @@ import sys import json import argparse from pathlib import Path -from datetime import datetime from typing import Dict, Any # 添加项目根目录到Python路径 @@ -27,30 +26,30 @@ def display_chat_stats(chat_id: str, stats: Dict[str, Any]): print(f"\n=== Chat ID: {chat_id} ===") print(f"版本: {stats.get('version', 'unknown')}") print(f"最后更新: {stats['last_updated']}") - - overall = stats['overall'] - print(f"\n📊 总体统计:") + + overall = stats["overall"] + print("\n📊 总体统计:") print(f" 总记录数: {overall['total_records']}") print(f" 平均总时间: {format_time(overall['avg_total_time'])}") - - print(f"\n⏱️ 各步骤平均时间:") - for step, avg_time in overall['avg_step_times'].items(): + + print("\n⏱️ 各步骤平均时间:") + for step, avg_time in overall["avg_step_times"].items(): print(f" {step}: {format_time(avg_time)}") - - print(f"\n🎯 按动作类型统计:") - by_action = stats['by_action'] - + + print("\n🎯 按动作类型统计:") + by_action = stats["by_action"] + # 按比例排序 - sorted_actions = sorted(by_action.items(), key=lambda x: x[1]['percentage'], reverse=True) - + sorted_actions = sorted(by_action.items(), key=lambda x: x[1]["percentage"], reverse=True) + for action, action_stats in sorted_actions: print(f" 📌 {action}:") print(f" 次数: {action_stats['count']} ({action_stats['percentage']:.1f}%)") print(f" 平均总时间: {format_time(action_stats['avg_total_time'])}") - - if action_stats['avg_step_times']: - print(f" 步骤时间:") - for step, step_time in action_stats['avg_step_times'].items(): + + if action_stats["avg_step_times"]: + print(" 步骤时间:") + for step, step_time in action_stats["avg_step_times"].items(): print(f" {step}: {format_time(step_time)}") @@ -58,27 +57,29 @@ def display_comparison(stats_data: Dict[str, Dict[str, Any]]): """显示多个聊天的对比数据""" if len(stats_data) < 2: return - - print(f"\n=== 多聊天对比 ===") - + + print("\n=== 多聊天对比 ===") + # 创建对比表格 chat_ids = list(stats_data.keys()) - - print(f"\n📊 总体对比:") + + print("\n📊 总体对比:") print(f"{'Chat ID':<20} {'版本':<12} {'记录数':<8} {'平均时间':<12} {'最常见动作':<15}") print("-" * 70) - + for chat_id in chat_ids: stats = stats_data[chat_id] - overall = stats['overall'] - + overall = stats["overall"] + # 找到最常见的动作 - most_common_action = max(stats['by_action'].items(), key=lambda x: x[1]['count']) + most_common_action = max(stats["by_action"].items(), key=lambda x: x[1]["count"]) most_common_name = most_common_action[0] - most_common_pct = most_common_action[1]['percentage'] - - version = stats.get('version', 'unknown') - print(f"{chat_id:<20} {version:<12} {overall['total_records']:<8} {format_time(overall['avg_total_time']):<12} {most_common_name}({most_common_pct:.0f}%)") + most_common_pct = most_common_action[1]["percentage"] + + version = stats.get("version", "unknown") + print( + f"{chat_id:<20} {version:<12} {overall['total_records']:<8} {format_time(overall['avg_total_time']):<12} {most_common_name}({most_common_pct:.0f}%)" + ) def view_session_logs(chat_id: str = None, latest: bool = False): @@ -87,50 +88,50 @@ def view_session_logs(chat_id: str = None, latest: bool = False): if not log_dir.exists(): print("❌ 日志目录不存在") return - + if chat_id: pattern = f"{chat_id}_*.json" else: pattern = "*.json" - + log_files = list(log_dir.glob(pattern)) - + if not log_files: print(f"❌ 没有找到匹配的日志文件: {pattern}") return - + if latest: # 按文件修改时间排序,取最新的 log_files.sort(key=lambda f: f.stat().st_mtime, reverse=True) log_files = log_files[:1] - + for log_file in log_files: print(f"\n=== 会话日志: {log_file.name} ===") - + try: - with open(log_file, 'r', encoding='utf-8') as f: + with open(log_file, "r", encoding="utf-8") as f: records = json.load(f) - + if not records: print(" 空文件") continue - + print(f" 记录数: {len(records)}") print(f" 时间范围: {records[0]['timestamp']} ~ {records[-1]['timestamp']}") - + # 统计动作分布 action_counts = {} total_time = 0 - + for record in records: - action = record['action_type'] + action = record["action_type"] action_counts[action] = action_counts.get(action, 0) + 1 - total_time += record['total_time'] - + total_time += record["total_time"] + print(f" 总耗时: {format_time(total_time)}") print(f" 平均耗时: {format_time(total_time / len(records))}") print(f" 动作分布: {dict(action_counts)}") - + except Exception as e: print(f" ❌ 读取文件失败: {e}") @@ -141,30 +142,30 @@ def main(): parser.add_argument("--logs", action="store_true", help="查看会话日志文件") parser.add_argument("--latest", action="store_true", help="只显示最新的日志文件") parser.add_argument("--compare", action="store_true", help="显示多聊天对比") - + args = parser.parse_args() - + if args.logs: view_session_logs(args.chat_id, args.latest) return - + # 读取统计数据 stats_file = Path("data/hfc/time.json") if not stats_file.exists(): print("❌ 统计数据文件不存在,请先运行一些HFC循环以生成数据") return - + try: - with open(stats_file, 'r', encoding='utf-8') as f: + with open(stats_file, "r", encoding="utf-8") as f: stats_data = json.load(f) except Exception as e: print(f"❌ 读取统计数据失败: {e}") return - + if not stats_data: print("❌ 统计数据为空") return - + if args.chat_id: if args.chat_id in stats_data: display_chat_stats(args.chat_id, stats_data[args.chat_id]) @@ -175,10 +176,10 @@ def main(): # 显示所有聊天的统计数据 for chat_id, stats in stats_data.items(): display_chat_stats(chat_id, stats) - + if args.compare: display_comparison(stats_data) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index e51fbc808..7e5139c51 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -151,7 +151,7 @@ class HeartFChatting: # 存储回调函数 self.on_stop_focus_chat = on_stop_focus_chat - + # 初始化性能记录器 # 如果没有指定版本号,则使用全局版本管理器的版本号 actual_version = performance_version or get_hfc_version() @@ -407,17 +407,17 @@ class HeartFChatting: + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "") + processor_time_log ) - + # 记录性能数据 try: - action_result = self._current_cycle_detail.loop_plan_info.get('action_result', {}) + action_result = self._current_cycle_detail.loop_plan_info.get("action_result", {}) cycle_performance_data = { "cycle_id": self._current_cycle_detail.cycle_id, - "action_type": action_result.get('action_type', 'unknown'), + "action_type": action_result.get("action_type", "unknown"), "total_time": self._current_cycle_detail.end_time - self._current_cycle_detail.start_time, "step_times": cycle_timers.copy(), - "reasoning": action_result.get('reasoning', ''), - "success": self._current_cycle_detail.loop_action_info.get('action_taken', False), + "reasoning": action_result.get("reasoning", ""), + "success": self._current_cycle_detail.loop_action_info.get("action_taken", False), } self.performance_logger.record_cycle(cycle_performance_data) except Exception as perf_e: diff --git a/src/chat/focus_chat/hfc_performance_logger.py b/src/chat/focus_chat/hfc_performance_logger.py index 4de1e9751..721830553 100644 --- a/src/chat/focus_chat/hfc_performance_logger.py +++ b/src/chat/focus_chat/hfc_performance_logger.py @@ -1,8 +1,6 @@ import json -import os -import time from datetime import datetime -from typing import Dict, List, Any, Optional +from typing import Dict, List, Any from pathlib import Path from src.common.logger import get_logger @@ -11,32 +9,34 @@ logger = get_logger("hfc_performance") class HFCPerformanceLogger: """HFC性能记录管理器""" - + # 版本号常量,可在启动时修改 INTERNAL_VERSION = "v1.0.0" - + def __init__(self, chat_id: str, version: str = None): self.chat_id = chat_id self.version = version or self.INTERNAL_VERSION self.log_dir = Path("log/hfc_loop") self.data_dir = Path("data/hfc") self.session_start_time = datetime.now() - + # 确保目录存在 self.log_dir.mkdir(parents=True, exist_ok=True) self.data_dir.mkdir(parents=True, exist_ok=True) - + # 当前会话的日志文件,包含版本号 version_suffix = self.version.replace(".", "_") - self.session_file = self.log_dir / f"{chat_id}_{version_suffix}_{self.session_start_time.strftime('%Y%m%d_%H%M%S')}.json" + self.session_file = ( + self.log_dir / f"{chat_id}_{version_suffix}_{self.session_start_time.strftime('%Y%m%d_%H%M%S')}.json" + ) self.current_session_data = [] - + # 统计数据文件 self.stats_file = self.data_dir / "time.json" - + # 初始化时计算历史统计数据 self._update_historical_stats() - + def record_cycle(self, cycle_data: Dict[str, Any]): """记录单次循环数据""" try: @@ -50,112 +50,110 @@ class HFCPerformanceLogger: "total_time": cycle_data.get("total_time", 0), "step_times": cycle_data.get("step_times", {}), "reasoning": cycle_data.get("reasoning", ""), - "success": cycle_data.get("success", False) + "success": cycle_data.get("success", False), } - + # 添加到当前会话数据 self.current_session_data.append(record) - + # 立即写入文件(防止数据丢失) self._write_session_data() - - logger.debug(f"记录HFC循环数据: cycle_id={record['cycle_id']}, action={record['action_type']}, time={record['total_time']:.2f}s") - + + logger.debug( + f"记录HFC循环数据: cycle_id={record['cycle_id']}, action={record['action_type']}, time={record['total_time']:.2f}s" + ) + except Exception as e: logger.error(f"记录HFC循环数据失败: {e}") - + def _write_session_data(self): """写入当前会话数据到文件""" try: - with open(self.session_file, 'w', encoding='utf-8') as f: + with open(self.session_file, "w", encoding="utf-8") as f: json.dump(self.current_session_data, f, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"写入会话数据失败: {e}") - + def _update_historical_stats(self): """更新历史统计数据""" try: # 读取所有历史会话文件 all_records = [] - + # 读取当前chat_id的所有历史文件(包括不同版本) for file_path in self.log_dir.glob(f"{self.chat_id}_*.json"): if file_path == self.session_file: continue # 跳过当前会话文件 - + try: - with open(file_path, 'r', encoding='utf-8') as f: + with open(file_path, "r", encoding="utf-8") as f: records = json.load(f) if isinstance(records, list): all_records.extend(records) except Exception as e: logger.warning(f"读取历史文件 {file_path} 失败: {e}") - + if not all_records: logger.info(f"没有找到 chat_id={self.chat_id} 的历史数据") return - + # 计算统计数据 stats = self._calculate_stats(all_records) - + # 更新统计文件 self._update_stats_file(stats) - + logger.info(f"更新了 chat_id={self.chat_id} 的历史统计数据,共 {len(all_records)} 条记录") - + except Exception as e: logger.error(f"更新历史统计数据失败: {e}") - + def _calculate_stats(self, records: List[Dict[str, Any]]) -> Dict[str, Any]: """计算统计数据""" if not records: return {} - + # 按动作类型分组 action_groups = {} total_times = [] step_time_totals = {} - + for record in records: action_type = record.get("action_type", "unknown") total_time = record.get("total_time", 0) step_times = record.get("step_times", {}) - + if action_type not in action_groups: - action_groups[action_type] = { - "count": 0, - "total_times": [], - "step_times": {} - } - + action_groups[action_type] = {"count": 0, "total_times": [], "step_times": {}} + action_groups[action_type]["count"] += 1 action_groups[action_type]["total_times"].append(total_time) total_times.append(total_time) - + # 记录步骤时间 for step_name, step_time in step_times.items(): if step_name not in action_groups[action_type]["step_times"]: action_groups[action_type]["step_times"][step_name] = [] action_groups[action_type]["step_times"][step_name].append(step_time) - + if step_name not in step_time_totals: step_time_totals[step_name] = [] step_time_totals[step_name].append(step_time) - + # 计算各种平均值和比例 total_records = len(records) - + # 整体统计 overall_stats = { "total_records": total_records, "avg_total_time": sum(total_times) / len(total_times) if total_times else 0, - "avg_step_times": {} + "avg_step_times": {}, } - + # 各步骤平均时间 for step_name, times in step_time_totals.items(): overall_stats["avg_step_times"][step_name] = sum(times) / len(times) if times else 0 - + # 按动作类型统计 action_stats = {} for action_type, data in action_groups.items(): @@ -163,76 +161,78 @@ class HFCPerformanceLogger: "count": data["count"], "percentage": (data["count"] / total_records) * 100, "avg_total_time": sum(data["total_times"]) / len(data["total_times"]) if data["total_times"] else 0, - "avg_step_times": {} + "avg_step_times": {}, } - + # 该动作各步骤平均时间 for step_name, times in data["step_times"].items(): action_stats[action_type]["avg_step_times"][step_name] = sum(times) / len(times) if times else 0 - + return { "chat_id": self.chat_id, "version": self.version, "last_updated": datetime.now().isoformat(), "overall": overall_stats, - "by_action": action_stats + "by_action": action_stats, } - + def _update_stats_file(self, new_stats: Dict[str, Any]): """更新统计文件""" try: # 读取现有统计数据 existing_stats = {} if self.stats_file.exists(): - with open(self.stats_file, 'r', encoding='utf-8') as f: + with open(self.stats_file, "r", encoding="utf-8") as f: existing_stats = json.load(f) - + # 更新当前chat_id和版本的统计数据 stats_key = f"{self.chat_id}_{self.version}" existing_stats[stats_key] = new_stats - + # 写回文件 - with open(self.stats_file, 'w', encoding='utf-8') as f: + with open(self.stats_file, "w", encoding="utf-8") as f: json.dump(existing_stats, f, ensure_ascii=False, indent=2) - + except Exception as e: logger.error(f"更新统计文件失败: {e}") - + def get_current_session_stats(self) -> Dict[str, Any]: """获取当前会话的统计数据""" if not self.current_session_data: return {} - + return self._calculate_stats(self.current_session_data) - + def finalize_session(self): """结束会话,进行最终统计""" try: if self.current_session_data: # 计算当前会话统计数据 - current_stats = self._calculate_stats(self.current_session_data) - + self._calculate_stats(self.current_session_data) + # 合并历史数据重新计算总体统计 all_records = self.current_session_data[:] - + # 读取历史数据 for file_path in self.log_dir.glob(f"{self.chat_id}_*.json"): if file_path == self.session_file: continue - + try: - with open(file_path, 'r', encoding='utf-8') as f: + with open(file_path, "r", encoding="utf-8") as f: records = json.load(f) if isinstance(records, list): all_records.extend(records) except Exception as e: logger.warning(f"读取历史文件 {file_path} 失败: {e}") - + # 重新计算总体统计 total_stats = self._calculate_stats(all_records) self._update_stats_file(total_stats) - - logger.info(f"完成会话统计,当前会话 {len(self.current_session_data)} 条记录,总共 {len(all_records)} 条记录") - + + logger.info( + f"完成会话统计,当前会话 {len(self.current_session_data)} 条记录,总共 {len(all_records)} 条记录" + ) + except Exception as e: - logger.error(f"结束会话统计失败: {e}") \ No newline at end of file + logger.error(f"结束会话统计失败: {e}") diff --git a/src/chat/focus_chat/hfc_version_manager.py b/src/chat/focus_chat/hfc_version_manager.py index 31d07d928..72954df06 100644 --- a/src/chat/focus_chat/hfc_version_manager.py +++ b/src/chat/focus_chat/hfc_version_manager.py @@ -18,21 +18,21 @@ logger = get_logger("hfc_version") class HFCVersionManager: """HFC版本号管理器""" - + # 默认版本号 DEFAULT_VERSION = "v1.0.0" - + # 当前运行时版本号 _current_version: Optional[str] = None - + @classmethod def set_version(cls, version: str) -> bool: """ 设置当前运行时版本号 - + 参数: version: 版本号字符串,格式如 v1.0.0 或 1.0.0 - + 返回: bool: 设置是否成功 """ @@ -48,103 +48,103 @@ class HFCVersionManager: except Exception as e: logger.error(f"设置版本号失败: {e}") return False - + @classmethod def get_version(cls) -> str: """ 获取当前版本号 - + 返回: str: 当前版本号 """ if cls._current_version: return cls._current_version - + # 尝试从环境变量获取 env_version = os.getenv("HFC_PERFORMANCE_VERSION") if env_version: if cls.set_version(env_version): return cls._current_version - + # 返回默认版本号 return cls.DEFAULT_VERSION - + @classmethod def auto_generate_version(cls, base_version: str = None) -> str: """ 自动生成版本号(基于时间戳) - + 参数: base_version: 基础版本号,如果不提供则使用默认版本 - + 返回: str: 生成的版本号 """ if not base_version: base_version = cls.DEFAULT_VERSION - + # 提取基础版本号的主要部分 - base_match = re.match(r'v?(\d+\.\d+)', base_version) + base_match = re.match(r"v?(\d+\.\d+)", base_version) if base_match: base_part = base_match.group(1) else: base_part = "1.0" - + # 添加时间戳 timestamp = datetime.now().strftime("%Y%m%d_%H%M") generated_version = f"v{base_part}.{timestamp}" - + cls.set_version(generated_version) logger.info(f"自动生成版本号: {generated_version}") - + return generated_version - + @classmethod def _validate_version(cls, version: str) -> Optional[str]: """ 验证版本号格式 - + 参数: version: 待验证的版本号 - + 返回: Optional[str]: 验证后的版本号,失败返回None """ if not version or not isinstance(version, str): return None - + version = version.strip() - + # 支持的格式: # v1.0.0, 1.0.0, v1.0, 1.0, v1.0.0.20241222_1530 等 patterns = [ - r'^v?(\d+\.\d+\.\d+)$', # v1.0.0 或 1.0.0 - r'^v?(\d+\.\d+)$', # v1.0 或 1.0 - r'^v?(\d+\.\d+\.\d+\.\w+)$', # v1.0.0.build 或 1.0.0.build - r'^v?(\d+\.\d+\.\w+)$', # v1.0.build 或 1.0.build + r"^v?(\d+\.\d+\.\d+)$", # v1.0.0 或 1.0.0 + r"^v?(\d+\.\d+)$", # v1.0 或 1.0 + r"^v?(\d+\.\d+\.\d+\.\w+)$", # v1.0.0.build 或 1.0.0.build + r"^v?(\d+\.\d+\.\w+)$", # v1.0.build 或 1.0.build ] - + for pattern in patterns: match = re.match(pattern, version) if match: # 确保版本号以v开头 - if not version.startswith('v'): - version = 'v' + version + if not version.startswith("v"): + version = "v" + version return version - + return None - + @classmethod def reset_version(cls): """重置版本号为默认值""" cls._current_version = None logger.info("HFC版本号已重置为默认值") - + @classmethod def get_version_info(cls) -> dict: """ 获取版本信息 - + 返回: dict: 版本相关信息 """ @@ -154,7 +154,7 @@ class HFCVersionManager: "default_version": cls.DEFAULT_VERSION, "is_custom": current != cls.DEFAULT_VERSION, "env_version": os.getenv("HFC_PERFORMANCE_VERSION"), - "timestamp": datetime.now().isoformat() + "timestamp": datetime.now().isoformat(), } @@ -182,4 +182,4 @@ def reset_hfc_version(): # 在模块加载时显示当前版本信息 if __name__ != "__main__": current_version = HFCVersionManager.get_version() - logger.debug(f"HFC性能记录模块已加载,当前版本: {current_version}") \ No newline at end of file + logger.debug(f"HFC性能记录模块已加载,当前版本: {current_version}") diff --git a/src/chat/message_receive/chat_stream.py b/src/chat/message_receive/chat_stream.py index 30e0e83da..93da0bdd1 100644 --- a/src/chat/message_receive/chat_stream.py +++ b/src/chat/message_receive/chat_stream.py @@ -345,6 +345,7 @@ class ChatManager: async def load_all_streams(self): """从数据库加载所有聊天流""" logger.info("正在从数据库加载所有聊天流") + def _db_load_all_streams_sync(): loaded_streams_data = [] for model_instance in ChatStreams.select(): diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index 81f820b2c..15aea43fe 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -187,36 +187,30 @@ class StatisticOutputTask(AsyncTask): logger.info("\n" + "\n".join(output)) async def run(self): - try: + try: now = datetime.now() - + # 使用线程池并行执行耗时操作 loop = asyncio.get_event_loop() - + # 在线程池中并行执行数据收集和之前的HTML生成(如果存在) with concurrent.futures.ThreadPoolExecutor() as executor: logger.info("正在收集统计数据...") - + # 数据收集任务 - collect_task = loop.run_in_executor( - executor, self._collect_all_statistics, now - ) - + collect_task = loop.run_in_executor(executor, self._collect_all_statistics, now) + # 等待数据收集完成 stats = await collect_task logger.info("统计数据收集完成") - + # 并行执行控制台输出和HTML报告生成 - console_task = loop.run_in_executor( - executor, self._statistic_console_output, stats, now - ) - html_task = loop.run_in_executor( - executor, self._generate_html_report, stats, now - ) - + console_task = loop.run_in_executor(executor, self._statistic_console_output, stats, now) + html_task = loop.run_in_executor(executor, self._generate_html_report, stats, now) + # 等待两个输出任务完成 await asyncio.gather(console_task, html_task) - + logger.info("统计数据输出完成") except Exception as e: logger.exception(f"输出统计数据过程中发生异常,错误信息:{e}") @@ -226,41 +220,38 @@ class StatisticOutputTask(AsyncTask): 备选方案:完全异步后台运行统计输出 使用此方法可以让统计任务完全非阻塞 """ + async def _async_collect_and_output(): try: import concurrent.futures - + now = datetime.now() loop = asyncio.get_event_loop() - + with concurrent.futures.ThreadPoolExecutor() as executor: logger.info("正在后台收集统计数据...") - + # 创建后台任务,不等待完成 collect_task = asyncio.create_task( loop.run_in_executor(executor, self._collect_all_statistics, now) ) - + stats = await collect_task logger.info("统计数据收集完成") - + # 创建并发的输出任务 output_tasks = [ - asyncio.create_task( - loop.run_in_executor(executor, self._statistic_console_output, stats, now) - ), - asyncio.create_task( - loop.run_in_executor(executor, self._generate_html_report, stats, now) - ) + asyncio.create_task(loop.run_in_executor(executor, self._statistic_console_output, stats, now)), + asyncio.create_task(loop.run_in_executor(executor, self._generate_html_report, stats, now)), ] - + # 等待所有输出任务完成 await asyncio.gather(*output_tasks) - + logger.info("统计数据后台输出完成") except Exception as e: logger.exception(f"后台统计数据输出过程中发生异常:{e}") - + # 创建后台任务,立即返回 asyncio.create_task(_async_collect_and_output()) @@ -1223,7 +1214,7 @@ class AsyncStatisticOutputTask(AsyncTask): def __init__(self, record_file_path: str = "maibot_statistics.html"): # 延迟0秒启动,运行间隔300秒 super().__init__(task_name="Async Statistics Data Output Task", wait_before_start=0, run_interval=300) - + # 直接复用 StatisticOutputTask 的初始化逻辑 temp_stat_task = StatisticOutputTask(record_file_path) self.name_mapping = temp_stat_task.name_mapping @@ -1232,49 +1223,46 @@ class AsyncStatisticOutputTask(AsyncTask): async def run(self): """完全异步执行统计任务""" + async def _async_collect_and_output(): try: now = datetime.now() loop = asyncio.get_event_loop() - + with concurrent.futures.ThreadPoolExecutor() as executor: logger.info("正在后台收集统计数据...") - + # 数据收集任务 collect_task = asyncio.create_task( loop.run_in_executor(executor, self._collect_all_statistics, now) ) - + stats = await collect_task logger.info("统计数据收集完成") - + # 创建并发的输出任务 output_tasks = [ - asyncio.create_task( - loop.run_in_executor(executor, self._statistic_console_output, stats, now) - ), - asyncio.create_task( - loop.run_in_executor(executor, self._generate_html_report, stats, now) - ) + asyncio.create_task(loop.run_in_executor(executor, self._statistic_console_output, stats, now)), + asyncio.create_task(loop.run_in_executor(executor, self._generate_html_report, stats, now)), ] - + # 等待所有输出任务完成 await asyncio.gather(*output_tasks) - + logger.info("统计数据后台输出完成") except Exception as e: logger.exception(f"后台统计数据输出过程中发生异常:{e}") - + # 创建后台任务,立即返回 asyncio.create_task(_async_collect_and_output()) # 复用 StatisticOutputTask 的所有方法 def _collect_all_statistics(self, now: datetime): return StatisticOutputTask._collect_all_statistics(self, now) - + def _statistic_console_output(self, stats: Dict[str, Any], now: datetime): return StatisticOutputTask._statistic_console_output(self, stats, now) - + def _generate_html_report(self, stats: dict[str, Any], now: datetime): return StatisticOutputTask._generate_html_report(self, stats, now) @@ -1282,11 +1270,11 @@ class AsyncStatisticOutputTask(AsyncTask): @staticmethod def _collect_model_request_for_period(collect_period: List[Tuple[str, datetime]]) -> Dict[str, Any]: return StatisticOutputTask._collect_model_request_for_period(collect_period) - - @staticmethod + + @staticmethod def _collect_online_time_for_period(collect_period: List[Tuple[str, datetime]], now: datetime) -> Dict[str, Any]: return StatisticOutputTask._collect_online_time_for_period(collect_period, now) - + def _collect_message_count_for_period(self, collect_period: List[Tuple[str, datetime]]) -> Dict[str, Any]: return StatisticOutputTask._collect_message_count_for_period(self, collect_period) @@ -1300,12 +1288,12 @@ class AsyncStatisticOutputTask(AsyncTask): def _format_chat_stat(self, stats: Dict[str, Any]) -> str: return StatisticOutputTask._format_chat_stat(self, stats) - + def _generate_chart_data(self, stat: dict[str, Any]) -> dict: return StatisticOutputTask._generate_chart_data(self, stat) - + def _collect_interval_data(self, now: datetime, hours: int, interval_minutes: int) -> dict: return StatisticOutputTask._collect_interval_data(self, now, hours, interval_minutes) - + def _generate_chart_tab(self, chart_data: dict) -> str: return StatisticOutputTask._generate_chart_tab(self, chart_data) diff --git a/src/common/database/database.py b/src/common/database/database.py index 59fcfac7c..249664155 100644 --- a/src/common/database/database.py +++ b/src/common/database/database.py @@ -72,11 +72,11 @@ os.makedirs(_DB_DIR, exist_ok=True) db = SqliteDatabase( _DB_FILE, pragmas={ - 'journal_mode': 'wal', # WAL模式提高并发性能 - 'cache_size': -64 * 1000, # 64MB缓存 - 'foreign_keys': 1, - 'ignore_check_constraints': 0, - 'synchronous': 0, # 异步写入提高性能 - 'busy_timeout': 1000, # 1秒超时而不是3秒 - } + "journal_mode": "wal", # WAL模式提高并发性能 + "cache_size": -64 * 1000, # 64MB缓存 + "foreign_keys": 1, + "ignore_check_constraints": 0, + "synchronous": 0, # 异步写入提高性能 + "busy_timeout": 1000, # 1秒超时而不是3秒 + }, ) diff --git a/src/individuality/individuality.py b/src/individuality/individuality.py index 02a5a1fc6..eda883efe 100644 --- a/src/individuality/individuality.py +++ b/src/individuality/individuality.py @@ -48,7 +48,6 @@ class Individuality: person_info_manager = get_person_info_manager() self.bot_person_id = person_info_manager.get_person_id("system", "bot_id") self.name = bot_nickname - # 检查配置变化,如果变化则清空 await self._check_config_and_clear_if_changed( @@ -63,7 +62,6 @@ class Individuality: # 初始化身份 self.identity = Identity(identity_detail=identity_detail) - logger.info("正在将所有人设写入impression") # 将所有人设写入impression impression_parts = [] diff --git a/src/main.py b/src/main.py index 1021f15fa..ec65c9212 100644 --- a/src/main.py +++ b/src/main.py @@ -101,7 +101,7 @@ class MainSystem: logger.info("willing管理器初始化成功") # 初始化聊天管理器 - + await get_chat_manager()._initialize() asyncio.create_task(get_chat_manager()._auto_save_task()) diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 00961bb5f..86e3b6fcd 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -62,11 +62,11 @@ class PersonInfoManager: try: db.connect(reuse_if_open=True) # 设置连接池参数 - if hasattr(db, 'execute_sql'): + if hasattr(db, "execute_sql"): # 设置SQLite优化参数 - db.execute_sql('PRAGMA cache_size = -64000') # 64MB缓存 - db.execute_sql('PRAGMA temp_store = memory') # 临时存储在内存中 - db.execute_sql('PRAGMA mmap_size = 268435456') # 256MB内存映射 + db.execute_sql("PRAGMA cache_size = -64000") # 64MB缓存 + db.execute_sql("PRAGMA temp_store = memory") # 临时存储在内存中 + db.execute_sql("PRAGMA mmap_size = 268435456") # 256MB内存映射 db.create_tables([PersonInfo], safe=True) except Exception as e: logger.error(f"数据库连接或 PersonInfo 表创建失败: {e}") @@ -165,7 +165,6 @@ class PersonInfoManager: async def update_one_field(self, person_id: str, field_name: str, value, data: dict = None): """更新某一个字段,会补全""" if field_name not in PersonInfo._meta.fields: - logger.debug(f"更新'{field_name}'失败,未在 PersonInfo Peewee 模型中定义的字段。") return @@ -178,20 +177,23 @@ class PersonInfoManager: def _db_update_sync(p_id: str, f_name: str, val_to_set): import time + start_time = time.time() try: record = PersonInfo.get_or_none(PersonInfo.person_id == p_id) query_time = time.time() - + if record: setattr(record, f_name, val_to_set) record.save() save_time = time.time() - + total_time = save_time - start_time if total_time > 0.5: # 如果超过500ms就记录日志 - logger.warning(f"数据库更新操作耗时 {total_time:.3f}秒 (查询: {query_time-start_time:.3f}s, 保存: {save_time-query_time:.3f}s) person_id={p_id}, field={f_name}") - + logger.warning( + f"数据库更新操作耗时 {total_time:.3f}秒 (查询: {query_time - start_time:.3f}s, 保存: {save_time - query_time:.3f}s) person_id={p_id}, field={f_name}" + ) + return True, False # Found and updated, no creation needed else: total_time = time.time() - start_time @@ -202,7 +204,6 @@ class PersonInfoManager: total_time = time.time() - start_time logger.error(f"数据库操作异常,耗时 {total_time:.3f}秒: {e}") raise - found, needs_creation = await asyncio.to_thread(_db_update_sync, person_id, field_name, processed_value)