diff --git a/src/chat/chat_loop/heartFC_chat.py b/src/chat/chat_loop/heartFC_chat.py index 2db7ca42a..ee190f86a 100644 --- a/src/chat/chat_loop/heartFC_chat.py +++ b/src/chat/chat_loop/heartFC_chat.py @@ -18,11 +18,12 @@ from src.chat.chat_loop.hfc_utils import CycleDetail from src.person_info.relationship_builder_manager import relationship_builder_manager from src.person_info.person_info import get_person_info_manager from src.plugin_system.base.component_types import ActionInfo, ChatMode -from src.plugin_system.apis import generator_api, send_api, message_api +from src.plugin_system.apis import generator_api, send_api, message_api, database_api from src.chat.willing.willing_manager import get_willing_manager from src.mais4u.mai_think import mai_thinking_manager -from maim_message.message_base import GroupInfo from src.mais4u.constant_s4u import ENABLE_S4U +from src.plugins.built_in.core_actions.no_reply import NoReplyAction +from src.chat.chat_loop.hfc_utils import send_typing, stop_typing ERROR_LOOP_INFO = { "loop_plan_info": { @@ -254,44 +255,19 @@ class HeartFChatting: person_name = await person_info_manager.get_value(person_id, "person_name") return f"{person_name}:{message_data.get('processed_plain_text')}" - async def send_typing(self): - group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") - - chat = await get_chat_manager().get_or_create_stream( - platform="amaidesu_default", - user_info=None, - group_info=group_info, - ) - - await send_api.custom_to_stream( - message_type="state", content="typing", stream_id=chat.stream_id, storage_message=False - ) - - async def stop_typing(self): - group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") - - chat = await get_chat_manager().get_or_create_stream( - platform="amaidesu_default", - user_info=None, - group_info=group_info, - ) - - await send_api.custom_to_stream( - message_type="state", content="stop_typing", stream_id=chat.stream_id, storage_message=False - ) - async def _observe(self, message_data: Optional[Dict[str, Any]] = None): # sourcery skip: hoist-statement-from-if, merge-comparisons, reintroduce-else if not message_data: message_data = {} action_type = "no_action" + reply_text = "" # 初始化reply_text变量,避免UnboundLocalError # 创建新的循环信息 cycle_timers, thinking_id = self.start_cycle() logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考[模式:{self.loop_mode}]") if ENABLE_S4U: - await self.send_typing() + await send_typing() async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()): loop_start_time = time.time() @@ -310,7 +286,7 @@ class HeartFChatting: # 如果normal,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的) if self.loop_mode == ChatMode.NORMAL: reply_to_str = await self.build_reply_to_str(message_data) - gen_task = asyncio.create_task(self._generate_response(message_data, available_actions, reply_to_str)) + gen_task = asyncio.create_task(self._generate_response(message_data, available_actions, reply_to_str, "chat.replyer.normal")) with Timer("规划器", cycle_timers): plan_result, target_message = await self.action_planner.plan(mode=self.loop_mode) @@ -326,7 +302,7 @@ class HeartFChatting: action_data["loop_start_time"] = loop_start_time if self.loop_mode == ChatMode.NORMAL: - if action_type == "no_action": + if action_type == "reply": logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复") elif is_parallel: logger.info( @@ -335,45 +311,86 @@ class HeartFChatting: else: logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定执行{action_type}动作") - if action_type == "no_action": + action_message: Dict[str, Any] = message_data or target_message # type: ignore + if action_type == "no_action" or (self.loop_mode == ChatMode.FOCUS and action_type == "reply"): # 等待回复生成完毕 - gather_timeout = global_config.chat.thinking_timeout - try: - response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout) - except asyncio.TimeoutError: - response_set = None + if action_type == "no_action": + gather_timeout = global_config.chat.thinking_timeout + try: + response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout) + except asyncio.TimeoutError: + logger.warning(f"{self.log_prefix} 回复生成超时>{global_config.chat.thinking_timeout}s,已跳过") + response_set = None - if response_set: - content = " ".join([item[1] for item in response_set if item[0] == "text"]) + if response_set: + content = " ".join([item[1] for item in response_set if item[0] == "text"]) - # 模型炸了,没有回复内容生成 - if not response_set: - logger.warning(f"{self.log_prefix}模型未生成回复内容") - return False - elif action_type not in ["no_action"] and not is_parallel: - logger.info( - f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复" - ) - return False - - logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定的回复内容: {content}") - - # 发送回复 (不再需要传入 chat) - reply_text = await self._send_response(response_set, reply_to_str, loop_start_time,message_data) + # 模型炸了或超时,没有回复内容生成 + if not response_set: + logger.warning(f"{self.log_prefix}模型未生成回复内容") + return False + elif action_type not in ["no_action"] and not is_parallel: + logger.info( + f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复" + ) + return False + else: + logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复 (focus模式)") + + # 构建reply_to字符串 + reply_to_str = await self.build_reply_to_str(action_message) + + # 生成回复 + with Timer("回复生成", cycle_timers): + response_set = await self._generate_response(action_message, available_actions, reply_to_str, request_type="chat.replyer.focus") + + if not response_set: + logger.warning(f"{self.log_prefix}模型未生成回复内容") + return False + + # 发送回复 + with Timer("回复发送", cycle_timers): + reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message) + + # 存储reply action信息 (focus模式) + person_info_manager = get_person_info_manager() + person_id = person_info_manager.get_person_id( + action_message.get("chat_info_platform", ""), + action_message.get("user_id", ""), + ) + person_name = await person_info_manager.get_value(person_id, "person_name") + action_prompt_display = f"你对{person_name}进行了回复:{reply_text}" + + await database_api.store_action_info( + chat_stream=self.chat_stream, + action_build_into_prompt=False, + action_prompt_display=action_prompt_display, + action_done=True, + thinking_id=thinking_id, + action_data={"reply_text": reply_text, "reply_to": reply_to_str}, + action_name="reply", + ) + # 构建循环信息 + loop_info = { + "loop_plan_info": { + "action_result": plan_result.get("action_result", {}), + }, + "loop_action_info": { + "action_taken": True, + "reply_text": reply_text, + "command": "", + "taken_time": time.time(), + }, + } + success = True + command = "" - - if ENABLE_S4U: - await self.stop_typing() - await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text) - return True else: - action_message: Dict[str, Any] = message_data or target_message # type: ignore - # 动作执行计时 with Timer("动作执行", cycle_timers): success, reply_text, command = await self._handle_action( @@ -392,11 +409,11 @@ class HeartFChatting: }, } - if loop_info["loop_action_info"]["command"] == "stop_focus_chat": - logger.info(f"{self.log_prefix} 麦麦决定停止专注聊天") - return False - # 停止该聊天模式的循环 + if ENABLE_S4U: + await stop_typing() + await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text) + self.end_cycle(loop_info, cycle_timers) self.print_cycle_info(cycle_timers) @@ -406,13 +423,11 @@ class HeartFChatting: # 管理no_reply计数器:当执行了非no_reply动作时,重置计数器 if action_type != "no_reply" and action_type != "no_action": # 导入NoReplyAction并重置计数器 - from src.plugins.built_in.core_actions.no_reply import NoReplyAction NoReplyAction.reset_consecutive_count() logger.info(f"{self.log_prefix} 执行了{action_type}动作,重置no_reply计数器") return True elif action_type == "no_action": - # 当执行回复动作时,也重置no_reply计数器 - from src.plugins.built_in.core_actions.no_reply import NoReplyAction + # 当执行回复动作时,也重置no_reply计数器s NoReplyAction.reset_consecutive_count() logger.info(f"{self.log_prefix} 执行了回复动作,重置no_reply计数器") @@ -551,7 +566,7 @@ class HeartFChatting: return False async def _generate_response( - self, message_data: dict, available_actions: Optional[Dict[str, ActionInfo]], reply_to: str + self, message_data: dict, available_actions: Optional[Dict[str, ActionInfo]], reply_to: str, request_type: str = "chat.replyer.normal" ) -> Optional[list]: """生成普通回复""" try: @@ -559,8 +574,8 @@ class HeartFChatting: chat_stream=self.chat_stream, reply_to=reply_to, available_actions=available_actions, - enable_tool=global_config.tool.enable_in_normal_chat, - request_type="chat.replyer.normal", + enable_tool=global_config.tool.enable_tool, + request_type=request_type, ) if not success or not reply_set: @@ -589,7 +604,7 @@ class HeartFChatting: f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复" ) else: - logger.debug( + logger.info( f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复" ) diff --git a/src/chat/chat_loop/hfc_utils.py b/src/chat/chat_loop/hfc_utils.py index a24656665..973c4f948 100644 --- a/src/chat/chat_loop/hfc_utils.py +++ b/src/chat/chat_loop/hfc_utils.py @@ -1,10 +1,13 @@ import time - from typing import Optional, Dict, Any from src.config.config import global_config -from src.common.message_repository import count_messages from src.common.logger import get_logger +from src.chat.message_receive.chat_stream import get_chat_manager +from src.plugin_system.apis import send_api +from maim_message.message_base import GroupInfo + +from src.common.message_repository import count_messages logger = get_logger(__name__) @@ -106,3 +109,30 @@ def get_recent_message_stats(minutes: float = 30, chat_id: Optional[str] = None) bot_reply_count = count_messages(bot_filter) return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count} + + +async def send_typing(): + group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") + + chat = await get_chat_manager().get_or_create_stream( + platform="amaidesu_default", + user_info=None, + group_info=group_info, + ) + + await send_api.custom_to_stream( + message_type="state", content="typing", stream_id=chat.stream_id, storage_message=False + ) + +async def stop_typing(): + group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心") + + chat = await get_chat_manager().get_or_create_stream( + platform="amaidesu_default", + user_info=None, + group_info=group_info, + ) + + await send_api.custom_to_stream( + message_type="state", content="stop_typing", stream_id=chat.stream_id, storage_message=False + ) \ No newline at end of file diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 33ec56418..f21cc8d32 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -312,7 +312,7 @@ class ActionPlanner: by_what = "聊天内容和用户的最新消息" target_prompt = "" no_action_block = """重要说明: -- 'no_action' 表示只进行普通聊天回复,不执行任何额外动作 +- 'reply' 表示只进行普通聊天回复,不执行任何额外动作 - 其他action表示在普通回复的基础上,执行相应的额外动作""" chat_context_description = "你现在正在一个群聊中" diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index 584ad4539..0b411a8eb 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -151,7 +151,6 @@ class DefaultReplyer: async def generate_reply_with_context( self, - reply_data: Optional[Dict[str, Any]] = None, reply_to: str = "", extra_info: str = "", available_actions: Optional[Dict[str, ActionInfo]] = None, @@ -160,29 +159,24 @@ class DefaultReplyer: ) -> Tuple[bool, Optional[str], Optional[str]]: """ 回复器 (Replier): 核心逻辑,负责生成回复文本。 - (已整合原 HeartFCGenerator 的功能) """ prompt = None if available_actions is None: available_actions = {} try: - if not reply_data: - reply_data = { - "reply_to": reply_to, - "extra_info": extra_info, - } - for key, value in reply_data.items(): - if not value: - logger.debug(f"回复数据跳过{key},生成回复时将忽略。") - # 3. 构建 Prompt with Timer("构建Prompt", {}): # 内部计时器,可选保留 prompt = await self.build_prompt_reply_context( - reply_data=reply_data, # 传递action_data + reply_to = reply_to, + extra_info=extra_info, available_actions=available_actions, enable_timeout=enable_timeout, enable_tool=enable_tool, ) + + if not prompt: + logger.warning("构建prompt失败,跳过回复生成") + return False, None, None # 4. 调用 LLM 生成回复 content = None @@ -282,14 +276,13 @@ class DefaultReplyer: traceback.print_exc() return False, None - async def build_relation_info(self, reply_data=None): + async def build_relation_info(self, reply_to: str = ""): if not global_config.relationship.enable_relationship: return "" relationship_fetcher = relationship_fetcher_manager.get_fetcher(self.chat_stream.stream_id) - if not reply_data: + if not reply_to: return "" - reply_to = reply_data.get("reply_to", "") sender, text = self._parse_reply_target(reply_to) if not sender or not text: return "" @@ -381,7 +374,7 @@ class DefaultReplyer: return memory_str - async def build_tool_info(self, chat_history, reply_data: Optional[Dict], enable_tool: bool = True): + async def build_tool_info(self, chat_history, reply_to: str = "", enable_tool: bool = True): """构建工具信息块 Args: @@ -395,10 +388,9 @@ class DefaultReplyer: if not enable_tool: return "" - if not reply_data: + if not reply_to: return "" - reply_to = reply_data.get("reply_to", "") sender, text = self._parse_reply_target(reply_to) if not text: @@ -577,7 +569,8 @@ class DefaultReplyer: async def build_prompt_reply_context( self, - reply_data: Dict[str, Any], + reply_to: str, + extra_info: str = "", available_actions: Optional[Dict[str, ActionInfo]] = None, enable_timeout: bool = False, enable_tool: bool = True, @@ -602,8 +595,6 @@ class DefaultReplyer: chat_id = chat_stream.stream_id person_info_manager = get_person_info_manager() is_group_chat = bool(chat_stream.group_info) - reply_to = reply_data.get("reply_to", "none") - extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "") if global_config.mood.enable_mood: chat_mood = mood_manager.get_mood_by_chat_id(chat_id) @@ -612,6 +603,13 @@ class DefaultReplyer: mood_prompt = "" sender, target = self._parse_reply_target(reply_to) + person_info_manager = get_person_info_manager() + person_id = person_info_manager.get_person_id_by_person_name(sender) + user_id = person_info_manager.get_value_sync(person_id, "user_id") + platform = chat_stream.platform + if user_id == global_config.bot.qq_account and platform == global_config.bot.platform: + logger.warning("选取了自身作为回复对象,跳过构建prompt") + return "" target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True) @@ -630,21 +628,6 @@ class DefaultReplyer: limit=global_config.chat.max_context_size * 2, ) - message_list_before_now = get_raw_msg_before_timestamp_with_chat( - chat_id=chat_id, - timestamp=time.time(), - limit=global_config.chat.max_context_size, - ) - chat_talking_prompt = build_readable_messages( - message_list_before_now, - replace_bot_name=True, - merge_messages=False, - timestamp_mode="normal_no_YMD", - read_mark=0.0, - truncate=True, - show_actions=True, - ) - message_list_before_short = get_raw_msg_before_timestamp_with_chat( chat_id=chat_id, timestamp=time.time(), @@ -664,10 +647,10 @@ class DefaultReplyer: self._time_and_run_task( self.build_expression_habits(chat_talking_prompt_short, target), "expression_habits" ), - self._time_and_run_task(self.build_relation_info(reply_data), "relation_info"), + self._time_and_run_task(self.build_relation_info(reply_to), "relation_info"), self._time_and_run_task(self.build_memory_block(chat_talking_prompt_short, target), "memory_block"), self._time_and_run_task( - self.build_tool_info(chat_talking_prompt_short, reply_data, enable_tool=enable_tool), "tool_info" + self.build_tool_info(chat_talking_prompt_short, reply_to, enable_tool=enable_tool), "tool_info" ), self._time_and_run_task(get_prompt_info(target, threshold=0.38), "prompt_info"), ) @@ -700,8 +683,8 @@ class DefaultReplyer: keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target) - if extra_info_block: - extra_info_block = f"以下是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策\n{extra_info_block}\n以上是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策" + if extra_info: + extra_info_block = f"以下是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策\n{extra_info}\n以上是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策" else: extra_info_block = "" diff --git a/src/config/official_configs.py b/src/config/official_configs.py index d852d5a3c..2c9f847c4 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -77,7 +77,7 @@ class ChatConfig(ConfigBase): 选择普通模型的概率为 1 - reasoning_normal_model_probability """ - thinking_timeout: int = 30 + thinking_timeout: int = 40 """麦麦最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢)""" talk_frequency: float = 1 @@ -299,11 +299,8 @@ class ExpressionConfig(ConfigBase): class ToolConfig(ConfigBase): """工具配置类""" - enable_in_normal_chat: bool = False - """是否在普通聊天中启用工具""" - - enable_in_focus_chat: bool = True - """是否在专注聊天中启用工具""" + enable_tool: bool = False + """是否在聊天中启用工具""" @dataclass class VoiceConfig(ConfigBase): diff --git a/src/plugin_system/apis/generator_api.py b/src/plugin_system/apis/generator_api.py index cbb1336ce..60ab7d5bb 100644 --- a/src/plugin_system/apis/generator_api.py +++ b/src/plugin_system/apis/generator_api.py @@ -107,10 +107,14 @@ async def generate_reply( return False, [], None logger.debug("[GeneratorAPI] 开始生成回复") + + if not reply_to: + reply_to = action_data.get("reply_to", "") + if not extra_info and action_data: + extra_info = action_data.get("extra_info", "") # 调用回复器生成回复 success, content, prompt = await replyer.generate_reply_with_context( - reply_data=action_data or {}, reply_to=reply_to, extra_info=extra_info, available_actions=available_actions, diff --git a/src/plugins/built_in/core_actions/_manifest.json b/src/plugins/built_in/core_actions/_manifest.json index ba1b20d6b..d7446497c 100644 --- a/src/plugins/built_in/core_actions/_manifest.json +++ b/src/plugins/built_in/core_actions/_manifest.json @@ -24,11 +24,6 @@ "is_built_in": true, "plugin_type": "action_provider", "components": [ - { - "type": "action", - "name": "reply", - "description": "参与聊天回复,发送文本进行表达" - }, { "type": "action", "name": "no_reply", diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index eb584a23a..f23f4ac74 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -61,7 +61,6 @@ class NoReplyAction(BaseAction): async def execute(self) -> Tuple[bool, str]: """执行不回复动作""" - import asyncio try: reason = self.action_data.get("reason", "") diff --git a/src/plugins/built_in/core_actions/plugin.py b/src/plugins/built_in/core_actions/plugin.py index c34f5a871..5bf80af33 100644 --- a/src/plugins/built_in/core_actions/plugin.py +++ b/src/plugins/built_in/core_actions/plugin.py @@ -18,7 +18,6 @@ from src.common.logger import get_logger # 导入API模块 - 标准Python包方式 from src.plugins.built_in.core_actions.no_reply import NoReplyAction from src.plugins.built_in.core_actions.emoji import EmojiAction -from src.plugins.built_in.core_actions.reply import ReplyAction logger = get_logger("core_actions") @@ -52,10 +51,9 @@ class CoreActionsPlugin(BasePlugin): config_schema: dict = { "plugin": { "enabled": ConfigField(type=bool, default=True, description="是否启用插件"), - "config_version": ConfigField(type=str, default="0.4.0", description="配置文件版本"), + "config_version": ConfigField(type=str, default="0.5.0", description="配置文件版本"), }, "components": { - "enable_reply": ConfigField(type=bool, default=True, description="是否启用回复动作"), "enable_no_reply": ConfigField(type=bool, default=True, description="是否启用不回复动作"), "enable_emoji": ConfigField(type=bool, default=True, description="是否启用发送表情/图片动作"), }, @@ -74,8 +72,6 @@ class CoreActionsPlugin(BasePlugin): # --- 根据配置注册组件 --- components = [] - if self.get_config("components.enable_reply", True): - components.append((ReplyAction.get_action_info(), ReplyAction)) if self.get_config("components.enable_no_reply", True): components.append((NoReplyAction.get_action_info(), NoReplyAction)) if self.get_config("components.enable_emoji", True): diff --git a/src/plugins/built_in/core_actions/reply.py b/src/plugins/built_in/core_actions/reply.py deleted file mode 100644 index 887879066..000000000 --- a/src/plugins/built_in/core_actions/reply.py +++ /dev/null @@ -1,150 +0,0 @@ -# 导入新插件系统 -from src.plugin_system import BaseAction, ActionActivationType, ChatMode -from src.config.config import global_config -import random -import time -from typing import Tuple -import asyncio -import re -import traceback - -# 导入依赖的系统组件 -from src.common.logger import get_logger - -# 导入API模块 - 标准Python包方式 -from src.plugin_system.apis import generator_api, message_api -# 注释:不再需要导入NoReplyAction,因为计数器管理已移至heartFC_chat.py -# from src.plugins.built_in.core_actions.no_reply import NoReplyAction -from src.person_info.person_info import get_person_info_manager -from src.mais4u.mai_think import mai_thinking_manager -from src.mais4u.constant_s4u import ENABLE_S4U - -logger = get_logger("reply_action") - - -class ReplyAction(BaseAction): - """回复动作 - 参与聊天回复""" - - # 激活设置 - focus_activation_type = ActionActivationType.NEVER - normal_activation_type = ActionActivationType.NEVER - mode_enable = ChatMode.FOCUS - parallel_action = False - - # 动作基本信息 - action_name = "reply" - action_description = "" - - # 动作参数定义 - action_parameters = {} - - # 动作使用场景 - action_require = [""] - - # 关联类型 - associated_types = ["text"] - - def _parse_reply_target(self, target_message: str) -> tuple: - sender = "" - target = "" - # 添加None检查,防止NoneType错误 - if target_message is None: - return sender, target - if ":" in target_message or ":" in target_message: - # 使用正则表达式匹配中文或英文冒号 - parts = re.split(pattern=r"[::]", string=target_message, maxsplit=1) - if len(parts) == 2: - sender = parts[0].strip() - target = parts[1].strip() - return sender, target - - async def execute(self) -> Tuple[bool, str]: - """执行回复动作""" - logger.debug(f"{self.log_prefix} 决定进行回复") - start_time = self.action_data.get("loop_start_time", time.time()) - - user_id = self.user_id - platform = self.platform - # logger.info(f"{self.log_prefix} 用户ID: {user_id}, 平台: {platform}") - person_id = get_person_info_manager().get_person_id(platform, user_id) # type: ignore - # logger.info(f"{self.log_prefix} 人物ID: {person_id}") - person_name = get_person_info_manager().get_value_sync(person_id, "person_name") - reply_to = f"{person_name}:{self.action_message.get('processed_plain_text', '')}" # type: ignore - logger.info(f"{self.log_prefix} 决定进行回复,目标: {reply_to}") - - try: - if prepared_reply := self.action_data.get("prepared_reply", ""): - reply_text = prepared_reply - else: - try: - success, reply_set, _ = await asyncio.wait_for( - generator_api.generate_reply( - extra_info="", - reply_to=reply_to, - chat_id=self.chat_id, - request_type="chat.replyer.focus", - enable_tool=global_config.tool.enable_in_focus_chat, - ), - timeout=global_config.chat.thinking_timeout, - ) - except asyncio.TimeoutError: - logger.warning(f"{self.log_prefix} 回复生成超时 ({global_config.chat.thinking_timeout}s)") - return False, "timeout" - - # 检查从start_time以来的新消息数量 - # 获取动作触发时间或使用默认值 - current_time = time.time() - new_message_count = message_api.count_new_messages( - chat_id=self.chat_id, start_time=start_time, end_time=current_time - ) - - # 根据新消息数量决定是否使用reply_to - need_reply = new_message_count >= random.randint(2, 4) - if need_reply: - logger.info( - f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复" - ) - else: - logger.debug( - f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复" - ) - - # 构建回复文本 - reply_text = "" - first_replied = False - reply_to_platform_id = f"{platform}:{user_id}" - for reply_seg in reply_set: - data = reply_seg[1] - if not first_replied: - if need_reply: - await self.send_text( - content=data, reply_to=reply_to, reply_to_platform_id=reply_to_platform_id, typing=False - ) - else: - await self.send_text(content=data, reply_to_platform_id=reply_to_platform_id, typing=False) - first_replied = True - else: - await self.send_text(content=data, reply_to_platform_id=reply_to_platform_id, typing=True) - reply_text += data - - # 存储动作记录 - reply_text = f"你对{person_name}进行了回复:{reply_text}" - - if ENABLE_S4U: - await mai_thinking_manager.get_mai_think(self.chat_id).do_think_after_response(reply_text) - - await self.store_action_info( - action_build_into_prompt=False, - action_prompt_display=reply_text, - action_done=True, - ) - - # 注释:重置NoReplyAction的连续计数器现在由heartFC_chat.py统一管理 - # NoReplyAction.reset_consecutive_count() - - return success, reply_text - - except Exception as e: - logger.error(f"{self.log_prefix} 回复动作执行失败: {e}") - traceback.print_exc() - return False, f"回复失败: {str(e)}" diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 08a28637c..39857d669 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -58,7 +58,7 @@ focus_value = 1 willing_amplifier = 1 # 麦麦回复意愿 max_context_size = 25 # 上下文长度 -thinking_timeout = 20 # 麦麦一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢) +thinking_timeout = 40 # 麦麦一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢) replyer_random_probability = 0.5 # 首要replyer模型被选择的概率 mentioned_bot_inevitable_reply = true # 提及 bot 大概率回复 @@ -107,8 +107,7 @@ ban_msgs_regex = [ willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现) [tool] -enable_in_normal_chat = false # 是否在普通聊天中启用工具 -enable_in_focus_chat = true # 是否在专注聊天中启用工具 +enable_tool = false # 是否在普通聊天中启用工具 [emoji] emoji_chance = 0.6 # 麦麦激活表情包动作的概率