From a8c25cdc15347afbadbb0b9e0aeecadfd244bf0c Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 6 Jan 2026 22:40:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:wait=E5=8F=AF=E8=A2=AB=E6=89=93=E6=96=AD?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E6=8E=A8=E8=8D=90=E4=BF=AE=E6=94=B9=E7=A7=81?= =?UTF-8?q?=E8=81=8Aplanner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/brain_chat/brain_chat.py | 54 +++++++++++++++++++++++----- src/chat/brain_chat/brain_planner.py | 2 +- src/config/config.py | 2 +- src/config/official_configs.py | 14 +++++--- src/dream/dream_generator.py | 3 +- template/bot_config_template.toml | 28 +++++++-------- 6 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/chat/brain_chat/brain_chat.py b/src/chat/brain_chat/brain_chat.py index b555bf0f..8fd2de94 100644 --- a/src/chat/brain_chat/brain_chat.py +++ b/src/chat/brain_chat/brain_chat.py @@ -87,6 +87,7 @@ class BrainChatting: # 循环控制内部状态 self.running: bool = False self._loop_task: Optional[asyncio.Task] = None # 主循环任务 + self._new_message_event = asyncio.Event() # 新消息事件,用于打断 wait # 添加循环信息管理相关的属性 self.history_loop: List[CycleDetail] = [] @@ -173,9 +174,10 @@ class BrainChatting: filter_intercept_message_level=1, ) - # 如果有新消息,更新 last_read_time + # 如果有新消息,更新 last_read_time 并触发事件以打断正在进行的 wait if len(recent_messages_list) >= 1: self.last_read_time = time.time() + self._new_message_event.set() # 触发新消息事件,打断 wait # 总是执行一次思考迭代(不管有没有新消息) # wait 动作会在其内部等待,不需要在这里处理 @@ -434,6 +436,9 @@ class BrainChatting: last_check_time = self.last_read_time check_interval = 1.0 # 每秒检查一次 + # 清除事件状态,准备等待新消息 + self._new_message_event.clear() + while self.running: # 检查是否有新消息 recent_messages_list = message_api.get_messages_by_time_in_chat( @@ -453,8 +458,15 @@ class BrainChatting: logger.info(f"{self.log_prefix} 检测到新消息,恢复循环") return - # 等待一段时间后再次检查 - await asyncio.sleep(check_interval) + # 等待新消息事件或超时后再次检查 + try: + await asyncio.wait_for(self._new_message_event.wait(), timeout=check_interval) + # 事件被触发,说明有新消息 + logger.info(f"{self.log_prefix} 检测到新消息事件,恢复循环") + return + except asyncio.TimeoutError: + # 超时后继续检查 + continue async def _handle_action( self, @@ -674,7 +686,10 @@ class BrainChatting: logger.warning(f"{self.log_prefix} wait_seconds 参数格式错误,使用默认值 5 秒") wait_seconds = 5 - logger.info(f"{self.log_prefix} 执行 wait 动作,等待 {wait_seconds} 秒") + logger.info(f"{self.log_prefix} 执行 wait 动作,等待 {wait_seconds} 秒(可被新消息打断)") + + # 清除事件状态,准备等待新消息 + self._new_message_event.clear() # 记录动作信息 await database_api.store_action_info( @@ -687,8 +702,17 @@ class BrainChatting: action_name="wait", ) - # 等待指定时间 - await asyncio.sleep(wait_seconds) + # 等待指定时间,但可被新消息打断 + try: + await asyncio.wait_for( + self._new_message_event.wait(), + timeout=wait_seconds + ) + # 如果事件被触发,说明有新消息到达 + logger.info(f"{self.log_prefix} wait 动作被新消息打断,提前结束等待") + except asyncio.TimeoutError: + # 超时正常完成 + pass logger.info(f"{self.log_prefix} wait 动作完成,继续下一次思考") @@ -707,7 +731,10 @@ class BrainChatting: # 使用默认等待时间 wait_seconds = 3 - logger.info(f"{self.log_prefix} 执行 listening(转换为 wait)动作,等待 {wait_seconds} 秒") + logger.info(f"{self.log_prefix} 执行 listening(转换为 wait)动作,等待 {wait_seconds} 秒(可被新消息打断)") + + # 清除事件状态,准备等待新消息 + self._new_message_event.clear() # 记录动作信息 await database_api.store_action_info( @@ -720,8 +747,17 @@ class BrainChatting: action_name="listening", ) - # 等待指定时间 - await asyncio.sleep(wait_seconds) + # 等待指定时间,但可被新消息打断 + try: + await asyncio.wait_for( + self._new_message_event.wait(), + timeout=wait_seconds + ) + # 如果事件被触发,说明有新消息到达 + logger.info(f"{self.log_prefix} listening 动作被新消息打断,提前结束等待") + except asyncio.TimeoutError: + # 超时正常完成 + pass logger.info(f"{self.log_prefix} listening 动作完成,继续下一次思考") diff --git a/src/chat/brain_chat/brain_planner.py b/src/chat/brain_chat/brain_planner.py index 4245549d..7d5990e4 100644 --- a/src/chat/brain_chat/brain_planner.py +++ b/src/chat/brain_chat/brain_planner.py @@ -402,7 +402,7 @@ class BrainPlanner: moderation_prompt=moderation_prompt_block, name_block=name_block, interest=interest, - plan_style=global_config.personality.private_plan_style, + plan_style=global_config.experimental.private_plan_style, ) return prompt, message_id_list diff --git a/src/config/config.py b/src/config/config.py index 69f1013e..cafde1b6 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -57,7 +57,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template") # 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 # 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/ -MMC_VERSION = "0.12.1" +MMC_VERSION = "0.12.2" def get_key_comment(toml_table, key): diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 9816fd1e..c0e1950f 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -57,9 +57,6 @@ class PersonalityConfig(ConfigBase): visual_style: str = "" """图片提示词""" - private_plan_style: str = "" - """私聊说话规则,行为风格""" - states: list[str] = field(default_factory=lambda: []) """状态列表,用于随机替换personality""" @@ -713,8 +710,8 @@ class DebugConfig(ConfigBase): class ExperimentalConfig(ConfigBase): """实验功能配置类""" - enable_friend_chat: bool = False - """是否启用好友聊天""" + private_plan_style: str = "" + """私聊说话规则,行为风格(实验性功能)""" chat_prompts: list[str] = field(default_factory=lambda: []) """ @@ -904,6 +901,13 @@ class DreamConfig(ConfigBase): return False + dream_visible: bool = False + """ + 做梦结果是否存储到上下文 + - True: 将梦境发送给配置的用户后,也会存储到聊天上下文中,在后续对话中可见 + - False: 仅发送梦境但不存储,不在后续对话上下文中出现 + """ + def __post_init__(self): """验证配置值""" if self.interval_minutes < 1: diff --git a/src/dream/dream_generator.py b/src/dream/dream_generator.py index edbc3160..c17c9b12 100644 --- a/src/dream/dream_generator.py +++ b/src/dream/dream_generator.py @@ -215,11 +215,12 @@ async def generate_dream_summary( f"platform={platform!r}, user_id={user_id!r}" ) else: + dream_visible = global_config.dream.dream_visible ok = await send_api.text_to_stream( dream_content, stream_id=stream_id, typing=False, - storage_message=True, + storage_message=dream_visible, ) if ok: logger.info( diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 3b21a432..bc26ef71 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "7.3.3" +version = "7.3.5" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- # 如果你想要修改配置文件,请递增version的值 @@ -40,20 +40,11 @@ multiple_reply_style = [ multiple_probability = 0.3 # 麦麦的说话规则和行为规则: -plan_style = """ -1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用 -2.如果相同的内容已经被执行,请不要重复执行 -3.你对技术相关话题,游戏和动漫相关话题感兴趣,也对日常话题感兴趣,不喜欢太过沉重严肃的话题 -4.请控制你的发言频率,不要太过频繁的发言 -5.如果有人对你感到厌烦,请减少回复 -6.如果有人在追问你,或者话题没有说完,请你继续回复""" - - -# 麦麦私聊的说话规则,行为风格: -private_plan_style = """ -1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用 -2.如果相同的内容已经被执行,请不要重复执行 -3.某句话如果已经被回复过,不要重复回复""" +plan_style = """1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用 +2.如果相同的action已经被执行,请不要重复执行该action +3.如果有人对你感到厌烦,请减少回复 +4.如果有人在追问你,或者话题没有说完,请你继续回复 +5.请分析哪些对话是和你说的,哪些是其他人之间的互动,不要误认为其他人之间的互动是和你说的""" # 多重人格,根据概率随机选择人格 states = [ @@ -153,6 +144,7 @@ first_delay_seconds = 1800 # 程序启动后首次做梦前的延迟时间(秒 # 为空字符串时不推送。 dream_send = "" +dream_visible = false # 做梦结果是否存储到上下文,True: 将梦境发送给配置的用户后,也会存储到聊天上下文中,在后续对话中可见;False: 仅发送梦境但不存储,不在后续对话上下文中出现 # 做梦时间段配置,格式:["HH:MM-HH:MM", ...] # 如果列表为空,则表示全天允许做梦。 # 如果配置了时间段,则只有在这些时间段内才会实际执行做梦流程。 @@ -298,6 +290,12 @@ trust_xff = false # 是否启用X-Forwarded-For代理解析(默认false) secure_cookie = false # 是否启用安全Cookie(仅通过HTTPS传输,默认false) [experimental] #实验性功能 +# 麦麦私聊的说话规则,行为风格(实验性功能) +private_plan_style = """ +1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用 +2.如果相同的内容已经被执行,请不要重复执行 +3.某句话如果已经被回复过,不要重复回复""" + # 为指定聊天添加额外的prompt配置 # 格式: ["platform:id:type:prompt内容", ...] # 示例: