From 6653bf3bd93121cac16a8e2adc82308238eaaffe Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 25 Jun 2025 00:59:18 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/utils/statistic.py | 451 +++++++++++++++++------------- template/bot_config_template.toml | 2 +- 2 files changed, 265 insertions(+), 188 deletions(-) diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index 68b1d68d1..b6ce7dc76 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -58,12 +58,6 @@ FOCUS_CYCLE_CNT_BY_VERSION = "focus_cycle_count_by_version" FOCUS_ACTION_RATIOS_BY_VERSION = "focus_action_ratios_by_version" FOCUS_AVG_TIMES_BY_VERSION = "focus_avg_times_by_version" -# 新增: 后处理器统计数据的键 -FOCUS_POST_PROCESSOR_TIMES = "focus_post_processor_times" -FOCUS_POST_PROCESSOR_COUNT = "focus_post_processor_count" -FOCUS_POST_PROCESSOR_SUCCESS_RATE = "focus_post_processor_success_rate" -FOCUS_PROCESSOR_TIMES = "focus_processor_times" # 前处理器统计 - class OnlineTimeRecordTask(AsyncTask): """在线时间记录任务""" @@ -501,10 +495,6 @@ class StatisticOutputTask(AsyncTask): FOCUS_AVG_TIMES_BY_VERSION: defaultdict(lambda: defaultdict(list)), "focus_exec_times_by_version_action": defaultdict(lambda: defaultdict(list)), "focus_action_ratios_by_chat": defaultdict(lambda: defaultdict(int)), - # 新增:前处理器和后处理器统计字段 - FOCUS_PROCESSOR_TIMES: defaultdict(list), # 前处理器时间 - FOCUS_POST_PROCESSOR_TIMES: defaultdict(list), # 后处理器时间 - FOCUS_POST_PROCESSOR_COUNT: defaultdict(int), # 后处理器执行次数 } for period_key, _ in collect_period } @@ -567,10 +557,6 @@ class StatisticOutputTask(AsyncTask): step_times = cycle_data.get("step_times", {}) version = cycle_data.get("version", "unknown") - # 新增:获取前处理器和后处理器时间 - processor_time_costs = cycle_data.get("processor_time_costs", {}) - post_processor_time_costs = cycle_data.get("post_processor_time_costs", {}) - # 更新聊天ID名称映射 if chat_id not in self.name_mapping: # 尝试获取实际的聊天名称 @@ -608,15 +594,6 @@ class StatisticOutputTask(AsyncTask): stat["focus_exec_times_by_chat_action"][chat_id][action_type].append(time_val) # 按版本和action类型收集执行时间 stat["focus_exec_times_by_version_action"][version][action_type].append(time_val) - - # 新增:前处理器时间统计 - for processor_name, time_val in processor_time_costs.items(): - stat[FOCUS_PROCESSOR_TIMES][processor_name].append(time_val) - - # 新增:后处理器时间统计 - for processor_name, time_val in post_processor_time_costs.items(): - stat[FOCUS_POST_PROCESSOR_TIMES][processor_name].append(time_val) - stat[FOCUS_POST_PROCESSOR_COUNT][processor_name] += 1 break except Exception as e: logger.warning(f"Failed to process cycle data: {e}") @@ -674,20 +651,6 @@ class StatisticOutputTask(AsyncTask): else: stat["focus_exec_times_by_version_action"][version][action_type] = 0.0 - # 新增:计算前处理器平均时间 - for processor_name, times in stat[FOCUS_PROCESSOR_TIMES].items(): - if times: - stat[FOCUS_PROCESSOR_TIMES][processor_name] = sum(times) / len(times) - else: - stat[FOCUS_PROCESSOR_TIMES][processor_name] = 0.0 - - # 新增:计算后处理器平均时间 - for processor_name, times in stat[FOCUS_POST_PROCESSOR_TIMES].items(): - if times: - stat[FOCUS_POST_PROCESSOR_TIMES][processor_name] = sum(times) / len(times) - else: - stat[FOCUS_POST_PROCESSOR_TIMES][processor_name] = 0.0 - def _collect_all_statistics(self, now: datetime) -> Dict[str, Dict[str, Any]]: """ 收集各时间段的统计数据 @@ -930,7 +893,7 @@ class StatisticOutputTask(AsyncTask): # format总在线时间 # 按模型分类统计 - "\n".join( + model_rows = "\n".join( [ f"" f"{model_name}" @@ -944,7 +907,7 @@ class StatisticOutputTask(AsyncTask): ] ) # 按请求类型分类统计 - "\n".join( + type_rows = "\n".join( [ f"" f"{req_type}" @@ -958,7 +921,7 @@ class StatisticOutputTask(AsyncTask): ] ) # 按模块分类统计 - "\n".join( + module_rows = "\n".join( [ f"" f"{module_name}" @@ -973,7 +936,7 @@ class StatisticOutputTask(AsyncTask): ) # 聊天消息统计 - "\n".join( + chat_rows = "\n".join( [ f"{self.name_mapping[chat_id][0]}{count}" for chat_id, count in sorted(stat_data[MSG_CNT_BY_CHAT].items()) @@ -996,6 +959,261 @@ class StatisticOutputTask(AsyncTask): ] ) + # 按聊天流统计 + _focus_chat_rows = "\n".join( + [ + f"{self.name_mapping.get(chat_id, (chat_id, 0))[0]}{count}{stat_data[FOCUS_TOTAL_TIME_BY_CHAT].get(chat_id, 0):.2f}秒" + for chat_id, count in sorted( + stat_data[FOCUS_CYCLE_CNT_BY_CHAT].items(), key=lambda x: x[1], reverse=True + ) + ] + ) + + # 全局阶段时间统计 + _focus_stage_rows = "\n".join( + [ + f"{stage}{avg_time:.3f}秒" + for stage, avg_time in sorted(stat_data[FOCUS_AVG_TIMES_BY_STAGE].items()) + ] + ) + + # 按Action类型的阶段时间统计 + focus_action_stage_items = [] + for action_type, stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].items(): + for stage, avg_time in stage_times.items(): + focus_action_stage_items.append((action_type, stage, avg_time)) + + _focus_action_stage_rows = "\n".join( + [ + f"{action_type}{stage}{avg_time:.3f}秒" + for action_type, stage, avg_time in sorted(focus_action_stage_items) + ] + ) + # 生成HTML + return f""" +
+

+ 统计时段: + {start_time.strftime("%Y-%m-%d %H:%M:%S")} ~ {now.strftime("%Y-%m-%d %H:%M:%S")} +

+

总在线时间: {_format_online_time(stat_data[ONLINE_TIME])}

+

总消息数: {stat_data[TOTAL_MSG_CNT]}

+

总请求数: {stat_data[TOTAL_REQ_CNT]}

+

总花费: {stat_data[TOTAL_COST]:.4f} ¥

+ +

按模型分类统计

+ + + + {model_rows} + +
模型名称调用次数输入Token输出TokenToken总量累计花费
+ +

按模块分类统计

+ + + + + + {module_rows} + +
模块名称调用次数输入Token输出TokenToken总量累计花费
+ +

按请求类型分类统计

+ + + + + + {type_rows} + +
请求类型调用次数输入Token输出TokenToken总量累计花费
+ +

聊天消息统计

+ + + + + + {chat_rows} + +
联系人/群组名称消息数量
+ + +
+ """ + + tab_content_list = [ + _format_stat_data(stat[period[0]], period[0], now - period[1]) + for period in self.stat_period + if period[0] != "all_time" + ] + + tab_content_list.append( + _format_stat_data(stat["all_time"], "all_time", datetime.fromtimestamp(local_storage["deploy_time"])) + ) + + # 添加Focus统计内容 + focus_tab = self._generate_focus_tab(stat) + tab_content_list.append(focus_tab) + + # 添加版本对比内容 + versions_tab = self._generate_versions_tab(stat) + tab_content_list.append(versions_tab) + + # 添加图表内容 + chart_data = self._generate_chart_data(stat) + tab_content_list.append(self._generate_chart_tab(chart_data)) + + joined_tab_list = "\n".join(tab_list) + joined_tab_content = "\n".join(tab_content_list) + + html_template = ( + """ + + + + + + MaiBot运行统计报告 + + + + +""" + + f""" +
+

MaiBot运行统计报告

+

统计截止时间: {now.strftime("%Y-%m-%d %H:%M:%S")}

+ +
+ {joined_tab_list} +
+ + {joined_tab_content} +
+""" + + """ + + + + """ + ) + + with open(self.record_file_path, "w", encoding="utf-8") as f: + f.write(html_template) + + def _generate_focus_tab(self, stat: dict[str, Any]) -> str: + """生成Focus统计独立分页的HTML内容""" + # 为每个时间段准备Focus数据 focus_sections = [] @@ -1024,8 +1242,8 @@ class StatisticOutputTask(AsyncTask): # 按聊天流统计(横向表格,显示各阶段时间差异和不同action的平均时间) focus_chat_rows = "" if stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION]: - # 获取所有阶段(包括后处理器) - basic_stages = ["观察", "并行调整动作、处理", "规划器", "后期处理器", "动作执行"] + # 获取前三个阶段(不包括执行动作) + basic_stages = ["观察", "并行调整动作、处理", "规划器"] existing_basic_stages = [] for stage in basic_stages: # 检查是否有任何聊天流在这个阶段有数据 @@ -1092,99 +1310,6 @@ class StatisticOutputTask(AsyncTask): ] ) - # 聊天流Action选择比例对比表(横向表格) - focus_chat_action_ratios_rows = "" - if stat_data.get("focus_action_ratios_by_chat"): - # 获取所有action类型(按全局频率排序) - all_action_types_for_ratio = sorted( - stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True - ) - - if all_action_types_for_ratio: - # 为每个聊天流生成数据行(按循环数排序) - chat_ratio_rows = [] - for chat_id in sorted( - stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(), - key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], - reverse=True, - ): - chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0] - total_cycles = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id] - chat_action_counts = stat_data["focus_action_ratios_by_chat"].get(chat_id, {}) - - row_cells = [f"{chat_name}
({total_cycles}次循环)"] - - # 添加每个action类型的数量和百分比 - for action_type in all_action_types_for_ratio: - count = chat_action_counts.get(action_type, 0) - ratio = (count / total_cycles * 100) if total_cycles > 0 else 0 - if count > 0: - row_cells.append(f"{count}
({ratio:.1f}%)") - else: - row_cells.append("-
(0%)") - - chat_ratio_rows.append(f"{''.join(row_cells)}") - - # 生成表头 - action_headers = "".join([f"{action_type}" for action_type in all_action_types_for_ratio]) - chat_action_ratio_table_header = f"聊天流{action_headers}" - focus_chat_action_ratios_rows = chat_action_ratio_table_header + "\n" + "\n".join(chat_ratio_rows) - - # 获取所有action类型(按出现频率排序) - all_action_types = sorted( - stat_data[FOCUS_ACTION_RATIOS].keys(), key=lambda x: stat_data[FOCUS_ACTION_RATIOS][x], reverse=True - ) - - # 为每个聊天流生成一行 - chat_rows = [] - for chat_id in sorted( - stat_data[FOCUS_CYCLE_CNT_BY_CHAT].keys(), - key=lambda x: stat_data[FOCUS_CYCLE_CNT_BY_CHAT][x], - reverse=True, - ): - chat_name = self.name_mapping.get(chat_id, (chat_id, 0))[0] - cycle_count = stat_data[FOCUS_CYCLE_CNT_BY_CHAT][chat_id] - - # 获取该聊天流的各阶段平均时间 - stage_times = stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].get(chat_id, {}) - - row_cells = [f"{chat_name}
({cycle_count}次循环)"] - - # 添加基础阶段时间 - for stage in existing_basic_stages: - time_val = stage_times.get(stage, 0.0) - row_cells.append(f"{time_val:.3f}秒") - - # 添加每个action类型的平均执行时间 - for action_type in all_action_types: - # 使用真实的按聊天流+action类型分组的执行时间数据 - exec_times_by_chat_action = stat_data.get("focus_exec_times_by_chat_action", {}) - chat_action_times = exec_times_by_chat_action.get(chat_id, {}) - avg_exec_time = chat_action_times.get(action_type, 0.0) - - if avg_exec_time > 0: - row_cells.append(f"{avg_exec_time:.3f}秒") - else: - row_cells.append("-") - - chat_rows.append(f"{''.join(row_cells)}") - - # 生成表头 - stage_headers = "".join([f"{stage}" for stage in existing_basic_stages]) - action_headers = "".join( - [f"{action_type}
(执行)" for action_type in all_action_types] - ) - focus_chat_table_header = f"聊天流{stage_headers}{action_headers}" - focus_chat_rows = focus_chat_table_header + "\n" + "\n".join(chat_rows) - - # 全局阶段时间统计 - focus_stage_rows = "\n".join( - [ - f"{stage}{avg_time:.3f}秒" - for stage, avg_time in sorted(stat_data[FOCUS_AVG_TIMES_BY_STAGE].items()) - ] - ) - # 聊天流Action选择比例对比表(横向表格) focus_chat_action_ratios_rows = "" if stat_data.get("focus_action_ratios_by_chat"): @@ -1226,8 +1351,8 @@ class StatisticOutputTask(AsyncTask): # 按Action类型的阶段时间统计(横向表格) focus_action_stage_rows = "" if stat_data[FOCUS_AVG_TIMES_BY_ACTION]: - # 获取所有阶段(按固定顺序,确保与实际Timer名称一致) - stage_order = ["观察", "并行调整动作、处理", "规划器", "后期处理器", "动作执行"] + # 获取所有阶段(按固定顺序) + stage_order = ["观察", "并行调整动作、处理", "规划器", "执行动作"] all_stages = [] for stage in stage_order: if any(stage in stage_times for stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].values()): @@ -1250,36 +1375,6 @@ class StatisticOutputTask(AsyncTask): focus_action_stage_table_header = f"Action类型{stage_headers}" focus_action_stage_rows = focus_action_stage_table_header + "\n" + "\n".join(action_rows) - # 新增:前处理器统计表格 - focus_processor_rows = "" - if stat_data.get(FOCUS_PROCESSOR_TIMES): - processor_rows = [] - for processor_name in sorted(stat_data[FOCUS_PROCESSOR_TIMES].keys()): - avg_time = stat_data[FOCUS_PROCESSOR_TIMES][processor_name] - processor_rows.append(f"{processor_name}{avg_time:.3f}秒") - focus_processor_rows = "\n".join(processor_rows) - - # 新增:前处理器统计表格 - focus_processor_rows = "" - if stat_data.get(FOCUS_PROCESSOR_TIMES): - processor_rows = [] - for processor_name in sorted(stat_data[FOCUS_PROCESSOR_TIMES].keys()): - avg_time = stat_data[FOCUS_PROCESSOR_TIMES][processor_name] - processor_rows.append(f"{processor_name}{avg_time:.3f}秒") - focus_processor_rows = "\n".join(processor_rows) - - # 新增:后处理器统计表格 - focus_post_processor_rows = "" - if stat_data.get(FOCUS_POST_PROCESSOR_TIMES): - post_processor_rows = [] - for processor_name in sorted(stat_data[FOCUS_POST_PROCESSOR_TIMES].keys()): - avg_time = stat_data[FOCUS_POST_PROCESSOR_TIMES][processor_name] - count = stat_data[FOCUS_POST_PROCESSOR_COUNT].get(processor_name, 0) - post_processor_rows.append( - f"{processor_name}{avg_time:.3f}秒{count}" - ) - focus_post_processor_rows = "\n".join(post_processor_rows) - # 计算时间范围 if period_name == "all_time": from src.manager.local_store_manager import local_storage @@ -1342,24 +1437,6 @@ class StatisticOutputTask(AsyncTask): {focus_action_stage_rows} - -
-
-

前处理器平均时间

- - - {focus_processor_rows} -
处理器名称平均耗时
-
- -
-

后处理器统计

- - - {focus_post_processor_rows} -
处理器名称平均耗时执行次数
-
-
""" @@ -2308,4 +2385,4 @@ class AsyncStatisticOutputTask(AsyncTask): return StatisticOutputTask._generate_versions_tab(self, stat) def _convert_defaultdict_to_dict(self, data): - return StatisticOutputTask._convert_defaultdict_to_dict(self, data) + return StatisticOutputTask._convert_defaultdict_to_dict(self, data) \ No newline at end of file diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 1c27bdc03..b1c22eb18 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -196,7 +196,7 @@ enable_kaomoji_protection = false # 是否启用颜文字保护 [log] date_style = "Y-m-d H:i:s" # 日期格式 log_level_style = "lite" # 日志级别样式,可选FULL,compact,lite -color_text = "title" # 日志文本颜色,可选none,title,full +color_text = "full" # 日志文本颜色,可选none,title,full log_level = "INFO" # 全局日志级别(向下兼容,优先级低于下面的分别设置) console_log_level = "INFO" # 控制台日志级别,可选: DEBUG, INFO, WARNING, ERROR, CRITICAL file_log_level = "DEBUG" # 文件日志级别,可选: DEBUG, INFO, WARNING, ERROR, CRITICAL