From 9ab0857d89b5fca588a0c9b63fd8a0f4a6e8f64a Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:44:51 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8=E6=83=85?= =?UTF-8?q?=E5=8A=A8=E4=BD=9C=E6=A8=A1=E5=9E=8B=E8=B0=83=E7=94=A8=E5=B9=B6?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=A7=86=E9=A2=91=E5=88=86=E6=9E=90=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 表情动作: 将模型调用从 `planner` 切换到 `utils`,以使用更合适的模型进行表情推荐。 - 视频分析: 增加检查逻辑,仅当分析成功且结果不为错误提示时,才将结果存入数据库,防止存储无效记录。 --- src/chat/utils/utils_video.py | 2 +- src/plugins/built_in/core_actions/emoji.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/chat/utils/utils_video.py b/src/chat/utils/utils_video.py index 1e186f058..c78acd89a 100644 --- a/src/chat/utils/utils_video.py +++ b/src/chat/utils/utils_video.py @@ -747,7 +747,7 @@ class VideoAnalyzer: os.unlink(temp_path) # 保存分析结果到数据库(仅保存成功的结果) - if success: + if success and not result.startswith("❌"): metadata = {"filename": filename, "file_size": len(video_bytes), "analysis_timestamp": time.time()} self._store_video_result(video_hash=video_hash, description=result, metadata=metadata) logger.info("✅ 分析结果已保存到数据库") diff --git a/src/plugins/built_in/core_actions/emoji.py b/src/plugins/built_in/core_actions/emoji.py index fe03f4478..69a236159 100644 --- a/src/plugins/built_in/core_actions/emoji.py +++ b/src/plugins/built_in/core_actions/emoji.py @@ -152,10 +152,10 @@ class EmojiAction(BaseAction): # 调用LLM models = llm_api.get_available_models() - chat_model_config = models.get("planner") + chat_model_config = models.get("utils") if not chat_model_config: - logger.error(f"{self.log_prefix} 未找到'planner'模型配置,无法调用LLM") - return False, "未找到'planner'模型配置" + logger.error(f"{self.log_prefix} 未找到'utils'模型配置,无法调用LLM") + return False, "未找到'utils'模型配置" success, chosen_emotion, _, _ = await llm_api.generate_with_model( prompt, model_config=chat_model_config, request_type="emoji" @@ -212,10 +212,10 @@ class EmojiAction(BaseAction): # 调用LLM models = llm_api.get_available_models() - chat_model_config = models.get("planner") + chat_model_config = models.get("utils") if not chat_model_config: - logger.error(f"{self.log_prefix} 未找到'planner'模型配置,无法调用LLM") - return False, "未找到'planner'模型配置" + logger.error(f"{self.log_prefix} 未找到'utils'模型配置,无法调用LLM") + return False, "未找到'utils'模型配置" success, chosen_description, _, _ = await llm_api.generate_with_model( prompt, model_config=chat_model_config, request_type="emoji" From 490a3f03fc25171fefbc8fe4eb85250303277321 Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Mon, 22 Sep 2025 18:49:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(chat):=20=E4=B8=BA=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E6=96=87=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E5=8E=86=E5=8F=B2=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当会话中没有已读消息时(例如,在机器人重启后),回复生成器现在会尝试从数据库中加载最近的聊天记录作为备用上下文。 此举旨在解决机器人因缺乏上下文而无法生成相关回复的问题,通过提供历史情景参考,显著提升了在中断对话后恢复聊天的连贯性。加载的数据库消息会与当前未读消息进行去重,以避免信息冗余。 --- src/chat/replyer/default_generator.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index 4b02282ce..a720fd745 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -699,7 +699,32 @@ class DefaultReplyer: ) read_history_prompt = f"这是已读历史消息,仅作为当前聊天情景的参考:\n{read_content}" else: - read_history_prompt = "暂无已读历史消息" + # 如果没有已读消息,则从数据库加载最近的上下文 + logger.info("暂无已读历史消息,正在从数据库加载上下文...") + fallback_messages = get_raw_msg_before_timestamp_with_chat( + chat_id=chat_id, + timestamp=time.time(), + limit=global_config.chat.max_context_size, + ) + if fallback_messages: + # 从 unread_messages 获取 message_id 列表,用于去重 + unread_message_ids = {msg.message_id for msg in unread_messages} + filtered_fallback_messages = [ + msg for msg in fallback_messages if msg.get("message_id") not in unread_message_ids + ] + + if filtered_fallback_messages: + read_content = build_readable_messages( + filtered_fallback_messages, + replace_bot_name=True, + timestamp_mode="normal_no_YMD", + truncate=True, + ) + read_history_prompt = f"这是已读历史消息,仅作为当前聊天情景的参考:\n{read_content}" + else: + read_history_prompt = "暂无已读历史消息" + else: + read_history_prompt = "暂无已读历史消息" # 构建未读历史消息 prompt(包含兴趣度) unread_history_prompt = "" From d13b83f10ce3350db20cbd2a3f9a4d2d06360a3b Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:19:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?perf(chat):=20=E5=B0=86=20planner=20?= =?UTF-8?q?=E7=9A=84=E9=9D=9E=E5=9B=9E=E5=A4=8D=E5=8A=A8=E4=BD=9C=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=90=8E=E5=8F=B0=E4=BB=BB=E5=8A=A1=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原先的 PlanExecutor 会 `await` 等待所有动作执行完毕,包括非直接回复用户的动作,这可能导致在这些动作耗时较长时,用户的响应被延迟。 本次修改将非回复类的动作(other_actions)放入 `asyncio.create_task` 中执行,使其成为后台任务。这样可以确保核心的回复流程不被阻塞,从而显著提升机器人的响应速度。 注意:后台任务的执行结果和统计数据将不会在本次执行周期中立即返回。 --- src/chat/planner_actions/plan_executor.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/chat/planner_actions/plan_executor.py b/src/chat/planner_actions/plan_executor.py index acd9b376f..f6d70de60 100644 --- a/src/chat/planner_actions/plan_executor.py +++ b/src/chat/planner_actions/plan_executor.py @@ -83,11 +83,11 @@ class PlanExecutor: execution_results.extend(reply_result["results"]) self.execution_stats["reply_executions"] += len(reply_actions) - # 并行执行其他动作 + # 将其他动作放入后台任务执行,避免阻塞主流程 if other_actions: - other_result = await self._execute_other_actions(other_actions, plan) - execution_results.extend(other_result["results"]) - self.execution_stats["other_action_executions"] += len(other_actions) + asyncio.create_task(self._execute_other_actions(other_actions, plan)) + logger.info(f"已将 {len(other_actions)} 个其他动作放入后台任务执行。") + # 注意:后台任务的结果不会立即计入本次返回的统计数据 # 更新总体统计 self.execution_stats["total_executed"] += len(plan.decided_actions) From 56b42defddf39aa26841e1886bf7eb4078248f56 Mon Sep 17 00:00:00 2001 From: tt-P607 <68868379+tt-P607@users.noreply.github.com> Date: Mon, 22 Sep 2025 22:52:28 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat(poke):=20=E4=BC=98=E5=8C=96=E6=88=B3?= =?UTF-8?q?=E4=B8=80=E6=88=B3=E5=8A=9F=E8=83=BD=EF=BC=8C=E4=BC=98=E5=85=88?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=20user=5Fid=20=E5=AE=9A=E4=BD=8D=E7=94=A8?= =?UTF-8?q?=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前的戳一戳功能仅依赖 `user_name` 来查找用户,这在某些情况下可能导致识别不准确或失败。 本次更新对计划执行器 (`PlanExecutor`) 进行了增强,使其在处理 `poke_user` 动作时,能直接从目标消息中提取 `user_id`。`PokeAction` 插件现在会优先使用这个更可靠的 `user_id`。如果 `user_id` 不存在,则回退到使用 `user_name` 作为备用方案。 这显著提高了戳一戳功能的准确性和稳定性。 --- src/chat/planner_actions/plan_executor.py | 26 +++++++++++++++- src/plugins/built_in/poke_plugin/plugin.py | 35 +++++++++++++--------- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/chat/planner_actions/plan_executor.py b/src/chat/planner_actions/plan_executor.py index f6d70de60..9c266b0ec 100644 --- a/src/chat/planner_actions/plan_executor.py +++ b/src/chat/planner_actions/plan_executor.py @@ -4,6 +4,7 @@ PlanExecutor: 接收 Plan 对象并执行其中的所有动作。 """ import asyncio +import re import time from typing import Dict, List @@ -216,12 +217,35 @@ class PlanExecutor: try: logger.info(f"执行其他动作: {action_info.action_type} (原因: {action_info.reasoning})") + action_data = action_info.action_data or {} + + # 针对 poke_user 动作,特殊处理 + if action_info.action_type == "poke_user": + target_message = action_info.action_message + if target_message: + # 优先直接获取 user_id,这才是最可靠的信息 + user_id = target_message.get("user_id") + if user_id: + action_data["user_id"] = user_id + logger.info(f"检测到戳一戳动作,目标用户ID: {user_id}") + else: + # 如果没有 user_id,再尝试用 user_nickname 作为备用方案 + user_name = target_message.get("user_nickname") + if user_name: + action_data["user_name"] = user_name + logger.info(f"检测到戳一戳动作,目标用户: {user_name}") + else: + logger.warning("无法从戳一戳消息中获取用户ID或昵称。") + + # 传递原始消息ID以支持引用 + action_data["target_message_id"] = target_message.get("message_id") + # 构建动作参数 action_params = { "chat_id": plan.chat_id, "target_message": action_info.action_message, "reasoning": action_info.reasoning, - "action_data": action_info.action_data or {}, + "action_data": action_data, } # 通过动作管理器执行动作 diff --git a/src/plugins/built_in/poke_plugin/plugin.py b/src/plugins/built_in/poke_plugin/plugin.py index 13cf33ca0..a37c45dd1 100644 --- a/src/plugins/built_in/poke_plugin/plugin.py +++ b/src/plugins/built_in/poke_plugin/plugin.py @@ -30,7 +30,8 @@ class PokeAction(BaseAction): # === 功能描述(必须填写)=== action_parameters = { - "user_name": "需要戳一戳的用户的名字", + "user_name": "需要戳一戳的用户的名字 (可选)", + "user_id": "需要戳一戳的用户的ID (可选,优先级更高)", "times": "需要戳一戳的次数 (默认为 1)", } action_require = ["当需要戳某个用户时使用", "当你想提醒特定用户时使用"] @@ -46,32 +47,38 @@ class PokeAction(BaseAction): async def execute(self) -> Tuple[bool, str]: """执行戳一戳的动作""" + user_id = self.action_data.get("user_id") user_name = self.action_data.get("user_name") + try: times = int(self.action_data.get("times", 1)) except (ValueError, TypeError): times = 1 - if not user_name: - logger.warning("戳一戳动作缺少 'user_name' 参数。") - return False, "缺少 'user_name' 参数" - - user_info = await get_person_info_manager().get_person_info_by_name(user_name) - if not user_info or not user_info.get("user_id"): - logger.info(f"找不到名为 '{user_name}' 的用户。") - return False, f"找不到名为 '{user_name}' 的用户" - - user_id = user_info.get("user_id") + # 优先使用 user_id + if not user_id: + if not user_name: + logger.warning("戳一戳动作缺少 'user_id' 或 'user_name' 参数。") + return False, "缺少用户标识参数" + + # 备用方案:通过 user_name 查找 + user_info = await get_person_info_manager().get_person_info_by_name(user_name) + if not user_info or not user_info.get("user_id"): + logger.info(f"找不到名为 '{user_name}' 的用户。") + return False, f"找不到名为 '{user_name}' 的用户" + user_id = user_info.get("user_id") + + display_name = user_name or user_id for i in range(times): - logger.info(f"正在向 {user_name} ({user_id}) 发送第 {i + 1}/{times} 次戳一戳...") + logger.info(f"正在向 {display_name} ({user_id}) 发送第 {i + 1}/{times} 次戳一戳...") await self.send_command( - "SEND_POKE", args={"qq_id": user_id}, display_message=f"戳了戳 {user_name} ({i + 1}/{times})" + "SEND_POKE", args={"qq_id": user_id}, display_message=f"戳了戳 {display_name} ({i + 1}/{times})" ) # 添加一个小的延迟,以避免发送过快 await asyncio.sleep(0.5) - success_message = f"已向 {user_name} 发送 {times} 次戳一戳。" + success_message = f"已向 {display_name} 发送 {times} 次戳一戳。" await self.store_action_info( action_build_into_prompt=True, action_prompt_display=success_message, action_done=True )