feat:添加后处理器统计时间
This commit is contained in:
@@ -22,9 +22,10 @@ class CycleDetail:
|
|||||||
|
|
||||||
# 新字段
|
# 新字段
|
||||||
self.loop_observation_info: Dict[str, Any] = {}
|
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_plan_info: Dict[str, Any] = {}
|
||||||
self.loop_action_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]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
"""将循环信息转换为字典格式"""
|
"""将循环信息转换为字典格式"""
|
||||||
@@ -76,9 +77,10 @@ class CycleDetail:
|
|||||||
"timers": self.timers,
|
"timers": self.timers,
|
||||||
"thinking_id": self.thinking_id,
|
"thinking_id": self.thinking_id,
|
||||||
"loop_observation_info": convert_to_serializable(self.loop_observation_info),
|
"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_plan_info": convert_to_serializable(self.loop_plan_info),
|
||||||
"loop_action_info": convert_to_serializable(self.loop_action_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):
|
def complete_cycle(self):
|
||||||
@@ -133,3 +135,4 @@ class CycleDetail:
|
|||||||
self.loop_processor_info = loop_info["loop_processor_info"]
|
self.loop_processor_info = loop_info["loop_processor_info"]
|
||||||
self.loop_plan_info = loop_info["loop_plan_info"]
|
self.loop_plan_info = loop_info["loop_plan_info"]
|
||||||
self.loop_action_info = loop_info["loop_action_info"]
|
self.loop_action_info = loop_info["loop_action_info"]
|
||||||
|
self.loop_post_processor_info = loop_info["loop_post_processor_info"]
|
||||||
|
|||||||
@@ -454,7 +454,19 @@ class HeartFChatting:
|
|||||||
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
formatted_ptime = f"{ptime * 1000:.2f}毫秒" if ptime < 1 else f"{ptime:.2f}秒"
|
||||||
processor_time_strings.append(f"{pname}: {formatted_ptime}")
|
processor_time_strings.append(f"{pname}: {formatted_ptime}")
|
||||||
processor_time_log = (
|
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(
|
logger.info(
|
||||||
@@ -463,6 +475,7 @@ class HeartFChatting:
|
|||||||
f"动作: {self._current_cycle_detail.loop_plan_info.get('action_result', {}).get('action_type', '未知动作')}"
|
f"动作: {self._current_cycle_detail.loop_plan_info.get('action_result', {}).get('action_type', '未知动作')}"
|
||||||
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
+ (f"\n详情: {'; '.join(timer_strings)}" if timer_strings else "")
|
||||||
+ processor_time_log
|
+ processor_time_log
|
||||||
|
+ post_processor_time_log
|
||||||
)
|
)
|
||||||
|
|
||||||
# 记录性能数据
|
# 记录性能数据
|
||||||
@@ -473,6 +486,8 @@ class HeartFChatting:
|
|||||||
"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,
|
"total_time": self._current_cycle_detail.end_time - self._current_cycle_detail.start_time,
|
||||||
"step_times": cycle_timers.copy(),
|
"step_times": cycle_timers.copy(),
|
||||||
|
"processor_time_costs": processor_time_costs, # 前处理器时间
|
||||||
|
"post_processor_time_costs": post_processor_time_costs, # 后处理器时间
|
||||||
"reasoning": action_result.get("reasoning", ""),
|
"reasoning": action_result.get("reasoning", ""),
|
||||||
"success": self._current_cycle_detail.loop_action_info.get("action_taken", False),
|
"success": self._current_cycle_detail.loop_action_info.get("action_taken", False),
|
||||||
}
|
}
|
||||||
@@ -754,6 +769,180 @@ class HeartFChatting:
|
|||||||
|
|
||||||
return updated_action_data
|
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:
|
async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> dict:
|
||||||
try:
|
try:
|
||||||
loop_start_time = time.time()
|
loop_start_time = time.time()
|
||||||
@@ -836,28 +1025,37 @@ class HeartFChatting:
|
|||||||
"observed_messages": plan_result.get("observed_messages", ""),
|
"observed_messages": plan_result.get("observed_messages", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
with Timer("执行动作", cycle_timers):
|
# 修正:将后期处理器从执行动作Timer中分离出来
|
||||||
action_type, action_data, reasoning = (
|
action_type, action_data, reasoning = (
|
||||||
plan_result.get("action_result", {}).get("action_type", "error"),
|
plan_result.get("action_result", {}).get("action_type", "error"),
|
||||||
plan_result.get("action_result", {}).get("action_data", {}),
|
plan_result.get("action_result", {}).get("action_data", {}),
|
||||||
plan_result.get("action_result", {}).get("reasoning", "未提供理由"),
|
plan_result.get("action_result", {}).get("reasoning", "未提供理由"),
|
||||||
)
|
)
|
||||||
|
|
||||||
if action_type == "reply":
|
if action_type == "reply":
|
||||||
action_str = "回复"
|
action_str = "回复"
|
||||||
elif action_type == "no_reply":
|
elif action_type == "no_reply":
|
||||||
action_str = "不回复"
|
action_str = "不回复"
|
||||||
else:
|
else:
|
||||||
action_str = action_type
|
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":
|
post_processor_time_costs = {}
|
||||||
with Timer("后期处理器", cycle_timers):
|
if action_type != "no_reply":
|
||||||
logger.debug(f"{self.log_prefix} 执行后期处理器(动作类型: {action_type})")
|
with Timer("后期处理器", cycle_timers):
|
||||||
action_data = await self._process_post_planning_processors(self.observations, action_data)
|
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(
|
success, reply_text, command = await self._handle_action(
|
||||||
action_type, reasoning, action_data, cycle_timers, thinking_id
|
action_type, reasoning, action_data, cycle_timers, thinking_id
|
||||||
)
|
)
|
||||||
@@ -869,11 +1067,17 @@ class HeartFChatting:
|
|||||||
"taken_time": time.time(),
|
"taken_time": time.time(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 添加后处理器统计到loop_info
|
||||||
|
loop_post_processor_info = {
|
||||||
|
"post_processor_time_costs": post_processor_time_costs,
|
||||||
|
}
|
||||||
|
|
||||||
loop_info = {
|
loop_info = {
|
||||||
"loop_observation_info": loop_observation_info,
|
"loop_observation_info": loop_observation_info,
|
||||||
"loop_processor_info": loop_processor_info,
|
"loop_processor_info": loop_processor_info,
|
||||||
"loop_plan_info": loop_plan_info,
|
"loop_plan_info": loop_plan_info,
|
||||||
"loop_action_info": loop_action_info,
|
"loop_action_info": loop_action_info,
|
||||||
|
"loop_post_processor_info": loop_post_processor_info, # 新增
|
||||||
}
|
}
|
||||||
|
|
||||||
return loop_info
|
return loop_info
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ class HFCPerformanceLogger:
|
|||||||
"action_type": cycle_data.get("action_type", "unknown"),
|
"action_type": cycle_data.get("action_type", "unknown"),
|
||||||
"total_time": cycle_data.get("total_time", 0),
|
"total_time": cycle_data.get("total_time", 0),
|
||||||
"step_times": cycle_data.get("step_times", {}),
|
"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", ""),
|
"reasoning": cycle_data.get("reasoning", ""),
|
||||||
"success": cycle_data.get("success", False),
|
"success": cycle_data.get("success", False),
|
||||||
}
|
}
|
||||||
@@ -51,9 +53,22 @@ class HFCPerformanceLogger:
|
|||||||
# 立即写入文件(防止数据丢失)
|
# 立即写入文件(防止数据丢失)
|
||||||
self._write_session_data()
|
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:
|
except Exception as e:
|
||||||
logger.error(f"记录HFC循环数据失败: {e}")
|
logger.error(f"记录HFC循环数据失败: {e}")
|
||||||
|
|||||||
@@ -440,6 +440,7 @@ class DefaultReplyer:
|
|||||||
chat_info=chat_talking_prompt,
|
chat_info=chat_talking_prompt,
|
||||||
memory_block=memory_block,
|
memory_block=memory_block,
|
||||||
structured_info_block=structured_info_block,
|
structured_info_block=structured_info_block,
|
||||||
|
relation_info_block=relation_info_block,
|
||||||
extra_info_block=extra_info_block,
|
extra_info_block=extra_info_block,
|
||||||
time_block=time_block,
|
time_block=time_block,
|
||||||
keywords_reaction_prompt=keywords_reaction_prompt,
|
keywords_reaction_prompt=keywords_reaction_prompt,
|
||||||
|
|||||||
@@ -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_ACTION_RATIOS_BY_VERSION = "focus_action_ratios_by_version"
|
||||||
FOCUS_AVG_TIMES_BY_VERSION = "focus_avg_times_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):
|
class OnlineTimeRecordTask(AsyncTask):
|
||||||
"""在线时间记录任务"""
|
"""在线时间记录任务"""
|
||||||
@@ -495,6 +501,10 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
FOCUS_AVG_TIMES_BY_VERSION: defaultdict(lambda: defaultdict(list)),
|
FOCUS_AVG_TIMES_BY_VERSION: defaultdict(lambda: defaultdict(list)),
|
||||||
"focus_exec_times_by_version_action": defaultdict(lambda: defaultdict(list)),
|
"focus_exec_times_by_version_action": defaultdict(lambda: defaultdict(list)),
|
||||||
"focus_action_ratios_by_chat": defaultdict(lambda: defaultdict(int)),
|
"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
|
for period_key, _ in collect_period
|
||||||
}
|
}
|
||||||
@@ -556,6 +566,10 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
total_time = cycle_data.get("total_time", 0.0)
|
total_time = cycle_data.get("total_time", 0.0)
|
||||||
step_times = cycle_data.get("step_times", {})
|
step_times = cycle_data.get("step_times", {})
|
||||||
version = cycle_data.get("version", "unknown")
|
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名称映射
|
# 更新聊天ID名称映射
|
||||||
if chat_id not in self.name_mapping:
|
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)
|
stat["focus_exec_times_by_chat_action"][chat_id][action_type].append(time_val)
|
||||||
# 按版本和action类型收集执行时间
|
# 按版本和action类型收集执行时间
|
||||||
stat["focus_exec_times_by_version_action"][version][action_type].append(time_val)
|
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
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Failed to process cycle data: {e}")
|
logger.warning(f"Failed to process cycle data: {e}")
|
||||||
@@ -651,6 +674,20 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
else:
|
else:
|
||||||
stat["focus_exec_times_by_version_action"][version][action_type] = 0.0
|
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]]:
|
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"<tr><td>{self.name_mapping.get(chat_id, (chat_id, 0))[0]}</td><td>{count}</td><td>{stat_data[FOCUS_TOTAL_TIME_BY_CHAT].get(chat_id, 0):.2f}秒</td></tr>"
|
|
||||||
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"<tr><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
|
|
||||||
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"<tr><td>{action_type}</td><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
|
|
||||||
for action_type, stage, avg_time in sorted(focus_action_stage_items)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
# 生成HTML
|
|
||||||
return f"""
|
|
||||||
<div id=\"{div_id}\" class=\"tab-content\">
|
|
||||||
<p class=\"info-item\">
|
|
||||||
<strong>统计时段: </strong>
|
|
||||||
{start_time.strftime("%Y-%m-%d %H:%M:%S")} ~ {now.strftime("%Y-%m-%d %H:%M:%S")}
|
|
||||||
</p>
|
|
||||||
<p class=\"info-item\"><strong>总在线时间: </strong>{_format_online_time(stat_data[ONLINE_TIME])}</p>
|
|
||||||
<p class=\"info-item\"><strong>总消息数: </strong>{stat_data[TOTAL_MSG_CNT]}</p>
|
|
||||||
<p class=\"info-item\"><strong>总请求数: </strong>{stat_data[TOTAL_REQ_CNT]}</p>
|
|
||||||
<p class=\"info-item\"><strong>总花费: </strong>{stat_data[TOTAL_COST]:.4f} ¥</p>
|
|
||||||
|
|
||||||
<h2>按模型分类统计</h2>
|
|
||||||
<table>
|
|
||||||
<thead><tr><th>模型名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
{model_rows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2>按模块分类统计</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>模块名称</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{module_rows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2>按请求类型分类统计</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>请求类型</th><th>调用次数</th><th>输入Token</th><th>输出Token</th><th>Token总量</th><th>累计花费</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{type_rows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2>聊天消息统计</h2>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>联系人/群组名称</th><th>消息数量</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{chat_rows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
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 = (
|
|
||||||
"""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="zh-CN">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>MaiBot运行统计报告</title>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #f4f7f6;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 20px auto;
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
h1, h2 {
|
|
||||||
color: #2c3e50;
|
|
||||||
border-bottom: 2px solid #3498db;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 2em;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 1.5em;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
.info-item {
|
|
||||||
background-color: #ecf0f1;
|
|
||||||
padding: 8px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 0.95em;
|
|
||||||
}
|
|
||||||
.info-item strong {
|
|
||||||
color: #2980b9;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
margin-top: 15px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
background-color: #3498db;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
tr:nth-child(even) {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: #7f8c8d;
|
|
||||||
}
|
|
||||||
.tabs {
|
|
||||||
overflow: hidden;
|
|
||||||
background: #ecf0f1;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.tabs button {
|
|
||||||
background: inherit; border: none; outline: none;
|
|
||||||
padding: 14px 16px; cursor: pointer;
|
|
||||||
transition: 0.3s; font-size: 16px;
|
|
||||||
}
|
|
||||||
.tabs button:hover {
|
|
||||||
background-color: #d4dbdc;
|
|
||||||
}
|
|
||||||
.tabs button.active {
|
|
||||||
background-color: #b3bbbd;
|
|
||||||
}
|
|
||||||
.tab-content {
|
|
||||||
display: none;
|
|
||||||
padding: 20px;
|
|
||||||
background-color: #fff;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
.tab-content.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
"""
|
|
||||||
+ f"""
|
|
||||||
<div class="container">
|
|
||||||
<h1>MaiBot运行统计报告</h1>
|
|
||||||
<p class="info-item"><strong>统计截止时间:</strong> {now.strftime("%Y-%m-%d %H:%M:%S")}</p>
|
|
||||||
|
|
||||||
<div class="tabs">
|
|
||||||
{joined_tab_list}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{joined_tab_content}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
+ """
|
|
||||||
<script>
|
|
||||||
let i, tab_content, tab_links;
|
|
||||||
tab_content = document.getElementsByClassName("tab-content");
|
|
||||||
tab_links = document.getElementsByClassName("tab-link");
|
|
||||||
|
|
||||||
tab_content[0].classList.add("active");
|
|
||||||
tab_links[0].classList.add("active");
|
|
||||||
|
|
||||||
function showTab(evt, tabName) {{
|
|
||||||
for (i = 0; i < tab_content.length; i++) tab_content[i].classList.remove("active");
|
|
||||||
for (i = 0; i < tab_links.length; i++) tab_links[i].classList.remove("active");
|
|
||||||
document.getElementById(tabName).classList.add("active");
|
|
||||||
evt.currentTarget.classList.add("active");
|
|
||||||
}}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
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数据
|
||||||
focus_sections = []
|
focus_sections = []
|
||||||
|
|
||||||
@@ -1242,11 +1024,11 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
# 按聊天流统计(横向表格,显示各阶段时间差异和不同action的平均时间)
|
# 按聊天流统计(横向表格,显示各阶段时间差异和不同action的平均时间)
|
||||||
focus_chat_rows = ""
|
focus_chat_rows = ""
|
||||||
if stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION]:
|
if stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION]:
|
||||||
# 获取前三个阶段(不包括执行动作)
|
# 获取所有阶段(包括后处理器)
|
||||||
basic_stages = ["观察", "并行调整动作、处理", "规划器"]
|
basic_stages = ["观察", "并行调整动作、处理", "规划器", "后期处理器", "动作执行"]
|
||||||
existing_basic_stages = []
|
existing_basic_stages = []
|
||||||
for stage in basic_stages:
|
for stage in basic_stages:
|
||||||
# 检查是否有任何聊天流在这个阶段有数据
|
# 检查是否有任何聊天流在这个阶段有数据
|
||||||
stage_exists = False
|
stage_exists = False
|
||||||
for _chat_id, stage_times in stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].items():
|
for _chat_id, stage_times in stat_data[FOCUS_AVG_TIMES_BY_CHAT_ACTION].items():
|
||||||
if stage in stage_times:
|
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"<td><strong>{chat_name}</strong><br><small>({total_cycles}次循环)</small></td>"]
|
||||||
|
|
||||||
|
# 添加每个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"<td>{count}<br><small>({ratio:.1f}%)</small></td>")
|
||||||
|
else:
|
||||||
|
row_cells.append("<td>-<br><small>(0%)</small></td>")
|
||||||
|
|
||||||
|
chat_ratio_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
|
# 生成表头
|
||||||
|
action_headers = "".join([f"<th>{action_type}</th>" for action_type in all_action_types_for_ratio])
|
||||||
|
chat_action_ratio_table_header = f"<tr><th>聊天流</th>{action_headers}</tr>"
|
||||||
|
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"<td><strong>{chat_name}</strong><br><small>({cycle_count}次循环)</small></td>"]
|
||||||
|
|
||||||
|
# 添加基础阶段时间
|
||||||
|
for stage in existing_basic_stages:
|
||||||
|
time_val = stage_times.get(stage, 0.0)
|
||||||
|
row_cells.append(f"<td>{time_val:.3f}秒</td>")
|
||||||
|
|
||||||
|
# 添加每个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"<td>{avg_exec_time:.3f}秒</td>")
|
||||||
|
else:
|
||||||
|
row_cells.append("<td>-</td>")
|
||||||
|
|
||||||
|
chat_rows.append(f"<tr>{''.join(row_cells)}</tr>")
|
||||||
|
|
||||||
|
# 生成表头
|
||||||
|
stage_headers = "".join([f"<th>{stage}</th>" for stage in existing_basic_stages])
|
||||||
|
action_headers = "".join(
|
||||||
|
[f"<th>{action_type}<br><small>(执行)</small></th>" for action_type in all_action_types]
|
||||||
|
)
|
||||||
|
focus_chat_table_header = f"<tr><th>聊天流</th>{stage_headers}{action_headers}</tr>"
|
||||||
|
focus_chat_rows = focus_chat_table_header + "\n" + "\n".join(chat_rows)
|
||||||
|
|
||||||
|
# 全局阶段时间统计
|
||||||
|
focus_stage_rows = "\n".join(
|
||||||
|
[
|
||||||
|
f"<tr><td>{stage}</td><td>{avg_time:.3f}秒</td></tr>"
|
||||||
|
for stage, avg_time in sorted(stat_data[FOCUS_AVG_TIMES_BY_STAGE].items())
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
# 聊天流Action选择比例对比表(横向表格)
|
# 聊天流Action选择比例对比表(横向表格)
|
||||||
focus_chat_action_ratios_rows = ""
|
focus_chat_action_ratios_rows = ""
|
||||||
if stat_data.get("focus_action_ratios_by_chat"):
|
if stat_data.get("focus_action_ratios_by_chat"):
|
||||||
@@ -1351,8 +1226,8 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
# 按Action类型的阶段时间统计(横向表格)
|
# 按Action类型的阶段时间统计(横向表格)
|
||||||
focus_action_stage_rows = ""
|
focus_action_stage_rows = ""
|
||||||
if stat_data[FOCUS_AVG_TIMES_BY_ACTION]:
|
if stat_data[FOCUS_AVG_TIMES_BY_ACTION]:
|
||||||
# 获取所有阶段(按固定顺序)
|
# 获取所有阶段(按固定顺序,确保与实际Timer名称一致)
|
||||||
stage_order = ["观察", "并行调整动作、处理", "规划器", "执行动作"]
|
stage_order = ["观察", "并行调整动作、处理", "规划器", "后期处理器", "动作执行"]
|
||||||
all_stages = []
|
all_stages = []
|
||||||
for stage in stage_order:
|
for stage in stage_order:
|
||||||
if any(stage in stage_times for stage_times in stat_data[FOCUS_AVG_TIMES_BY_ACTION].values()):
|
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"<tr><th>Action类型</th>{stage_headers}</tr>"
|
focus_action_stage_table_header = f"<tr><th>Action类型</th>{stage_headers}</tr>"
|
||||||
focus_action_stage_rows = focus_action_stage_table_header + "\n" + "\n".join(action_rows)
|
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"<tr><td>{processor_name}</td><td>{avg_time:.3f}秒</td></tr>")
|
||||||
|
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"<tr><td>{processor_name}</td><td>{avg_time:.3f}秒</td></tr>")
|
||||||
|
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"<tr><td>{processor_name}</td><td>{avg_time:.3f}秒</td><td>{count}</td></tr>")
|
||||||
|
focus_post_processor_rows = "\n".join(post_processor_rows)
|
||||||
|
|
||||||
# 计算时间范围
|
# 计算时间范围
|
||||||
if period_name == "all_time":
|
if period_name == "all_time":
|
||||||
from src.manager.local_store_manager import local_storage
|
from src.manager.local_store_manager import local_storage
|
||||||
@@ -1437,6 +1340,24 @@ class StatisticOutputTask(AsyncTask):
|
|||||||
<tbody>{focus_action_stage_rows}</tbody>
|
<tbody>{focus_action_stage_rows}</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="focus-stats-grid">
|
||||||
|
<div class="focus-stat-item">
|
||||||
|
<h3>前处理器平均时间</h3>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>处理器名称</th><th>平均耗时</th></tr></thead>
|
||||||
|
<tbody>{focus_processor_rows}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="focus-stat-item">
|
||||||
|
<h3>后处理器统计</h3>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>处理器名称</th><th>平均耗时</th><th>执行次数</th></tr></thead>
|
||||||
|
<tbody>{focus_post_processor_rows}</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -230,9 +230,22 @@ class NoReplyAction(BaseAction):
|
|||||||
)
|
)
|
||||||
return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
||||||
|
|
||||||
# 如果有新消息且距离上次判断>=1秒,进行LLM判断
|
# 判定条件:累计3条消息或等待超过5秒且有新消息
|
||||||
if new_message_count > 0 and (current_time - last_judge_time) >= min_judge_interval:
|
time_since_last_judge = current_time - last_judge_time
|
||||||
logger.info(f"{self.log_prefix} 检测到{new_message_count}条新消息,进行智能判断...")
|
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(
|
recent_messages = message_api.get_messages_by_time_in_chat(
|
||||||
@@ -309,6 +322,14 @@ class NoReplyAction(BaseAction):
|
|||||||
skip_probability = self._skip_probability_medium
|
skip_probability = self._skip_probability_medium
|
||||||
else:
|
else:
|
||||||
frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n"
|
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(
|
logger.info(
|
||||||
f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示"
|
f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示"
|
||||||
|
|||||||
Reference in New Issue
Block a user