diff --git a/src/chat/chat_loop/cycle_processor.py b/src/chat/chat_loop/cycle_processor.py index 9325df339..f67f7808c 100644 --- a/src/chat/chat_loop/cycle_processor.py +++ b/src/chat/chat_loop/cycle_processor.py @@ -21,6 +21,14 @@ logger = get_logger("hfc.processor") class CycleProcessor: def __init__(self, context: HfcContext, response_handler: ResponseHandler, cycle_tracker: CycleTracker): + """ + 初始化循环处理器 + + Args: + context: HFC聊天上下文对象,包含聊天流、能量值等信息 + response_handler: 响应处理器,负责生成和发送回复 + cycle_tracker: 循环跟踪器,负责记录和管理每次思考循环的信息 + """ self.context = context self.response_handler = response_handler self.cycle_tracker = cycle_tracker @@ -28,6 +36,22 @@ class CycleProcessor: self.action_modifier = ActionModifier(action_manager=self.context.action_manager, chat_id=self.context.stream_id) async def observe(self, message_data: Optional[Dict[str, Any]] = None) -> bool: + """ + 观察和处理单次思考循环的核心方法 + + Args: + message_data: 可选的消息数据字典,包含用户消息、平台信息等 + + Returns: + bool: 处理是否成功 + + 功能说明: + - 开始新的思考循环并记录计时 + - 修改可用动作并获取动作列表 + - 根据聊天模式和提及情况决定是否跳过规划器 + - 执行动作规划或直接回复 + - 根据动作类型分发到相应的处理方法 + """ if not message_data: message_data = {} @@ -105,6 +129,24 @@ class CycleProcessor: return True async def _handle_reply_action(self, message_data, available_actions, gen_task, loop_start_time, cycle_timers, thinking_id, plan_result): + """ + 处理回复类型的动作 + + Args: + message_data: 消息数据 + available_actions: 可用动作列表 + gen_task: 预先创建的生成任务(可能为None) + loop_start_time: 循环开始时间 + cycle_timers: 循环计时器 + thinking_id: 思考ID + plan_result: 规划结果 + + 功能说明: + - 根据聊天模式决定是否使用预生成的回复或实时生成 + - 在NORMAL模式下使用异步生成提高效率 + - 在FOCUS模式下同步生成确保及时响应 + - 发送生成的回复并结束循环 + """ if self.context.loop_mode == ChatMode.NORMAL: if not gen_task: reply_to_str = await self._build_reply_to_str(message_data) @@ -136,6 +178,27 @@ class CycleProcessor: self.cycle_tracker.end_cycle(loop_info, cycle_timers) async def _handle_other_actions(self, action_type, reasoning, action_data, is_parallel, gen_task, action_message, cycle_timers, thinking_id, plan_result, loop_start_time): + """ + 处理非回复类型的动作(如no_reply、自定义动作等) + + Args: + action_type: 动作类型 + reasoning: 动作理由 + action_data: 动作数据 + is_parallel: 是否并行执行 + gen_task: 生成任务 + action_message: 动作消息 + cycle_timers: 循环计时器 + thinking_id: 思考ID + plan_result: 规划结果 + loop_start_time: 循环开始时间 + + 功能说明: + - 在NORMAL模式下可能并行执行回复生成和动作处理 + - 等待所有异步任务完成 + - 整合回复和动作的执行结果 + - 构建最终循环信息并结束循环 + """ background_reply_task = None if self.context.loop_mode == ChatMode.NORMAL and is_parallel and gen_task: background_reply_task = asyncio.create_task(self._handle_parallel_reply(gen_task, loop_start_time, action_message, cycle_timers, thinking_id, plan_result)) @@ -172,6 +235,26 @@ class CycleProcessor: self.cycle_tracker.end_cycle(loop_info, cycle_timers) async def _handle_parallel_reply(self, gen_task, loop_start_time, action_message, cycle_timers, thinking_id, plan_result): + """ + 处理并行回复生成 + + Args: + gen_task: 回复生成任务 + loop_start_time: 循环开始时间 + action_message: 动作消息 + cycle_timers: 循环计时器 + thinking_id: 思考ID + plan_result: 规划结果 + + Returns: + tuple: (循环信息, 回复文本, 计时器信息) 或 None + + 功能说明: + - 等待并行回复生成任务完成(带超时) + - 构建回复目标字符串 + - 发送生成的回复 + - 返回循环信息供上级方法使用 + """ try: response_set = await asyncio.wait_for(gen_task, timeout=global_config.chat.thinking_timeout) except asyncio.TimeoutError: @@ -186,6 +269,25 @@ class CycleProcessor: ) async def _handle_action(self, action, reasoning, action_data, cycle_timers, thinking_id, action_message) -> tuple[bool, str, str]: + """ + 处理具体的动作执行 + + Args: + action: 动作名称 + reasoning: 执行理由 + action_data: 动作数据 + cycle_timers: 循环计时器 + thinking_id: 思考ID + action_message: 动作消息 + + Returns: + tuple: (执行是否成功, 回复文本, 命令文本) + + 功能说明: + - 创建对应的动作处理器 + - 执行动作并捕获异常 + - 返回执行结果供上级方法整合 + """ if not self.context.chat_stream: return False, "", "" try: @@ -210,6 +312,19 @@ class CycleProcessor: return False, "", "" def _get_direct_reply_plan(self, loop_start_time): + """ + 获取直接回复的规划结果 + + Args: + loop_start_time: 循环开始时间 + + Returns: + dict: 包含直接回复动作的规划结果 + + 功能说明: + - 在某些情况下跳过复杂规划,直接返回回复动作 + - 主要用于NORMAL模式下没有其他可用动作时的简化处理 + """ return { "action_result": { "action_type": "reply", @@ -222,6 +337,20 @@ class CycleProcessor: } async def _build_reply_to_str(self, message_data: dict): + """ + 构建回复目标字符串 + + Args: + message_data: 消息数据字典 + + Returns: + str: 格式化的回复目标字符串,格式为"用户名:消息内容" + + 功能说明: + - 从消息数据中提取平台和用户ID信息 + - 通过人员信息管理器获取用户昵称 + - 构建用于回复显示的格式化字符串 + """ from src.person_info.person_info import get_person_info_manager person_info_manager = get_person_info_manager() platform = message_data.get("chat_info_platform") or message_data.get("user_platform") or (self.context.chat_stream.platform if self.context.chat_stream else "default") @@ -231,6 +360,24 @@ class CycleProcessor: return f"{person_name}:{message_data.get('processed_plain_text')}" def _build_final_loop_info(self, reply_loop_info, action_success, action_reply_text, action_command, plan_result): + """ + 构建最终的循环信息 + + Args: + reply_loop_info: 回复循环信息(可能为None) + action_success: 动作执行是否成功 + action_reply_text: 动作回复文本 + action_command: 动作命令 + plan_result: 规划结果 + + Returns: + dict: 完整的循环信息,包含规划信息和动作信息 + + 功能说明: + - 如果有回复循环信息,则在其基础上添加动作信息 + - 如果没有回复信息,则创建新的循环信息结构 + - 整合所有执行结果供循环跟踪器记录 + """ if reply_loop_info: loop_info = reply_loop_info loop_info["loop_action_info"].update({ diff --git a/src/chat/chat_loop/cycle_tracker.py b/src/chat/chat_loop/cycle_tracker.py index f0308a3e2..8d0763ae5 100644 --- a/src/chat/chat_loop/cycle_tracker.py +++ b/src/chat/chat_loop/cycle_tracker.py @@ -9,9 +9,31 @@ logger = get_logger("hfc.cycle") class CycleTracker: def __init__(self, context: HfcContext): + """ + 初始化循环跟踪器 + + Args: + context: HFC聊天上下文对象 + + 功能说明: + - 负责跟踪和记录每次思考循环的详细信息 + - 管理循环的开始、结束和信息存储 + """ self.context = context def start_cycle(self) -> Tuple[Dict[str, float], str]: + """ + 开始新的思考循环 + + Returns: + tuple: (循环计时器字典, 思考ID字符串) + + 功能说明: + - 增加循环计数器 + - 创建新的循环详情对象 + - 生成唯一的思考ID + - 初始化循环计时器 + """ self.context.cycle_counter += 1 self.context.current_cycle_detail = CycleDetail(self.context.cycle_counter) self.context.current_cycle_detail.thinking_id = f"tid{str(round(time.time(), 2))}" @@ -19,6 +41,19 @@ class CycleTracker: return cycle_timers, self.context.current_cycle_detail.thinking_id def end_cycle(self, loop_info: Dict[str, Any], cycle_timers: Dict[str, float]): + """ + 结束当前思考循环 + + Args: + loop_info: 循环信息,包含规划和动作信息 + cycle_timers: 循环计时器,记录各阶段耗时 + + 功能说明: + - 设置循环详情的完整信息 + - 将当前循环加入历史记录 + - 记录计时器和结束时间 + - 打印循环统计信息 + """ if self.context.current_cycle_detail: self.context.current_cycle_detail.set_loop_info(loop_info) self.context.history_loop.append(self.context.current_cycle_detail) @@ -27,6 +62,18 @@ class CycleTracker: self.print_cycle_info(cycle_timers) def print_cycle_info(self, cycle_timers: Dict[str, float]): + """ + 打印循环统计信息 + + Args: + cycle_timers: 循环计时器字典 + + 功能说明: + - 格式化各阶段的耗时信息 + - 计算总体循环持续时间 + - 输出详细的性能统计日志 + - 显示选择的动作类型 + """ if not self.context.current_cycle_detail: return diff --git a/src/chat/chat_loop/energy_manager.py b/src/chat/chat_loop/energy_manager.py index ecd0296ea..57dfc44d2 100644 --- a/src/chat/chat_loop/energy_manager.py +++ b/src/chat/chat_loop/energy_manager.py @@ -10,24 +10,63 @@ logger = get_logger("hfc.energy") class EnergyManager: def __init__(self, context: HfcContext): + """ + 初始化能量管理器 + + Args: + context: HFC聊天上下文对象 + + 功能说明: + - 管理聊天机器人的能量值系统 + - 根据聊天模式自动调整能量消耗 + - 控制能量值的衰减和记录 + """ self.context = context self._energy_task: Optional[asyncio.Task] = None self.last_energy_log_time = 0 self.energy_log_interval = 90 async def start(self): + """ + 启动能量管理器 + + 功能说明: + - 检查运行状态,避免重复启动 + - 创建能量循环异步任务 + - 设置任务完成回调 + - 记录启动日志 + """ if self.context.running and not self._energy_task: self._energy_task = asyncio.create_task(self._energy_loop()) self._energy_task.add_done_callback(self._handle_energy_completion) logger.info(f"{self.context.log_prefix} 能量管理器已启动") async def stop(self): + """ + 停止能量管理器 + + 功能说明: + - 取消正在运行的能量循环任务 + - 等待任务完全停止 + - 记录停止日志 + """ if self._energy_task and not self._energy_task.done(): self._energy_task.cancel() await asyncio.sleep(0) logger.info(f"{self.context.log_prefix} 能量管理器已停止") def _handle_energy_completion(self, task: asyncio.Task): + """ + 处理能量循环任务完成 + + Args: + task: 完成的异步任务对象 + + 功能说明: + - 处理任务正常完成或异常情况 + - 记录相应的日志信息 + - 区分取消和异常终止的情况 + """ try: if exception := task.exception(): logger.error(f"{self.context.log_prefix} 能量循环异常: {exception}") @@ -37,6 +76,16 @@ class EnergyManager: logger.info(f"{self.context.log_prefix} 能量循环被取消") async def _energy_loop(self): + """ + 能量管理的主循环 + + 功能说明: + - 每10秒执行一次能量更新 + - 根据群聊配置设置固定的聊天模式和能量值 + - 在自动模式下根据聊天模式进行能量衰减 + - NORMAL模式每次衰减0.3,FOCUS模式每次衰减0.6 + - 确保能量值不低于0.3的最小值 + """ while self.context.running: await asyncio.sleep(10) @@ -63,6 +112,17 @@ class EnergyManager: self._log_energy_change("能量值衰减") def _should_log_energy(self) -> bool: + """ + 判断是否应该记录能量变化日志 + + Returns: + bool: 如果距离上次记录超过间隔时间则返回True + + 功能说明: + - 控制能量日志的记录频率,避免日志过于频繁 + - 默认间隔90秒记录一次详细日志 + - 其他时间使用调试级别日志 + """ current_time = time.time() if current_time - self.last_energy_log_time >= self.energy_log_interval: self.last_energy_log_time = current_time @@ -70,6 +130,18 @@ class EnergyManager: return False def _log_energy_change(self, action: str, reason: str = ""): + """ + 记录能量变化日志 + + Args: + action: 能量变化的动作描述 + reason: 可选的变化原因 + + 功能说明: + - 根据时间间隔决定使用info还是debug级别的日志 + - 格式化能量值显示(保留一位小数) + - 可选择性地包含变化原因 + """ if self._should_log_energy(): log_message = f"{self.context.log_prefix} {action},当前能量值:{self.context.energy_value:.1f}" if reason: diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index 565106921..cd6392bdc 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -24,6 +24,18 @@ logger = get_logger("hfc") class HeartFChatting: def __init__(self, chat_id: str): + """ + 初始化心跳聊天管理器 + + Args: + chat_id: 聊天ID标识符 + + 功能说明: + - 创建聊天上下文和所有子管理器 + - 初始化循环跟踪器、响应处理器、循环处理器等核心组件 + - 设置能量管理器、主动思考器和普通模式处理器 + - 初始化聊天模式并记录初始化完成日志 + """ self.context = HfcContext(chat_id) self.cycle_tracker = CycleTracker(self.context) @@ -39,6 +51,16 @@ class HeartFChatting: logger.info(f"{self.context.log_prefix} HeartFChatting 初始化完成") def _initialize_chat_mode(self): + """ + 初始化聊天模式 + + 功能说明: + - 检测是否为群聊环境 + - 根据全局配置设置强制聊天模式 + - 在focus模式下设置能量值为35 + - 在normal模式下设置能量值为15 + - 如果是auto模式则保持默认设置 + """ is_group_chat = self.context.chat_stream.group_info is not None if self.context.chat_stream else False if is_group_chat and global_config.chat.group_chat_mode != "auto": if global_config.chat.group_chat_mode == "focus": @@ -49,6 +71,16 @@ class HeartFChatting: self.context.energy_value = 15 async def start(self): + """ + 启动心跳聊天系统 + + 功能说明: + - 检查是否已经在运行,避免重复启动 + - 初始化关系构建器和表达学习器 + - 启动能量管理器和主动思考器 + - 创建主聊天循环任务并设置完成回调 + - 记录启动完成日志 + """ if self.context.running: return self.context.running = True @@ -64,6 +96,16 @@ class HeartFChatting: logger.info(f"{self.context.log_prefix} HeartFChatting 启动完成") async def stop(self): + """ + 停止心跳聊天系统 + + 功能说明: + - 检查是否正在运行,避免重复停止 + - 设置运行状态为False + - 停止能量管理器和主动思考器 + - 取消主聊天循环任务 + - 记录停止完成日志 + """ if not self.context.running: return self.context.running = False @@ -77,6 +119,18 @@ class HeartFChatting: logger.info(f"{self.context.log_prefix} HeartFChatting 已停止") def _handle_loop_completion(self, task: asyncio.Task): + """ + 处理主循环任务完成 + + Args: + task: 完成的异步任务对象 + + 功能说明: + - 处理任务异常完成的情况 + - 区分正常停止和异常终止 + - 记录相应的日志信息 + - 处理取消任务的情况 + """ try: if exception := task.exception(): logger.error(f"{self.context.log_prefix} HeartFChatting: 脱离了聊天(异常): {exception}") @@ -87,6 +141,16 @@ class HeartFChatting: logger.info(f"{self.context.log_prefix} HeartFChatting: 结束了聊天") async def _main_chat_loop(self): + """ + 主聊天循环 + + 功能说明: + - 持续运行聊天处理循环 + - 每次循环调用_loop_body处理消息 + - 处理取消和异常情况 + - 在异常时尝试重新启动循环 + - 记录循环结束日志 + """ try: while self.context.running: await self._loop_body() @@ -101,6 +165,17 @@ class HeartFChatting: logger.error(f"{self.context.log_prefix} 结束了当前聊天循环") async def _loop_body(self): + """ + 单次循环体处理 + + 功能说明: + - 检查是否处于睡眠模式,如果是则跳过处理 + - 获取最近的新消息(过滤机器人自己的消息和命令) + - 更新最后消息时间和读取时间 + - 根据当前聊天模式执行不同的处理逻辑 + - FOCUS模式:直接处理所有消息并检查退出条件 + - NORMAL模式:检查进入FOCUS模式的条件,并通过normal_mode_handler处理消息 + """ if schedule_manager.is_sleeping(): return @@ -129,6 +204,15 @@ class HeartFChatting: await self.normal_mode_handler.handle_message(message) def _check_focus_exit(self): + """ + 检查是否应该退出FOCUS模式 + + 功能说明: + - 区分私聊和群聊环境 + - 在强制私聊focus模式下,能量值低于1时重置为5但不退出 + - 在群聊focus模式下,如果配置为focus则不退出 + - 其他情况下,能量值低于1时退出到NORMAL模式 + """ is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False is_group_chat = not is_private_chat @@ -145,6 +229,19 @@ class HeartFChatting: self.context.loop_mode = ChatMode.NORMAL def _check_focus_entry(self, new_message_count: int): + """ + 检查是否应该进入FOCUS模式 + + Args: + new_message_count: 新消息数量 + + 功能说明: + - 区分私聊和群聊环境 + - 强制私聊focus模式:直接进入FOCUS模式并设置能量值为10 + - 群聊normal模式:不进入FOCUS模式 + - 根据focus_value配置和消息数量决定是否进入FOCUS模式 + - 当消息数量超过阈值或能量值达到30时进入FOCUS模式 + """ is_private_chat = self.context.chat_stream.group_info is None if self.context.chat_stream else False is_group_chat = not is_private_chat diff --git a/src/chat/chat_loop/hfc_context.py b/src/chat/chat_loop/hfc_context.py index c067a06d0..255824a8d 100644 --- a/src/chat/chat_loop/hfc_context.py +++ b/src/chat/chat_loop/hfc_context.py @@ -9,6 +9,21 @@ from src.chat.chat_loop.hfc_utils import CycleDetail class HfcContext: def __init__(self, chat_id: str): + """ + 初始化HFC聊天上下文 + + Args: + chat_id: 聊天ID标识符 + + 功能说明: + - 存储和管理单个聊天会话的所有状态信息 + - 包含聊天流、关系构建器、表达学习器等核心组件 + - 管理聊天模式、能量值、时间戳等关键状态 + - 提供循环历史记录和当前循环详情的存储 + + Raises: + ValueError: 如果找不到对应的聊天流 + """ self.stream_id: str = chat_id self.chat_stream: Optional[ChatStream] = get_chat_manager().get_stream(self.stream_id) if not self.chat_stream: diff --git a/src/chat/chat_loop/hfc_utils.py b/src/chat/chat_loop/hfc_utils.py index 973c4f948..43c48c8cb 100644 --- a/src/chat/chat_loop/hfc_utils.py +++ b/src/chat/chat_loop/hfc_utils.py @@ -13,9 +13,28 @@ logger = get_logger(__name__) class CycleDetail: - """循环信息记录类""" + """ + 循环信息记录类 + + 功能说明: + - 记录单次思考循环的详细信息 + - 包含循环ID、思考ID、时间戳等基本信息 + - 存储循环的规划信息和动作信息 + - 提供序列化和转换功能 + """ def __init__(self, cycle_id: int): + """ + 初始化循环详情记录 + + Args: + cycle_id: 循环ID,用于标识循环的顺序 + + 功能说明: + - 设置循环基本标识信息 + - 初始化时间戳和计时器 + - 准备循环信息存储容器 + """ self.cycle_id = cycle_id self.thinking_id = "" self.start_time = time.time() @@ -26,7 +45,18 @@ class CycleDetail: self.loop_action_info: Dict[str, Any] = {} def to_dict(self) -> Dict[str, Any]: - """将循环信息转换为字典格式""" + """ + 将循环信息转换为字典格式 + + Returns: + dict: 包含所有循环信息的字典,已处理循环引用和序列化问题 + + 功能说明: + - 递归转换复杂对象为可序列化格式 + - 防止循环引用导致的无限递归 + - 限制递归深度避免栈溢出 + - 只保留基本数据类型和可序列化的值 + """ def convert_to_serializable(obj, depth=0, seen=None): if seen is None: @@ -79,18 +109,36 @@ class CycleDetail: } def set_loop_info(self, loop_info: Dict[str, Any]): - """设置循环信息""" + """ + 设置循环信息 + + Args: + loop_info: 包含循环规划和动作信息的字典 + + 功能说明: + - 从传入的循环信息中提取规划和动作信息 + - 更新当前循环详情的相关字段 + """ self.loop_plan_info = loop_info["loop_plan_info"] self.loop_action_info = loop_info["loop_action_info"] def get_recent_message_stats(minutes: float = 30, chat_id: Optional[str] = None) -> dict: """ + 获取最近消息统计信息 + Args: - minutes (float): 检索的分钟数,默认30分钟 - chat_id (str, optional): 指定的chat_id,仅统计该chat下的消息。为None时统计全部。 + minutes: 检索的分钟数,默认30分钟 + chat_id: 指定的chat_id,仅统计该chat下的消息。为None时统计全部 + Returns: dict: {"bot_reply_count": int, "total_message_count": int} + + 功能说明: + - 统计指定时间范围内的消息数量 + - 区分机器人回复和总消息数 + - 可以针对特定聊天或全局统计 + - 用于分析聊天活跃度和机器人参与度 """ now = time.time() @@ -112,6 +160,15 @@ def get_recent_message_stats(minutes: float = 30, chat_id: Optional[str] = None) async def send_typing(): + """ + 发送打字状态指示 + + 功能说明: + - 创建内心聊天流(用于状态显示) + - 发送typing状态消息 + - 不存储到消息记录中 + - 用于S4U功能的视觉反馈 + """ group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") chat = await get_chat_manager().get_or_create_stream( @@ -125,6 +182,15 @@ async def send_typing(): ) async def stop_typing(): + """ + 停止打字状态指示 + + 功能说明: + - 创建内心聊天流(用于状态显示) + - 发送stop_typing状态消息 + - 不存储到消息记录中 + - 结束S4U功能的视觉反馈 + """ group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") chat = await get_chat_manager().get_or_create_stream( diff --git a/src/chat/chat_loop/normal_mode_handler.py b/src/chat/chat_loop/normal_mode_handler.py index a78269eb7..0b9152715 100644 --- a/src/chat/chat_loop/normal_mode_handler.py +++ b/src/chat/chat_loop/normal_mode_handler.py @@ -13,11 +13,40 @@ logger = get_logger("hfc.normal_mode") class NormalModeHandler: def __init__(self, context: HfcContext, cycle_processor: "CycleProcessor"): + """ + 初始化普通模式处理器 + + Args: + context: HFC聊天上下文对象 + cycle_processor: 循环处理器,用于处理决定回复的消息 + + 功能说明: + - 处理NORMAL模式下的消息 + - 根据兴趣度和回复概率决定是否回复 + - 管理意愿系统和回复概率计算 + """ self.context = context self.cycle_processor = cycle_processor self.willing_manager = get_willing_manager() async def handle_message(self, message_data: Dict[str, Any]) -> bool: + """ + 处理NORMAL模式下的单条消息 + + Args: + message_data: 消息数据字典,包含用户信息、消息内容、兴趣值等 + + Returns: + bool: 是否进行了回复处理 + + 功能说明: + - 计算消息的兴趣度和基础回复概率 + - 应用谈话频率调整回复概率 + - 过滤表情和图片消息(设置回复概率为0) + - 根据概率随机决定是否回复 + - 如果决定回复则调用循环处理器进行处理 + - 记录详细的决策日志 + """ if not self.context.chat_stream: return False diff --git a/src/chat/chat_loop/proactive_thinker.py b/src/chat/chat_loop/proactive_thinker.py index 864cba97c..ce990b888 100644 --- a/src/chat/chat_loop/proactive_thinker.py +++ b/src/chat/chat_loop/proactive_thinker.py @@ -15,6 +15,19 @@ logger = get_logger("hfc.proactive") class ProactiveThinker: def __init__(self, context: HfcContext, cycle_processor: "CycleProcessor"): + """ + 初始化主动思考器 + + Args: + context: HFC聊天上下文对象 + cycle_processor: 循环处理器,用于执行主动思考的结果 + + 功能说明: + - 管理机器人的主动发言功能 + - 根据沉默时间和配置触发主动思考 + - 提供私聊和群聊不同的思考提示模板 + - 使用3-sigma规则计算动态思考间隔 + """ self.context = context self.cycle_processor = cycle_processor self._proactive_thinking_task: Optional[asyncio.Task] = None @@ -35,18 +48,47 @@ class ProactiveThinker: } async def start(self): + """ + 启动主动思考器 + + 功能说明: + - 检查运行状态和配置,避免重复启动 + - 只有在启用主动思考功能时才启动 + - 创建主动思考循环异步任务 + - 设置任务完成回调处理 + - 记录启动日志 + """ if self.context.running and not self._proactive_thinking_task and global_config.chat.enable_proactive_thinking: self._proactive_thinking_task = asyncio.create_task(self._proactive_thinking_loop()) self._proactive_thinking_task.add_done_callback(self._handle_proactive_thinking_completion) logger.info(f"{self.context.log_prefix} 主动思考器已启动") async def stop(self): + """ + 停止主动思考器 + + 功能说明: + - 取消正在运行的主动思考任务 + - 等待任务完全停止 + - 记录停止日志 + """ if self._proactive_thinking_task and not self._proactive_thinking_task.done(): self._proactive_thinking_task.cancel() await asyncio.sleep(0) logger.info(f"{self.context.log_prefix} 主动思考器已停止") def _handle_proactive_thinking_completion(self, task: asyncio.Task): + """ + 处理主动思考任务完成 + + Args: + task: 完成的异步任务对象 + + 功能说明: + - 处理任务正常完成或异常情况 + - 记录相应的日志信息 + - 区分取消和异常终止的情况 + """ try: if exception := task.exception(): logger.error(f"{self.context.log_prefix} 主动思考循环异常: {exception}") @@ -56,6 +98,17 @@ class ProactiveThinker: logger.info(f"{self.context.log_prefix} 主动思考循环被取消") async def _proactive_thinking_loop(self): + """ + 主动思考的主循环 + + 功能说明: + - 每15秒检查一次是否需要主动思考 + - 只在FOCUS模式下进行主动思考 + - 检查是否启用主动思考功能 + - 计算沉默时间并与动态间隔比较 + - 达到条件时执行主动思考并更新最后消息时间 + - 处理执行过程中的异常 + """ while self.context.running: await asyncio.sleep(15) @@ -79,6 +132,19 @@ class ProactiveThinker: logger.error(traceback.format_exc()) def _should_enable_proactive_thinking(self) -> bool: + """ + 检查是否应该启用主动思考 + + Returns: + bool: 如果应该启用主动思考则返回True + + 功能说明: + - 检查聊天流是否存在 + - 检查当前聊天是否在启用列表中(如果配置了列表) + - 根据聊天类型(群聊/私聊)和配置决定是否启用 + - 群聊需要proactive_thinking_in_group为True + - 私聊需要proactive_thinking_in_private为True + """ if not self.context.chat_stream: return False @@ -101,6 +167,19 @@ class ProactiveThinker: return True def _get_dynamic_thinking_interval(self) -> float: + """ + 获取动态思考间隔 + + Returns: + float: 计算得出的思考间隔时间(秒) + + 功能说明: + - 使用3-sigma规则计算正态分布的思考间隔 + - 基于base_interval和delta_sigma配置计算 + - 处理特殊情况(为0或负数的配置) + - 如果timing_utils不可用则使用固定间隔 + - 间隔范围被限制在1秒到86400秒(1天)之间 + """ try: from src.utils.timing_utils import get_normal_distributed_interval @@ -131,6 +210,21 @@ class ProactiveThinker: return max(300, abs(global_config.chat.proactive_thinking_interval)) def _format_duration(self, seconds: float) -> str: + """ + 格式化持续时间为中文描述 + + Args: + seconds: 持续时间(秒) + + Returns: + str: 格式化后的时间字符串,如"1小时30分45秒" + + 功能说明: + - 将秒数转换为小时、分钟、秒的组合 + - 只显示非零的时间单位 + - 如果所有单位都为0则显示"0秒" + - 用于主动思考日志的时间显示 + """ hours = int(seconds // 3600) minutes = int((seconds % 3600) // 60) secs = int(seconds % 60) @@ -146,6 +240,19 @@ class ProactiveThinker: return "".join(parts) async def _execute_proactive_thinking(self, silence_duration: float): + """ + 执行主动思考 + + Args: + silence_duration: 沉默持续时间(秒) + + 功能说明: + - 格式化沉默时间并记录触发日志 + - 获取适当的思考提示模板 + - 创建主动思考类型的消息数据 + - 调用循环处理器执行思考和可能的回复 + - 处理执行过程中的异常 + """ formatted_time = self._format_duration(silence_duration) logger.info(f"{self.context.log_prefix} 触发主动思考,已沉默{formatted_time}") @@ -172,6 +279,21 @@ class ProactiveThinker: logger.error(traceback.format_exc()) def _get_proactive_prompt(self, formatted_time: str) -> str: + """ + 获取主动思考的提示模板 + + Args: + formatted_time: 格式化后的沉默时间字符串 + + Returns: + str: 填充了时间信息的提示模板 + + 功能说明: + - 优先使用自定义的提示模板(如果配置了) + - 根据聊天类型(群聊/私聊)选择默认模板 + - 将格式化的时间信息填入模板 + - 返回完整的主动思考提示文本 + """ if hasattr(global_config.chat, 'proactive_thinking_prompt_template') and global_config.chat.proactive_thinking_prompt_template.strip(): return global_config.chat.proactive_thinking_prompt_template.format(time=formatted_time) diff --git a/src/chat/chat_loop/response_handler.py b/src/chat/chat_loop/response_handler.py index 6cbac1f95..f3cec57a6 100644 --- a/src/chat/chat_loop/response_handler.py +++ b/src/chat/chat_loop/response_handler.py @@ -13,6 +13,17 @@ logger = get_logger("hfc.response") class ResponseHandler: def __init__(self, context: HfcContext): + """ + 初始化响应处理器 + + Args: + context: HFC聊天上下文对象 + + 功能说明: + - 负责生成和发送机器人的回复 + - 处理回复的格式化和发送逻辑 + - 管理回复状态和日志记录 + """ self.context = context async def generate_and_send_reply( @@ -25,6 +36,27 @@ class ResponseHandler: thinking_id, plan_result, ) -> Tuple[Dict[str, Any], str, Dict[str, float]]: + """ + 生成并发送回复的主方法 + + Args: + response_set: 生成的回复内容集合 + reply_to_str: 回复目标字符串 + loop_start_time: 循环开始时间 + action_message: 动作消息数据 + cycle_timers: 循环计时器 + thinking_id: 思考ID + plan_result: 规划结果 + + Returns: + tuple: (循环信息, 回复文本, 计时器信息) + + 功能说明: + - 发送生成的回复内容 + - 存储动作信息到数据库 + - 构建并返回完整的循环信息 + - 用于上级方法的状态跟踪 + """ reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message) person_info_manager = get_person_info_manager() @@ -65,6 +97,25 @@ class ResponseHandler: return loop_info, reply_text, cycle_timers async def _send_response(self, reply_set, reply_to, thinking_start_time, message_data) -> str: + """ + 发送回复内容的具体实现 + + Args: + reply_set: 回复内容集合,包含多个回复段 + reply_to: 回复目标 + thinking_start_time: 思考开始时间 + message_data: 消息数据 + + Returns: + str: 完整的回复文本 + + 功能说明: + - 检查是否有新消息需要回复 + - 处理主动思考的"沉默"决定 + - 根据消息数量决定是否添加回复引用 + - 逐段发送回复内容,支持打字效果 + - 正确处理元组格式的回复段 + """ current_time = time.time() new_message_count = message_api.count_new_messages( chat_id=self.context.stream_id, start_time=thinking_start_time, end_time=current_time @@ -131,6 +182,24 @@ class ResponseHandler: reply_to: str, request_type: str = "chat.replyer.normal", ) -> Optional[list]: + """ + 生成回复内容 + + Args: + message_data: 消息数据 + available_actions: 可用动作列表 + reply_to: 回复目标 + request_type: 请求类型,默认为普通回复 + + Returns: + list: 生成的回复内容列表,失败时返回None + + 功能说明: + - 调用生成器API生成回复 + - 根据配置启用或禁用工具功能 + - 处理生成失败的情况 + - 记录生成过程中的错误和异常 + """ try: success, reply_set, _ = await generator_api.generate_reply( chat_stream=self.context.chat_stream,