From 220219c9bae8fde12ede479f3616e9f8b515c8ef Mon Sep 17 00:00:00 2001 From: lmst2 Date: Mon, 31 Mar 2025 21:10:51 +0100 Subject: [PATCH 1/5] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9C=89=E5=85=B3?= =?UTF-8?q?=E6=97=B6=E5=8C=BA=E7=9A=84=E8=AE=BE=E7=BD=AE=EF=BC=8C=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E5=9C=A8bot=5Fconfig=E9=87=8C=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=97=B6=E5=8C=BA=EF=BC=8C=E6=9D=A5=E6=94=B9=E5=8F=98=E6=9C=BA?= =?UTF-8?q?=E5=99=A8=E4=BA=BA=E4=BD=9C=E6=81=AF=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=B8=80=E4=BA=9Bllm=20logger=E7=9A=84=E5=B0=8Ftweak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++ src/plugins/config/config.py | 2 + src/plugins/memory_system/Hippocampus.py | 1 - src/plugins/models/utils_model.py | 56 +++++++++++----------- src/plugins/schedule/schedule_generator.py | 17 ++++--- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index d257c3689..292ea0ad0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ log/ logs/ /test /src/test +nonebot-maibot-adapter/ +*.zip +run.bat +run.py message_queue_content.txt message_queue_content.bat message_queue_window.bat diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index 41ef7a3e8..ace0ab2ee 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -149,6 +149,7 @@ class BotConfig: PROMPT_SCHEDULE_GEN = "无日程" SCHEDULE_DOING_UPDATE_INTERVAL: int = 300 # 日程表更新间隔 单位秒 SCHEDULE_TEMPERATURE: float = 0.5 # 日程表温度,建议0.5-1.0 + TIME_ZONE: str = "Asia/Shanghai" # 时区 # message MAX_CONTEXT_SIZE: int = 15 # 上下文最大消息数 @@ -349,6 +350,7 @@ class BotConfig: ) if config.INNER_VERSION in SpecifierSet(">=1.0.2"): config.SCHEDULE_TEMPERATURE = schedule_config.get("schedule_temperature", config.SCHEDULE_TEMPERATURE) + config.TIME_ZONE = schedule_config.get("time_zone", config.TIME_ZONE) def emoji(parent: dict): emoji_config = parent["emoji"] diff --git a/src/plugins/memory_system/Hippocampus.py b/src/plugins/memory_system/Hippocampus.py index 717cebe17..7f781ac31 100644 --- a/src/plugins/memory_system/Hippocampus.py +++ b/src/plugins/memory_system/Hippocampus.py @@ -14,7 +14,6 @@ from src.common.logger import get_module_logger, LogConfig, MEMORY_STYLE_CONFIG from src.plugins.memory_system.sample_distribution import MemoryBuildScheduler # 分布生成器 from .memory_config import MemoryConfig - def get_closest_chat_from_db(length: int, timestamp: str): # print(f"获取最接近指定时间戳的聊天记录,长度: {length}, 时间戳: {timestamp}") # print(f"当前时间: {timestamp},转换后时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timestamp))}") diff --git a/src/plugins/models/utils_model.py b/src/plugins/models/utils_model.py index 263e11618..69a80c9b0 100644 --- a/src/plugins/models/utils_model.py +++ b/src/plugins/models/utils_model.py @@ -179,9 +179,6 @@ class LLM_request: # logger.debug(f"{logger_msg}发送请求到URL: {api_url}") # logger.info(f"使用模型: {self.model_name}") - # 流式输出标志 - if stream_mode: - payload["stream"] = stream_mode # 构建请求体 if image_base64: @@ -189,6 +186,11 @@ class LLM_request: elif payload is None: payload = await self._build_payload(prompt) + # 流式输出标志 + # 先构建payload,再添加流式输出标志 + if stream_mode: + payload["stream"] = stream_mode + for retry in range(policy["max_retries"]): try: # 使用上下文管理器处理会话 @@ -202,13 +204,13 @@ class LLM_request: # 处理需要重试的状态码 if response.status in policy["retry_codes"]: wait_time = policy["base_wait"] * (2**retry) - logger.warning(f"错误码: {response.status}, 等待 {wait_time}秒后重试") + logger.warning(f"模型 {self.model_name} 错误码: {response.status}, 等待 {wait_time}秒后重试") if response.status == 413: logger.warning("请求体过大,尝试压缩...") image_base64 = compress_base64_image_by_scale(image_base64) payload = await self._build_payload(prompt, image_base64, image_format) elif response.status in [500, 503]: - logger.error(f"错误码: {response.status} - {error_code_mapping.get(response.status)}") + logger.error(f"模型 {self.model_name} 错误码: {response.status} - {error_code_mapping.get(response.status)}") raise RuntimeError("服务器负载过高,模型恢复失败QAQ") else: logger.warning(f"请求限制(429),等待{wait_time}秒后重试...") @@ -216,7 +218,7 @@ class LLM_request: await asyncio.sleep(wait_time) continue elif response.status in policy["abort_codes"]: - logger.error(f"错误码: {response.status} - {error_code_mapping.get(response.status)}") + logger.error(f"模型 {self.model_name} 错误码: {response.status} - {error_code_mapping.get(response.status)}") # 尝试获取并记录服务器返回的详细错误信息 try: error_json = await response.json() @@ -228,7 +230,7 @@ class LLM_request: error_message = error_obj.get("message") error_status = error_obj.get("status") logger.error( - f"服务器错误详情: 代码={error_code}, 状态={error_status}, " + f"模型 {self.model_name} 服务器错误详情: 代码={error_code}, 状态={error_status}, " f"消息={error_message}" ) elif isinstance(error_json, dict) and "error" in error_json: @@ -238,13 +240,13 @@ class LLM_request: error_message = error_obj.get("message") error_status = error_obj.get("status") logger.error( - f"服务器错误详情: 代码={error_code}, 状态={error_status}, 消息={error_message}" + f"模型 {self.model_name} 服务器错误详情: 代码={error_code}, 状态={error_status}, 消息={error_message}" ) else: # 记录原始错误响应内容 - logger.error(f"服务器错误响应: {error_json}") + logger.error(f"模型 {self.model_name} 服务器错误响应: {error_json}") except Exception as e: - logger.warning(f"无法解析服务器错误响应: {str(e)}") + logger.warning(f"模型 {self.model_name} 无法解析服务器错误响应: {str(e)}") if response.status == 403: # 只针对硅基流动的V3和R1进行降级处理 @@ -273,7 +275,7 @@ class LLM_request: retry -= 1 # 不计入重试次数 continue - raise RuntimeError(f"请求被拒绝: {error_code_mapping.get(response.status)}") + raise RuntimeError(f"模型 {self.model_name} 请求被拒绝: {error_code_mapping.get(response.status)}") response.raise_for_status() reasoning_content = "" @@ -318,12 +320,12 @@ class LLM_request: flag_delta_content_finished = True except Exception as e: - logger.exception(f"解析流式输出错误: {str(e)}") + logger.exception(f"模型 {self.model_name} 解析流式输出错误: {str(e)}") except GeneratorExit: - logger.warning("流式输出被中断") + logger.warning(f"模型 {self.model_name} 流式输出被中断") break except Exception as e: - logger.error(f"处理流式输出时发生错误: {str(e)}") + logger.error(f"模型 {self.model_name} 处理流式输出时发生错误: {str(e)}") break content = accumulated_content think_match = re.search(r"(.*?)", content, re.DOTALL) @@ -353,7 +355,7 @@ class LLM_request: # 处理aiohttp抛出的响应错误 if retry < policy["max_retries"] - 1: wait_time = policy["base_wait"] * (2**retry) - logger.error(f"HTTP响应错误,等待{wait_time}秒后重试... 状态码: {e.status}, 错误: {e.message}") + logger.error(f"模型 {self.model_name} HTTP响应错误,等待{wait_time}秒后重试... 状态码: {e.status}, 错误: {e.message}") try: if hasattr(e, "response") and e.response and hasattr(e.response, "text"): error_text = await e.response.text() @@ -364,27 +366,27 @@ class LLM_request: if "error" in error_item and isinstance(error_item["error"], dict): error_obj = error_item["error"] logger.error( - f"服务器错误详情: 代码={error_obj.get('code')}, " + f"模型 {self.model_name} 服务器错误详情: 代码={error_obj.get('code')}, " f"状态={error_obj.get('status')}, " f"消息={error_obj.get('message')}" ) elif isinstance(error_json, dict) and "error" in error_json: error_obj = error_json.get("error", {}) logger.error( - f"服务器错误详情: 代码={error_obj.get('code')}, " + f"模型 {self.model_name} 服务器错误详情: 代码={error_obj.get('code')}, " f"状态={error_obj.get('status')}, " f"消息={error_obj.get('message')}" ) else: - logger.error(f"服务器错误响应: {error_json}") + logger.error(f"模型 {self.model_name} 服务器错误响应: {error_json}") except (json.JSONDecodeError, TypeError) as json_err: - logger.warning(f"响应不是有效的JSON: {str(json_err)}, 原始内容: {error_text[:200]}") + logger.warning(f"模型 {self.model_name} 响应不是有效的JSON: {str(json_err)}, 原始内容: {error_text[:200]}") except (AttributeError, TypeError, ValueError) as parse_err: - logger.warning(f"无法解析响应错误内容: {str(parse_err)}") + logger.warning(f"模型 {self.model_name} 无法解析响应错误内容: {str(parse_err)}") await asyncio.sleep(wait_time) else: - logger.critical(f"HTTP响应错误达到最大重试次数: 状态码: {e.status}, 错误: {e.message}") + logger.critical(f"模型 {self.model_name} HTTP响应错误达到最大重试次数: 状态码: {e.status}, 错误: {e.message}") # 安全地检查和记录请求详情 if ( image_base64 @@ -401,14 +403,14 @@ class LLM_request: f"{image_base64[:10]}...{image_base64[-10:]}" ) logger.critical(f"请求头: {await self._build_headers(no_key=True)} 请求体: {payload}") - raise RuntimeError(f"API请求失败: 状态码 {e.status}, {e.message}") from e + raise RuntimeError(f"模型 {self.model_name} API请求失败: 状态码 {e.status}, {e.message}") from e except Exception as e: if retry < policy["max_retries"] - 1: wait_time = policy["base_wait"] * (2**retry) - logger.error(f"请求失败,等待{wait_time}秒后重试... 错误: {str(e)}") + logger.error(f"模型 {self.model_name} 请求失败,等待{wait_time}秒后重试... 错误: {str(e)}") await asyncio.sleep(wait_time) else: - logger.critical(f"请求失败: {str(e)}") + logger.critical(f"模型 {self.model_name} 请求失败: {str(e)}") # 安全地检查和记录请求详情 if ( image_base64 @@ -425,10 +427,10 @@ class LLM_request: f"{image_base64[:10]}...{image_base64[-10:]}" ) logger.critical(f"请求头: {await self._build_headers(no_key=True)} 请求体: {payload}") - raise RuntimeError(f"API请求失败: {str(e)}") from e + raise RuntimeError(f"模型 {self.model_name} API请求失败: {str(e)}") from e - logger.error("达到最大重试次数,请求仍然失败") - raise RuntimeError("达到最大重试次数,API请求仍然失败") + logger.error(f"模型 {self.model_name} 达到最大重试次数,请求仍然失败") + raise RuntimeError(f"模型 {self.model_name} 达到最大重试次数,API请求仍然失败") async def _transform_parameters(self, params: dict) -> dict: """ diff --git a/src/plugins/schedule/schedule_generator.py b/src/plugins/schedule/schedule_generator.py index a6a312624..036e37503 100644 --- a/src/plugins/schedule/schedule_generator.py +++ b/src/plugins/schedule/schedule_generator.py @@ -3,6 +3,7 @@ import os import sys from typing import Dict import asyncio +from dateutil import tz # 添加项目根目录到 Python 路径 root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..")) @@ -13,6 +14,8 @@ from src.common.logger import get_module_logger, SCHEDULE_STYLE_CONFIG, LogConfi from src.plugins.models.utils_model import LLM_request # noqa: E402 from src.plugins.config.config import global_config # noqa: E402 +TIME_ZONE = tz.gettz(global_config.TIME_ZONE) # 设置时区 + schedule_config = LogConfig( # 使用海马体专用样式 @@ -44,7 +47,7 @@ class ScheduleGenerator: self.personality = "" self.behavior = "" - self.start_time = datetime.datetime.now() + self.start_time = datetime.datetime.now(TIME_ZONE) self.schedule_doing_update_interval = 300 # 最好大于60 @@ -74,7 +77,7 @@ class ScheduleGenerator: while True: # print(self.get_current_num_task(1, True)) - current_time = datetime.datetime.now() + current_time = datetime.datetime.now(TIME_ZONE) # 检查是否需要重新生成日程(日期变化) if current_time.date() != self.start_time.date(): @@ -100,7 +103,7 @@ class ScheduleGenerator: Returns: tuple: (today_schedule_text, today_schedule) 今天的日程文本和解析后的日程字典 """ - today = datetime.datetime.now() + today = datetime.datetime.now(TIME_ZONE) yesterday = today - datetime.timedelta(days=1) # 先检查昨天的日程 @@ -156,7 +159,7 @@ class ScheduleGenerator: """打印完整的日程安排""" if not self.today_schedule_text: logger.warning("今日日程有误,将在下次运行时重新生成") - db.schedule.delete_one({"date": datetime.datetime.now().strftime("%Y-%m-%d")}) + db.schedule.delete_one({"date": datetime.datetime.now(TIME_ZONE).strftime("%Y-%m-%d")}) else: logger.info("=== 今日日程安排 ===") logger.info(self.today_schedule_text) @@ -165,7 +168,7 @@ class ScheduleGenerator: async def update_today_done_list(self): # 更新数据库中的 today_done_list - today_str = datetime.datetime.now().strftime("%Y-%m-%d") + today_str = datetime.datetime.now(TIME_ZONE).strftime("%Y-%m-%d") existing_schedule = db.schedule.find_one({"date": today_str}) if existing_schedule: @@ -177,7 +180,7 @@ class ScheduleGenerator: async def move_doing(self, mind_thinking: str = ""): try: - current_time = datetime.datetime.now() + current_time = datetime.datetime.now(TIME_ZONE) if mind_thinking: doing_prompt = self.construct_doing_prompt(current_time, mind_thinking) else: @@ -246,7 +249,7 @@ class ScheduleGenerator: def save_today_schedule_to_db(self): """保存日程到数据库,同时初始化 today_done_list""" - date_str = datetime.datetime.now().strftime("%Y-%m-%d") + date_str = datetime.datetime.now(TIME_ZONE).strftime("%Y-%m-%d") schedule_data = { "date": date_str, "schedule": self.today_schedule_text, From 5acc043ab0105ab5721edcfa4e324374f2e131f1 Mon Sep 17 00:00:00 2001 From: lmst2 Date: Mon, 31 Mar 2025 21:20:50 +0100 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E4=B8=8B?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/bot_config_template.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 5a13710e5..81870ad4f 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -48,6 +48,7 @@ enable_schedule_gen = true # 是否启用日程表(尚未完成) prompt_schedule_gen = "用几句话描述描述性格特点或行动规律,这个特征会用来生成日程表" schedule_doing_update_interval = 900 # 日程表更新间隔 单位秒 schedule_temperature = 0.5 # 日程表温度,建议0.5-1.0 +time_zone = "Asia/Shanghai" # 给你的机器人设置时区,可以解决运行电脑时区和国内时区不同的情况,或者模拟国外留学生日程 [platforms] # 必填项目,填写每个平台适配器提供的链接 nonebot-qq="http://127.0.0.1:18002/api/message" From ff7ba5742f04add3cf92e8733900348631e72114 Mon Sep 17 00:00:00 2001 From: lmst2 Date: Mon, 31 Mar 2025 21:42:42 +0100 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=B6=E5=8C=BA?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=80=BB=E8=BE=91=EF=BC=8C=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=97=A0=E6=95=88=E6=97=B6=E5=8C=BA=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/config/config.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index ace0ab2ee..400122010 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -1,6 +1,7 @@ import os from dataclasses import dataclass, field from typing import Dict, List, Optional +from dateutil import tz import tomli import tomlkit @@ -350,7 +351,11 @@ class BotConfig: ) if config.INNER_VERSION in SpecifierSet(">=1.0.2"): config.SCHEDULE_TEMPERATURE = schedule_config.get("schedule_temperature", config.SCHEDULE_TEMPERATURE) - config.TIME_ZONE = schedule_config.get("time_zone", config.TIME_ZONE) + time_zone = schedule_config.get("time_zone", config.TIME_ZONE) + if tz.gettz(time_zone) is None: + logger.error(f"无效的时区: {time_zone},使用默认值: {config.TIME_ZONE}") + else: + config.TIME_ZONE = time_zone def emoji(parent: dict): emoji_config = parent["emoji"] From 9609edbd34920a63f4b02da79de2acd3206acb83 Mon Sep 17 00:00:00 2001 From: lmst2 Date: Thu, 3 Apr 2025 14:22:13 +0100 Subject: [PATCH 4/5] =?UTF-8?q?=E7=BB=99=E6=9C=80=E5=A4=A7=E4=BB=8E?= =?UTF-8?q?=E4=BA=8B=E6=AC=A1=E6=95=B0log=E4=BF=A1=E6=81=AF=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=85=B7=E4=BD=93=E9=94=99=E8=AF=AF=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/models/utils_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/models/utils_model.py b/src/plugins/models/utils_model.py index fb3d9b51c..09e251750 100644 --- a/src/plugins/models/utils_model.py +++ b/src/plugins/models/utils_model.py @@ -468,8 +468,8 @@ class LLM_request: logger.critical(f"请求头: {await self._build_headers(no_key=True)} 请求体: {payload}") raise RuntimeError(f"模型 {self.model_name} API请求失败: {str(e)}") from e - logger.error(f"模型 {self.model_name} 达到最大重试次数,请求仍然失败") - raise RuntimeError(f"模型 {self.model_name} 达到最大重试次数,API请求仍然失败") + logger.error(f"模型 {self.model_name} 达到最大重试次数,请求仍然失败,错误: {str(e)}") + raise RuntimeError(f"模型 {self.model_name} 达到最大重试次数,API请求仍然失败,错误: {str(e)}") async def _transform_parameters(self, params: dict) -> dict: """ From e89f8d9844b548cd52a86a47a20d4f8f850e02d5 Mon Sep 17 00:00:00 2001 From: lmst2 Date: Fri, 4 Apr 2025 00:28:20 +0100 Subject: [PATCH 5/5] edit max retry --- src/plugins/models/utils_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/models/utils_model.py b/src/plugins/models/utils_model.py index 09e251750..f29f1fa47 100644 --- a/src/plugins/models/utils_model.py +++ b/src/plugins/models/utils_model.py @@ -153,7 +153,7 @@ class LLM_request: # 合并重试策略 default_retry = { - "max_retries": 3, + "max_retries": 10, "base_wait": 15, "retry_codes": [429, 413, 500, 503], "abort_codes": [400, 401, 402, 403],