diff --git a/src/chat/planner_actions/plan_executor.py b/src/chat/planner_actions/plan_executor.py index acd9b376f..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 @@ -83,11 +84,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) @@ -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/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 = "" 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" 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 )