diff --git a/src/chat/focus_chat/expressors/default_expressor.py b/src/chat/focus_chat/expressors/default_expressor.py index 2681359f9..66caf1619 100644 --- a/src/chat/focus_chat/expressors/default_expressor.py +++ b/src/chat/focus_chat/expressors/default_expressor.py @@ -72,7 +72,7 @@ def init_prompt(): class DefaultExpressor: - def __init__(self, chat_id: str): + def __init__(self, chat_stream: ChatStream): self.log_prefix = "expressor" # TODO: API-Adapter修改标记 self.express_model = LLMRequest( @@ -83,13 +83,9 @@ class DefaultExpressor: ) self.heart_fc_sender = HeartFCSender() - self.chat_id = chat_id - self.chat_stream: Optional[ChatStream] = None - self.is_group_chat = True - self.chat_target_info = None - - async def initialize(self): - self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id) + self.chat_id = chat_stream.stream_id + self.chat_stream = chat_stream + self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id) async def _create_thinking_message(self, anchor_message: Optional[MessageRecv], thinking_id: str): """创建思考消息 (尝试锚定到 anchor_message)""" @@ -285,7 +281,7 @@ class DefaultExpressor: timestamp=time.time(), limit=global_config.focus_chat.observation_context_size, ) - chat_talking_prompt = await build_readable_messages( + chat_talking_prompt = build_readable_messages( message_list_before_now, replace_bot_name=True, merge_messages=True, diff --git a/src/chat/focus_chat/expressors/exprssion_learner.py b/src/chat/focus_chat/expressors/exprssion_learner.py index dc862f96b..68d03a602 100644 --- a/src/chat/focus_chat/expressors/exprssion_learner.py +++ b/src/chat/focus_chat/expressors/exprssion_learner.py @@ -223,7 +223,7 @@ class ExpressionLearner: return None # 转化成str chat_id: str = random_msg[0]["chat_id"] - # random_msg_str: str = await build_readable_messages(random_msg, timestamp_mode="normal") + # random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal") random_msg_str: str = await build_anonymous_messages(random_msg) # print(f"random_msg_str:{random_msg_str}") diff --git a/src/chat/focus_chat/heartFC_Cycleinfo.py b/src/chat/focus_chat/heartFC_Cycleinfo.py index 134c808bb..b8fc1ef22 100644 --- a/src/chat/focus_chat/heartFC_Cycleinfo.py +++ b/src/chat/focus_chat/heartFC_Cycleinfo.py @@ -2,6 +2,7 @@ import time import os from typing import Optional, Dict, Any from src.common.logger_manager import get_logger +import json logger = get_logger("hfc") # Logger Name Changed @@ -111,14 +112,14 @@ class CycleDetail: dir_name = "".join( char for char in dir_name if char.isalnum() or char in ["_", "-", "/"] or "\u4e00" <= char <= "\u9fff" ) - print("dir_name:", dir_name) + # print("dir_name:", dir_name) if dir_name and not os.path.exists(dir_name): os.makedirs(dir_name, exist_ok=True) # 写入文件 - import json + file_path = os.path.join(dir_name, os.path.basename(file_path)) - print("file_path:", file_path) + # print("file_path:", file_path) with open(file_path, "a", encoding="utf-8") as f: f.write(json.dumps(self.to_dict(), ensure_ascii=False) + "\n") diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index bc4f95041..53e213acb 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -21,6 +21,7 @@ from src.chat.focus_chat.info_processors.working_memory_processor import Working # from src.chat.focus_chat.info_processors.action_processor import ActionProcessor from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation +from src.chat.heart_flow.observation.chatting_observation import ChattingObservation from src.chat.heart_flow.observation.structure_observation import StructureObservation from src.chat.heart_flow.observation.actions_observation import ActionObservation from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor @@ -37,9 +38,15 @@ from src.config.config import global_config install(extra_lines=3) +# 定义观察器映射:键是观察器名称,值是 (观察器类, 初始化参数) +OBSERVATION_CLASSES = { + "ChattingObservation": (ChattingObservation, "chat_id"), + "WorkingMemoryObservation": (WorkingMemoryObservation, "observe_id"), + "HFCloopObservation": (HFCloopObservation, "observe_id"), + "StructureObservation": (StructureObservation, "observe_id"), +} # 定义处理器映射:键是处理器名称,值是 (处理器类, 可选的配置键名) -# 如果配置键名为 None,则该处理器默认启用且不能通过 focus_chat_processor 配置禁用 PROCESSOR_CLASSES = { "ChattingInfoProcessor": (ChattingInfoProcessor, None), "MindProcessor": (MindProcessor, "mind_processor"), @@ -81,7 +88,6 @@ class HeartFChatting: def __init__( self, chat_id: str, - observations: list[Observation], on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None, ): """ @@ -89,53 +95,44 @@ class HeartFChatting: 参数: chat_id: 聊天流唯一标识符(如stream_id) - observations: 关联的观察列表 on_stop_focus_chat: 当收到stop_focus_chat命令时调用的回调函数 """ # 基础属性 self.stream_id: str = chat_id # 聊天流ID - self.chat_stream: Optional[ChatStream] = None # 关联的聊天流 - self.log_prefix: str = str(chat_id) # Initial default, will be updated - self.hfcloop_observation = HFCloopObservation(observe_id=self.stream_id) - self.chatting_observation = observations[0] - self.structure_observation = StructureObservation(observe_id=self.stream_id) - + self.chat_stream = chat_manager.get_stream(self.stream_id) + self.log_prefix = f"[{chat_manager.get_stream_name(self.stream_id) or self.stream_id}]" + self.memory_activator = MemoryActivator() - self.working_memory = WorkingMemory(chat_id=self.stream_id) - self.working_observation = WorkingMemoryObservation( - observe_id=self.stream_id, working_memory=self.working_memory - ) - + + # 初始化观察器 + self.observations: List[Observation] = [] + self._register_observations() + # 根据配置文件和默认规则确定启用的处理器 - self.enabled_processor_names: List[str] = [] config_processor_settings = global_config.focus_chat_processor + self.enabled_processor_names = [ + proc_name for proc_name, (_proc_class, config_key) in PROCESSOR_CLASSES.items() + if not config_key or getattr(config_processor_settings, config_key, True) + ] - for proc_name, (_proc_class, config_key) in PROCESSOR_CLASSES.items(): - if config_key: # 此处理器可通过配置控制 - if getattr(config_processor_settings, config_key, True): # 默认启用 (如果配置中未指定该键) - self.enabled_processor_names.append(proc_name) - else: # 此处理器不在配置映射中 (config_key is None),默认启用 - self.enabled_processor_names.append(proc_name) - - logger.info(f"{self.log_prefix} 将启用的处理器: {self.enabled_processor_names}") + # logger.info(f"{self.log_prefix} 将启用的处理器: {self.enabled_processor_names}") + self.processors: List[BaseProcessor] = [] self._register_default_processors() - self.expressor = DefaultExpressor(chat_id=self.stream_id) - self.replyer = DefaultReplyer(chat_id=self.stream_id) + self.expressor = DefaultExpressor(chat_stream=self.chat_stream) + self.replyer = DefaultReplyer(chat_stream=self.chat_stream) + + self.action_manager = ActionManager() self.action_planner = PlannerFactory.create_planner( log_prefix=self.log_prefix, action_manager=self.action_manager ) self.action_modifier = ActionModifier(action_manager=self.action_manager) self.action_observation = ActionObservation(observe_id=self.stream_id) - self.action_observation.set_action_manager(self.action_manager) - self.all_observations = observations - # 初始化状态控制 - self._initialized = False self._processing_lock = asyncio.Lock() # 循环控制内部状态 @@ -151,41 +148,24 @@ class HeartFChatting: # 存储回调函数 self.on_stop_focus_chat = on_stop_focus_chat - async def _initialize(self) -> bool: - """ - 执行懒初始化操作 + def _register_observations(self): + """注册所有观察器""" + self.observations = [] # 清空已有的 - 功能: - 1. 获取聊天类型(群聊/私聊)和目标信息 - 2. 获取聊天流对象 - 3. 设置日志前缀 + for name, (observation_class, param_name) in OBSERVATION_CLASSES.items(): + try: + # 根据参数名使用正确的参数 + kwargs = {param_name: self.stream_id} + observation = observation_class(**kwargs) + self.observations.append(observation) + logger.debug(f"{self.log_prefix} 注册观察器 {name}") + except Exception as e: + logger.error(f"{self.log_prefix} 观察器 {name} 构造失败: {e}") - 返回: - bool: 初始化是否成功 - - 注意: - - 如果已经初始化过会直接返回True - - 需要获取chat_stream对象才能继续后续操作 - """ - # 如果已经初始化过,直接返回成功 - if self._initialized: - return True - - try: - await self.expressor.initialize() - await self.replyer.initialize() - self.chat_stream = await asyncio.to_thread(chat_manager.get_stream, self.stream_id) - self.expressor.chat_stream = self.chat_stream - self.replyer.chat_stream = self.chat_stream - self.log_prefix = f"[{chat_manager.get_stream_name(self.stream_id) or self.stream_id}]" - except Exception as e: - logger.error(f"[HFC:{self.stream_id}] 初始化HFC时发生错误: {e}") - return False - - # 标记初始化完成 - self._initialized = True - logger.debug(f"{self.log_prefix} 初始化完成,准备开始处理消息") - return True + if self.observations: + logger.info(f"{self.log_prefix} 已注册观察器: {[o.__class__.__name__ for o in self.observations]}") + else: + logger.warning(f"{self.log_prefix} 没有注册任何观察器") def _register_default_processors(self): """根据 self.enabled_processor_names 注册信息处理器""" @@ -218,20 +198,12 @@ class HeartFChatting: if self.processors: logger.info( - f"{self.log_prefix} 已根据配置和默认规则注册处理器: {[p.__class__.__name__ for p in self.processors]}" + f"{self.log_prefix} 已注册处理器: {[p.__class__.__name__ for p in self.processors]}" ) else: logger.warning(f"{self.log_prefix} 没有注册任何处理器。这可能是由于配置错误或所有处理器都被禁用了。") async def start(self): - """ - 启动 HeartFChatting 的主循环。 - 注意:调用此方法前必须确保已经成功初始化。 - """ - logger.info(f"{self.log_prefix} 开始认真聊天(HFC)...") - await self._start_loop_if_needed() - - async def _start_loop_if_needed(self): """检查是否需要启动主循环,如果未激活则启动。""" # 如果循环已经激活,直接返回 if self._loop_active: @@ -313,7 +285,13 @@ class HeartFChatting: self._current_cycle_detail.set_loop_info(loop_info) - self.hfcloop_observation.add_loop_info(self._current_cycle_detail) + # 从observations列表中获取HFCloopObservation + hfcloop_observation = next((obs for obs in self.observations if isinstance(obs, HFCloopObservation)), None) + if hfcloop_observation: + hfcloop_observation.add_loop_info(self._current_cycle_detail) + else: + logger.warning(f"{self.log_prefix} 未找到HFCloopObservation实例") + self._current_cycle_detail.timers = cycle_timers # 防止循环过快消耗资源 @@ -457,35 +435,27 @@ class HeartFChatting: async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> dict: try: with Timer("观察", cycle_timers): - await self.chatting_observation.observe() - await self.working_observation.observe() - await self.hfcloop_observation.observe() - await self.structure_observation.observe() - observations: List[Observation] = [] - observations.append(self.chatting_observation) - observations.append(self.working_observation) - observations.append(self.hfcloop_observation) - observations.append(self.structure_observation) + # 执行所有观察器的观察 + for observation in self.observations: + await observation.observe() loop_observation_info = { - "observations": observations, + "observations": self.observations, } - self.all_observations = observations - with Timer("调整动作", cycle_timers): # 处理特殊的观察 - await self.action_modifier.modify_actions(observations=observations) + await self.action_modifier.modify_actions(observations=self.observations) await self.action_observation.observe() - observations.append(self.action_observation) + self.observations.append(self.action_observation) # 根据配置决定是否并行执行回忆和处理器阶段 # print(global_config.focus_chat.parallel_processing) if global_config.focus_chat.parallel_processing: # 并行执行回忆和处理器阶段 with Timer("并行回忆和处理", cycle_timers): - memory_task = asyncio.create_task(self.memory_activator.activate_memory(observations)) - processor_task = asyncio.create_task(self._process_processors(observations, [])) + memory_task = asyncio.create_task(self.memory_activator.activate_memory(self.observations)) + processor_task = asyncio.create_task(self._process_processors(self.observations, [])) # 等待两个任务完成 running_memorys, (all_plan_info, processor_time_costs) = await asyncio.gather( @@ -494,10 +464,10 @@ class HeartFChatting: else: # 串行执行 with Timer("回忆", cycle_timers): - running_memorys = await self.memory_activator.activate_memory(observations) + running_memorys = await self.memory_activator.activate_memory(self.observations) with Timer("执行 信息处理器", cycle_timers): - all_plan_info, processor_time_costs = await self._process_processors(observations, running_memorys) + all_plan_info, processor_time_costs = await self._process_processors(self.observations, running_memorys) loop_processor_info = { "all_plan_info": all_plan_info, @@ -530,7 +500,7 @@ class HeartFChatting: logger.debug(f"{self.log_prefix} 麦麦想要:'{action_str}'") success, reply_text, command = await self._handle_action( - action_type, reasoning, action_data, cycle_timers, thinking_id + action_type, reasoning, action_data, cycle_timers, thinking_id, self.observations ) loop_action_info = { @@ -566,6 +536,7 @@ class HeartFChatting: action_data: dict, cycle_timers: dict, thinking_id: str, + observations: List[Observation], ) -> tuple[bool, str, str]: """ 处理规划动作,使用动作工厂创建相应的动作处理器 @@ -589,7 +560,7 @@ class HeartFChatting: reasoning=reasoning, cycle_timers=cycle_timers, thinking_id=thinking_id, - observations=self.all_observations, + observations=observations, expressor=self.expressor, replyer=self.replyer, chat_stream=self.chat_stream, @@ -613,7 +584,7 @@ class HeartFChatting: success, reply_text = result command = "" logger.debug( - f"{self.log_prefix} 麦麦执行了'{action}', 原因'{reasoning}',返回结果'{success}', '{reply_text}', '{command}'" + f"{self.log_prefix} 麦麦执行了'{action}', 返回结果'{success}', '{reply_text}', '{command}'" ) return success, reply_text, command diff --git a/src/chat/focus_chat/heartflow_message_processor.py b/src/chat/focus_chat/heartflow_message_processor.py index 480ce70d5..10c7682b5 100644 --- a/src/chat/focus_chat/heartflow_message_processor.py +++ b/src/chat/focus_chat/heartflow_message_processor.py @@ -180,8 +180,6 @@ class HeartFCMessageReceiver: userinfo = message.message_info.user_info messageinfo = message.message_info - # 2. 消息缓冲与流程序化 - # await message_buffer.start_caching_messages(message) chat = await chat_manager.get_or_create_stream( platform=messageinfo.platform, @@ -199,21 +197,8 @@ class HeartFCMessageReceiver: ): return - # 4. 缓冲检查 - # buffer_result = await message_buffer.query_buffer_result(message) - # if not buffer_result: - # msg_type = _get_message_type(message) - # type_messages = { - # "text": f"触发缓冲,消息:{message.processed_plain_text}", - # "image": "触发缓冲,表情包/图片等待中", - # "seglist": "触发缓冲,消息列表等待中", - # } - # logger.debug(type_messages.get(msg_type, "触发未知类型缓冲")) - # return - # 5. 消息存储 await self.storage.store_message(message, chat) - logger.trace(f"存储成功: {message.processed_plain_text}") # 6. 兴趣度计算与更新 interested_rate, is_mentioned = await _calculate_interest(message) diff --git a/src/chat/focus_chat/replyer/default_replyer.py b/src/chat/focus_chat/replyer/default_replyer.py index 6335cc9c4..633930d21 100644 --- a/src/chat/focus_chat/replyer/default_replyer.py +++ b/src/chat/focus_chat/replyer/default_replyer.py @@ -83,7 +83,7 @@ def init_prompt(): class DefaultReplyer: - def __init__(self, chat_id: str): + def __init__(self, chat_stream: ChatStream): self.log_prefix = "replyer" # TODO: API-Adapter修改标记 self.express_model = LLMRequest( @@ -94,13 +94,9 @@ class DefaultReplyer: ) self.heart_fc_sender = HeartFCSender() - self.chat_id = chat_id - self.chat_stream: Optional[ChatStream] = None - self.is_group_chat = True - self.chat_target_info = None - - async def initialize(self): - self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id) + self.chat_id = chat_stream.stream_id + self.chat_stream = chat_stream + self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id) async def _create_thinking_message(self, anchor_message: Optional[MessageRecv], thinking_id: str): """创建思考消息 (尝试锚定到 anchor_message)""" @@ -347,7 +343,7 @@ class DefaultReplyer: timestamp=time.time(), limit=global_config.focus_chat.observation_context_size, ) - chat_talking_prompt = await build_readable_messages( + chat_talking_prompt = build_readable_messages( message_list_before_now, replace_bot_name=True, merge_messages=True, diff --git a/src/chat/heart_flow/observation/chatting_observation.py b/src/chat/heart_flow/observation/chatting_observation.py index c57e3a49c..eeb7ee7f1 100644 --- a/src/chat/heart_flow/observation/chatting_observation.py +++ b/src/chat/heart_flow/observation/chatting_observation.py @@ -45,10 +45,7 @@ class ChattingObservation(Observation): self.chat_id = chat_id self.platform = "qq" - # --- Initialize attributes (defaults) --- - self.is_group_chat: bool = False - self.chat_target_info: Optional[dict] = None - # --- End Initialization --- + self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id) # --- Other attributes initialized in __init__ --- self.talking_message = [] @@ -65,6 +62,12 @@ class ChattingObservation(Observation): self.oldest_messages = [] self.oldest_messages_str = "" self.compressor_prompt = "" + + initial_messages = get_raw_msg_before_timestamp_with_chat(self.chat_id, self.last_observe_time, 10) + self.last_observe_time = initial_messages[-1]["time"] if initial_messages else self.last_observe_time + self.talking_message = initial_messages + self.talking_message_str = build_readable_messages(self.talking_message) + def to_dict(self) -> dict: """将观察对象转换为可序列化的字典""" @@ -84,16 +87,6 @@ class ChattingObservation(Observation): "last_observe_time": self.last_observe_time, } - async def initialize(self): - self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id) - logger.debug(f"初始化observation: self.is_group_chat: {self.is_group_chat}") - logger.debug(f"初始化observation: self.chat_target_info: {self.chat_target_info}") - initial_messages = get_raw_msg_before_timestamp_with_chat(self.chat_id, self.last_observe_time, 10) - self.last_observe_time = initial_messages[-1]["time"] if initial_messages else self.last_observe_time - # logger.error(f"初始化observation: initial_messages: {initial_messages}\n\n\n\n{self.last_observe_time}") - self.talking_message = initial_messages - self.talking_message_str = await build_readable_messages(self.talking_message) - # 进行一次观察 返回观察结果observe_info def get_observe_info(self, ids=None): mid_memory_str = "" @@ -226,7 +219,7 @@ class ChattingObservation(Observation): self.talking_message = self.talking_message[messages_to_remove_count:] # 保留后半部分,即最新的 # print(f"压缩中:oldest_messages: {oldest_messages}") - oldest_messages_str = await build_readable_messages( + oldest_messages_str = build_readable_messages( messages=oldest_messages, timestamp_mode="normal_no_YMD", read_mark=0 ) @@ -270,13 +263,13 @@ class ChattingObservation(Observation): # 构建中 # print(f"构建中:self.talking_message: {self.talking_message}") - self.talking_message_str = await build_readable_messages( + self.talking_message_str = build_readable_messages( messages=self.talking_message, timestamp_mode="lite", read_mark=last_obs_time_mark, ) # print(f"构建中:self.talking_message_str: {self.talking_message_str}") - self.talking_message_str_truncate = await build_readable_messages( + self.talking_message_str_truncate = build_readable_messages( messages=self.talking_message, timestamp_mode="normal_no_YMD", read_mark=last_obs_time_mark, diff --git a/src/chat/heart_flow/observation/working_observation.py b/src/chat/heart_flow/observation/working_observation.py index e94343b01..8cb4a6d3a 100644 --- a/src/chat/heart_flow/observation/working_observation.py +++ b/src/chat/heart_flow/observation/working_observation.py @@ -12,12 +12,12 @@ logger = get_logger("observation") # 所有观察的基类 class WorkingMemoryObservation: - def __init__(self, observe_id, working_memory: WorkingMemory): + def __init__(self, observe_id): self.observe_info = "" self.observe_id = observe_id self.last_observe_time = datetime.now().timestamp() - self.working_memory = working_memory + self.working_memory = WorkingMemory(chat_id=observe_id) self.retrieved_working_memory = [] diff --git a/src/chat/heart_flow/sub_heartflow.py b/src/chat/heart_flow/sub_heartflow.py index 8a488c576..3cfa829e1 100644 --- a/src/chat/heart_flow/sub_heartflow.py +++ b/src/chat/heart_flow/sub_heartflow.py @@ -41,11 +41,10 @@ class SubHeartflow: self.chat_state_last_time: float = 0 self.history_chat_state: List[Tuple[ChatState, float]] = [] - # --- Initialize attributes --- - self.is_group_chat: bool = False - self.chat_target_info: Optional[dict] = None - # --- End Initialization --- - + self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.chat_id) + self.log_prefix = ( + chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id + ) # 兴趣消息集合 self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {} @@ -60,7 +59,7 @@ class SubHeartflow: # 观察,目前只有聊天观察,可以载入多个 # 负责对处理过的消息进行观察 - self.observations: List[ChattingObservation] = [] # 观察列表 + # self.observations: List[ChattingObservation] = [] # 观察列表 # self.running_knowledges = [] # 运行中的知识,待完善 # 日志前缀 - Moved determination to initialize @@ -69,16 +68,6 @@ class SubHeartflow: async def initialize(self): """异步初始化方法,创建兴趣流并确定聊天类型""" - # --- Use utility function to determine chat type and fetch info --- - self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id) - # Update log prefix after getting info (potential stream name) - self.log_prefix = ( - chat_manager.get_stream_name(self.subheartflow_id) or self.subheartflow_id - ) # Keep this line or adjust if utils provides name - # logger.debug( - # f"SubHeartflow {self.chat_id} initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}" - # ) - # 根据配置决定初始状态 if global_config.chat.chat_mode == "focus": logger.debug(f"{self.log_prefix} 配置为 focus 模式,将直接尝试进入 FOCUSED 状态。") @@ -214,23 +203,17 @@ class SubHeartflow: # 如果实例不存在,则创建并启动 logger.info(f"{log_prefix} 麦麦准备开始专注聊天...") try: - # 创建 HeartFChatting 实例,并传递 从构造函数传入的 回调函数 self.heart_fc_instance = HeartFChatting( chat_id=self.subheartflow_id, - observations=self.observations, + # observations=self.observations, on_stop_focus_chat=self._handle_stop_focus_chat_request, ) - # 初始化并启动 HeartFChatting - if await self.heart_fc_instance._initialize(): - await self.heart_fc_instance.start() - logger.debug(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。") - return True - else: - logger.error(f"{log_prefix} HeartFChatting 初始化失败,无法进入专注模式。") - self.heart_fc_instance = None # 初始化失败,清理实例 - return False + await self.heart_fc_instance.start() + logger.debug(f"{log_prefix} 麦麦已成功进入专注聊天模式 (新实例已启动)。") + return True + except Exception as e: logger.error(f"{log_prefix} 创建或启动 HeartFChatting 实例时出错: {e}") logger.error(traceback.format_exc()) diff --git a/src/chat/heart_flow/subheartflow_manager.py b/src/chat/heart_flow/subheartflow_manager.py index bad4393c9..9ad73ff8f 100644 --- a/src/chat/heart_flow/subheartflow_manager.py +++ b/src/chat/heart_flow/subheartflow_manager.py @@ -98,9 +98,9 @@ class SubHeartflowManager: ) # 首先创建并添加聊天观察者 - observation = ChattingObservation(chat_id=subheartflow_id) - await observation.initialize() - new_subflow.add_observation(observation) + # observation = ChattingObservation(chat_id=subheartflow_id) + # await observation.initialize() + # new_subflow.add_observation(observation) # 然后再进行异步初始化,此时 SubHeartflow 内部若需启动 HeartFChatting,就能拿到 observation await new_subflow.initialize() diff --git a/src/chat/heart_flow/utils_chat.py b/src/chat/heart_flow/utils_chat.py index f796254c4..e43f6b000 100644 --- a/src/chat/heart_flow/utils_chat.py +++ b/src/chat/heart_flow/utils_chat.py @@ -7,7 +7,7 @@ from src.person_info.person_info import person_info_manager logger = get_logger("heartflow_utils") -async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]: +def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]: """ 获取聊天类型(是否群聊)和私聊对象信息。 @@ -24,8 +24,7 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di chat_target_info = None try: - chat_stream = await asyncio.to_thread(chat_manager.get_stream, chat_id) # Use to_thread if get_stream is sync - # If get_stream is already async, just use: chat_stream = await chat_manager.get_stream(chat_id) + chat_stream = chat_manager.get_stream(chat_id) if chat_stream: if chat_stream.group_info: @@ -49,11 +48,11 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di # Try to fetch person info try: # Assume get_person_id is sync (as per original code), keep using to_thread - person_id = await asyncio.to_thread(person_info_manager.get_person_id, platform, user_id) + person_id = person_info_manager.get_person_id(platform, user_id) person_name = None if person_id: # get_value is async, so await it directly - person_name = await person_info_manager.get_value(person_id, "person_name") + person_name = person_info_manager.get_value_sync(person_id, "person_name") target_info["person_id"] = person_id target_info["person_name"] = person_name diff --git a/src/chat/memory_system/Hippocampus.py b/src/chat/memory_system/Hippocampus.py index 300563658..3f47cd116 100644 --- a/src/chat/memory_system/Hippocampus.py +++ b/src/chat/memory_system/Hippocampus.py @@ -1422,7 +1422,7 @@ class ParahippocampalGyrus: # 1. 使用 build_readable_messages 生成格式化文本 # build_readable_messages 只返回一个字符串,不需要解包 - input_text = await build_readable_messages( + input_text = build_readable_messages( messages, merge_messages=True, # 合并连续消息 timestamp_mode="normal_no_YMD", # 使用 'YYYY-MM-DD HH:MM:SS' 格式 diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index adf634e20..6855486b8 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -40,8 +40,7 @@ class NormalChat: # Interest dict self.interest_dict = interest_dict - self.is_group_chat: bool = False - self.chat_target_info: Optional[dict] = None + self.is_group_chat, self.chat_target_info = get_chat_type_and_target_info(self.stream_id) self.willing_amplifier = 1 self.start_time = time.time() @@ -72,8 +71,7 @@ class NormalChat: """异步初始化,获取聊天类型和目标信息。""" if self._initialized: return - - self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.stream_id) + self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id # 初始化Normal Chat专用表达器 diff --git a/src/chat/normal_chat/normal_chat_planner.py b/src/chat/normal_chat/normal_chat_planner.py index 4fd6978b9..096e33996 100644 --- a/src/chat/normal_chat/normal_chat_planner.py +++ b/src/chat/normal_chat/normal_chat_planner.py @@ -21,6 +21,7 @@ def init_prompt(): """ 你的自我认知是: {self_info_block} +请记住你的性格,身份和特点。 注意,除了下面动作选项之外,你在聊天中不能做其他任何事情,这是你能力的边界,现在请你选择合适的action: @@ -32,37 +33,30 @@ def init_prompt(): - 其他action表示在普通回复的基础上,执行相应的额外动作 你必须从上面列出的可用action中选择一个,并说明原因。 -你的决策必须以严格的 JSON 格式输出,且仅包含 JSON 内容,不要有任何其他文字或解释。 - {moderation_prompt} -当前聊天上下文: +你是群内的一员,你现在正在参与群内的闲聊,以下是群内的聊天内容: {chat_context} 基于以上聊天上下文和用户的最新消息,选择最合适的action。 -请你以下面格式输出你选择的action: -{{ - "action": "action_name", - "reasoning": "说明你做出该action的原因", - "参数1": "参数1的值", - "参数2": "参数2的值", - "参数3": "参数3的值", - ... -}} - -请输出你的决策 JSON:""", +请以动作的输出要求,以严格的 JSON 格式输出,且仅包含 JSON 内容。 +请输出你提取的JSON,不要有任何其他文字或解释: +""", "normal_chat_planner_prompt", ) Prompt( """ -action_name: {action_name} - 描述:{action_description} - 参数: -{action_parameters} - 动作要求: -{action_require}""", +动作:{action_name} +该动作的描述:{action_description} +使用该动作的场景: +{action_require} +输出要求: +{{ + "action": "{action_name}",{action_parameters} +}} +""", "normal_chat_action_prompt", ) @@ -230,22 +224,26 @@ class NormalChatPlanner: action_parameters = action_info.get("parameters", {}) action_require = action_info.get("require", []) - # 格式化参数 - parameters_text = "" - for param_name, param_desc in action_parameters.items(): - parameters_text += f" - {param_name}: {param_desc}\n" + if action_parameters: + param_text = "\n" + for param_name, param_description in action_parameters: + param_text += f' "{param_name}":"{param_description}"\n' + param_text = param_text.rstrip('\n') + else: + param_text = "" + - # 格式化要求 require_text = "" - for req in action_require: - require_text += f" - {req}\n" + for require_item in action_require: + require_text += f"- {require_item}\n" + require_text = require_text.rstrip('\n') # 构建单个动作的提示 action_prompt = await global_prompt_manager.format_prompt( "normal_chat_action_prompt", action_name=action_name, action_description=action_description, - action_parameters=parameters_text, + action_parameters=param_text, action_require=require_text, ) action_options_text += action_prompt + "\n\n" diff --git a/src/chat/normal_chat/normal_prompt.py b/src/chat/normal_chat/normal_prompt.py index 5efbf656e..0bafc6833 100644 --- a/src/chat/normal_chat/normal_prompt.py +++ b/src/chat/normal_chat/normal_prompt.py @@ -190,7 +190,7 @@ class PromptBuilder: timestamp=time.time(), limit=global_config.focus_chat.observation_context_size, ) - chat_talking_prompt = await build_readable_messages( + chat_talking_prompt = build_readable_messages( message_list_before_now, replace_bot_name=True, merge_messages=False, diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 59cac2139..85cf5ce5a 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -150,7 +150,7 @@ def num_new_messages_since_with_users( return count_messages(message_filter=filter_query) -async def _build_readable_messages_internal( +def _build_readable_messages_internal( messages: List[Dict[str, Any]], replace_bot_name: bool = True, merge_messages: bool = False, @@ -214,7 +214,7 @@ async def _build_readable_messages_internal( if replace_bot_name and user_id == global_config.bot.qq_account: person_name = f"{global_config.bot.nickname}(你)" else: - person_name = await person_info_manager.get_value(person_id, "person_name") + person_name = person_info_manager.get_value_sync(person_id, "person_name") # 如果 person_name 未设置,则使用消息中的 nickname 或默认名称 if not person_name: @@ -232,7 +232,7 @@ async def _build_readable_messages_internal( aaa = match.group(1) bbb = match.group(2) reply_person_id = person_info_manager.get_person_id(platform, bbb) - reply_person_name = await person_info_manager.get_value(reply_person_id, "person_name") + reply_person_name = person_info_manager.get_value_sync(reply_person_id, "person_name") if not reply_person_name: reply_person_name = aaa # 在内容前加上回复信息 @@ -249,7 +249,7 @@ async def _build_readable_messages_internal( aaa = m.group(1) bbb = m.group(2) at_person_id = person_info_manager.get_person_id(platform, bbb) - at_person_name = await person_info_manager.get_value(at_person_id, "person_name") + at_person_name = person_info_manager.get_value_sync(at_person_id, "person_name") if not at_person_name: at_person_name = aaa new_content += f"@{at_person_name}" @@ -377,13 +377,13 @@ async def build_readable_messages_with_list( 将消息列表转换为可读的文本格式,并返回原始(时间戳, 昵称, 内容)列表。 允许通过参数控制格式化行为。 """ - formatted_string, details_list = await _build_readable_messages_internal( + formatted_string, details_list = _build_readable_messages_internal( messages, replace_bot_name, merge_messages, timestamp_mode, truncate ) return formatted_string, details_list -async def build_readable_messages( +def build_readable_messages( messages: List[Dict[str, Any]], replace_bot_name: bool = True, merge_messages: bool = False, @@ -398,7 +398,7 @@ async def build_readable_messages( """ if read_mark <= 0: # 没有有效的 read_mark,直接格式化所有消息 - formatted_string, _ = await _build_readable_messages_internal( + formatted_string, _ = _build_readable_messages_internal( messages, replace_bot_name, merge_messages, timestamp_mode, truncate ) return formatted_string @@ -410,10 +410,10 @@ async def build_readable_messages( # 分别格式化 # 注意:这里决定对已读和未读部分都应用相同的 truncate 设置 # 如果需要不同的行为(例如只截断已读部分),需要调整这里的调用 - formatted_before, _ = await _build_readable_messages_internal( + formatted_before, _ = _build_readable_messages_internal( messages_before_mark, replace_bot_name, merge_messages, timestamp_mode, truncate ) - formatted_after, _ = await _build_readable_messages_internal( + formatted_after, _ = _build_readable_messages_internal( messages_after_mark, replace_bot_name, merge_messages, diff --git a/src/experimental/PFC/action_planner.py b/src/experimental/PFC/action_planner.py index 6ab4c2305..f60354bfb 100644 --- a/src/experimental/PFC/action_planner.py +++ b/src/experimental/PFC/action_planner.py @@ -273,7 +273,7 @@ class ActionPlanner: if hasattr(observation_info, "new_messages_count") and observation_info.new_messages_count > 0: if hasattr(observation_info, "unprocessed_messages") and observation_info.unprocessed_messages: new_messages_list = observation_info.unprocessed_messages - new_messages_str = await build_readable_messages( + new_messages_str = build_readable_messages( new_messages_list, replace_bot_name=True, merge_messages=False, diff --git a/src/experimental/PFC/conversation.py b/src/experimental/PFC/conversation.py index 0216e8e9e..e007c7601 100644 --- a/src/experimental/PFC/conversation.py +++ b/src/experimental/PFC/conversation.py @@ -89,7 +89,7 @@ class Conversation: timestamp=time.time(), limit=30, # 加载最近30条作为初始上下文,可以调整 ) - chat_talking_prompt = await build_readable_messages( + chat_talking_prompt = build_readable_messages( initial_messages, replace_bot_name=True, merge_messages=False, diff --git a/src/experimental/PFC/observation_info.py b/src/experimental/PFC/observation_info.py index 5e14bf1d6..cc3dbf97c 100644 --- a/src/experimental/PFC/observation_info.py +++ b/src/experimental/PFC/observation_info.py @@ -366,7 +366,7 @@ class ObservationInfo: # 更新历史记录字符串 (只使用最近一部分生成,例如20条) history_slice_for_str = self.chat_history[-20:] try: - self.chat_history_str = await build_readable_messages( + self.chat_history_str = build_readable_messages( history_slice_for_str, replace_bot_name=True, merge_messages=False, diff --git a/src/experimental/PFC/pfc.py b/src/experimental/PFC/pfc.py index 78397780d..f0666b674 100644 --- a/src/experimental/PFC/pfc.py +++ b/src/experimental/PFC/pfc.py @@ -91,7 +91,7 @@ class GoalAnalyzer: if observation_info.new_messages_count > 0: new_messages_list = observation_info.unprocessed_messages - new_messages_str = await build_readable_messages( + new_messages_str = build_readable_messages( new_messages_list, replace_bot_name=True, merge_messages=False, @@ -224,7 +224,7 @@ class GoalAnalyzer: async def analyze_conversation(self, goal, reasoning): messages = self.chat_observer.get_cached_messages() - chat_history_text = await build_readable_messages( + chat_history_text = build_readable_messages( messages, replace_bot_name=True, merge_messages=False, diff --git a/src/experimental/PFC/pfc_KnowledgeFetcher.py b/src/experimental/PFC/pfc_KnowledgeFetcher.py index b94cd5b1f..82eb2618f 100644 --- a/src/experimental/PFC/pfc_KnowledgeFetcher.py +++ b/src/experimental/PFC/pfc_KnowledgeFetcher.py @@ -53,7 +53,7 @@ class KnowledgeFetcher: Tuple[str, str]: (获取的知识, 知识来源) """ # 构建查询上下文 - chat_history_text = await build_readable_messages( + chat_history_text = build_readable_messages( chat_history, replace_bot_name=True, merge_messages=False, diff --git a/src/experimental/PFC/reply_generator.py b/src/experimental/PFC/reply_generator.py index 0fababc67..1a6563a77 100644 --- a/src/experimental/PFC/reply_generator.py +++ b/src/experimental/PFC/reply_generator.py @@ -173,7 +173,7 @@ class ReplyGenerator: chat_history_text = observation_info.chat_history_str if observation_info.new_messages_count > 0 and observation_info.unprocessed_messages: new_messages_list = observation_info.unprocessed_messages - new_messages_str = await build_readable_messages( + new_messages_str = build_readable_messages( new_messages_list, replace_bot_name=True, merge_messages=False, diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 0f212f417..2facda2ed 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -361,6 +361,30 @@ class PersonInfoManager: logger.trace(f"获取{person_id}的{field_name}失败或值为None,已返回默认值{default_value} (Peewee)") return default_value + @staticmethod + def get_value_sync(person_id: str, field_name: str): + """同步版本:获取指定person_id文档的字段值,若不存在该字段,则返回该字段的全局默认值""" + if not person_id: + logger.debug("get_value_sync获取失败:person_id不能为空") + return person_info_default.get(field_name) + + if field_name not in PersonInfo._meta.fields: + if field_name in person_info_default: + logger.trace(f"字段'{field_name}'不在Peewee模型中,但存在于默认配置中。返回配置默认值。") + return copy.deepcopy(person_info_default[field_name]) + logger.debug(f"get_value_sync获取失败:字段'{field_name}'未在Peewee模型和默认配置中定义。") + return None + + record = PersonInfo.get_or_none(PersonInfo.person_id == person_id) + if record: + value = getattr(record, field_name) + if value is not None: + return value + + default_value = copy.deepcopy(person_info_default.get(field_name)) + logger.trace(f"获取{person_id}的{field_name}失败或值为None,已返回默认值{default_value} (Peewee)") + return default_value + @staticmethod async def get_values(person_id: str, field_names: list) -> dict: """获取指定person_id文档的多个字段值,若不存在该字段,则返回该字段的全局默认值""" diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 5990a1013..5e4176cfb 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -345,7 +345,7 @@ class RelationshipManager: if user_messages: - readable_messages = await build_readable_messages( + readable_messages = build_readable_messages( messages=user_messages, replace_bot_name=True, timestamp_mode="relative", diff --git a/src/plugins/doubao_pic/actions/generate_pic_config.py b/src/plugins/doubao_pic/actions/generate_pic_config.py index 4d0ffc045..b4326ae4c 100644 --- a/src/plugins/doubao_pic/actions/generate_pic_config.py +++ b/src/plugins/doubao_pic/actions/generate_pic_config.py @@ -36,9 +36,9 @@ def generate_config(): print("请记得编辑该文件,填入您的火山引擎API 密钥。") except IOError as e: print(f"错误:无法写入配置文件 {config_file_path}。原因: {e}") - else: - print(f"配置文件已存在: {config_file_path}") - print("未进行任何更改。如果您想重新生成,请先删除或重命名现有文件。") + # else: + # print(f"配置文件已存在: {config_file_path}") + # print("未进行任何更改。如果您想重新生成,请先删除或重命名现有文件。") if __name__ == "__main__": diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index a2d645f7c..9fe19224f 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "2.12.1" +version = "2.12.2" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #如果你想要修改配置文件,请在修改后将version的值进行变更 @@ -95,16 +95,11 @@ talk_frequency_down_groups = [] #降低回复频率的群号码 [focus_chat] #专注聊天 think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗 consecutive_replies = 1 # 连续回复能力,值越高,麦麦连续回复的概率越高 - processor_max_time = 20 # 处理器最大时间,单位秒,如果超过这个时间,处理器会自动停止 - observation_context_size = 20 # 观察到的最长上下文大小 compressed_length = 8 # 不能大于observation_context_size,心流上下文压缩的最短压缩长度,超过心流观察到的上下文长度,会压缩,最短压缩长度为5 compress_length_limit = 4 #最多压缩份数,超过该数值的压缩上下文会被删除 -# 不建议更改 -planner_type = "simple" # 规划器类型,可选值:complex(复杂规划器), simple(简单规划器) - [focus_chat_processor] # 专注聊天处理器,打开可以实现更多功能,但是会增加token消耗 self_identify_processor = true # 是否启用自我识别处理器 relation_processor = true # 是否启用关系识别处理器 diff --git a/tests/test_build_readable_messages.py b/tests/test_build_readable_messages.py index 3bdabe966..da963e45a 100644 --- a/tests/test_build_readable_messages.py +++ b/tests/test_build_readable_messages.py @@ -134,10 +134,8 @@ class TestBuildReadableMessages(unittest.TestCase): simple_msgs = [test_msg] # 运行内部函数 - result_text, result_details = asyncio.run( - _build_readable_messages_internal( - simple_msgs, replace_bot_name=True, merge_messages=False, timestamp_mode="absolute", truncate=False - ) + result_text, result_details = _build_readable_messages_internal( + simple_msgs, replace_bot_name=True, merge_messages=False, timestamp_mode="absolute", truncate=False ) logger.info(f"内部函数返回结果: {result_text[:200] if result_text else '空'}")