diff --git a/src/common/database/monthly_plan_db.py b/src/common/database/monthly_plan_db.py index 01acf2d5a..7f15fbbf7 100644 --- a/src/common/database/monthly_plan_db.py +++ b/src/common/database/monthly_plan_db.py @@ -63,7 +63,7 @@ def get_active_plans_for_month(month: str) -> List[MonthlyPlan]: plans = session.query(MonthlyPlan).filter( MonthlyPlan.target_month == month, MonthlyPlan.status == 'active' - ).all() + ).order_by(MonthlyPlan.created_at.desc()).all() return plans except Exception as e: logger.error(f"查询 {month} 的有效月度计划时发生错误: {e}") diff --git a/src/main.py b/src/main.py index 6a5f989b0..fbe63d90b 100644 --- a/src/main.py +++ b/src/main.py @@ -216,8 +216,6 @@ MoFox_Bot(第三方修改版) from src.chat.memory_system.async_memory_optimizer import async_memory_manager await async_memory_manager.initialize() logger.info("记忆管理器初始化成功") - except ImportError: - logger.warning("异步记忆优化方法不可用,将回退使用同步模式") except Exception as e: logger.error(f"记忆管理器初始化失败: {e}") else: diff --git a/src/schedule/monthly_plan_manager.py b/src/schedule/monthly_plan_manager.py index bc55544d2..9716c80bf 100644 --- a/src/schedule/monthly_plan_manager.py +++ b/src/schedule/monthly_plan_manager.py @@ -66,8 +66,9 @@ class MonthlyPlanManager: target_month = datetime.now().strftime("%Y-%m") if not has_active_plans(target_month): - logger.info(f" {target_month} 没有任何有效的月度计划,将立即生成。") - return await self.generate_monthly_plans(target_month) + logger.info(f" {target_month} 没有任何有效的月度计划,将触发同步生成。") + generation_successful = await self._generate_monthly_plans_logic(target_month) + return generation_successful else: logger.info(f"{target_month} 已存在有效的月度计划。") plans = get_active_plans_for_month(target_month) @@ -76,8 +77,8 @@ class MonthlyPlanManager: max_plans = global_config.monthly_plan_system.max_plans_per_month if len(plans) > max_plans: logger.warning(f"当前月度计划数量 ({len(plans)}) 超出上限 ({max_plans}),将自动删除多余的计划。") - # 按创建时间升序排序(旧的在前),然后删除超出上限的部分(新的) - plans_to_delete = sorted(plans, key=lambda p: p.created_at, reverse=True)[:len(plans)-max_plans] + # 数据库查询结果已按创建时间降序排序(新的在前),直接截取超出上限的部分进行删除 + plans_to_delete = plans[:len(plans)-max_plans] delete_ids = [p.id for p in plans_to_delete] delete_plans_by_ids(delete_ids) # 重新获取计划列表 @@ -88,9 +89,20 @@ class MonthlyPlanManager: logger.info(f"当前月度计划内容:\n{plan_texts}") return True # 已经有计划,也算成功 - async def generate_monthly_plans(self, target_month: Optional[str] = None) -> bool: + async def generate_monthly_plans(self, target_month: Optional[str] = None): """ - 生成指定月份的月度计划 + 启动月度计划生成。 + """ + if self.generation_running: + logger.info("月度计划生成任务已在运行中,跳过重复启动") + return + + logger.info(f"已触发 {target_month or '当前月份'} 的月度计划生成任务。") + await self._generate_monthly_plans_logic(target_month) + + async def _generate_monthly_plans_logic(self, target_month: Optional[str] = None) -> bool: + """ + 生成指定月份的月度计划的核心逻辑 :param target_month: 目标月份,格式为 "YYYY-MM"。如果为 None,则为当前月份。 :return: 是否生成成功 @@ -138,14 +150,6 @@ class MonthlyPlanManager: finally: self.generation_running = False - def trigger_generate_monthly_plans(self, target_month: Optional[str] = None): - """ - 以非阻塞的方式启动月度计划生成任务。 - 这允许其他模块(如ScheduleManager)触发计划生成,而无需等待其完成。 - """ - logger.info(f"已触发 {target_month or '当前月份'} 的非阻塞月度计划生成任务。") - asyncio.create_task(self.generate_monthly_plans(target_month)) - def _get_previous_month(self, current_month: str) -> str: """获取上个月的月份字符串""" try: @@ -289,8 +293,6 @@ class MonthlyPlanManager: except Exception as e: logger.error(f" 归档 {target_month} 月度计划时发生错误: {e}") - - class MonthlyPlanGenerationTask(AsyncTask): """每月初自动生成新月度计划的任务""" @@ -324,7 +326,7 @@ class MonthlyPlanGenerationTask(AsyncTask): # 生成新月份的计划 current_month = next_month.strftime("%Y-%m") logger.info(f" 到达月初,开始生成 {current_month} 的月度计划...") - await self.monthly_plan_manager.generate_monthly_plans(current_month) + await self.monthly_plan_manager._generate_monthly_plans_logic(current_month) except asyncio.CancelledError: logger.info(" 每月月度计划生成任务被取消。") diff --git a/src/schedule/schedule_manager.py b/src/schedule/schedule_manager.py index f8685e161..d83841ce6 100644 --- a/src/schedule/schedule_manager.py +++ b/src/schedule/schedule_manager.py @@ -159,42 +159,36 @@ class ScheduleManager: schedule_record = session.query(Schedule).filter(Schedule.date == today_str).first() if schedule_record: logger.info(f"从数据库加载今天的日程 ({today_str})。") - - try: - schedule_data = orjson.loads(str(schedule_record.schedule_data)) - - # 使用Pydantic验证日程数据 - if self._validate_schedule_with_pydantic(schedule_data): - self.today_schedule = schedule_data - schedule_str = f"已成功加载今天的日程 ({today_str}):\n" - if self.today_schedule: - for item in self.today_schedule: - schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n" - logger.info(schedule_str) - else: - logger.warning("数据库中的日程数据格式无效,将异步重新生成日程") - await self.generate_and_save_schedule() - except orjson.JSONDecodeError as e: - logger.error(f"日程数据JSON解析失败: {e},将异步重新生成日程") - await self.generate_and_save_schedule() + schedule_data = orjson.loads(str(schedule_record.schedule_data)) + if self._validate_schedule_with_pydantic(schedule_data): + self.today_schedule = schedule_data + schedule_str = f"已成功加载今天的日程 ({today_str}):\n" + if self.today_schedule: + for item in self.today_schedule: + schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n" + logger.info(schedule_str) + return # 成功加载,直接返回 + else: + logger.warning("数据库中的日程数据格式无效,将重新生成日程") else: - logger.info(f"数据库中未找到今天的日程 ({today_str}),将异步调用 LLM 生成。") - await self.generate_and_save_schedule() + logger.info(f"数据库中未找到今天的日程 ({today_str}),将调用 LLM 生成。") + + # 仅在需要时生成 + await self.generate_and_save_schedule() + except Exception as e: logger.error(f"加载或生成日程时出错: {e}") - # 出错时也尝试异步生成 - logger.info("尝试异步生成日程作为备用方案...") + logger.info("尝试生成日程作为备用方案...") await self.generate_and_save_schedule() async def generate_and_save_schedule(self): - """启动异步日程生成任务,避免阻塞主程序""" + """启动日程生成任务""" if self.schedule_generation_running: logger.info("日程生成任务已在运行中,跳过重复启动") return - # 创建异步任务进行日程生成,不阻塞主程序 - asyncio.create_task(self._async_generate_and_save_schedule()) - logger.info("已启动异步日程生成任务") + # 直接执行日程生成 + await self._async_generate_and_save_schedule() async def _async_generate_and_save_schedule(self): """异步生成并保存日程的内部方法""" @@ -233,13 +227,15 @@ class ScheduleManager: if not sampled_plans: logger.info("可用的月度计划已耗尽或不足,触发后台补充生成...") from mmc.src.schedule.monthly_plan_manager import monthly_plan_manager - - # 以非阻塞方式触发月度计划生成 - monthly_plan_manager.trigger_generate_monthly_plans(current_month_str) - - # 注意:这里不再等待生成结果,因此后续代码不会立即获得新计划。 - # 日程将基于当前可用的信息生成,新计划将在下一次日程生成时可用。 - logger.info("月度计划的后台生成任务已启动,本次日程将不包含新计划。") + + # 等待月度计划生成完成 + await monthly_plan_manager.ensure_and_generate_plans_if_needed(current_month_str) + + # 重新获取月度计划 + sampled_plans = get_smart_plans_for_daily_schedule( + current_month_str, max_count=3, avoid_days=avoid_days + ) + logger.info("月度计划补充生成完毕,继续日程生成任务。") if sampled_plans: plan_texts = "\n".join([f"- {plan.plan_text}" for plan in sampled_plans]) @@ -471,9 +467,9 @@ class DailyScheduleGenerationTask(AsyncTask): # 2. 等待直到零点 await asyncio.sleep(sleep_seconds) - # 3. 执行异步日程生成 - logger.info("到达每日零点,开始异步生成新的一天日程...") - await self.schedule_manager.generate_and_save_schedule() + # 3. 执行日程生成 + logger.info("到达每日零点,开始生成新的一天日程...") + await self.schedule_manager._async_generate_and_save_schedule() except asyncio.CancelledError: logger.info("每日日程生成任务被取消。")