diff --git a/src/chat/focus_chat/heartFC_Cycleinfo.py b/src/chat/focus_chat/heartFC_Cycleinfo.py index 37eea6e9a..120381df3 100644 --- a/src/chat/focus_chat/heartFC_Cycleinfo.py +++ b/src/chat/focus_chat/heartFC_Cycleinfo.py @@ -22,9 +22,10 @@ class CycleDetail: # 新字段 self.loop_observation_info: Dict[str, Any] = {} - self.loop_process_info: Dict[str, Any] = {} + self.loop_processor_info: Dict[str, Any] = {} # 前处理器信息 self.loop_plan_info: Dict[str, Any] = {} self.loop_action_info: Dict[str, Any] = {} + self.loop_post_processor_info: Dict[str, Any] = {} # 后处理器信息 def to_dict(self) -> Dict[str, Any]: """将循环信息转换为字典格式""" @@ -76,9 +77,10 @@ class CycleDetail: "timers": self.timers, "thinking_id": self.thinking_id, "loop_observation_info": convert_to_serializable(self.loop_observation_info), - "loop_process_info": convert_to_serializable(self.loop_process_info), + "loop_processor_info": convert_to_serializable(self.loop_processor_info), "loop_plan_info": convert_to_serializable(self.loop_plan_info), "loop_action_info": convert_to_serializable(self.loop_action_info), + "loop_post_processor_info": convert_to_serializable(self.loop_post_processor_info), } def complete_cycle(self): @@ -133,3 +135,4 @@ class CycleDetail: self.loop_processor_info = loop_info["loop_processor_info"] self.loop_plan_info = loop_info["loop_plan_info"] self.loop_action_info = loop_info["loop_action_info"] + self.loop_post_processor_info = loop_info["loop_post_processor_info"] diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index e9bfa4b80..584aa4087 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -454,7 +454,19 @@ class HeartFChatting: formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒" processor_time_strings.append(f"{pname}: {formatted_ptime}") processor_time_log = ( - ("\n各处理器耗时: " + "; ".join(processor_time_strings)) if processor_time_strings else "" + ("\n前处理器耗时: " + "; ".join(processor_time_strings)) if processor_time_strings else "" + ) + + # 新增:输出每个后处理器的耗时 + post_processor_time_costs = self._current_cycle_detail.loop_post_processor_info.get( + "post_processor_time_costs", {} + ) + post_processor_time_strings = [] + for pname, ptime in post_processor_time_costs.items(): + formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒" + post_processor_time_strings.append(f"{pname}: {formatted_ptime}") + post_processor_time_log = ( + ("\n后处理器耗时: " + "; ".join(post_processor_time_strings)) if post_processor_time_strings else "" ) logger.info( @@ -463,6 +475,7 @@ class HeartFChatting: f"动作: {self._current_cycle_detail.loop_plan_info.get('action_result', {}).get('action_type', '未知动作')}" + (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "") + processor_time_log + + post_processor_time_log ) # 记录性能数据 @@ -473,6 +486,8 @@ class HeartFChatting: "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(), + "processor_time_costs": processor_time_costs, # 前处理器时间 + "post_processor_time_costs": post_processor_time_costs, # 后处理器时间 "reasoning": action_result.get("reasoning", ""), "success": self._current_cycle_detail.loop_action_info.get("action_taken", False), } @@ -754,6 +769,180 @@ class HeartFChatting: return updated_action_data + async def _process_post_planning_processors_with_timing(self, observations: List[Observation], action_data: dict) -> tuple[dict, dict]: + """ + 处理后期处理器(规划后执行的处理器)并收集详细时间统计 + 包括:关系处理器、表达选择器、记忆激活器 + + 参数: + observations: 观察器列表 + action_data: 原始动作数据 + + 返回: + tuple[dict, dict]: (更新后的动作数据, 后处理器时间统计) + """ + logger.info(f"{self.log_prefix} 开始执行后期处理器(带详细统计)") + + # 创建所有后期任务 + task_list = [] + task_to_name_map = {} + task_start_times = {} + post_processor_time_costs = {} + + # 添加后期处理器任务 + for processor in self.post_planning_processors: + processor_name = processor.__class__.__name__ + + async def run_processor_with_timeout_and_timing(proc=processor, name=processor_name): + start_time = time.time() + try: + result = await asyncio.wait_for( + proc.process_info(observations=observations), + timeout=global_config.focus_chat.processor_max_time, + ) + end_time = time.time() + post_processor_time_costs[name] = end_time - start_time + logger.debug(f"{self.log_prefix} 后期处理器 {name} 耗时: {end_time - start_time:.3f}秒") + return result + except Exception as e: + end_time = time.time() + post_processor_time_costs[name] = end_time - start_time + logger.warning(f"{self.log_prefix} 后期处理器 {name} 执行异常,耗时: {end_time - start_time:.3f}秒") + raise e + + task = asyncio.create_task(run_processor_with_timeout_and_timing()) + task_list.append(task) + task_to_name_map[task] = ("processor", processor_name) + task_start_times[task] = time.time() + logger.info(f"{self.log_prefix} 启动后期处理器任务: {processor_name}") + + # 添加记忆激活器任务 + async def run_memory_with_timeout_and_timing(): + start_time = time.time() + try: + result = await asyncio.wait_for( + self.memory_activator.activate_memory(observations), + timeout=MEMORY_ACTIVATION_TIMEOUT, + ) + end_time = time.time() + post_processor_time_costs["MemoryActivator"] = end_time - start_time + logger.debug(f"{self.log_prefix} 记忆激活器耗时: {end_time - start_time:.3f}秒") + return result + except Exception as e: + end_time = time.time() + post_processor_time_costs["MemoryActivator"] = end_time - start_time + logger.warning(f"{self.log_prefix} 记忆激活器执行异常,耗时: {end_time - start_time:.3f}秒") + raise e + + memory_task = asyncio.create_task(run_memory_with_timeout_and_timing()) + task_list.append(memory_task) + task_to_name_map[memory_task] = ("memory", "MemoryActivator") + task_start_times[memory_task] = time.time() + logger.info(f"{self.log_prefix} 启动记忆激活器任务") + + # 如果没有任何后期任务,直接返回 + if not task_list: + logger.info(f"{self.log_prefix} 没有启用的后期处理器或记忆激活器") + return action_data, {} + + # 等待所有任务完成 + pending_tasks = set(task_list) + all_post_plan_info = [] + running_memorys = [] + + while pending_tasks: + done, pending_tasks = await asyncio.wait(pending_tasks, return_when=asyncio.FIRST_COMPLETED) + + for task in done: + task_type, task_name = task_to_name_map[task] + + try: + result = await task + + if task_type == "processor": + logger.info(f"{self.log_prefix} 后期处理器 {task_name} 已完成!") + if result is not None: + all_post_plan_info.extend(result) + else: + logger.warning(f"{self.log_prefix} 后期处理器 {task_name} 返回了 None") + elif task_type == "memory": + logger.info(f"{self.log_prefix} 记忆激活器已完成!") + if result is not None: + running_memorys = result + else: + logger.warning(f"{self.log_prefix} 记忆激活器返回了 None") + running_memorys = [] + + except asyncio.TimeoutError: + # 对于超时任务,记录已用时间 + elapsed_time = time.time() - task_start_times[task] + if task_type == "processor": + post_processor_time_costs[task_name] = elapsed_time + logger.warning( + f"{self.log_prefix} 后期处理器 {task_name} 超时(>{global_config.focus_chat.processor_max_time}s),已跳过,耗时: {elapsed_time:.3f}秒" + ) + elif task_type == "memory": + post_processor_time_costs["MemoryActivator"] = elapsed_time + logger.warning(f"{self.log_prefix} 记忆激活器超时(>{MEMORY_ACTIVATION_TIMEOUT}s),已跳过,耗时: {elapsed_time:.3f}秒") + running_memorys = [] + except Exception as e: + # 对于异常任务,记录已用时间 + elapsed_time = time.time() - task_start_times[task] + if task_type == "processor": + post_processor_time_costs[task_name] = elapsed_time + logger.error( + f"{self.log_prefix} 后期处理器 {task_name} 执行失败,耗时: {elapsed_time:.3f}秒. 错误: {e}", + exc_info=True, + ) + elif task_type == "memory": + post_processor_time_costs["MemoryActivator"] = elapsed_time + logger.error(f"{self.log_prefix} 记忆激活器执行失败,耗时: {elapsed_time:.3f}秒. 错误: {e}", exc_info=True) + running_memorys = [] + + # 将后期处理器的结果整合到 action_data 中 + updated_action_data = action_data.copy() + + relation_info = "" + selected_expressions = [] + structured_info = "" + + for info in all_post_plan_info: + if isinstance(info, RelationInfo): + relation_info = info.get_processed_info() + elif isinstance(info, ExpressionSelectionInfo): + selected_expressions = info.get_expressions_for_action_data() + elif isinstance(info, StructuredInfo): + structured_info = info.get_processed_info() + + if relation_info: + updated_action_data["relation_info_block"] = relation_info + + if selected_expressions: + updated_action_data["selected_expressions"] = selected_expressions + + if structured_info: + updated_action_data["structured_info"] = structured_info + + # 特殊处理running_memorys + if running_memorys: + memory_str = "以下是当前在聊天中,你回忆起的记忆:\n" + for running_memory in running_memorys: + memory_str += f"{running_memory['content']}\n" + updated_action_data["memory_block"] = memory_str + logger.info(f"{self.log_prefix} 添加了 {len(running_memorys)} 个激活的记忆到action_data") + + if all_post_plan_info or running_memorys: + logger.info( + f"{self.log_prefix} 后期处理完成,产生了 {len(all_post_plan_info)} 个信息项和 {len(running_memorys)} 个记忆" + ) + + # 输出详细统计信息 + if post_processor_time_costs: + stats_str = ", ".join([f"{name}: {time_cost:.3f}s" for name, time_cost in post_processor_time_costs.items()]) + logger.info(f"{self.log_prefix} 后期处理器详细耗时统计: {stats_str}") + + return updated_action_data, post_processor_time_costs + async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> dict: try: loop_start_time = time.time() @@ -836,28 +1025,37 @@ class HeartFChatting: "observed_messages": plan_result.get("observed_messages", ""), } - with Timer("执行动作", cycle_timers): - action_type, action_data, reasoning = ( - plan_result.get("action_result", {}).get("action_type", "error"), - plan_result.get("action_result", {}).get("action_data", {}), - plan_result.get("action_result", {}).get("reasoning", "未提供理由"), - ) + # 修正:将后期处理器从执行动作Timer中分离出来 + action_type, action_data, reasoning = ( + plan_result.get("action_result", {}).get("action_type", "error"), + plan_result.get("action_result", {}).get("action_data", {}), + plan_result.get("action_result", {}).get("reasoning", "未提供理由"), + ) - if action_type == "reply": - action_str = "回复" - elif action_type == "no_reply": - action_str = "不回复" - else: - action_str = action_type + if action_type == "reply": + action_str = "回复" + elif action_type == "no_reply": + action_str = "不回复" + else: + action_str = action_type - logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}'") + logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}'") - # 如果动作不是no_reply,则执行后期处理器 - if action_type != "no_reply": - with Timer("后期处理器", cycle_timers): - logger.debug(f"{self.log_prefix} 执行后期处理器(动作类型: {action_type})") - action_data = await self._process_post_planning_processors(self.observations, action_data) + # 添加:单独计时后期处理器,并收集详细统计 + post_processor_time_costs = {} + if action_type != "no_reply": + with Timer("后期处理器", cycle_timers): + logger.debug(f"{self.log_prefix} 执行后期处理器(动作类型: {action_type})") + # 记录详细的后处理器时间 + post_start_time = time.time() + action_data, post_processor_time_costs = await self._process_post_planning_processors_with_timing(self.observations, action_data) + post_end_time = time.time() + logger.info(f"{self.log_prefix} 后期处理器总耗时: {post_end_time - post_start_time:.3f}秒") + else: + logger.debug(f"{self.log_prefix} 跳过后期处理器(动作类型: {action_type})") + # 修正:纯动作执行计时 + with Timer("动作执行", cycle_timers): success, reply_text, command = await self._handle_action( action_type, reasoning, action_data, cycle_timers, thinking_id ) @@ -869,11 +1067,17 @@ class HeartFChatting: "taken_time": time.time(), } + # 添加后处理器统计到loop_info + loop_post_processor_info = { + "post_processor_time_costs": post_processor_time_costs, + } + loop_info = { "loop_observation_info": loop_observation_info, "loop_processor_info": loop_processor_info, "loop_plan_info": loop_plan_info, "loop_action_info": loop_action_info, + "loop_post_processor_info": loop_post_processor_info, # 新增 } return loop_info diff --git a/src/chat/focus_chat/hfc_performance_logger.py b/src/chat/focus_chat/hfc_performance_logger.py index 16f222e80..4d7e0561b 100644 --- a/src/chat/focus_chat/hfc_performance_logger.py +++ b/src/chat/focus_chat/hfc_performance_logger.py @@ -41,6 +41,8 @@ class HFCPerformanceLogger: "action_type": cycle_data.get("action_type", "unknown"), "total_time": cycle_data.get("total_time", 0), "step_times": cycle_data.get("step_times", {}), + "processor_time_costs": cycle_data.get("processor_time_costs", {}), # 前处理器时间 + "post_processor_time_costs": cycle_data.get("post_processor_time_costs", {}), # 后处理器时间 "reasoning": cycle_data.get("reasoning", ""), "success": cycle_data.get("success", False), } @@ -51,9 +53,22 @@ class HFCPerformanceLogger: # 立即写入文件(防止数据丢失) self._write_session_data() - logger.debug( - f"记录HFC循环数据: cycle_id={record['cycle_id']}, action={record['action_type']}, time={record['total_time']:.2f}s" - ) + # 构建详细的日志信息 + log_parts = [ + f"cycle_id={record['cycle_id']}", + f"action={record['action_type']}", + f"time={record['total_time']:.2f}s" + ] + + # 添加后处理器时间信息到日志 + if record['post_processor_time_costs']: + post_processor_stats = ", ".join([ + f"{name}: {time_cost:.3f}s" + for name, time_cost in record['post_processor_time_costs'].items() + ]) + log_parts.append(f"post_processors=({post_processor_stats})") + + logger.debug(f"记录HFC循环数据: {', '.join(log_parts)}") except Exception as e: logger.error(f"记录HFC循环数据失败: {e}") diff --git a/src/chat/focus_chat/replyer/default_generator.py b/src/chat/focus_chat/replyer/default_generator.py index 7a0142dd8..e93945c00 100644 --- a/src/chat/focus_chat/replyer/default_generator.py +++ b/src/chat/focus_chat/replyer/default_generator.py @@ -440,6 +440,7 @@ class DefaultReplyer: chat_info=chat_talking_prompt, memory_block=memory_block, structured_info_block=structured_info_block, + relation_info_block=relation_info_block, extra_info_block=extra_info_block, time_block=time_block, keywords_reaction_prompt=keywords_reaction_prompt, diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index bb3f53a1a..59c7dc164 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -58,6 +58,12 @@ 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): """在线时间记录任务""" @@ -495,6 +501,10 @@ 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 } @@ -556,6 +566,10 @@ class StatisticOutputTask(AsyncTask): total_time = cycle_data.get("total_time", 0.0) 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: @@ -594,6 +608,15 @@ 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}") @@ -651,6 +674,20 @@ 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]]: """ 收集各时间段的统计数据 @@ -959,261 +996,6 @@ 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 = [] @@ -1242,11 +1024,11 @@ 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: - # 检查是否有任何聊天流在这个阶段有数据 + # 检查是否有任何聊天流在这个阶段有数据 stage_exists = False for _chat_id, stage_times in stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].items(): if stage in stage_times: @@ -1310,6 +1092,99 @@ 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"): @@ -1351,8 +1226,8 @@ class StatisticOutputTask(AsyncTask): # 按Action类型的阶段时间统计(横向表格) focus_action_stage_rows = "" if stat_data[FOCUS_AVG_TIMES_BY_ACTION]: - # 获取所有阶段(按固定顺序) - stage_order = ["观察", "并行调整动作、处理", "规划器", "执行动作"] + # 获取所有阶段(按固定顺序,确保与实际Timer名称一致) + 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()): @@ -1375,6 +1250,34 @@ 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 @@ -1437,6 +1340,24 @@ class StatisticOutputTask(AsyncTask): {focus_action_stage_rows} + +
+
+

前处理器平均时间

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

后处理器统计

+ + + {focus_post_processor_rows} +
处理器名称平均耗时执行次数
+
+
""" diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index 1a8843d53..cbf2a837b 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -230,9 +230,22 @@ class NoReplyAction(BaseAction): ) return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)" - # 如果有新消息且距离上次判断>=1秒,进行LLM判断 - if new_message_count > 0 and (current_time - last_judge_time) >= min_judge_interval: - logger.info(f"{self.log_prefix} 检测到{new_message_count}条新消息,进行智能判断...") + # 判定条件:累计3条消息或等待超过5秒且有新消息 + time_since_last_judge = current_time - last_judge_time + should_judge = ( + new_message_count >= 3 or # 累计3条消息 + (new_message_count > 0 and time_since_last_judge >= 5.0) # 等待超过5秒且有新消息 + ) + + if should_judge and time_since_last_judge >= min_judge_interval: + # 判断触发原因 + trigger_reason = "" + if new_message_count >= 3: + trigger_reason = f"累计{new_message_count}条消息" + elif time_since_last_judge >= 5.0: + trigger_reason = f"等待{time_since_last_judge:.1f}秒且有{new_message_count}条新消息" + + logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...") # 获取最近的消息内容用于判断 recent_messages = message_api.get_messages_by_time_in_chat( @@ -309,6 +322,14 @@ class NoReplyAction(BaseAction): skip_probability = self._skip_probability_medium else: frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n" + skip_probability = self._skip_probability_heavy + + # 根据配置和概率决定是否跳过LLM判断 + if self._skip_judge_when_tired and random.random() < skip_probability: + should_skip_llm_judge = True + logger.info( + f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability*100:.0f}%)" + ) logger.info( f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示"