diff --git a/README.md b/README.md index b005bc18..8dfbbe43 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ MaiMBot是一个开源项目,我们非常欢迎你的参与。你的贡献, ### 💬交流群 - [五群](https://qm.qq.com/q/JxvHZnxyec) 1022489779(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 - [一群](https://qm.qq.com/q/VQ3XZrWgMs) 766798517 【已满】(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 -- [二群](https://qm.qq.com/q/RzmCiRtHEW) 571780722 【已满】(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 +- [二群](https://qm.qq.com/q/RzmCiRtHEW) 571780722(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 - [三群](https://qm.qq.com/q/wlH5eT8OmQ) 1035228475【已满】(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 - [四群](https://qm.qq.com/q/wlH5eT8OmQ) 729957033【已满】(开发和建议相关讨论)不一定有空回复,会优先写文档和代码 diff --git a/src/common/logger.py b/src/common/logger.py index 45d6f415..8a9d0892 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -122,6 +122,40 @@ SENDER_STYLE_CONFIG = { }, } +HEARTFLOW_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "麦麦大脑袋 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦大脑袋 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 麦麦大脑袋 | {message}"), # noqa: E501 + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 麦麦大脑袋 | {message}"), + }, +} + +SCHEDULE_STYLE_CONFIG = { + "advanced": { + "console_format": ( + "{time:YYYY-MM-DD HH:mm:ss} | " + "{level: <8} | " + "{extra[module]: <12} | " + "在干嘛 | " + "{message}" + ), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}"), + }, + "simple": { + "console_format": ("{time:MM-DD HH:mm} | 在干嘛 | {message}"), + "file_format": ("{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {extra[module]: <15} | 在干嘛 | {message}"), + }, +} + LLM_STYLE_CONFIG = { "advanced": { "console_format": ( @@ -183,6 +217,8 @@ SENDER_STYLE_CONFIG = SENDER_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SENDER LLM_STYLE_CONFIG = LLM_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else LLM_STYLE_CONFIG["advanced"] CHAT_STYLE_CONFIG = CHAT_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else CHAT_STYLE_CONFIG["advanced"] MOOD_STYLE_CONFIG = MOOD_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else MOOD_STYLE_CONFIG["advanced"] +SCHEDULE_STYLE_CONFIG = SCHEDULE_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else SCHEDULE_STYLE_CONFIG["advanced"] +HEARTFLOW_STYLE_CONFIG = HEARTFLOW_STYLE_CONFIG["simple"] if SIMPLE_OUTPUT else HEARTFLOW_STYLE_CONFIG["advanced"] def is_registered_module(record: dict) -> bool: """检查是否为已注册的模块""" diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index f51184a7..55b83e88 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -79,10 +79,18 @@ async def start_background_tasks(): # 只启动表情包管理任务 asyncio.create_task(emoji_manager.start_periodic_check()) - await bot_schedule.initialize() - bot_schedule.print_schedule() +@driver.on_startup +async def init_schedule(): + """在 NoneBot2 启动时初始化日程系统""" + bot_schedule.initialize( + name=global_config.BOT_NICKNAME, + personality=global_config.PROMPT_PERSONALITY, + behavior=global_config.PROMPT_SCHEDULE_GEN, + interval=global_config.SCHEDULE_DOING_UPDATE_INTERVAL) + asyncio.create_task(bot_schedule.mai_schedule_start()) + @driver.on_startup async def init_relationships(): """在 NoneBot2 启动时初始化关系管理器""" @@ -157,13 +165,13 @@ async def print_mood_task(): mood_manager.print_mood_status() -@scheduler.scheduled_job("interval", seconds=7200, id="generate_schedule") -async def generate_schedule_task(): - """每2小时尝试生成一次日程""" - logger.debug("尝试生成日程") - await bot_schedule.initialize() - if not bot_schedule.enable_output: - bot_schedule.print_schedule() +# @scheduler.scheduled_job("interval", seconds=7200, id="generate_schedule") +# async def generate_schedule_task(): +# """每2小时尝试生成一次日程""" +# logger.debug("尝试生成日程") +# await bot_schedule.initialize() +# if not bot_schedule.enable_output: +# bot_schedule.print_schedule() @scheduler.scheduled_job("interval", seconds=3600, id="remove_recalled_message") diff --git a/src/plugins/chat/config.py b/src/plugins/chat/config.py index 2d9badbc..1ac2a7ea 100644 --- a/src/plugins/chat/config.py +++ b/src/plugins/chat/config.py @@ -42,6 +42,7 @@ class BotConfig: # schedule ENABLE_SCHEDULE_GEN: bool = False # 是否启用日程生成 PROMPT_SCHEDULE_GEN = "无日程" + SCHEDULE_DOING_UPDATE_INTERVAL: int = 300 # 日程表更新间隔 单位秒 # message MAX_CONTEXT_SIZE: int = 15 # 上下文最大消息数 @@ -219,6 +220,8 @@ class BotConfig: schedule_config = parent["schedule"] config.ENABLE_SCHEDULE_GEN = schedule_config.get("enable_schedule_gen", config.ENABLE_SCHEDULE_GEN) config.PROMPT_SCHEDULE_GEN = schedule_config.get("prompt_schedule_gen", config.PROMPT_SCHEDULE_GEN) + config.SCHEDULE_DOING_UPDATE_INTERVAL = schedule_config.get( + "schedule_doing_update_interval", config.SCHEDULE_DOING_UPDATE_INTERVAL) logger.info( f"载入自定义日程prompt:{schedule_config.get('prompt_schedule_gen', config.PROMPT_SCHEDULE_GEN)}") diff --git a/src/plugins/chat/llm_generator.py b/src/plugins/chat/llm_generator.py index 556f36e2..b694e249 100644 --- a/src/plugins/chat/llm_generator.py +++ b/src/plugins/chat/llm_generator.py @@ -51,13 +51,13 @@ class ResponseGenerator: # 从global_config中获取模型概率值并选择模型 rand = random.random() if rand < global_config.MODEL_R1_PROBABILITY: - self.current_model_type = "r1" + self.current_model_type = "深深地" current_model = self.model_r1 elif rand < global_config.MODEL_R1_PROBABILITY + global_config.MODEL_V3_PROBABILITY: - self.current_model_type = "v3" + self.current_model_type = "浅浅的" current_model = self.model_v3 else: - self.current_model_type = "r1_distill" + self.current_model_type = "又浅又浅的" current_model = self.model_r1_distill logger.info(f"{global_config.BOT_NICKNAME}{self.current_model_type}思考中") diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index d79e9e7a..16c971a1 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -59,6 +59,11 @@ class Message_Sender: logger.warning(f"消息“{message.processed_plain_text}”已被撤回,不发送") break if not is_recalled: + + typing_time = calculate_typing_time(message.processed_plain_text) + logger.info(f"麦麦正在打字,预计需要{typing_time}秒") + await asyncio.sleep(typing_time) + message_json = message.to_dict() message_send = MessageSendCQ(data=message_json) message_preview = truncate_message(message.processed_plain_text) @@ -95,7 +100,7 @@ class MessageContainer: self.max_size = max_size self.messages = [] self.last_send_time = 0 - self.thinking_timeout = 20 # 思考超时时间(秒) + self.thinking_timeout = 10 # 思考超时时间(秒) def get_timeout_messages(self) -> List[MessageSending]: """获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序""" @@ -204,7 +209,7 @@ class MessageManager: # print(thinking_time) if ( message_earliest.is_head - and message_earliest.update_thinking_time() > 15 + and message_earliest.update_thinking_time() > 20 and not message_earliest.is_private_message() # 避免在私聊时插入reply ): logger.debug(f"设置回复消息{message_earliest.processed_plain_text}") @@ -231,7 +236,7 @@ class MessageManager: # print(msg.is_private_message()) if ( msg.is_head - and msg.update_thinking_time() > 15 + and msg.update_thinking_time() > 25 and not msg.is_private_message() # 避免在私聊时插入reply ): logger.debug(f"设置回复消息{msg.processed_plain_text}") diff --git a/src/plugins/chat/prompt_builder.py b/src/plugins/chat/prompt_builder.py index 063e1edb..8f8c6c3a 100644 --- a/src/plugins/chat/prompt_builder.py +++ b/src/plugins/chat/prompt_builder.py @@ -57,9 +57,7 @@ class PromptBuilder: mood_prompt = mood_manager.get_prompt() # 日程构建 - # current_date = time.strftime("%Y-%m-%d", time.localtime()) - # current_time = time.strftime("%H:%M:%S", time.localtime()) - # bot_schedule_now_time, bot_schedule_now_activity = bot_schedule.get_current_task() + # schedule_prompt = f'''你现在正在做的事情是:{bot_schedule.get_current_num_task(num = 1,time_info = False)}''' # 获取聊天上下文 chat_in_group = True @@ -172,6 +170,8 @@ class PromptBuilder: {moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。""" prompt_check_if_response = "" + + return prompt, prompt_check_if_response def _build_initiative_prompt_select(self, group_id, probability_1=0.8, probability_2=0.1): diff --git a/src/plugins/schedule/offline_llm.py b/src/plugins/schedule/offline_llm.py index e4dc23f9..5276f380 100644 --- a/src/plugins/schedule/offline_llm.py +++ b/src/plugins/schedule/offline_llm.py @@ -1,10 +1,7 @@ import asyncio import os -import time -from typing import Tuple, Union import aiohttp -import requests from src.common.logger import get_module_logger logger = get_module_logger("offline_llm") @@ -22,57 +19,7 @@ class LLMModel: logger.info(f"API URL: {self.base_url}") # 使用 logger 记录 base_url - def generate_response(self, prompt: str) -> Union[str, Tuple[str, str]]: - """根据输入的提示生成模型的响应""" - headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} - - # 构建请求体 - data = { - "model": self.model_name, - "messages": [{"role": "user", "content": prompt}], - "temperature": 0.5, - **self.params, - } - - # 发送请求到完整的 chat/completions 端点 - api_url = f"{self.base_url.rstrip('/')}/chat/completions" - logger.info(f"Request URL: {api_url}") # 记录请求的 URL - - max_retries = 3 - base_wait_time = 15 # 基础等待时间(秒) - - for retry in range(max_retries): - try: - response = requests.post(api_url, headers=headers, json=data) - - if response.status_code == 429: - wait_time = base_wait_time * (2**retry) # 指数退避 - logger.warning(f"遇到请求限制(429),等待{wait_time}秒后重试...") - time.sleep(wait_time) - continue - - response.raise_for_status() # 检查其他响应状态 - - result = response.json() - if "choices" in result and len(result["choices"]) > 0: - content = result["choices"][0]["message"]["content"] - reasoning_content = result["choices"][0]["message"].get("reasoning_content", "") - return content, reasoning_content - return "没有返回结果", "" - - except Exception as e: - if retry < max_retries - 1: # 如果还有重试机会 - wait_time = base_wait_time * (2**retry) - logger.error(f"[回复]请求失败,等待{wait_time}秒后重试... 错误: {str(e)}") - time.sleep(wait_time) - else: - logger.error(f"请求失败: {str(e)}") - return f"请求失败: {str(e)}", "" - - logger.error("达到最大重试次数,请求仍然失败") - return "达到最大重试次数,请求仍然失败", "" - - async def generate_response_async(self, prompt: str) -> Union[str, Tuple[str, str]]: + async def generate_response_async(self, prompt: str) -> str: """异步方式根据输入的提示生成模型的响应""" headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"} @@ -80,7 +27,7 @@ class LLMModel: data = { "model": self.model_name, "messages": [{"role": "user", "content": prompt}], - "temperature": 0.5, + "temperature": 0.7, **self.params, } diff --git a/src/plugins/schedule/schedule_generator.py b/src/plugins/schedule/schedule_generator.py index b26b2954..41cf187e 100644 --- a/src/plugins/schedule/schedule_generator.py +++ b/src/plugins/schedule/schedule_generator.py @@ -1,159 +1,154 @@ import datetime -import json -import re -from typing import Dict, Union - -from nonebot import get_driver - +import os +import sys +from typing import Dict +import asyncio # 添加项目根目录到 Python 路径 +root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) +sys.path.append(root_path) -from src.plugins.chat.config import global_config -from ...common.database import db # 使用正确的导入语法 -from ..models.utils_model import LLM_request -from src.common.logger import get_module_logger +from src.common.database import db # noqa: E402 +from src.common.logger import get_module_logger, SCHEDULE_STYLE_CONFIG, LogConfig # noqa: E402 +from src.plugins.models.utils_model import LLM_request # noqa: E402 +from src.plugins.chat.config import global_config # noqa: E402 -logger = get_module_logger("scheduler") -driver = get_driver() -config = driver.config +schedule_config = LogConfig( + # 使用海马体专用样式 + console_format=SCHEDULE_STYLE_CONFIG["console_format"], + file_format=SCHEDULE_STYLE_CONFIG["file_format"], +) +logger = get_module_logger("scheduler", config=schedule_config) class ScheduleGenerator: - enable_output: bool = True + # enable_output: bool = True def __init__(self): - # 根据global_config.llm_normal这一字典配置指定模型 - # self.llm_scheduler = LLMModel(model = global_config.llm_normal,temperature=0.9) - self.llm_scheduler = LLM_request(model=global_config.llm_normal, temperature=0.9, request_type="scheduler") + # 使用离线LLM模型 + self.llm_scheduler_all = LLM_request( + model= global_config.llm_reasoning, temperature=0.9, max_tokens=7000,request_type="schedule") + self.llm_scheduler_doing = LLM_request( + model= global_config.llm_normal, temperature=0.9, max_tokens=2048,request_type="schedule") + self.today_schedule_text = "" - self.today_schedule = {} - self.tomorrow_schedule_text = "" - self.tomorrow_schedule = {} + self.today_done_list = [] + self.yesterday_schedule_text = "" - self.yesterday_schedule = {} + self.yesterday_done_list = [] - async def initialize(self): + self.name = "" + self.personality = "" + self.behavior = "" + + self.start_time = datetime.datetime.now() + + self.schedule_doing_update_interval = 300 #最好大于60 + + def initialize( + self,name: str = "bot_name", + personality: str = "你是一个爱国爱党的新时代青年", + behavior: str = "你非常外向,喜欢尝试新事物和人交流", + interval: int = 60): + """初始化日程系统""" + self.name = name + self.behavior = behavior + self.schedule_doing_update_interval = interval + + for pers in personality: + self.personality += pers + "\n" + + + async def mai_schedule_start(self): + """启动日程系统,每5分钟执行一次move_doing,并在日期变化时重新检查日程""" + try: + logger.info(f"日程系统启动/刷新时间: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}") + # 初始化日程 + await self.check_and_create_today_schedule() + self.print_schedule() + + while True: + print(self.get_current_num_task(1, True)) + + current_time = datetime.datetime.now() + + # 检查是否需要重新生成日程(日期变化) + if current_time.date() != self.start_time.date(): + logger.info("检测到日期变化,重新生成日程") + self.start_time = current_time + await self.check_and_create_today_schedule() + self.print_schedule() + + # 执行当前活动 + # mind_thinking = subheartflow_manager.current_state.current_mind + + await self.move_doing() + + await asyncio.sleep(self.schedule_doing_update_interval) + + except Exception as e: + logger.error(f"日程系统运行时出错: {str(e)}") + logger.exception("详细错误信息:") + + async def check_and_create_today_schedule(self): + """检查昨天的日程,并确保今天有日程安排 + + Returns: + tuple: (today_schedule_text, today_schedule) 今天的日程文本和解析后的日程字典 + """ today = datetime.datetime.now() - tomorrow = datetime.datetime.now() + datetime.timedelta(days=1) - yesterday = datetime.datetime.now() - datetime.timedelta(days=1) + yesterday = today - datetime.timedelta(days=1) + + # 先检查昨天的日程 + self.yesterday_schedule_text, self.yesterday_done_list = self.load_schedule_from_db(yesterday) + if self.yesterday_schedule_text: + logger.debug(f"已加载{yesterday.strftime('%Y-%m-%d')}的日程") + + # 检查今天的日程 + self.today_schedule_text, self.today_done_list = self.load_schedule_from_db(today) + if not self.today_done_list: + self.today_done_list = [] + if not self.today_schedule_text: + logger.info(f"{today.strftime('%Y-%m-%d')}的日程不存在,准备生成新的日程") + self.today_schedule_text = await self.generate_daily_schedule(target_date=today) - self.today_schedule_text, self.today_schedule = await self.generate_daily_schedule(target_date=today) - self.tomorrow_schedule_text, self.tomorrow_schedule = await self.generate_daily_schedule( - target_date=tomorrow, read_only=True - ) - self.yesterday_schedule_text, self.yesterday_schedule = await self.generate_daily_schedule( - target_date=yesterday, read_only=True - ) - - async def generate_daily_schedule( - self, target_date: datetime.datetime = None, read_only: bool = False - ) -> Dict[str, str]: + self.save_today_schedule_to_db() + + def construct_daytime_prompt(self, target_date: datetime.datetime): date_str = target_date.strftime("%Y-%m-%d") weekday = target_date.strftime("%A") - schedule_text = str - - existing_schedule = db.schedule.find_one({"date": date_str}) - if existing_schedule: - if self.enable_output: - logger.debug(f"{date_str}的日程已存在:") - schedule_text = existing_schedule["schedule"] - # print(self.schedule_text) - - elif not read_only: - logger.debug(f"{date_str}的日程不存在,准备生成新的日程。") - prompt = ( - f"""我是{global_config.BOT_NICKNAME},{global_config.PROMPT_SCHEDULE_GEN},请为我生成{date_str}({weekday})的日程安排,包括:""" - + """ - 1. 早上的学习和工作安排 - 2. 下午的活动和任务 - 3. 晚上的计划和休息时间 - 请按照时间顺序列出具体时间点和对应的活动,用一个时间点而不是时间段来表示时间,用JSON格式返回日程表, - 仅返回内容,不要返回注释,不要添加任何markdown或代码块样式,时间采用24小时制, - 格式为{"时间": "活动","时间": "活动",...}。""" - ) - - try: - schedule_text, _, _ = await self.llm_scheduler.generate_response(prompt) - db.schedule.insert_one({"date": date_str, "schedule": schedule_text}) - self.enable_output = True - except Exception as e: - logger.error(f"生成日程失败: {str(e)}") - schedule_text = "生成日程时出错了" - # print(self.schedule_text) + prompt = f"你是{self.name},{self.personality},{self.behavior}" + prompt += f"你昨天的日程是:{self.yesterday_schedule_text}\n" + prompt += f"请为你生成{date_str}({weekday})的日程安排,结合你的个人特点和行为习惯\n" + prompt += "推测你的日程安排,包括你一天都在做什么,从起床到睡眠,有什么发现和思考,具体一些,详细一些,需要1500字以上,精确到每半个小时,记得写明时间\n" #noqa: E501 + prompt += "直接返回你的日程,从起床到睡觉,不要输出其他内容:" + return prompt + + def construct_doing_prompt(self,time: datetime.datetime,mind_thinking: str = ""): + now_time = time.strftime("%H:%M") + if self.today_done_list: + previous_doings = self.get_current_num_task(5, True) + # print(previous_doings) else: - if self.enable_output: - logger.debug(f"{date_str}的日程不存在。") - schedule_text = "忘了" - - return schedule_text, None - - schedule_form = self._parse_schedule(schedule_text) - return schedule_text, schedule_form - - def _parse_schedule(self, schedule_text: str) -> Union[bool, Dict[str, str]]: - """解析日程文本,转换为时间和活动的字典""" - try: - reg = r"\{(.|\r|\n)+\}" - matched = re.search(reg, schedule_text)[0] - schedule_dict = json.loads(matched) - self._check_schedule_validity(schedule_dict) - return schedule_dict - except json.JSONDecodeError: - logger.exception("解析日程失败: {}".format(schedule_text)) - return False - except ValueError as e: - logger.exception(f"解析日程失败: {str(e)}") - return False - except Exception as e: - logger.exception(f"解析日程发生错误:{str(e)}") - return False - - def _check_schedule_validity(self, schedule_dict: Dict[str, str]): - """检查日程是否合法""" - if not schedule_dict: - return - for time_str in schedule_dict.keys(): - try: - self._parse_time(time_str) - except ValueError: - raise ValueError("日程时间格式不正确") from None - - def _parse_time(self, time_str: str) -> str: - """解析时间字符串,转换为时间""" - return datetime.datetime.strptime(time_str, "%H:%M") - - def get_current_task(self) -> str: - """获取当前时间应该进行的任务""" - current_time = datetime.datetime.now().strftime("%H:%M") - - # 找到最接近当前时间的任务 - closest_time = None - min_diff = float("inf") - - # 检查今天的日程 - if not self.today_schedule: - return "摸鱼" - for time_str in self.today_schedule.keys(): - diff = abs(self._time_diff(current_time, time_str)) - if closest_time is None or diff < min_diff: - closest_time = time_str - min_diff = diff - - # 检查昨天的日程中的晚间任务 - if self.yesterday_schedule: - for time_str in self.yesterday_schedule.keys(): - if time_str >= "20:00": # 只考虑晚上8点之后的任务 - # 计算与昨天这个时间点的差异(需要加24小时) - diff = abs(self._time_diff(current_time, time_str)) - if diff < min_diff: - closest_time = time_str - min_diff = diff - return closest_time, self.yesterday_schedule[closest_time] - - if closest_time: - return closest_time, self.today_schedule[closest_time] - return "摸鱼" + previous_doings = "你没做什么事情" + + + prompt = f"你是{self.name},{self.personality},{self.behavior}" + prompt += f"你今天的日程是:{self.today_schedule_text}\n" + prompt += f"你之前做了的事情是:{previous_doings},从之前到现在已经过去了{self.schedule_doing_update_interval/60}分钟了\n" #noqa: E501 + if mind_thinking: + prompt += f"你脑子里在想:{mind_thinking}\n" + prompt += f"现在是{now_time},结合你的个人特点和行为习惯," + prompt += "推测你现在做什么,具体一些,详细一些\n" + prompt += "直接返回你在做的事情,不要输出其他内容:" + return prompt + + async def generate_daily_schedule( + self, target_date: datetime.datetime = None,) -> Dict[str, str]: + daytime_prompt = self.construct_daytime_prompt(target_date) + daytime_response,_ = await self.llm_scheduler_all.generate_response_async(daytime_prompt) + return daytime_response def _time_diff(self, time1: str, time2: str) -> int: """计算两个时间字符串之间的分钟差""" @@ -174,14 +169,138 @@ class ScheduleGenerator: def print_schedule(self): """打印完整的日程安排""" - if not self._parse_schedule(self.today_schedule_text): - logger.warning("今日日程有误,将在两小时后重新生成") + if not self.today_schedule_text: + logger.warning("今日日程有误,将在下次运行时重新生成") db.schedule.delete_one({"date": datetime.datetime.now().strftime("%Y-%m-%d")}) else: logger.info("=== 今日日程安排 ===") - for time_str, activity in self.today_schedule.items(): - logger.info(f"时间[{time_str}]: 活动[{activity}]") + logger.info(self.today_schedule_text) logger.info("==================") self.enable_output = False + + async def update_today_done_list(self): + # 更新数据库中的 today_done_list + today_str = datetime.datetime.now().strftime("%Y-%m-%d") + existing_schedule = db.schedule.find_one({"date": today_str}) + + if existing_schedule: + # 更新数据库中的 today_done_list + db.schedule.update_one( + {"date": today_str}, + {"$set": {"today_done_list": self.today_done_list}} + ) + logger.debug(f"已更新{today_str}的已完成活动列表") + else: + logger.warning(f"未找到{today_str}的日程记录") + + async def move_doing(self,mind_thinking: str = ""): + current_time = datetime.datetime.now() + if mind_thinking: + doing_prompt = self.construct_doing_prompt(current_time,mind_thinking) + else: + doing_prompt = self.construct_doing_prompt(current_time) + + # print(doing_prompt) + doing_response,_ = await self.llm_scheduler_doing.generate_response_async(doing_prompt) + self.today_done_list.append((current_time,doing_response)) + + await self.update_today_done_list() + + logger.info(f"当前活动: {doing_response}") + + return doing_response + + async def get_task_from_time_to_time(self, start_time: str, end_time: str): + """获取指定时间范围内的任务列表 + + Args: + start_time (str): 开始时间,格式为"HH:MM" + end_time (str): 结束时间,格式为"HH:MM" + + Returns: + list: 时间范围内的任务列表 + """ + result = [] + for task in self.today_done_list: + task_time = task[0] # 获取任务的时间戳 + task_time_str = task_time.strftime("%H:%M") + + # 检查任务时间是否在指定范围内 + if self._time_diff(start_time, task_time_str) >= 0 and self._time_diff(task_time_str, end_time) >= 0: + result.append(task) + + return result + + def get_current_num_task(self, num=1, time_info = False): + """获取最新加入的指定数量的日程 + + Args: + num (int): 需要获取的日程数量,默认为1 + + Returns: + list: 最新加入的日程列表 + """ + if not self.today_done_list: + return [] + + # 确保num不超过列表长度 + num = min(num, len(self.today_done_list)) + pre_doings = "" + for doing in self.today_done_list[-num:]: + + if time_info: + time_str = doing[0].strftime("%H:%M") + pre_doings += time_str + "时," + doing[1] + "\n" + else: + pre_doings += doing[1] + "\n" + + # 返回最新的num条日程 + return pre_doings + + def save_today_schedule_to_db(self): + """保存日程到数据库,同时初始化 today_done_list""" + date_str = datetime.datetime.now().strftime("%Y-%m-%d") + schedule_data = { + "date": date_str, + "schedule": self.today_schedule_text, + "today_done_list": self.today_done_list if hasattr(self, 'today_done_list') else [] + } + # 使用 upsert 操作,如果存在则更新,不存在则插入 + db.schedule.update_one( + {"date": date_str}, + {"$set": schedule_data}, + upsert=True + ) + logger.debug(f"已保存{date_str}的日程到数据库") + + def load_schedule_from_db(self, date: datetime.datetime): + """从数据库加载日程,同时加载 today_done_list""" + date_str = date.strftime("%Y-%m-%d") + existing_schedule = db.schedule.find_one({"date": date_str}) + + if existing_schedule: + schedule_text = existing_schedule["schedule"] + return schedule_text, existing_schedule.get("today_done_list", []) + else: + logger.debug(f"{date_str}的日程不存在") + return None, None + +async def main(): + # 使用示例 + scheduler = ScheduleGenerator() + scheduler.initialize( + name="麦麦", + personality="你叫麦麦,你19岁,是一个大二的女大学生,你有一头黑色短发,你会刷贴吧,你现在在学习心理学", + behavior="你比较内向,一般熬夜比较晚,然后第二天早上10点起床吃早午饭", + interval=60) + await scheduler.mai_schedule_start() + + + # 当作为组件导入时使用的实例 bot_schedule = ScheduleGenerator() + +if __name__ == "__main__": + import asyncio + # 当直接运行此文件时执行 + asyncio.run(main()) diff --git a/src/plugins/schedule/schedule_generator_pro.py b/src/plugins/schedule/schedule_generator_pro.py deleted file mode 100644 index 5a2c2a68..00000000 --- a/src/plugins/schedule/schedule_generator_pro.py +++ /dev/null @@ -1,222 +0,0 @@ -import datetime -import json -import re -import os -import sys -from typing import Dict, Union -# 添加项目根目录到 Python 路径 -root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) -sys.path.append(root_path) - -from src.common.database import db # noqa: E402 -from src.common.logger import get_module_logger # noqa: E402 -from src.plugins.schedule.offline_llm import LLMModel # noqa: E402 - -logger = get_module_logger("scheduler") - - -class ScheduleGenerator: - enable_output: bool = True - - def __init__(self, name: str = "bot_name", personality: str = "你是一个爱国爱党的新时代青年", behavior: str = "你非常外向,喜欢尝试新事物和人交流"): - # 使用离线LLM模型 - self.llm_scheduler = LLMModel(model_name="Pro/deepseek-ai/DeepSeek-V3", temperature=0.9) - - self.today_schedule_text = "" - self.today_done_list = [] - - self.yesterday_schedule_text = "" - self.yesterday_done_list = [] - - self.name = name - self.personality = personality - self.behavior = behavior - - self.start_time = datetime.datetime.now() - - async def mai_schedule_start(self): - """启动日程系统,每5分钟执行一次move_doing,并在日期变化时重新检查日程""" - try: - logger.info(f"日程系统启动/刷新时间: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}") - # 初始化日程 - await self.check_and_create_today_schedule() - self.print_schedule() - - while True: - current_time = datetime.datetime.now() - - # 检查是否需要重新生成日程(日期变化) - if current_time.date() != self.start_time.date(): - logger.info("检测到日期变化,重新生成日程") - self.start_time = current_time - await self.check_and_create_today_schedule() - self.print_schedule() - - # 执行当前活动 - current_activity = await self.move_doing() - logger.info(f"当前活动: {current_activity}") - - # 等待5分钟 - await asyncio.sleep(300) # 300秒 = 5分钟 - - except Exception as e: - logger.error(f"日程系统运行时出错: {str(e)}") - logger.exception("详细错误信息:") - - async def check_and_create_today_schedule(self): - """检查昨天的日程,并确保今天有日程安排 - - Returns: - tuple: (today_schedule_text, today_schedule) 今天的日程文本和解析后的日程字典 - """ - today = datetime.datetime.now() - yesterday = today - datetime.timedelta(days=1) - - # 先检查昨天的日程 - self.yesterday_schedule_text, self.yesterday_done_list = self.load_schedule_from_db(yesterday) - if self.yesterday_schedule_text: - logger.debug(f"已加载{yesterday.strftime('%Y-%m-%d')}的日程") - - # 检查今天的日程 - self.today_schedule_text, self.today_done_list = self.load_schedule_from_db(today) - if not self.today_schedule_text: - logger.info(f"{today.strftime('%Y-%m-%d')}的日程不存在,准备生成新的日程") - self.today_schedule_text = await self.generate_daily_schedule(target_date=today) - - self.save_today_schedule_to_db() - - def construct_daytime_prompt(self, target_date: datetime.datetime): - date_str = target_date.strftime("%Y-%m-%d") - weekday = target_date.strftime("%A") - - prompt = f"我是{self.name},{self.personality},{self.behavior}" - prompt += f"我昨天的日程是:{self.yesterday_schedule_text}\n" - prompt += f"请为我生成{date_str}({weekday})的日程安排,结合我的个人特点和行为习惯\n" - prompt += "推测我的日程安排,包括我一天都在做什么,有什么发现和思考,具体一些,详细一些,记得写明时间\n" - prompt += "直接返回我的日程,不要输出其他内容:" - return prompt - - def construct_doing_prompt(self,time: datetime.datetime): - now_time = time.strftime("%H:%M") - previous_doing = self.today_done_list[-20:] if len(self.today_done_list) > 20 else self.today_done_list - prompt = f"我是{self.name},{self.personality},{self.behavior}" - prompt += f"我今天的日程是:{self.today_schedule_text}\n" - prompt += f"我之前做了的事情是:{previous_doing}\n" - prompt += f"现在是{now_time},结合我的个人特点和行为习惯," - prompt += "推测我现在做什么,具体一些,详细一些\n" - prompt += "直接返回我在做的事情,不要输出其他内容:" - return prompt - - async def generate_daily_schedule( - self, target_date: datetime.datetime = None,) -> Dict[str, str]: - daytime_prompt = self.construct_daytime_prompt(target_date) - daytime_response, _ = await self.llm_scheduler.generate_response(daytime_prompt) - return daytime_response - - def _time_diff(self, time1: str, time2: str) -> int: - """计算两个时间字符串之间的分钟差""" - if time1 == "24:00": - time1 = "23:59" - if time2 == "24:00": - time2 = "23:59" - t1 = datetime.datetime.strptime(time1, "%H:%M") - t2 = datetime.datetime.strptime(time2, "%H:%M") - diff = int((t2 - t1).total_seconds() / 60) - # 考虑时间的循环性 - if diff < -720: - diff += 1440 # 加一天的分钟 - elif diff > 720: - diff -= 1440 # 减一天的分钟 - # print(f"时间1[{time1}]: 时间2[{time2}],差值[{diff}]分钟") - return diff - - def print_schedule(self): - """打印完整的日程安排""" - if not self.today_schedule_text: - logger.warning("今日日程有误,将在下次运行时重新生成") - db.schedule.delete_one({"date": datetime.datetime.now().strftime("%Y-%m-%d")}) - else: - logger.info("=== 今日日程安排 ===") - logger.info(self.today_schedule_text) - logger.info("==================") - self.enable_output = False - - async def update_today_done_list(self): - # 更新数据库中的 today_done_list - today_str = datetime.datetime.now().strftime("%Y-%m-%d") - existing_schedule = db.schedule.find_one({"date": today_str}) - - if existing_schedule: - # 更新数据库中的 today_done_list - db.schedule.update_one( - {"date": today_str}, - {"$set": {"today_done_list": self.today_done_list}} - ) - logger.debug(f"已更新{today_str}的已完成活动列表") - else: - logger.warning(f"未找到{today_str}的日程记录") - - async def move_doing(self): - current_time = datetime.datetime.now() - time_str = current_time.strftime("%H:%M") - doing_prompt = self.construct_doing_prompt(current_time) - doing_response, _ = await self.llm_scheduler.generate_response(doing_prompt) - self.today_done_list.append(current_time,time_str + "在" + doing_response) - - await self.update_today_done_list() - - return doing_response - - - - - def save_today_schedule_to_db(self): - """保存日程到数据库,同时初始化 today_done_list""" - date_str = datetime.datetime.now().strftime("%Y-%m-%d") - schedule_data = { - "date": date_str, - "schedule": self.today_schedule_text, - "today_done_list": self.today_done_list if hasattr(self, 'today_done_list') else [] - } - # 使用 upsert 操作,如果存在则更新,不存在则插入 - db.schedule.update_one( - {"date": date_str}, - {"$set": schedule_data}, - upsert=True - ) - logger.debug(f"已保存{date_str}的日程到数据库") - - def load_schedule_from_db(self, date: datetime.datetime): - """从数据库加载日程,同时加载 today_done_list""" - date_str = date.strftime("%Y-%m-%d") - existing_schedule = db.schedule.find_one({"date": date_str}) - - if existing_schedule: - schedule_text = existing_schedule["schedule"] - return schedule_text, existing_schedule.get("today_done_list", []) - else: - logger.debug(f"{date_str}的日程不存在") - return None, None - -async def main(): - # 使用示例 - scheduler = ScheduleGenerator(name="麦麦", personality="你叫麦麦,你19岁,是一个大二的女大学生,你有一头黑色短发,你会刷贴吧,你现在在学习心理学", behavior="你比较内向") - await scheduler.check_and_create_today_schedule() - scheduler.print_schedule() - print("\n当前任务:") - print(await scheduler.get_current_task()) - - print("昨天日程:") - print(scheduler.yesterday_schedule) - print("今天日程:") - print(scheduler.today_schedule) - print("明天日程:") - print(scheduler.tomorrow_schedule) - -# 当作为组件导入时使用的实例 -bot_schedule = ScheduleGenerator() - -if __name__ == "__main__": - import asyncio - # 当直接运行此文件时执行 - asyncio.run(main()) diff --git a/src/plugins/utils/statistic.py b/src/plugins/utils/statistic.py index f03067cb..aad33e88 100644 --- a/src/plugins/utils/statistic.py +++ b/src/plugins/utils/statistic.py @@ -20,6 +20,13 @@ class LLMStatistics: self.output_file = output_file self.running = False self.stats_thread = None + self._init_database() + + def _init_database(self): + """初始化数据库集合""" + if "online_time" not in db.list_collection_names(): + db.create_collection("online_time") + db.online_time.create_index([("timestamp", 1)]) def start(self): """启动统计线程""" @@ -35,6 +42,16 @@ class LLMStatistics: if self.stats_thread: self.stats_thread.join() + def _record_online_time(self): + """记录在线时间""" + try: + db.online_time.insert_one({ + "timestamp": datetime.now(), + "duration": 5 # 5分钟 + }) + except Exception: + logger.exception("记录在线时间失败") + def _collect_statistics_for_period(self, start_time: datetime) -> Dict[str, Any]: """收集指定时间段的LLM请求统计数据 @@ -56,10 +73,11 @@ class LLMStatistics: "tokens_by_type": defaultdict(int), "tokens_by_user": defaultdict(int), "tokens_by_model": defaultdict(int), + # 新增在线时间统计 + "online_time_minutes": 0, } cursor = db.llm_usage.find({"timestamp": {"$gte": start_time}}) - total_requests = 0 for doc in cursor: @@ -74,7 +92,7 @@ class LLMStatistics: prompt_tokens = doc.get("prompt_tokens", 0) completion_tokens = doc.get("completion_tokens", 0) - total_tokens = prompt_tokens + completion_tokens # 根据数据库字段调整 + total_tokens = prompt_tokens + completion_tokens stats["tokens_by_type"][request_type] += total_tokens stats["tokens_by_user"][user_id] += total_tokens stats["tokens_by_model"][model_name] += total_tokens @@ -91,6 +109,11 @@ class LLMStatistics: if total_requests > 0: stats["average_tokens"] = stats["total_tokens"] / total_requests + # 统计在线时间 + online_time_cursor = db.online_time.find({"timestamp": {"$gte": start_time}}) + for doc in online_time_cursor: + stats["online_time_minutes"] += doc.get("duration", 0) + return stats def _collect_all_statistics(self) -> Dict[str, Dict[str, Any]]: @@ -115,7 +138,8 @@ class LLMStatistics: output.append(f"总请求数: {stats['total_requests']}") if stats["total_requests"] > 0: output.append(f"总Token数: {stats['total_tokens']}") - output.append(f"总花费: {stats['total_cost']:.4f}¥\n") + output.append(f"总花费: {stats['total_cost']:.4f}¥") + output.append(f"在线时间: {stats['online_time_minutes']}分钟\n") data_fmt = "{:<32} {:>10} {:>14} {:>13.4f} ¥" @@ -184,13 +208,16 @@ class LLMStatistics: """统计循环,每1分钟运行一次""" while self.running: try: + # 记录在线时间 + self._record_online_time() + # 收集并保存统计数据 all_stats = self._collect_all_statistics() self._save_statistics(all_stats) except Exception: logger.exception("统计数据处理失败") - # 等待1分钟 - for _ in range(60): + # 等待5分钟 + for _ in range(300): # 5分钟 = 300秒 if not self.running: break time.sleep(1) diff --git a/src/think_flow_demo/current_mind.py b/src/think_flow_demo/current_mind.py index 6facdbf9..32d77ef7 100644 --- a/src/think_flow_demo/current_mind.py +++ b/src/think_flow_demo/current_mind.py @@ -2,9 +2,11 @@ from .outer_world import outer_world import asyncio from src.plugins.moods.moods import MoodManager from src.plugins.models.utils_model import LLM_request -from src.plugins.chat.config import global_config +from src.plugins.chat.config import global_config, BotConfig import re import time +from src.plugins.schedule.schedule_generator import bot_schedule + class CuttentState: def __init__(self): self.willing = 0 @@ -34,6 +36,8 @@ class SubHeartflow: if not self.current_mind: self.current_mind = "你什么也没想" + + self.personality_info = " ".join(BotConfig.PROMPT_PERSONALITY) def assign_observe(self,stream_id): self.outer_world = outer_world.get_world_by_stream_id(stream_id) @@ -44,25 +48,26 @@ class SubHeartflow: current_time = time.time() if current_time - self.last_reply_time > 180: # 3分钟 = 180秒 # print(f"{self.observe_chat_id}麦麦已经3分钟没有回复了,暂时停止思考") - await asyncio.sleep(25) # 每30秒检查一次 + await asyncio.sleep(60) # 每30秒检查一次 else: await self.do_a_thinking() await self.judge_willing() - await asyncio.sleep(25) + await asyncio.sleep(60) async def do_a_thinking(self): print("麦麦小脑袋转起来了") self.current_state.update_current_state_info() - personality_info = open("src/think_flow_demo/personality_info.txt", "r", encoding="utf-8").read() current_thinking_info = self.current_mind mood_info = self.current_state.mood - related_memory_info = 'memory' + related_memory_info = '' message_stream_info = self.outer_world.talking_summary + schedule_info = bot_schedule.get_current_num_task(num = 2,time_info = False) prompt = "" + prompt += f"你刚刚在做的事情是:{schedule_info}\n" # prompt += f"麦麦的总体想法是:{self.main_heartflow_info}\n\n" - prompt += f"{personality_info}\n" + prompt += f"{self.personality_info}\n" prompt += f"现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{message_stream_info}\n" prompt += f"你想起来{related_memory_info}。" prompt += f"刚刚你的想法是{current_thinking_info}。" @@ -80,7 +85,6 @@ class SubHeartflow: # print("麦麦脑袋转起来了") self.current_state.update_current_state_info() - personality_info = open("src/think_flow_demo/personality_info.txt", "r", encoding="utf-8").read() current_thinking_info = self.current_mind mood_info = self.current_state.mood related_memory_info = 'memory' @@ -89,7 +93,7 @@ class SubHeartflow: reply_info = reply_content prompt = "" - prompt += f"{personality_info}\n" + prompt += f"{self.personality_info}\n" prompt += f"现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{message_stream_info}\n" prompt += f"你想起来{related_memory_info}。" prompt += f"刚刚你的想法是{current_thinking_info}。" @@ -110,12 +114,11 @@ class SubHeartflow: async def judge_willing(self): # print("麦麦闹情绪了1") - personality_info = open("src/think_flow_demo/personality_info.txt", "r", encoding="utf-8").read() current_thinking_info = self.current_mind mood_info = self.current_state.mood # print("麦麦闹情绪了2") prompt = "" - prompt += f"{personality_info}\n" + prompt += f"{self.personality_info}\n" prompt += "现在你正在上网,和qq群里的网友们聊天" prompt += f"你现在的想法是{current_thinking_info}。" prompt += f"你现在{mood_info}。" diff --git a/src/think_flow_demo/heartflow.py b/src/think_flow_demo/heartflow.py index 45843e49..dcdbe508 100644 --- a/src/think_flow_demo/heartflow.py +++ b/src/think_flow_demo/heartflow.py @@ -1,8 +1,17 @@ from .current_mind import SubHeartflow from src.plugins.moods.moods import MoodManager from src.plugins.models.utils_model import LLM_request -from src.plugins.chat.config import global_config +from src.plugins.chat.config import global_config, BotConfig +from src.plugins.schedule.schedule_generator import bot_schedule import asyncio +from src.common.logger import get_module_logger, LogConfig, HEARTFLOW_STYLE_CONFIG # noqa: E402 + +heartflow_config = LogConfig( + # 使用海马体专用样式 + console_format=HEARTFLOW_STYLE_CONFIG["console_format"], + file_format=HEARTFLOW_STYLE_CONFIG["file_format"], +) +logger = get_module_logger("heartflow", config=heartflow_config) class CuttentState: def __init__(self): @@ -30,22 +39,24 @@ class Heartflow: async def heartflow_start_working(self): while True: - # await self.do_a_thinking() - await asyncio.sleep(60) + await self.do_a_thinking() + await asyncio.sleep(600) async def do_a_thinking(self): - print("麦麦大脑袋转起来了") + logger.info("麦麦大脑袋转起来了") self.current_state.update_current_state_info() - personality_info = open("src/think_flow_demo/personality_info.txt", "r", encoding="utf-8").read() + personality_info = " ".join(BotConfig.PROMPT_PERSONALITY) current_thinking_info = self.current_mind mood_info = self.current_state.mood related_memory_info = 'memory' sub_flows_info = await self.get_all_subheartflows_minds() + schedule_info = bot_schedule.get_current_num_task(num = 5,time_info = True) + prompt = "" + prompt += f"你刚刚在做的事情是:{schedule_info}\n" prompt += f"{personality_info}\n" - # prompt += f"现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{message_stream_info}\n" prompt += f"你想起来{related_memory_info}。" prompt += f"刚刚你的主要想法是{current_thinking_info}。" prompt += f"你还有一些小想法,因为你在参加不同的群聊天,是你正在做的事情:{sub_flows_info}\n" @@ -58,7 +69,10 @@ class Heartflow: self.update_current_mind(reponse) self.current_mind = reponse - print(f"麦麦的总体脑内状态:{self.current_mind}") + logger.info(f"麦麦的总体脑内状态:{self.current_mind}") + logger.info("麦麦想了想,当前活动:") + await bot_schedule.move_doing(self.current_mind) + for _, subheartflow in self._subheartflows.items(): subheartflow.main_heartflow_info = reponse @@ -77,13 +91,13 @@ class Heartflow: return await self.minds_summary(sub_minds) async def minds_summary(self,minds_str): - personality_info = open("src/think_flow_demo/personality_info.txt", "r", encoding="utf-8").read() + personality_info = " ".join(BotConfig.PROMPT_PERSONALITY) mood_info = self.current_state.mood prompt = "" prompt += f"{personality_info}\n" prompt += f"现在{global_config.BOT_NICKNAME}的想法是:{self.current_mind}\n" - prompt += f"现在麦麦在qq群里进行聊天,聊天的话题如下:{minds_str}\n" + prompt += f"现在{global_config.BOT_NICKNAME}在qq群里进行聊天,聊天的话题如下:{minds_str}\n" prompt += f"你现在{mood_info}\n" prompt += '''现在请你总结这些聊天内容,注意关注聊天内容对原有的想法的影响,输出连贯的内心独白 不要太长,但是记得结合上述的消息,要记得你的人设,关注新内容:''' diff --git a/src/think_flow_demo/personality_info.txt b/src/think_flow_demo/personality_info.txt new file mode 100644 index 00000000..d7b9e4ec --- /dev/null +++ b/src/think_flow_demo/personality_info.txt @@ -0,0 +1,3 @@ +// 为了解决issue-589,已经将心流引用的内容改为了bot_config.toml中的prompt_personality +// 请移步配置文件进行更改 +你叫麦麦,你19岁,是一个大二的女大学生,你有一头黑色短发,你会刷贴吧 \ No newline at end of file diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 6591d427..668b40b8 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -43,6 +43,7 @@ personality_3_probability = 0.1 # 第三种人格出现概率,请确保三个 [schedule] enable_schedule_gen = true # 是否启用日程表(尚未完成) prompt_schedule_gen = "用几句话描述描述性格特点或行动规律,这个特征会用来生成日程表" +schedule_doing_update_interval = 900 # 日程表更新间隔 单位秒 [message] max_context_size = 15 # 麦麦获得的上文数量,建议15,太短太长都会导致脑袋尖尖 diff --git a/配置文件错误排查.py b/配置文件错误排查.py index 11417113..d277ceb4 100644 --- a/配置文件错误排查.py +++ b/配置文件错误排查.py @@ -1,8 +1,7 @@ import tomli import sys -import re from pathlib import Path -from typing import Dict, Any, List, Set, Tuple +from typing import Dict, Any, List, Tuple def load_toml_file(file_path: str) -> Dict[str, Any]: """加载TOML文件""" @@ -184,10 +183,15 @@ def check_model_configurations(config: Dict[str, Any], env_vars: Dict[str, str]) provider = model_config["provider"].upper() # 检查拼写错误 - for known_provider, correct_provider in reverse_mapping.items(): + for known_provider, _correct_provider in reverse_mapping.items(): # 使用模糊匹配检测拼写错误 - if provider != known_provider and _similar_strings(provider, known_provider) and provider not in reverse_mapping: - errors.append(f"[model.{model_name}]的provider '{model_config['provider']}' 可能拼写错误,应为 '{known_provider}'") + if (provider != known_provider and + _similar_strings(provider, known_provider) and + provider not in reverse_mapping): + errors.append( + f"[model.{model_name}]的provider '{model_config['provider']}' " + f"可能拼写错误,应为 '{known_provider}'" + ) break return errors @@ -223,7 +227,7 @@ def check_api_providers(config: Dict[str, Any], env_vars: Dict[str, str]) -> Lis # 检查配置文件中使用的所有提供商 used_providers = set() - for model_category, model_config in config["model"].items(): + for _model_category, model_config in config["model"].items(): if "provider" in model_config: provider = model_config["provider"] used_providers.add(provider) @@ -247,7 +251,7 @@ def check_api_providers(config: Dict[str, Any], env_vars: Dict[str, str]) -> Lis # 特别检查常见的拼写错误 for provider in used_providers: if provider.upper() == "SILICONFOLW": - errors.append(f"提供商 'SILICONFOLW' 存在拼写错误,应为 'SILICONFLOW'") + errors.append("提供商 'SILICONFOLW' 存在拼写错误,应为 'SILICONFLOW'") return errors @@ -272,7 +276,7 @@ def check_groups_configuration(config: Dict[str, Any]) -> List[str]: "main": "groups.talk_allowed中存在默认示例值'123',请修改为真实的群号", "details": [ f" 当前值: {groups['talk_allowed']}", - f" '123'为示例值,需要替换为真实群号" + " '123'为示例值,需要替换为真实群号" ] }) @@ -371,7 +375,8 @@ def check_memory_config(config: Dict[str, Any]) -> List[str]: if "memory_compress_rate" in memory and (memory["memory_compress_rate"] <= 0 or memory["memory_compress_rate"] > 1): errors.append(f"memory.memory_compress_rate值无效: {memory['memory_compress_rate']}, 应在0-1之间") - if "memory_forget_percentage" in memory and (memory["memory_forget_percentage"] <= 0 or memory["memory_forget_percentage"] > 1): + if ("memory_forget_percentage" in memory + and (memory["memory_forget_percentage"] <= 0 or memory["memory_forget_percentage"] > 1)): errors.append(f"memory.memory_forget_percentage值无效: {memory['memory_forget_percentage']}, 应在0-1之间") return errors @@ -393,7 +398,10 @@ def check_personality_config(config: Dict[str, Any]) -> List[str]: else: # 检查数组长度 if len(personality["prompt_personality"]) < 1: - errors.append(f"personality.prompt_personality数组长度不足,当前长度: {len(personality['prompt_personality'])}, 需要至少1项") + errors.append( + f"personality.prompt_personality至少需要1项," + f"当前长度: {len(personality['prompt_personality'])}" + ) else: # 模板默认值 template_values = [ @@ -452,10 +460,13 @@ def check_bot_config(config: Dict[str, Any]) -> List[str]: def format_results(all_errors): """格式化检查结果""" - sections_errors, prob_sum_errors, prob_range_errors, model_errors, api_errors, groups_errors, kr_errors, willing_errors, memory_errors, personality_errors, bot_results = all_errors + sections_errors, prob_sum_errors, prob_range_errors, model_errors, api_errors, groups_errors, kr_errors, willing_errors, memory_errors, personality_errors, bot_results = all_errors # noqa: E501, F821 bot_errors, bot_infos = bot_results - if not any([sections_errors, prob_sum_errors, prob_range_errors, model_errors, api_errors, groups_errors, kr_errors, willing_errors, memory_errors, personality_errors, bot_errors]): + if not any([ + sections_errors, prob_sum_errors, + prob_range_errors, model_errors, api_errors, groups_errors, + kr_errors, willing_errors, memory_errors, personality_errors, bot_errors]): result = "✅ 配置文件检查通过,未发现问题。" # 添加机器人信息 @@ -574,7 +585,10 @@ def main(): bot_results = check_bot_config(config) # 格式化并打印结果 - all_errors = (sections_errors, prob_sum_errors, prob_range_errors, model_errors, api_errors, groups_errors, kr_errors, willing_errors, memory_errors, personality_errors, bot_results) + all_errors = ( + sections_errors, prob_sum_errors, + prob_range_errors, model_errors, api_errors, groups_errors, + kr_errors, willing_errors, memory_errors, personality_errors, bot_results) result = format_results(all_errors) print("📋 机器人配置检查结果:") print(result) @@ -586,7 +600,9 @@ def main(): bot_errors, _ = bot_results # 计算普通错误列表的长度 - for errors in [sections_errors, model_errors, api_errors, groups_errors, kr_errors, willing_errors, memory_errors, bot_errors]: + for errors in [ + sections_errors, model_errors, api_errors, + groups_errors, kr_errors, willing_errors, memory_errors, bot_errors]: total_errors += len(errors) # 计算元组列表的长度(概率相关错误)