From f327f45aa389ef5c64de3cee834368dca9235418 Mon Sep 17 00:00:00 2001 From: AL76 Date: Sun, 9 Mar 2025 20:29:33 +0800 Subject: [PATCH 1/8] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96src/plugins/chat/?= =?UTF-8?q?=5F=5Finit=5F=5F.py=E7=9A=84import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 0bffaed1..4411b3ce 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -1,6 +1,4 @@ import asyncio -import os -import random import time from loguru import logger From 6a669334520e7fc70c753b37187fef990d976525 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:05:35 +0800 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E7=8E=AF=E5=A2=83.env.dev=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bot.py b/bot.py index d43d5e60..4a463f5d 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,7 @@ import os import shutil +import sys + import nonebot import time from dotenv import load_dotenv @@ -22,11 +24,12 @@ def easter_egg(): rainbow_text += rainbow_colors[i % len(rainbow_colors)] + char print(rainbow_text) + def init_config(): # 初次启动检测 if not os.path.exists("config/bot_config.toml"): logger.warning("检测到bot_config.toml不存在,正在从模板复制") - + # 检查config目录是否存在 if not os.path.exists("config"): os.makedirs("config") @@ -35,6 +38,7 @@ def init_config(): shutil.copy("template/bot_config_template.toml", "config/bot_config.toml") logger.info("复制完成,请修改config/bot_config.toml和.env.prod中的配置后重新启动") + def init_env(): # 初始化.env 默认ENVIRONMENT=prod if not os.path.exists(".env"): @@ -46,11 +50,17 @@ def init_env(): logger.error("检测到.env.prod文件不存在") shutil.copy("template.env", "./.env.prod") + # 检测.env.dev文件是否存在,不存在的话直接复制生产环境配置 + if not os.path.exists(".env.dev"): + logger.error("检测到.env.dev文件不存在") + shutil.copy(".env.prod", "./.env.dev") + # 首先加载基础环境变量.env if os.path.exists(".env"): load_dotenv(".env") logger.success("成功加载基础环境变量配置") + def load_env(): # 使用闭包实现对加载器的横向扩展,避免大量重复判断 def prod(): @@ -70,7 +80,7 @@ def load_env(): logger.info(f"[load_env] 当前的 ENVIRONMENT 变量值:{env}") if env in fn_map: - fn_map[env]() # 根据映射执行闭包函数 + fn_map[env]() # 根据映射执行闭包函数 elif os.path.exists(f".env.{env}"): logger.success(f"加载{env}环境变量配置") @@ -115,6 +125,7 @@ def scan_provider(env_config: dict): ) raise ValueError(f"请检查 '{provider_name}' 提供商配置是否丢失 BASE_URL 或 KEY 环境变量") + if __name__ == "__main__": # 利用 TZ 环境变量设定程序工作的时区 # 仅保证行为一致,不依赖 localtime(),实际对生产环境几乎没有作用 From 8baef077204065bcd50a9bc4a8ac6b632976c0a8 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:22:58 +0800 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=85=A8?= =?UTF-8?q?=E5=B1=80logger=E5=88=9D=E5=A7=8B=E5=8C=96=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bot.py b/bot.py index 4a463f5d..fbe300a6 100644 --- a/bot.py +++ b/bot.py @@ -12,6 +12,7 @@ import platform # 获取没有加载env时的环境变量 env_mask = {key: os.getenv(key) for key in os.environ} + def easter_egg(): # 彩蛋 from colorama import init, Fore @@ -91,6 +92,17 @@ def load_env(): RuntimeError(f"ENVIRONMENT 配置错误,请检查 .env 文件中的 ENVIRONMENT 变量及对应 .env.{env} 是否存在") +def load_logger(): + logger.remove() # 移除默认配置 + logger.add( + sys.stderr, + format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <7} | {name:.<8}:{function:.<8}:{line: >4} - {message}", + colorize=True, + level=os.getenv("LOG_LEVEL", "INFO") # 根据环境设置日志级别,默认为INFO + ) + def scan_provider(env_config: dict): provider = {} @@ -136,6 +148,7 @@ if __name__ == "__main__": init_config() init_env() load_env() + load_logger() env_config = {key: os.getenv(key) for key in os.environ} scan_provider(env_config) From 31a5514a0f3e88a3e67f750d6e8ffae8841a14eb Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:30:41 +0800 Subject: [PATCH 4/8] =?UTF-8?q?fix:=20=E8=B0=83=E6=95=B4=E5=85=A8=E5=B1=80?= =?UTF-8?q?logger=E5=8A=A0=E8=BD=BD=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index fbe300a6..3defe3fe 100644 --- a/bot.py +++ b/bot.py @@ -145,10 +145,11 @@ if __name__ == "__main__": time.tzset() easter_egg() + load_logger() init_config() init_env() load_env() - load_logger() + env_config = {key: os.getenv(key) for key in os.environ} scan_provider(env_config) From 980cde7230376f77b134213de4c1434687171839 Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:40:32 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96scheduler=5Fgenera?= =?UTF-8?q?tor=E6=97=A5=E5=BF=97&=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/schedule/schedule_generator.py | 102 +++++++++++---------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/src/plugins/schedule/schedule_generator.py b/src/plugins/schedule/schedule_generator.py index 8a036152..ffe99a2d 100644 --- a/src/plugins/schedule/schedule_generator.py +++ b/src/plugins/schedule/schedule_generator.py @@ -13,21 +13,21 @@ from ..models.utils_model import LLM_request driver = get_driver() config = driver.config - Database.initialize( - host= config.MONGODB_HOST, - port= int(config.MONGODB_PORT), - db_name= config.DATABASE_NAME, - username= config.MONGODB_USERNAME, - password= config.MONGODB_PASSWORD, - auth_source=config.MONGODB_AUTH_SOURCE - ) + host=config.MONGODB_HOST, + port=int(config.MONGODB_PORT), + db_name=config.DATABASE_NAME, + username=config.MONGODB_USERNAME, + password=config.MONGODB_PASSWORD, + auth_source=config.MONGODB_AUTH_SOURCE +) + class ScheduleGenerator: def __init__(self): - #根据global_config.llm_normal这一字典配置指定模型 + # 根据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) + self.llm_scheduler = LLM_request(model=global_config.llm_normal, temperature=0.9) self.db = Database.get_instance() self.today_schedule_text = "" self.today_schedule = {} @@ -35,39 +35,41 @@ class ScheduleGenerator: self.tomorrow_schedule = {} self.yesterday_schedule_text = "" self.yesterday_schedule = {} - + async def initialize(self): today = datetime.datetime.now() tomorrow = datetime.datetime.now() + datetime.timedelta(days=1) yesterday = datetime.datetime.now() - datetime.timedelta(days=1) - + 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.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]: + date_str = target_date.strftime("%Y-%m-%d") weekday = target_date.strftime("%A") - schedule_text = str - + existing_schedule = self.db.db.schedule.find_one({"date": date_str}) if existing_schedule: - print(f"{date_str}的日程已存在:") + logger.info(f"{date_str}的日程已存在:") schedule_text = existing_schedule["schedule"] # print(self.schedule_text) elif read_only == False: - print(f"{date_str}的日程不存在,准备生成新的日程。") - prompt = f"""我是{global_config.BOT_NICKNAME},{global_config.PROMPT_SCHEDULE_GEN},请为我生成{date_str}({weekday})的日程安排,包括:"""+\ - """ + logger.info(f"{date_str}的日程不存在,准备生成新的日程。") + prompt = f"""我是{global_config.BOT_NICKNAME},{global_config.PROMPT_SCHEDULE_GEN},请为我生成{date_str}({weekday})的日程安排,包括:""" + \ + """ 1. 早上的学习和工作安排 2. 下午的活动和任务 3. 晚上的计划和休息时间 请按照时间顺序列出具体时间点和对应的活动,用一个时间点而不是时间段来表示时间,用JSON格式返回日程表,仅返回内容,不要返回注释,时间采用24小时制,格式为{"时间": "活动","时间": "活动",...}。""" - + try: schedule_text, _ = await self.llm_scheduler.generate_response(prompt) self.db.db.schedule.insert_one({"date": date_str, "schedule": schedule_text}) @@ -76,36 +78,35 @@ class ScheduleGenerator: schedule_text = "生成日程时出错了" # print(self.schedule_text) else: - print(f"{date_str}的日程不存在。") + logger.info(f"{date_str}的日程不存在。") schedule_text = "忘了" - return schedule_text,None - + return schedule_text, None + schedule_form = self._parse_schedule(schedule_text) - return schedule_text,schedule_form - + return schedule_text, schedule_form + def _parse_schedule(self, schedule_text: str) -> Union[bool, Dict[str, str]]: """解析日程文本,转换为时间和活动的字典""" - try: + try: schedule_dict = json.loads(schedule_text) return schedule_dict except json.JSONDecodeError as e: - print(schedule_text) - print(f"解析日程失败: {str(e)}") + logger.exception("解析日程失败: {}".format(schedule_text)) return False - + 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 "摸鱼" @@ -114,7 +115,7 @@ class ScheduleGenerator: 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(): @@ -125,17 +126,17 @@ class ScheduleGenerator: 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 "摸鱼" - + def _time_diff(self, time1: str, time2: str) -> int: """计算两个时间字符串之间的分钟差""" - if time1=="24:00": - time1="23:59" - if time2=="24:00": - time2="23:59" + 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) @@ -146,17 +147,18 @@ class ScheduleGenerator: diff -= 1440 # 减一天的分钟 # print(f"时间1[{time1}]: 时间2[{time2}],差值[{diff}]分钟") return diff - + def print_schedule(self): """打印完整的日程安排""" if not self._parse_schedule(self.today_schedule_text): - print("今日日程有误,将在下次运行时重新生成") + logger.warning("今日日程有误,将在下次运行时重新生成") self.db.db.schedule.delete_one({"date": datetime.datetime.now().strftime("%Y-%m-%d")}) else: - print("\n=== 今日日程安排 ===") + logger.info("\n=== 今日日程安排 ===") for time_str, activity in self.today_schedule.items(): - print(f"时间[{time_str}]: 活动[{activity}]") - print("==================\n") + logger.info(f"时间[{time_str}]: 活动[{activity}]") + logger.info("==================\n") + # def main(): # # 使用示例 @@ -165,7 +167,7 @@ class ScheduleGenerator: # scheduler.print_schedule() # print("\n当前任务:") # print(scheduler.get_current_task()) - + # print("昨天日程:") # print(scheduler.yesterday_schedule) # print("今天日程:") @@ -175,5 +177,5 @@ class ScheduleGenerator: # if __name__ == "__main__": # main() - + bot_schedule = ScheduleGenerator() From 41aa974d2e9653f00024981e28189af50718b6ed Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:41:27 +0800 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96chat/config.py?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97&=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/config.py | 163 +++++++++++++++++++------------------ 1 file changed, 83 insertions(+), 80 deletions(-) diff --git a/src/plugins/chat/config.py b/src/plugins/chat/config.py index c027753c..332c2840 100644 --- a/src/plugins/chat/config.py +++ b/src/plugins/chat/config.py @@ -6,45 +6,46 @@ import tomli from loguru import logger from packaging import version from packaging.version import Version, InvalidVersion -from packaging.specifiers import SpecifierSet,InvalidSpecifier +from packaging.specifiers import SpecifierSet, InvalidSpecifier + @dataclass class BotConfig: - """机器人配置类""" + """机器人配置类""" INNER_VERSION: Version = None BOT_QQ: Optional[int] = 1 BOT_NICKNAME: Optional[str] = None - + # 消息处理相关配置 MIN_TEXT_LENGTH: int = 2 # 最小处理文本长度 MAX_CONTEXT_SIZE: int = 15 # 上下文最大消息数 emoji_chance: float = 0.2 # 发送表情包的基础概率 - + ENABLE_PIC_TRANSLATE: bool = True # 是否启用图片翻译 - + talk_allowed_groups = set() talk_frequency_down_groups = set() thinking_timeout: int = 100 # 思考时间 - + response_willing_amplifier: float = 1.0 # 回复意愿放大系数 response_interested_rate_amplifier: float = 1.0 # 回复兴趣度放大系数 down_frequency_rate: float = 3.5 # 降低回复频率的群组回复意愿降低系数 - + ban_user_id = set() - + build_memory_interval: int = 30 # 记忆构建间隔(秒) forget_memory_interval: int = 300 # 记忆遗忘间隔(秒) EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟) EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟) EMOJI_SAVE: bool = True # 偷表情包 - EMOJI_CHECK: bool = False #是否开启过滤 - EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求 + EMOJI_CHECK: bool = False # 是否开启过滤 + EMOJI_CHECK_PROMPT: str = "符合公序良俗" # 表情包过滤要求 ban_words = set() max_response_length: int = 1024 # 最大回复长度 - + # 模型配置 llm_reasoning: Dict[str, str] = field(default_factory=lambda: {}) llm_reasoning_minor: Dict[str, str] = field(default_factory=lambda: {}) @@ -60,34 +61,34 @@ class BotConfig: MODEL_R1_PROBABILITY: float = 0.8 # R1模型概率 MODEL_V3_PROBABILITY: float = 0.1 # V3模型概率 MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 - + enable_advance_output: bool = False # 是否启用高级输出 - enable_kuuki_read: bool = True # 是否启用读空气功能 - - mood_update_interval: float = 1.0 # 情绪更新间隔 单位秒 - mood_decay_rate: float = 0.95 # 情绪衰减率 - mood_intensity_factor: float = 0.7 # 情绪强度因子 + enable_kuuki_read: bool = True # 是否启用读空气功能 - keywords_reaction_rules = [] # 关键词回复规则 + mood_update_interval: float = 1.0 # 情绪更新间隔 单位秒 + mood_decay_rate: float = 0.95 # 情绪衰减率 + mood_intensity_factor: float = 0.7 # 情绪强度因子 - chinese_typo_enable=True # 是否启用中文错别字生成器 - chinese_typo_error_rate=0.03 # 单字替换概率 - chinese_typo_min_freq=7 # 最小字频阈值 - chinese_typo_tone_error_rate=0.2 # 声调错误概率 - chinese_typo_word_replace_rate=0.02 # 整词替换概率 + keywords_reaction_rules = [] # 关键词回复规则 + + chinese_typo_enable = True # 是否启用中文错别字生成器 + chinese_typo_error_rate = 0.03 # 单字替换概率 + chinese_typo_min_freq = 7 # 最小字频阈值 + chinese_typo_tone_error_rate = 0.2 # 声调错误概率 + chinese_typo_word_replace_rate = 0.02 # 整词替换概率 # 默认人设 - PROMPT_PERSONALITY=[ + PROMPT_PERSONALITY = [ "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", "是一个女大学生,你有黑色头发,你会刷小红书", "是一个女大学生,你会刷b站,对ACG文化感兴趣" ] - PROMPT_SCHEDULE_GEN="一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" - - PERSONALITY_1: float = 0.6 # 第一种人格概率 - PERSONALITY_2: float = 0.3 # 第二种人格概率 - PERSONALITY_3: float = 0.1 # 第三种人格概率 - + PROMPT_SCHEDULE_GEN = "一个曾经学习地质,现在学习心理学和脑科学的女大学生,喜欢刷qq,贴吧,知乎和小红书" + + PERSONALITY_1: float = 0.6 # 第一种人格概率 + PERSONALITY_2: float = 0.3 # 第二种人格概率 + PERSONALITY_3: float = 0.1 # 第三种人格概率 + @staticmethod def get_config_dir() -> str: """获取配置文件目录""" @@ -97,7 +98,7 @@ class BotConfig: if not os.path.exists(config_dir): os.makedirs(config_dir) return config_dir - + @classmethod def convert_to_specifierset(cls, value: str) -> SpecifierSet: """将 字符串 版本表达式转换成 SpecifierSet @@ -117,7 +118,7 @@ class BotConfig: exit(1) return converted - + @classmethod def get_config_version(cls, toml: dict) -> Version: """提取配置文件的 SpecifierSet 版本数据 @@ -129,14 +130,14 @@ class BotConfig: if 'inner' in toml: try: - config_version : str = toml["inner"]["version"] + config_version: str = toml["inner"]["version"] except KeyError as e: logger.error(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") raise KeyError(f"配置文件中 inner 段 不存在 {e}, 这是错误的配置文件") else: - toml["inner"] = { "version": "0.0.0" } + toml["inner"] = {"version": "0.0.0"} config_version = toml["inner"]["version"] - + try: ver = version.parse(config_version) except InvalidVersion as e: @@ -148,38 +149,38 @@ class BotConfig: raise InvalidVersion("配置文件中 inner段 的 version 键是错误的版本描述\n") return ver - + @classmethod def load_config(cls, config_path: str = None) -> "BotConfig": """从TOML配置文件加载配置""" config = cls() def personality(parent: dict): - personality_config=parent['personality'] - personality=personality_config.get('prompt_personality') + personality_config = parent['personality'] + personality = personality_config.get('prompt_personality') if len(personality) >= 2: logger.info(f"载入自定义人格:{personality}") - config.PROMPT_PERSONALITY=personality_config.get('prompt_personality',config.PROMPT_PERSONALITY) - logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule',config.PROMPT_SCHEDULE_GEN)}") - config.PROMPT_SCHEDULE_GEN=personality_config.get('prompt_schedule',config.PROMPT_SCHEDULE_GEN) - + config.PROMPT_PERSONALITY = personality_config.get('prompt_personality', config.PROMPT_PERSONALITY) + logger.info(f"载入自定义日程prompt:{personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN)}") + config.PROMPT_SCHEDULE_GEN = personality_config.get('prompt_schedule', config.PROMPT_SCHEDULE_GEN) + if config.INNER_VERSION in SpecifierSet(">=0.0.2"): - config.PERSONALITY_1=personality_config.get('personality_1_probability',config.PERSONALITY_1) - config.PERSONALITY_2=personality_config.get('personality_2_probability',config.PERSONALITY_2) - config.PERSONALITY_3=personality_config.get('personality_3_probability',config.PERSONALITY_3) + config.PERSONALITY_1 = personality_config.get('personality_1_probability', config.PERSONALITY_1) + config.PERSONALITY_2 = personality_config.get('personality_2_probability', config.PERSONALITY_2) + config.PERSONALITY_3 = personality_config.get('personality_3_probability', config.PERSONALITY_3) def emoji(parent: dict): emoji_config = parent["emoji"] config.EMOJI_CHECK_INTERVAL = emoji_config.get("check_interval", config.EMOJI_CHECK_INTERVAL) config.EMOJI_REGISTER_INTERVAL = emoji_config.get("register_interval", config.EMOJI_REGISTER_INTERVAL) - config.EMOJI_CHECK_PROMPT = emoji_config.get('check_prompt',config.EMOJI_CHECK_PROMPT) - config.EMOJI_SAVE = emoji_config.get('auto_save',config.EMOJI_SAVE) - config.EMOJI_CHECK = emoji_config.get('enable_check',config.EMOJI_CHECK) - + config.EMOJI_CHECK_PROMPT = emoji_config.get('check_prompt', config.EMOJI_CHECK_PROMPT) + config.EMOJI_SAVE = emoji_config.get('auto_save', config.EMOJI_SAVE) + config.EMOJI_CHECK = emoji_config.get('enable_check', config.EMOJI_CHECK) + def cq_code(parent: dict): cq_code_config = parent["cq_code"] config.ENABLE_PIC_TRANSLATE = cq_code_config.get("enable_pic_translate", config.ENABLE_PIC_TRANSLATE) - + def bot(parent: dict): # 机器人基础配置 bot_config = parent["bot"] @@ -191,12 +192,13 @@ class BotConfig: response_config = parent["response"] config.MODEL_R1_PROBABILITY = response_config.get("model_r1_probability", config.MODEL_R1_PROBABILITY) config.MODEL_V3_PROBABILITY = response_config.get("model_v3_probability", config.MODEL_V3_PROBABILITY) - config.MODEL_R1_DISTILL_PROBABILITY = response_config.get("model_r1_distill_probability", config.MODEL_R1_DISTILL_PROBABILITY) + config.MODEL_R1_DISTILL_PROBABILITY = response_config.get("model_r1_distill_probability", + config.MODEL_R1_DISTILL_PROBABILITY) config.max_response_length = response_config.get("max_response_length", config.max_response_length) - + def model(parent: dict): # 加载模型配置 - model_config:dict = parent["model"] + model_config: dict = parent["model"] config_list = [ "llm_reasoning", @@ -213,24 +215,24 @@ class BotConfig: for item in config_list: if item in model_config: - cfg_item:dict = model_config[item] + cfg_item: dict = model_config[item] # base_url 的例子: SILICONFLOW_BASE_URL # key 的例子: SILICONFLOW_KEY cfg_target = { - "name" : "", - "base_url" : "", - "key" : "", - "pri_in" : 0, - "pri_out" : 0 + "name": "", + "base_url": "", + "key": "", + "pri_in": 0, + "pri_out": 0 } if config.INNER_VERSION in SpecifierSet("<=0.0.0"): cfg_target = cfg_item elif config.INNER_VERSION in SpecifierSet(">=0.0.1"): - stable_item = ["name","pri_in","pri_out"] - pricing_item = ["pri_in","pri_out"] + stable_item = ["name", "pri_in", "pri_out"] + pricing_item = ["pri_in", "pri_out"] # 从配置中原始拷贝稳定字段 for i in stable_item: # 如果 字段 属于计费项 且获取不到,那默认值是 0 @@ -244,18 +246,16 @@ class BotConfig: logger.error(f"{item} 中的必要字段 {e} 不存在,请检查") raise KeyError(f"{item} 中的必要字段 {e} 不存在,请检查") - provider = cfg_item.get("provider") if provider == None: logger.error(f"provider 字段在模型配置 {item} 中不存在,请检查") raise KeyError(f"provider 字段在模型配置 {item} 中不存在,请检查") - + cfg_target["base_url"] = f"{provider}_BASE_URL" cfg_target["key"] = f"{provider}_KEY" - # 如果 列表中的项目在 model_config 中,利用反射来设置对应项目 - setattr(config,item,cfg_target) + setattr(config, item, cfg_target) else: logger.error(f"模型 {item} 在config中不存在,请检查") raise KeyError(f"模型 {item} 在config中不存在,请检查") @@ -265,12 +265,14 @@ class BotConfig: config.MIN_TEXT_LENGTH = msg_config.get("min_text_length", config.MIN_TEXT_LENGTH) config.MAX_CONTEXT_SIZE = msg_config.get("max_context_size", config.MAX_CONTEXT_SIZE) config.emoji_chance = msg_config.get("emoji_chance", config.emoji_chance) - config.ban_words=msg_config.get("ban_words",config.ban_words) + config.ban_words = msg_config.get("ban_words", config.ban_words) if config.INNER_VERSION in SpecifierSet(">=0.0.2"): config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout) - config.response_willing_amplifier = msg_config.get("response_willing_amplifier", config.response_willing_amplifier) - config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier", config.response_interested_rate_amplifier) + config.response_willing_amplifier = msg_config.get("response_willing_amplifier", + config.response_willing_amplifier) + config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier", + config.response_interested_rate_amplifier) config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate) def memory(parent: dict): @@ -294,8 +296,10 @@ class BotConfig: config.chinese_typo_enable = chinese_typo_config.get("enable", config.chinese_typo_enable) config.chinese_typo_error_rate = chinese_typo_config.get("error_rate", config.chinese_typo_error_rate) config.chinese_typo_min_freq = chinese_typo_config.get("min_freq", config.chinese_typo_min_freq) - config.chinese_typo_tone_error_rate = chinese_typo_config.get("tone_error_rate", config.chinese_typo_tone_error_rate) - config.chinese_typo_word_replace_rate = chinese_typo_config.get("word_replace_rate", config.chinese_typo_word_replace_rate) + config.chinese_typo_tone_error_rate = chinese_typo_config.get("tone_error_rate", + config.chinese_typo_tone_error_rate) + config.chinese_typo_word_replace_rate = chinese_typo_config.get("word_replace_rate", + config.chinese_typo_word_replace_rate) def groups(parent: dict): groups_config = parent["groups"] @@ -382,7 +386,7 @@ class BotConfig: except(tomli.TOMLDecodeError) as e: logger.critical(f"配置文件bot_config.toml填写有误,请检查第{e.lineno}行第{e.colno}处:{e.msg}") exit(1) - + # 获取配置文件版本 config.INNER_VERSION = cls.get_config_version(toml_dict) @@ -406,31 +410,32 @@ class BotConfig: f"当前程序仅支持以下版本范围: {group_specifierset}" ) raise InvalidVersion(f"当前程序仅支持以下版本范围: {group_specifierset}") - + # 如果 necessary 项目存在,而且显式声明是 False,进入特殊处理 elif "necessary" in include_configs[key] and include_configs[key].get("necessary") == False: # 通过 pass 处理的项虽然直接忽略也是可以的,但是为了不增加理解困难,依然需要在这里显式处理 if key == "keywords_reaction": pass - + else: # 如果用户根本没有需要的配置项,提示缺少配置 logger.error(f"配置文件中缺少必需的字段: '{key}'") raise KeyError(f"配置文件中缺少必需的字段: '{key}'") logger.success(f"成功加载配置文件: {config_path}") - - return config - + + return config + + # 获取配置文件路径 bot_config_floder_path = BotConfig.get_config_dir() -print(f"正在品鉴配置文件目录: {bot_config_floder_path}") +logger.debug(f"正在品鉴配置文件目录: {bot_config_floder_path}") bot_config_path = os.path.join(bot_config_floder_path, "bot_config.toml") if os.path.exists(bot_config_path): # 如果开发环境配置文件不存在,则使用默认配置文件 - print(f"异常的新鲜,异常的美味: {bot_config_path}") + logger.debug(f"异常的新鲜,异常的美味: {bot_config_path}") logger.info("使用bot配置文件") else: # 配置文件不存在 @@ -439,8 +444,6 @@ else: global_config = BotConfig.load_config(config_path=bot_config_path) - if not global_config.enable_advance_output: logger.remove() pass - From 1b5344f32fe7122842b5564248805428af1009fc Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 22:42:04 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96bot=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E7=9A=84=E6=97=A5=E5=BF=97&=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 1 - src/plugins/chat/__init__.py | 50 ++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/bot.py b/bot.py index 3defe3fe..84ce5067 100644 --- a/bot.py +++ b/bot.py @@ -150,7 +150,6 @@ if __name__ == "__main__": init_env() load_env() - env_config = {key: os.getenv(key) for key in os.environ} scan_provider(env_config) diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 4411b3ce..485d9d75 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -28,16 +28,15 @@ driver = get_driver() config = driver.config Database.initialize( - host= config.MONGODB_HOST, - port= int(config.MONGODB_PORT), - db_name= config.DATABASE_NAME, - username= config.MONGODB_USERNAME, - password= config.MONGODB_PASSWORD, - auth_source= config.MONGODB_AUTH_SOURCE + host=config.MONGODB_HOST, + port=int(config.MONGODB_PORT), + db_name=config.DATABASE_NAME, + username=config.MONGODB_USERNAME, + password=config.MONGODB_PASSWORD, + auth_source=config.MONGODB_AUTH_SOURCE ) print("\033[1;32m[初始化数据库完成]\033[0m") - # 导入其他模块 from ..memory_system.memory import hippocampus, memory_graph from .bot import ChatBot @@ -57,24 +56,24 @@ group_msg = on_message(priority=5) scheduler = require("nonebot_plugin_apscheduler").scheduler - @driver.on_startup async def start_background_tasks(): """启动后台任务""" # 启动LLM统计 llm_stats.start() - print("\033[1;32m[初始化]\033[0m LLM统计功能已启动") - + logger.success("[初始化]LLM统计功能已启动") + # 初始化并启动情绪管理器 mood_manager = MoodManager.get_instance() mood_manager.start_mood_update(update_interval=global_config.mood_update_interval) - print("\033[1;32m[初始化]\033[0m 情绪管理器已启动") - + logger.success("[初始化]情绪管理器已启动") + # 只启动表情包管理任务 asyncio.create_task(emoji_manager.start_periodic_check(interval_MINS=global_config.EMOJI_CHECK_INTERVAL)) await bot_schedule.initialize() bot_schedule.print_schedule() - + + @driver.on_startup async def init_relationships(): """在 NoneBot2 启动时初始化关系管理器""" @@ -82,45 +81,52 @@ async def init_relationships(): await relationship_manager.load_all_relationships() asyncio.create_task(relationship_manager._start_relationship_manager()) + @driver.on_bot_connect async def _(bot: Bot): """Bot连接成功时的处理""" global _message_manager_started print(f"\033[1;38;5;208m-----------{global_config.BOT_NICKNAME}成功连接!-----------\033[0m") await willing_manager.ensure_started() - + message_sender.set_bot(bot) print("\033[1;38;5;208m-----------消息发送器已启动!-----------\033[0m") - + if not _message_manager_started: asyncio.create_task(message_manager.start_processor()) _message_manager_started = True print("\033[1;38;5;208m-----------消息处理器已启动!-----------\033[0m") - + asyncio.create_task(emoji_manager._periodic_scan(interval_MINS=global_config.EMOJI_REGISTER_INTERVAL)) print("\033[1;38;5;208m-----------开始偷表情包!-----------\033[0m") - + + @group_msg.handle() async def _(bot: Bot, event: GroupMessageEvent, state: T_State): await chat_bot.handle_message(event, bot) + # 添加build_memory定时任务 @scheduler.scheduled_job("interval", seconds=global_config.build_memory_interval, id="build_memory") async def build_memory_task(): """每build_memory_interval秒执行一次记忆构建""" - print("\033[1;32m[记忆构建]\033[0m -------------------------------------------开始构建记忆-------------------------------------------") + print( + "\033[1;32m[记忆构建]\033[0m -------------------------------------------开始构建记忆-------------------------------------------") start_time = time.time() await hippocampus.operation_build_memory(chat_size=20) end_time = time.time() - print(f"\033[1;32m[记忆构建]\033[0m -------------------------------------------记忆构建完成:耗时: {end_time - start_time:.2f} 秒-------------------------------------------") - -@scheduler.scheduled_job("interval", seconds=global_config.forget_memory_interval, id="forget_memory") + print( + f"\033[1;32m[记忆构建]\033[0m -------------------------------------------记忆构建完成:耗时: {end_time - start_time:.2f} 秒-------------------------------------------") + + +@scheduler.scheduled_job("interval", seconds=global_config.forget_memory_interval, id="forget_memory") async def forget_memory_task(): """每30秒执行一次记忆构建""" # print("\033[1;32m[记忆遗忘]\033[0m 开始遗忘记忆...") # await hippocampus.operation_forget_topic(percentage=0.1) # print("\033[1;32m[记忆遗忘]\033[0m 记忆遗忘完成") + @scheduler.scheduled_job("interval", seconds=global_config.build_memory_interval + 10, id="merge_memory") async def merge_memory_task(): """每30秒执行一次记忆构建""" @@ -128,9 +134,9 @@ async def merge_memory_task(): # await hippocampus.operation_merge_memory(percentage=0.1) # print("\033[1;32m[记忆整合]\033[0m 记忆整合完成") + @scheduler.scheduled_job("interval", seconds=30, id="print_mood") async def print_mood_task(): """每30秒打印一次情绪状态""" mood_manager = MoodManager.get_instance() mood_manager.print_mood_status() - From 8428a066642a4249bacb1c7c4aad235544f7fb5d Mon Sep 17 00:00:00 2001 From: AL76 <735756072@qq.com> Date: Sun, 9 Mar 2025 23:39:44 +0800 Subject: [PATCH 8/8] fix: memory logger optimization (CR comment) --- src/plugins/memory_system/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/plugins/memory_system/__init__.py diff --git a/src/plugins/memory_system/__init__.py b/src/plugins/memory_system/__init__.py new file mode 100644 index 00000000..e69de29b