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} ¥
+
+
按模型分类统计
+
+ | 模型名称 | 调用次数 | 输入Token | 输出Token | Token总量 | 累计花费 |
+
+ {model_rows}
+
+
+
+
按模块分类统计
+
+
+ | 模块名称 | 调用次数 | 输入Token | 输出Token | Token总量 | 累计花费 |
+
+
+ {module_rows}
+
+
+
+
按请求类型分类统计
+
+
+ | 请求类型 | 调用次数 | 输入Token | 输出Token | Token总量 | 累计花费 |
+
+
+ {type_rows}
+
+
+
+
聊天消息统计
+
+
+ | 联系人/群组名称 | 消息数量 |
+
+
+ {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