diff --git a/changelogs/changelog.md b/changelogs/changelog.md index 41c760e85..eab206f1b 100644 --- a/changelogs/changelog.md +++ b/changelogs/changelog.md @@ -10,6 +10,8 @@ - 优化计时信息和Log - 添加回复超时检查 - normal的插件允许llm激活 +- 合并action激活器 +- emoji统一可选随机激活或llm激活 ## [0.8.1] - 2025-7-5 diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index 1009edde5..a6d12b821 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -21,9 +21,9 @@ from src.chat.heart_flow.observation.actions_observation import ActionObservatio from src.chat.focus_chat.memory_activator import MemoryActivator from src.chat.focus_chat.info_processors.base_processor import BaseProcessor -from src.chat.focus_chat.planners.planner_simple import ActionPlanner -from src.chat.focus_chat.planners.modify_actions import ActionModifier -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.planner_focus import ActionPlanner +from src.chat.planner_actions.action_modifier import ActionModifier +from src.chat.planner_actions.action_manager import ActionManager from src.config.config import global_config from src.chat.focus_chat.hfc_performance_logger import HFCPerformanceLogger from src.chat.focus_chat.hfc_version_manager import get_hfc_version @@ -50,24 +50,6 @@ PROCESSOR_CLASSES = { logger = get_logger("hfc") # Logger Name Changed -async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str): - """处理循环延迟""" - cycle_duration = time.monotonic() - cycle_start_time - - try: - sleep_duration = 0.0 - if not action_taken_this_cycle and cycle_duration < 1: - sleep_duration = 1 - cycle_duration - elif cycle_duration < 0.2: - sleep_duration = 0.2 - - if sleep_duration > 0: - await asyncio.sleep(sleep_duration) - - except asyncio.CancelledError: - logger.info(f"{log_prefix} Sleep interrupted, loop likely cancelling.") - raise - class HeartFChatting: """ @@ -80,7 +62,6 @@ class HeartFChatting: self, chat_id: str, on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None, - performance_version: str = None, ): """ HeartFChatting 初始化函数 @@ -122,7 +103,7 @@ class HeartFChatting: self.action_planner = ActionPlanner( log_prefix=self.log_prefix, action_manager=self.action_manager ) - self.action_modifier = ActionModifier(action_manager=self.action_manager) + self.action_modifier = ActionModifier(action_manager=self.action_manager, chat_id=self.stream_id) self.action_observation = ActionObservation(observe_id=self.stream_id) self.action_observation.set_action_manager(self.action_manager) @@ -146,7 +127,7 @@ class HeartFChatting: # 初始化性能记录器 # 如果没有指定版本号,则使用全局版本管理器的版本号 - actual_version = performance_version or get_hfc_version() + actual_version = get_hfc_version() self.performance_logger = HFCPerformanceLogger(chat_id, actual_version) logger.info( @@ -287,7 +268,6 @@ class HeartFChatting: # 初始化周期状态 cycle_timers = {} - loop_cycle_start_time = time.monotonic() # 执行规划和处理阶段 try: @@ -370,11 +350,6 @@ class HeartFChatting: self._current_cycle_detail.timers = cycle_timers - # 防止循环过快消耗资源 - await _handle_cycle_delay( - loop_info["loop_action_info"]["action_taken"], loop_cycle_start_time, self.log_prefix - ) - # 完成当前循环并保存历史 self._current_cycle_detail.complete_cycle() self._cycle_history.append(self._current_cycle_detail) @@ -407,7 +382,7 @@ class HeartFChatting: self.performance_logger.record_cycle(cycle_performance_data) except Exception as perf_e: logger.warning(f"{self.log_prefix} 记录性能数据失败: {perf_e}") - + await asyncio.sleep(global_config.focus_chat.think_interval) except asyncio.CancelledError: @@ -543,6 +518,7 @@ class HeartFChatting: # 调用完整的动作修改流程 await self.action_modifier.modify_actions( observations=self.observations, + mode="focus", ) await self.action_observation.observe() @@ -567,7 +543,7 @@ class HeartFChatting: logger.debug(f"{self.log_prefix} 并行阶段完成,准备进入规划器,plan_info数量: {len(all_plan_info)}") with Timer("规划器", cycle_timers): - plan_result = await self.action_planner.plan(all_plan_info, self.observations, loop_start_time) + plan_result = await self.action_planner.plan(all_plan_info, loop_start_time) loop_plan_info = { "action_result": plan_result.get("action_result", {}), diff --git a/src/chat/focus_chat/planners/base_planner.py b/src/chat/focus_chat/planners/base_planner.py deleted file mode 100644 index 0492039e1..000000000 --- a/src/chat/focus_chat/planners/base_planner.py +++ /dev/null @@ -1,28 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List, Dict, Any -from src.chat.focus_chat.planners.action_manager import ActionManager -from src.chat.focus_chat.info.info_base import InfoBase - - -class BasePlanner(ABC): - """规划器基类""" - - def __init__(self, log_prefix: str, action_manager: ActionManager): - self.log_prefix = log_prefix - self.action_manager = action_manager - - @abstractmethod - async def plan( - self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]], loop_start_time: float - ) -> Dict[str, Any]: - """ - 规划下一步行动 - - Args: - all_plan_info: 所有计划信息 - running_memorys: 回忆信息 - loop_start_time: 循环开始时间 - Returns: - Dict[str, Any]: 规划结果 - """ - pass diff --git a/src/chat/heart_flow/observation/actions_observation.py b/src/chat/heart_flow/observation/actions_observation.py index 12e972daf..125032140 100644 --- a/src/chat/heart_flow/observation/actions_observation.py +++ b/src/chat/heart_flow/observation/actions_observation.py @@ -2,7 +2,7 @@ # 外部世界可以是某个聊天 不同平台的聊天 也可以是任意媒体 from datetime import datetime from src.common.logger import get_logger -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.action_manager import ActionManager logger = get_logger("observation") diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index d81f7f48b..6817670f0 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -9,18 +9,17 @@ from src.plugin_system.apis import generator_api from maim_message import UserInfo, Seg from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager from src.chat.utils.timer_calculator import Timer - +from src.common.message_repository import count_messages from src.chat.utils.prompt_builder import global_prompt_manager from ..message_receive.message import MessageSending, MessageRecv, MessageThinking, MessageSet from src.chat.message_receive.message_sender import message_manager from src.chat.normal_chat.willing.willing_manager import get_willing_manager -from src.chat.normal_chat.normal_chat_utils import get_recent_message_stats -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.action_manager import ActionManager from src.person_info.relationship_builder_manager import relationship_builder_manager from .priority_manager import PriorityManager import traceback -from src.chat.normal_chat.normal_chat_planner import NormalChatPlanner -from src.chat.normal_chat.normal_chat_action_modifier import NormalChatActionModifier +from src.chat.planner_actions.planner_normal import NormalChatPlanner +from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info from src.manager.mood_manager import mood_manager @@ -71,7 +70,7 @@ class NormalChat: # Planner相关初始化 self.action_manager = ActionManager() self.planner = NormalChatPlanner(self.stream_name, self.action_manager) - self.action_modifier = NormalChatActionModifier(self.action_manager, self.stream_id, self.stream_name) + self.action_modifier = ActionModifier(self.action_manager, self.stream_id) self.enable_planner = global_config.normal_chat.enable_planner # 从配置中读取是否启用planner # 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply} @@ -569,8 +568,8 @@ class NormalChat: available_actions = None if self.enable_planner: try: - await self.action_modifier.modify_actions_for_normal_chat( - self.chat_stream, self.recent_replies, message.processed_plain_text + await self.action_modifier.modify_actions( + mode="normal", message_content=message.processed_plain_text ) available_actions = self.action_manager.get_using_actions_for_mode("normal") except Exception as e: @@ -1003,3 +1002,29 @@ class NormalChat: except Exception as e: logger.error(f"[{self.stream_name}] 清理思考消息 {thinking_id} 时出错: {e}") + +def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict: + """ + Args: + minutes (int): 检索的分钟数,默认30分钟 + chat_id (str, optional): 指定的chat_id,仅统计该chat下的消息。为None时统计全部。 + Returns: + dict: {"bot_reply_count": int, "total_message_count": int} + """ + + now = time.time() + start_time = now - minutes * 60 + bot_id = global_config.bot.qq_account + + filter_base = {"time": {"$gte": start_time}} + if chat_id is not None: + filter_base["chat_id"] = chat_id + + # 总消息数 + total_message_count = count_messages(filter_base) + # bot自身回复数 + bot_filter = filter_base.copy() + bot_filter["user_id"] = bot_id + bot_reply_count = count_messages(bot_filter) + + return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count} \ No newline at end of file diff --git a/src/chat/normal_chat/normal_chat_action_modifier.py b/src/chat/normal_chat/normal_chat_action_modifier.py deleted file mode 100644 index d2f715cb4..000000000 --- a/src/chat/normal_chat/normal_chat_action_modifier.py +++ /dev/null @@ -1,403 +0,0 @@ -from typing import List, Any, Dict -from src.common.logger import get_logger -from src.chat.focus_chat.planners.action_manager import ActionManager -from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat -from src.config.config import global_config -import random -import time -import asyncio - -logger = get_logger("normal_chat_action_modifier") - - -class NormalChatActionModifier: - """Normal Chat动作修改器 - - 负责根据Normal Chat的上下文和状态动态调整可用的动作集合 - 实现与Focus Chat类似的动作激活策略,但将LLM_JUDGE转换为概率激活以提升性能 - """ - - def __init__(self, action_manager: ActionManager, stream_id: str, stream_name: str): - """初始化动作修改器""" - self.action_manager = action_manager - self.stream_id = stream_id - self.stream_name = stream_name - self.log_prefix = f"[{stream_name}]动作修改器" - - # 缓存所有注册的动作 - self.all_actions = self.action_manager.get_registered_actions() - - async def modify_actions_for_normal_chat( - self, - chat_stream, - recent_replies: List[dict], - message_content: str, - **kwargs: Any, - ): - """为Normal Chat修改可用动作集合 - - 实现动作激活策略: - 1. 基于关联类型的动态过滤 - 2. 基于激活类型的智能判定(LLM_JUDGE转为概率激活) - - Args: - chat_stream: 聊天流对象 - recent_replies: 最近的回复记录 - message_content: 当前消息内容 - **kwargs: 其他参数 - """ - - reasons = [] - merged_action_changes = {"add": [], "remove": []} - type_mismatched_actions = [] # 在外层定义避免作用域问题 - - self.action_manager.restore_default_actions() - - # 第一阶段:基于关联类型的动态过滤 - if chat_stream: - chat_context = chat_stream.context if hasattr(chat_stream, "context") else None - if chat_context: - # 获取Normal模式下的可用动作(已经过滤了mode_enable) - current_using_actions = self.action_manager.get_using_actions_for_mode("normal") - # print(f"current_using_actions: {current_using_actions}") - for action_name in current_using_actions.keys(): - if action_name in self.all_actions: - data = self.all_actions[action_name] - if data.get("associated_types"): - if not chat_context.check_types(data["associated_types"]): - type_mismatched_actions.append(action_name) - logger.debug(f"{self.log_prefix} 动作 {action_name} 关联类型不匹配,移除该动作") - - if type_mismatched_actions: - merged_action_changes["remove"].extend(type_mismatched_actions) - reasons.append(f"移除{type_mismatched_actions}(关联类型不匹配)") - - # 第二阶段:应用激活类型判定 - # 构建聊天内容 - 使用与planner一致的方式 - chat_content = "" - if chat_stream and hasattr(chat_stream, "stream_id"): - try: - # 获取消息历史,使用与normal_chat_planner相同的方法 - message_list_before_now = get_raw_msg_before_timestamp_with_chat( - chat_id=chat_stream.stream_id, - timestamp=time.time(), - limit=global_config.chat.max_context_size, # 使用相同的配置 - ) - - # 构建可读的聊天上下文 - chat_content = build_readable_messages( - message_list_before_now, - replace_bot_name=True, - merge_messages=False, - timestamp_mode="relative", - read_mark=0.0, - show_actions=True, - ) - - logger.debug(f"{self.log_prefix} 成功构建聊天内容,长度: {len(chat_content)}") - - except Exception as e: - logger.warning(f"{self.log_prefix} 构建聊天内容失败: {e}") - chat_content = "" - - # 获取当前Normal模式下的动作集进行激活判定 - current_actions = self.action_manager.get_using_actions_for_mode("normal") - - # print(f"current_actions: {current_actions}") - # print(f"chat_content: {chat_content}") - final_activated_actions = await self._apply_normal_activation_filtering( - current_actions, chat_content, message_content, recent_replies - ) - # print(f"final_activated_actions: {final_activated_actions}") - - # 统一处理所有需要移除的动作,避免重复移除 - all_actions_to_remove = set() # 使用set避免重复 - - # 添加关联类型不匹配的动作 - if type_mismatched_actions: - all_actions_to_remove.update(type_mismatched_actions) - - # 添加激活类型判定未通过的动作 - for action_name in current_actions.keys(): - if action_name not in final_activated_actions: - all_actions_to_remove.add(action_name) - - # 统计移除原因(避免重复) - activation_failed_actions = [ - name - for name in current_actions.keys() - if name not in final_activated_actions and name not in type_mismatched_actions - ] - if activation_failed_actions: - reasons.append(f"移除{activation_failed_actions}(激活类型判定未通过)") - - # 统一执行移除操作 - for action_name in all_actions_to_remove: - success = self.action_manager.remove_action_from_using(action_name) - if success: - logger.debug(f"{self.log_prefix} 移除动作: {action_name}") - else: - logger.debug(f"{self.log_prefix} 动作 {action_name} 已经不在使用集中,跳过移除") - - # 应用动作添加(如果有的话) - for action_name in merged_action_changes["add"]: - if action_name in self.all_actions: - success = self.action_manager.add_action_to_using(action_name) - if success: - logger.debug(f"{self.log_prefix} 添加动作: {action_name}") - - # 记录变更原因 - if reasons: - logger.info(f"{self.log_prefix} 动作调整完成: {' | '.join(reasons)}") - - # 获取最终的Normal模式可用动作并记录 - final_actions = self.action_manager.get_using_actions_for_mode("normal") - logger.debug(f"{self.log_prefix} 当前Normal模式可用动作: {list(final_actions.keys())}") - - async def _apply_normal_activation_filtering( - self, - actions_with_info: Dict[str, Any], - chat_content: str = "", - message_content: str = "", - recent_replies: List[dict] = None, - ) -> Dict[str, Any]: - """ - 应用Normal模式的激活类型过滤逻辑 - - 与Focus模式的区别: - 1. LLM_JUDGE类型转换为概率激活(避免LLM调用) - 2. RANDOM类型保持概率激活 - 3. KEYWORD类型保持关键词匹配 - 4. ALWAYS类型直接激活 - - Args: - actions_with_info: 带完整信息的动作字典 - chat_content: 聊天内容 - message_content: 当前消息内容 - recent_replies: 最近的回复记录列表 - - Returns: - Dict[str, Any]: 过滤后激活的actions字典 - """ - activated_actions = {} - - # 分类处理不同激活类型的actions - always_actions = {} - random_actions = {} - keyword_actions = {} - llm_judge_actions = {} - - for action_name, action_info in actions_with_info.items(): - # 使用normal_activation_type - activation_type = action_info.get("normal_activation_type", "always") - - # 现在统一是字符串格式的激活类型值 - if activation_type == "always": - always_actions[action_name] = action_info - elif activation_type == "random": - random_actions[action_name] = action_info - elif activation_type == "llm_judge": - llm_judge_actions[action_name] = action_info - elif activation_type == "keyword": - keyword_actions[action_name] = action_info - else: - logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理") - - # 1. 处理ALWAYS类型(直接激活) - for action_name, action_info in always_actions.items(): - activated_actions[action_name] = action_info - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: ALWAYS类型直接激活") - - # 2. 处理RANDOM类型(概率激活) - for action_name, action_info in random_actions.items(): - probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY) - should_activate = random.random() < probability - if should_activate: - activated_actions[action_name] = action_info - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: RANDOM类型触发(概率{probability})") - else: - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: RANDOM类型未触发(概率{probability})") - - # 3. 处理KEYWORD类型(关键词匹配) - for action_name, action_info in keyword_actions.items(): - should_activate = self._check_keyword_activation(action_name, action_info, chat_content, message_content) - if should_activate: - activated_actions[action_name] = action_info - keywords = action_info.get("activation_keywords", []) - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: KEYWORD类型匹配关键词({keywords})") - else: - keywords = action_info.get("activation_keywords", []) - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: KEYWORD类型未匹配关键词({keywords})") - - # 4. 处理LLM_JUDGE类型(并行判定) - if llm_judge_actions: - # 直接并行处理所有LLM判定actions - llm_results = await self._process_llm_judge_actions_parallel( - llm_judge_actions, - chat_content, - ) - - # 添加激活的LLM判定actions - for action_name, should_activate in llm_results.items(): - if should_activate: - activated_actions[action_name] = llm_judge_actions[action_name] - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: LLM_JUDGE类型判定通过") - else: - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: LLM_JUDGE类型判定未通过") - - - - logger.debug(f"{self.log_prefix}Normal模式激活类型过滤完成: {list(activated_actions.keys())}") - return activated_actions - - def _check_keyword_activation( - self, - action_name: str, - action_info: Dict[str, Any], - chat_content: str = "", - message_content: str = "", - ) -> bool: - """ - 检查是否匹配关键词触发条件 - - Args: - action_name: 动作名称 - action_info: 动作信息 - chat_content: 聊天内容(已经是格式化后的可读消息) - - Returns: - bool: 是否应该激活此action - """ - - activation_keywords = action_info.get("activation_keywords", []) - case_sensitive = action_info.get("keyword_case_sensitive", False) - - if not activation_keywords: - logger.warning(f"{self.log_prefix}动作 {action_name} 设置为关键词触发但未配置关键词") - return False - - # 使用构建好的聊天内容作为检索文本 - search_text = chat_content + message_content - - # 如果不区分大小写,转换为小写 - if not case_sensitive: - search_text = search_text.lower() - - # 检查每个关键词 - matched_keywords = [] - for keyword in activation_keywords: - check_keyword = keyword if case_sensitive else keyword.lower() - if check_keyword in search_text: - matched_keywords.append(keyword) - - # print(f"search_text: {search_text}") - # print(f"activation_keywords: {activation_keywords}") - - if matched_keywords: - logger.debug(f"{self.log_prefix}动作 {action_name} 匹配到关键词: {matched_keywords}") - return True - else: - logger.debug(f"{self.log_prefix}动作 {action_name} 未匹配到任何关键词: {activation_keywords}") - return False - - - async def _process_llm_judge_actions_parallel( - self, - llm_judge_actions: Dict[str, Any], - chat_content: str = "", - ) -> Dict[str, bool]: - """ - 并行处理LLM判定actions,支持智能缓存 - - Args: - llm_judge_actions: 需要LLM判定的actions - chat_content: 聊天内容 - - Returns: - Dict[str, bool]: action名称到激活结果的映射 - """ - - # 生成当前上下文的哈希值 - current_context_hash = self._generate_context_hash(chat_content) - current_time = time.time() - - results = {} - tasks_to_run = {} - - # 检查缓存 - for action_name, action_info in llm_judge_actions.items(): - cache_key = f"{action_name}_{current_context_hash}" - - # 检查是否有有效的缓存 - if ( - cache_key in self._llm_judge_cache - and current_time - self._llm_judge_cache[cache_key]["timestamp"] < self._cache_expiry_time - ): - results[action_name] = self._llm_judge_cache[cache_key]["result"] - logger.debug( - f"{self.log_prefix}使用缓存结果 {action_name}: {'激活' if results[action_name] else '未激活'}" - ) - else: - # 需要进行LLM判定 - tasks_to_run[action_name] = action_info - - # 如果有需要运行的任务,并行执行 - if tasks_to_run: - logger.debug(f"{self.log_prefix}并行执行LLM判定,任务数: {len(tasks_to_run)}") - - # 创建并行任务 - tasks = [] - task_names = [] - - for action_name, action_info in tasks_to_run.items(): - task = self._llm_judge_action( - action_name, - action_info, - chat_content, - ) - tasks.append(task) - task_names.append(action_name) - - # 并行执行所有任务 - try: - task_results = await asyncio.gather(*tasks, return_exceptions=True) - - # 处理结果并更新缓存 - for _, (action_name, result) in enumerate(zip(task_names, task_results)): - if isinstance(result, Exception): - logger.error(f"{self.log_prefix}LLM判定action {action_name} 时出错: {result}") - results[action_name] = False - else: - results[action_name] = result - - # 更新缓存 - cache_key = f"{action_name}_{current_context_hash}" - self._llm_judge_cache[cache_key] = {"result": result, "timestamp": current_time} - - logger.debug(f"{self.log_prefix}并行LLM判定完成,耗时: {time.time() - current_time:.2f}s") - - except Exception as e: - logger.error(f"{self.log_prefix}并行LLM判定失败: {e}") - # 如果并行执行失败,为所有任务返回False - for action_name in tasks_to_run.keys(): - results[action_name] = False - - # 清理过期缓存 - self._cleanup_expired_cache(current_time) - - return results - - def get_available_actions_count(self) -> int: - """获取当前可用动作数量(排除默认的no_action)""" - current_actions = self.action_manager.get_using_actions_for_mode("normal") - # 排除no_action(如果存在) - filtered_actions = {k: v for k, v in current_actions.items() if k != "no_action"} - return len(filtered_actions) - - def should_skip_planning(self) -> bool: - """判断是否应该跳过规划过程""" - available_count = self.get_available_actions_count() - if available_count == 0: - logger.debug(f"{self.log_prefix} 没有可用动作,跳过规划") - return True - return False diff --git a/src/chat/normal_chat/normal_chat_utils.py b/src/chat/normal_chat/normal_chat_utils.py deleted file mode 100644 index 2ebd3bdaa..000000000 --- a/src/chat/normal_chat/normal_chat_utils.py +++ /dev/null @@ -1,30 +0,0 @@ -import time -from src.config.config import global_config -from src.common.message_repository import count_messages - - -def get_recent_message_stats(minutes: int = 30, chat_id: str = None) -> dict: - """ - Args: - minutes (int): 检索的分钟数,默认30分钟 - chat_id (str, optional): 指定的chat_id,仅统计该chat下的消息。为None时统计全部。 - Returns: - dict: {"bot_reply_count": int, "total_message_count": int} - """ - - now = time.time() - start_time = now - minutes * 60 - bot_id = global_config.bot.qq_account - - filter_base = {"time": {"$gte": start_time}} - if chat_id is not None: - filter_base["chat_id"] = chat_id - - # 总消息数 - total_message_count = count_messages(filter_base) - # bot自身回复数 - bot_filter = filter_base.copy() - bot_filter["user_id"] = bot_id - bot_reply_count = count_messages(bot_filter) - - return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count} diff --git a/src/chat/focus_chat/planners/action_manager.py b/src/chat/planner_actions/action_manager.py similarity index 98% rename from src/chat/focus_chat/planners/action_manager.py rename to src/chat/planner_actions/action_manager.py index 8dec6889a..c7f9bd6c1 100644 --- a/src/chat/focus_chat/planners/action_manager.py +++ b/src/chat/planner_actions/action_manager.py @@ -292,10 +292,6 @@ class ActionManager: ) self._using_actions = self._default_actions.copy() - def restore_default_actions(self) -> None: - """恢复默认动作集到使用集""" - self._using_actions = self._default_actions.copy() - def add_system_action_if_needed(self, action_name: str) -> bool: """ 根据需要添加系统动作到使用集 diff --git a/src/chat/focus_chat/planners/modify_actions.py b/src/chat/planner_actions/action_modifier.py similarity index 57% rename from src/chat/focus_chat/planners/modify_actions.py rename to src/chat/planner_actions/action_modifier.py index 1ec25567b..c57842ae4 100644 --- a/src/chat/focus_chat/planners/modify_actions.py +++ b/src/chat/planner_actions/action_modifier.py @@ -10,7 +10,8 @@ import random import asyncio import hashlib import time -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.action_manager import ActionManager +from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages logger = get_logger("action_manager") @@ -23,12 +24,13 @@ class ActionModifier: 支持并行判定和智能缓存优化。 """ - log_prefix = "动作处理" - - def __init__(self, action_manager: ActionManager): + def __init__(self, action_manager: ActionManager, chat_id: str): """初始化动作处理器""" + self.chat_id = chat_id + self.chat_stream = get_chat_manager().get_stream(self.chat_id) + self.log_prefix = f"[{get_chat_manager().get_stream_name(self.chat_id) or self.chat_id}]" + self.action_manager = action_manager - self.all_actions = self.action_manager.get_using_actions_for_mode("focus") # 用于LLM判定的小模型 self.llm_judge = LLMRequest( @@ -43,11 +45,12 @@ class ActionModifier: async def modify_actions( self, + mode: str = "focus", observations: Optional[List[Observation]] = None, - **kwargs: Any, + message_content: str = "", ): """ - 完整的动作修改流程,整合传统观察处理和新的激活类型判定 + 动作修改流程,整合传统观察处理和新的激活类型判定 这个方法处理完整的动作管理流程: 1. 基于观察的传统动作修改(循环历史分析、类型匹配等) @@ -57,230 +60,156 @@ class ActionModifier: """ logger.debug(f"{self.log_prefix}开始完整动作修改流程") + removals_s1 = [] + removals_s2 = [] + + self.action_manager.restore_actions() + all_actions = self.action_manager.get_using_actions_for_mode(mode) + + message_list_before_now_half = get_raw_msg_before_timestamp_with_chat( + chat_id=self.chat_stream.stream_id, + timestamp=time.time(), + limit=int(global_config.chat.max_context_size * 0.5), + ) + chat_content = build_readable_messages( + message_list_before_now_half, + replace_bot_name=True, + merge_messages=False, + timestamp_mode="relative", + read_mark=0.0, + show_actions=True, + ) + + if message_content: + chat_content = chat_content + "\n" + f"现在,最新的消息是:{message_content}" + # === 第一阶段:传统观察处理 === - chat_content = None - if observations: - hfc_obs = None - chat_obs = None - - # 收集所有观察对象 for obs in observations: if isinstance(obs, HFCloopObservation): - hfc_obs = obs - if isinstance(obs, ChattingObservation): - chat_obs = obs - chat_content = obs.talking_message_str_truncate_short + # 获取适用于FOCUS模式的动作 + removals_from_loop = await self.analyze_loop_actions(obs) + if removals_from_loop: + removals_s1.extend(removals_from_loop) - # 合并所有动作变更 - merged_action_changes = {"add": [], "remove": []} - reasons = [] + # 检查动作的关联类型 + chat_context = self.chat_stream.context + type_mismatched_actions = self._check_action_associated_types(all_actions, chat_context) - # 处理HFCloopObservation - 传统的循环历史分析 - if hfc_obs: - obs = hfc_obs - # 获取适用于FOCUS模式的动作 - all_actions = self.all_actions - action_changes = await self.analyze_loop_actions(obs) - if action_changes["add"] or action_changes["remove"]: - # 合并动作变更 - merged_action_changes["add"].extend(action_changes["add"]) - merged_action_changes["remove"].extend(action_changes["remove"]) - reasons.append("基于循环历史分析") + if type_mismatched_actions: + removals_s1.extend(type_mismatched_actions) - # 详细记录循环历史分析的变更原因 - for action_name in action_changes["add"]: - logger.info(f"{self.log_prefix}添加动作: {action_name},原因: 循环历史分析建议添加") - for action_name in action_changes["remove"]: - logger.info(f"{self.log_prefix}移除动作: {action_name},原因: 循环历史分析建议移除") + # 应用第一阶段的移除 + for action_name, reason in removals_s1: + self.action_manager.remove_action_from_using(action_name) + logger.debug(f"{self.log_prefix}阶段一移除动作: {action_name},原因: {reason}") - # 处理ChattingObservation - 传统的类型匹配检查 - if chat_obs: - # 检查动作的关联类型 - chat_context = get_chat_manager().get_stream(chat_obs.chat_id).context - type_mismatched_actions = [] - - for action_name in all_actions.keys(): - data = all_actions[action_name] - if data.get("associated_types"): - if not chat_context.check_types(data["associated_types"]): - type_mismatched_actions.append(action_name) - associated_types_str = ", ".join(data["associated_types"]) - logger.info( - f"{self.log_prefix}移除动作: {action_name},原因: 关联类型不匹配(需要: {associated_types_str})" - ) - - if type_mismatched_actions: - # 合并到移除列表中 - merged_action_changes["remove"].extend(type_mismatched_actions) - reasons.append("基于关联类型检查") - - # 应用传统的动作变更到ActionManager - for action_name in merged_action_changes["add"]: - if action_name in self.action_manager.get_registered_actions(): - self.action_manager.add_action_to_using(action_name) - logger.debug(f"{self.log_prefix}应用添加动作: {action_name},原因集合: {reasons}") - - for action_name in merged_action_changes["remove"]: - self.action_manager.remove_action_from_using(action_name) - logger.debug(f"{self.log_prefix}应用移除动作: {action_name},原因集合: {reasons}") - - logger.info( - f"{self.log_prefix}传统动作修改完成,当前使用动作: {list(self.action_manager.get_using_actions().keys())}" - ) - - # 注释:已移除exit_focus_chat动作,现在由no_reply动作处理频率检测退出专注模式 # === 第二阶段:激活类型判定 === - # 如果提供了聊天上下文,则进行激活类型判定 if chat_content is not None: logger.debug(f"{self.log_prefix}开始激活类型判定阶段") - # 获取当前使用的动作集(经过第一阶段处理,且适用于FOCUS模式) - current_using_actions = self.action_manager.get_using_actions() - all_registered_actions = self.action_manager.get_registered_actions() - - # 构建完整的动作信息 - current_actions_with_info = {} - for action_name in current_using_actions.keys(): - if action_name in all_registered_actions: - current_actions_with_info[action_name] = all_registered_actions[action_name] - else: - logger.warning(f"{self.log_prefix}使用中的动作 {action_name} 未在已注册动作中找到") - - # 应用激活类型判定 - final_activated_actions = await self._apply_activation_type_filtering( - current_actions_with_info, + # 获取当前使用的动作集(经过第一阶段处理) + current_using_actions = self.action_manager.get_using_actions_for_mode(mode) + + # 获取因激活类型判定而需要移除的动作 + removals_s2 = await self._get_deactivated_actions_by_type( + current_using_actions, + mode, chat_content, ) - # 更新ActionManager,移除未激活的动作 - actions_to_remove = [] - removal_reasons = {} - - for action_name in current_using_actions.keys(): - if action_name not in final_activated_actions: - actions_to_remove.append(action_name) - # 确定移除原因 - if action_name in all_registered_actions: - action_info = all_registered_actions[action_name] - activation_type = action_info.get("focus_activation_type", "always") - - # 处理字符串格式的激活类型值 - if activation_type == "random": - probability = action_info.get("random_probability", 0.3) - removal_reasons[action_name] = f"RANDOM类型未触发(概率{probability})" - elif activation_type == "llm_judge": - removal_reasons[action_name] = "LLM判定未激活" - elif activation_type == "keyword": - keywords = action_info.get("activation_keywords", []) - removal_reasons[action_name] = f"关键词未匹配(关键词: {keywords})" - else: - removal_reasons[action_name] = "激活判定未通过" - else: - removal_reasons[action_name] = "动作信息不完整" - - for action_name in actions_to_remove: + # 应用第二阶段的移除 + for action_name, reason in removals_s2: self.action_manager.remove_action_from_using(action_name) - reason = removal_reasons.get(action_name, "未知原因") - logger.info(f"{self.log_prefix}移除动作: {action_name},原因: {reason}") - - # 注释:已完全移除exit_focus_chat动作 - - logger.info(f"{self.log_prefix}激活类型判定完成,最终可用动作: {list(final_activated_actions.keys())}") + logger.debug(f"{self.log_prefix}阶段二移除动作: {action_name},原因: {reason}") + + # === 统一日志记录 === + all_removals = removals_s1 + removals_s2 + if all_removals: + removals_summary = " | ".join([f"{name}({reason})" for name, reason in all_removals]) logger.info( - f"{self.log_prefix}完整动作修改流程结束,最终动作集: {list(self.action_manager.get_using_actions().keys())}" + f"{self.log_prefix}{mode}模式动作修改流程结束,最终可用动作: {list(self.action_manager.get_using_actions_for_mode(mode).keys())}||移除记录: {removals_summary}" ) - async def _apply_activation_type_filtering( + def _check_action_associated_types(self, all_actions, chat_context): + type_mismatched_actions = [] + for action_name, data in all_actions.items(): + if data.get("associated_types"): + if not chat_context.check_types(data["associated_types"]): + associated_types_str = ", ".join(data["associated_types"]) + reason = f"适配器不支持(需要: {associated_types_str})" + type_mismatched_actions.append((action_name, reason)) + logger.debug( + f"{self.log_prefix}决定移除动作: {action_name},原因: {reason}" + ) + return type_mismatched_actions + + async def _get_deactivated_actions_by_type( self, actions_with_info: Dict[str, Any], + mode: str = "focus", chat_content: str = "", - ) -> Dict[str, Any]: + ) -> List[tuple[str, str]]: """ - 应用激活类型过滤逻辑,支持四种激活类型的并行处理 + 根据激活类型过滤,返回需要停用的动作列表及原因 Args: actions_with_info: 带完整信息的动作字典 chat_content: 聊天内容 Returns: - Dict[str, Any]: 过滤后激活的actions字典 + List[Tuple[str, str]]: 需要停用的 (action_name, reason) 元组列表 """ - activated_actions = {} + deactivated_actions = [] # 分类处理不同激活类型的actions - always_actions = {} - random_actions = {} llm_judge_actions = {} - keyword_actions = {} + + actions_to_check = list(actions_with_info.items()) + random.shuffle(actions_to_check) - for action_name, action_info in actions_with_info.items(): - activation_type = action_info.get("focus_activation_type", "always") + for action_name, action_info in actions_to_check: + activation_type = f"{mode}_activation_type" + activation_type = action_info.get(activation_type, "always") - # print(f"action_name: {action_name}, activation_type: {activation_type}") - - # 现在统一是字符串格式的激活类型值 if activation_type == "always": - always_actions[action_name] = action_info + continue # 总是激活,无需处理 + elif activation_type == "random": - random_actions[action_name] = action_info + probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY) + if not (random.random() < probability): + reason = f"RANDOM类型未触发(概率{probability})" + deactivated_actions.append((action_name, reason)) + logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}") + + elif activation_type == "keyword": + if not self._check_keyword_activation(action_name, action_info, chat_content): + keywords = action_info.get("activation_keywords", []) + reason = f"关键词未匹配(关键词: {keywords})" + deactivated_actions.append((action_name, reason)) + logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}") + elif activation_type == "llm_judge": llm_judge_actions[action_name] = action_info - elif activation_type == "keyword": - keyword_actions[action_name] = action_info + else: logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理") - # 1. 处理ALWAYS类型(直接激活) - for action_name, action_info in always_actions.items(): - activated_actions[action_name] = action_info - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: ALWAYS类型直接激活") - - # 2. 处理RANDOM类型 - for action_name, action_info in random_actions.items(): - probability = action_info.get("random_activation_probability", ActionManager.DEFAULT_RANDOM_PROBABILITY) - should_activate = random.random() < probability - if should_activate: - activated_actions[action_name] = action_info - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: RANDOM类型触发(概率{probability})") - else: - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: RANDOM类型未触发(概率{probability})") - - # 3. 处理KEYWORD类型(快速判定) - for action_name, action_info in keyword_actions.items(): - should_activate = self._check_keyword_activation( - action_name, - action_info, - chat_content, - ) - if should_activate: - activated_actions[action_name] = action_info - keywords = action_info.get("activation_keywords", []) - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: KEYWORD类型匹配关键词({keywords})") - else: - keywords = action_info.get("activation_keywords", []) - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: KEYWORD类型未匹配关键词({keywords})") - - # 4. 处理LLM_JUDGE类型(并行判定) + # 并行处理LLM_JUDGE类型 if llm_judge_actions: - # 直接并行处理所有LLM判定actions llm_results = await self._process_llm_judge_actions_parallel( llm_judge_actions, chat_content, ) - - # 添加激活的LLM判定actions for action_name, should_activate in llm_results.items(): - if should_activate: - activated_actions[action_name] = llm_judge_actions[action_name] - logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: LLM_JUDGE类型判定通过") - else: - logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: LLM_JUDGE类型判定未通过") + if not should_activate: + reason = "LLM判定未激活" + deactivated_actions.append((action_name, reason)) + logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}") - logger.debug(f"{self.log_prefix}激活类型过滤完成: {list(activated_actions.keys())}") - return activated_actions + return deactivated_actions async def process_actions_for_planner( self, observed_messages_str: str = "", chat_context: Optional[str] = None, extra_context: Optional[str] = None @@ -538,22 +467,19 @@ class ActionModifier: logger.debug(f"{self.log_prefix}动作 {action_name} 未匹配到任何关键词: {activation_keywords}") return False - async def analyze_loop_actions(self, obs: HFCloopObservation) -> Dict[str, List[str]]: - """分析最近的循环内容并决定动作的增减 + async def analyze_loop_actions(self, obs: HFCloopObservation) -> List[tuple[str, str]]: + """分析最近的循环内容并决定动作的移除 Returns: - Dict[str, List[str]]: 包含要增加和删除的动作 - { - "add": ["action1", "action2"], - "remove": ["action3"] - } + List[Tuple[str, str]]: 包含要删除的动作及原因的元组列表 + [("action3", "some reason")] """ - result = {"add": [], "remove": []} + removals = [] # 获取最近10次循环 recent_cycles = obs.history_loop[-10:] if len(obs.history_loop) > 10 else obs.history_loop if not recent_cycles: - return result + return removals reply_sequence = [] # 记录最近的动作序列 @@ -584,36 +510,39 @@ class ActionModifier: # 根据最近的reply情况决定是否移除reply动作 if len(last_max_reply_num) >= max_reply_num and all(last_max_reply_num): # 如果最近max_reply_num次都是reply,直接移除 - result["remove"].append("reply") + reason = f"连续回复过多(最近{len(last_max_reply_num)}次全是reply,超过阈值{max_reply_num})" + removals.append(("reply", reason)) # reply_count = len(last_max_reply_num) - no_reply_count - logger.info( - f"{self.log_prefix}移除reply动作,原因: 连续回复过多(最近{len(last_max_reply_num)}次全是reply,超过阈值{max_reply_num})" - ) elif len(last_max_reply_num) >= sec_thres_reply_num and all(last_max_reply_num[-sec_thres_reply_num:]): # 如果最近sec_thres_reply_num次都是reply,40%概率移除 removal_probability = 0.4 / global_config.focus_chat.consecutive_replies if random.random() < removal_probability: - result["remove"].append("reply") - logger.info( - f"{self.log_prefix}移除reply动作,原因: 连续回复较多(最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)" - ) - else: - logger.debug( - f"{self.log_prefix}连续回复检测:最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,未触发" - ) + reason = f"连续回复较多(最近{sec_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)" + removals.append(("reply", reason)) elif len(last_max_reply_num) >= one_thres_reply_num and all(last_max_reply_num[-one_thres_reply_num:]): # 如果最近one_thres_reply_num次都是reply,20%概率移除 removal_probability = 0.2 / global_config.focus_chat.consecutive_replies if random.random() < removal_probability: - result["remove"].append("reply") - logger.info( - f"{self.log_prefix}移除reply动作,原因: 连续回复检测(最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)" - ) - else: - logger.debug( - f"{self.log_prefix}连续回复检测:最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,未触发" - ) + reason = f"连续回复检测(最近{one_thres_reply_num}次全是reply,{removal_probability:.2f}概率移除,触发移除)" + removals.append(("reply", reason)) else: logger.debug(f"{self.log_prefix}连续回复检测:无需移除reply动作,最近回复模式正常") - return result + return removals + + + + def get_available_actions_count(self) -> int: + """获取当前可用动作数量(排除默认的no_action)""" + current_actions = self.action_manager.get_using_actions_for_mode("normal") + # 排除no_action(如果存在) + filtered_actions = {k: v for k, v in current_actions.items() if k != "no_action"} + return len(filtered_actions) + + def should_skip_planning(self) -> bool: + """判断是否应该跳过规划过程""" + available_count = self.get_available_actions_count() + if available_count == 0: + logger.debug(f"{self.log_prefix} 没有可用动作,跳过规划") + return True + return False \ No newline at end of file diff --git a/src/chat/focus_chat/planners/planner_simple.py b/src/chat/planner_actions/planner_focus.py similarity index 97% rename from src/chat/focus_chat/planners/planner_simple.py rename to src/chat/planner_actions/planner_focus.py index 8b06c7bed..bb3bdcacd 100644 --- a/src/chat/focus_chat/planners/planner_simple.py +++ b/src/chat/planner_actions/planner_focus.py @@ -9,9 +9,8 @@ from src.chat.focus_chat.info.obs_info import ObsInfo from src.chat.focus_chat.info.action_info import ActionInfo from src.common.logger import get_logger from src.chat.utils.prompt_builder import Prompt, global_prompt_manager -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.action_manager import ActionManager from json_repair import repair_json -from src.chat.focus_chat.planners.base_planner import BasePlanner from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info from datetime import datetime @@ -69,9 +68,10 @@ def init_prompt(): ) -class ActionPlanner(BasePlanner): +class ActionPlanner: def __init__(self, log_prefix: str, action_manager: ActionManager): - super().__init__(log_prefix, action_manager) + self.log_prefix = log_prefix + self.action_manager = action_manager # LLM规划器配置 self.planner_llm = LLMRequest( model=global_config.model.planner, @@ -84,7 +84,7 @@ class ActionPlanner(BasePlanner): ) async def plan( - self, all_plan_info: List[InfoBase], running_memorys: List[Dict[str, Any]], loop_start_time: float + self, all_plan_info: List[InfoBase],loop_start_time: float ) -> Dict[str, Any]: """ 规划器 (Planner): 使用LLM根据上下文决定做出什么动作。 diff --git a/src/chat/normal_chat/normal_chat_planner.py b/src/chat/planner_actions/planner_normal.py similarity index 99% rename from src/chat/normal_chat/normal_chat_planner.py rename to src/chat/planner_actions/planner_normal.py index 83d12caa1..fce446b58 100644 --- a/src/chat/normal_chat/normal_chat_planner.py +++ b/src/chat/planner_actions/planner_normal.py @@ -6,7 +6,7 @@ from src.config.config import global_config from src.common.logger import get_logger from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.individuality.individuality import get_individuality -from src.chat.focus_chat.planners.action_manager import ActionManager +from src.chat.planner_actions.action_manager import ActionManager from src.chat.message_receive.message import MessageThinking from json_repair import repair_json from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat diff --git a/src/common/logger.py b/src/common/logger.py index 30a2e4bd7..c0fa7be2d 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -340,7 +340,7 @@ MODULE_COLORS = { "memory": "\033[34m", "hfc": "\033[96m", "base_action": "\033[96m", - "action_manager": "\033[34m", + "action_manager": "\033[32m", # 关系系统 "relation": "\033[38;5;201m", # 深粉色 # 聊天相关模块 @@ -414,7 +414,7 @@ MODULE_COLORS = { "confirm": "\033[1;93m", # 黄色+粗体 # 模型相关 "model_utils": "\033[38;5;164m", # 紫红色 - + "relationship_fetcher": "\033[38;5;170m", # 浅紫色 "relationship_builder": "\033[38;5;117m", # 浅蓝色 } diff --git a/src/config/official_configs.py b/src/config/official_configs.py index a07bc25f5..1c28ab7c8 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -273,12 +273,6 @@ class MessageReceiveConfig(ConfigBase): class NormalChatConfig(ConfigBase): """普通聊天配置类""" - message_buffer: bool = False - """消息缓冲器""" - - emoji_chance: float = 0.2 - """发送表情包的基础概率""" - willing_mode: str = "classical" """意愿模式""" @@ -295,14 +289,6 @@ class NormalChatConfig(ConfigBase): enable_planner: bool = False """是否启用动作规划器""" - gather_timeout: int = 110 # planner和generator的并行执行超时时间 - """planner和generator的并行执行超时时间""" - - auto_focus_threshold: float = 1.0 # 自动切换到专注模式的阈值,值越大越难触发 - """自动切换到专注模式的阈值,值越大越难触发""" - - fatigue_talk_frequency: float = 0.2 # 疲劳模式下的基础对话频率 (条/分钟) - """疲劳模式下的基础对话频率 (条/分钟)""" @dataclass @@ -362,6 +348,12 @@ class ToolConfig(ConfigBase): @dataclass class EmojiConfig(ConfigBase): """表情包配置类""" + + emoji_chance: float = 0.6 + """发送表情包的基础概率""" + + emoji_activate_type: str = "random" + """表情包激活类型,可选:random,llm,random下,表情包动作随机启用,llm下,表情包动作根据llm判断是否启用""" max_reg_num: int = 200 """表情包最大注册数量""" diff --git a/src/person_info/relationship_fetcher.py b/src/person_info/relationship_fetcher.py index 006f99e18..e2bde69de 100644 --- a/src/person_info/relationship_fetcher.py +++ b/src/person_info/relationship_fetcher.py @@ -142,7 +142,7 @@ class RelationshipFetcher: # 检查是否返回了不需要查询的标志 if "none" in content_json: - logger.info(f"{self.log_prefix} LLM判断当前不需要查询任何信息:{content_json.get('none', '')}") + logger.debug(f"{self.log_prefix} LLM判断当前不需要查询任何信息:{content_json.get('none', '')}") return None info_type = content_json.get("info_type") diff --git a/src/plugin_system/apis/emoji_api.py b/src/plugin_system/apis/emoji_api.py index 3fdcf1b55..33c0f23d7 100644 --- a/src/plugin_system/apis/emoji_api.py +++ b/src/plugin_system/apis/emoji_api.py @@ -31,7 +31,7 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]] Optional[Tuple[str, str, str]]: (base64编码, 表情包描述, 匹配的情感标签) 或 None """ try: - logger.info(f"[EmojiAPI] 根据描述获取表情包: {description}") + logger.debug(f"[EmojiAPI] 根据描述获取表情包: {description}") emoji_manager = get_emoji_manager() emoji_result = await emoji_manager.get_emoji_for_text(description) @@ -47,7 +47,7 @@ async def get_by_description(description: str) -> Optional[Tuple[str, str, str]] logger.error(f"[EmojiAPI] 无法将表情包文件转换为base64: {emoji_path}") return None - logger.info(f"[EmojiAPI] 成功获取表情包: {emoji_description}, 匹配情感: {matched_emotion}") + logger.debug(f"[EmojiAPI] 成功获取表情包: {emoji_description}, 匹配情感: {matched_emotion}") return emoji_base64, emoji_description, matched_emotion except Exception as e: diff --git a/src/plugin_system/apis/send_api.py b/src/plugin_system/apis/send_api.py index d9b1eff7c..c0486e164 100644 --- a/src/plugin_system/apis/send_api.py +++ b/src/plugin_system/apis/send_api.py @@ -116,7 +116,7 @@ async def _send_to_target( ) if sent_msg: - logger.info(f"[SendAPI] 成功发送消息到 {stream_id}") + logger.debug(f"[SendAPI] 成功发送消息到 {stream_id}") return True else: logger.error("[SendAPI] 发送消息失败") diff --git a/src/plugins/built_in/core_actions/emoji.py b/src/plugins/built_in/core_actions/emoji.py index c1fe0f0fb..128214427 100644 --- a/src/plugins/built_in/core_actions/emoji.py +++ b/src/plugins/built_in/core_actions/emoji.py @@ -18,7 +18,7 @@ class EmojiAction(BaseAction): """表情动作 - 发送表情包""" # 激活设置 - focus_activation_type = ActionActivationType.LLM_JUDGE + focus_activation_type = ActionActivationType.RANDOM normal_activation_type = ActionActivationType.RANDOM mode_enable = ChatMode.ALL parallel_action = True diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index a96d3ab13..2b7194063 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -180,8 +180,15 @@ class CoreActionsPlugin(BasePlugin): """返回插件包含的组件列表""" # --- 从配置动态设置Action/Command --- - emoji_chance = global_config.normal_chat.emoji_chance - EmojiAction.random_activation_probability = emoji_chance + emoji_chance = global_config.emoji.emoji_chance + if global_config.emoji.emoji_activate_type == "random": + EmojiAction.random_activation_probability = emoji_chance + EmojiAction.focus_activation_type = ActionActivationType.RANDOM + EmojiAction.normal_activation_type = ActionActivationType.RANDOM + elif global_config.emoji.emoji_activate_type == "llm": + EmojiAction.random_activation_probability = 0.0 + EmojiAction.focus_activation_type = ActionActivationType.LLM_JUDGE + EmojiAction.normal_activation_type = ActionActivationType.LLM_JUDGE no_reply_probability = self.get_config("no_reply.random_probability", 0.8) NoReplyAction.random_activation_probability = no_reply_probability diff --git a/src/tools/tool_executor.py b/src/tools/tool_executor.py index 34a35ae3b..b43dfcff3 100644 --- a/src/tools/tool_executor.py +++ b/src/tools/tool_executor.py @@ -128,7 +128,8 @@ class ToolExecutor: if tool_results: self._set_cache(cache_key, tool_results) - logger.info(f"{self.log_prefix}工具执行完成,共执行{len(used_tools)}个工具: {used_tools}") + if used_tools: + logger.info(f"{self.log_prefix}工具执行完成,共执行{len(used_tools)}个工具: {used_tools}") if return_details: return tool_results, used_tools, prompt diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 40ab3b36f..478d62ed8 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "3.3.0" +version = "3.4.0" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #如果你想要修改配置文件,请在修改后将version的值进行变更 @@ -118,7 +118,6 @@ ban_msgs_regex = [ [normal_chat] #普通聊天 #一般回复参数 -emoji_chance = 0.2 # 麦麦一般回复时使用表情包的概率 willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现) response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数 mentioned_bot_inevitable_reply = true # 提及 bot 必然回复 @@ -137,6 +136,9 @@ enable_in_normal_chat = false # 是否在普通聊天中启用工具 enable_in_focus_chat = true # 是否在专注聊天中启用工具 [emoji] +emoji_chance = 0.6 # 麦麦激活表情包动作的概率 +emoji_activate_type = "random" # 表情包激活类型,可选:random,llm ; random下,表情包动作随机启用,llm下,表情包动作根据llm判断是否启用 + max_reg_num = 60 # 表情包最大注册数量 do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包 check_interval = 10 # 检查表情包(注册,破损,删除)的时间间隔(分钟)