feat:wait可被打断,不推荐修改私聊planner

pull/1481/head
SengokuCola 2026-01-06 22:40:46 +08:00
parent 92a7836103
commit a8c25cdc15
6 changed files with 71 additions and 32 deletions

View File

@ -87,6 +87,7 @@ class BrainChatting:
# 循环控制内部状态 # 循环控制内部状态
self.running: bool = False self.running: bool = False
self._loop_task: Optional[asyncio.Task] = None # 主循环任务 self._loop_task: Optional[asyncio.Task] = None # 主循环任务
self._new_message_event = asyncio.Event() # 新消息事件,用于打断 wait
# 添加循环信息管理相关的属性 # 添加循环信息管理相关的属性
self.history_loop: List[CycleDetail] = [] self.history_loop: List[CycleDetail] = []
@ -173,9 +174,10 @@ class BrainChatting:
filter_intercept_message_level=1, filter_intercept_message_level=1,
) )
# 如果有新消息,更新 last_read_time # 如果有新消息,更新 last_read_time 并触发事件以打断正在进行的 wait
if len(recent_messages_list) >= 1: if len(recent_messages_list) >= 1:
self.last_read_time = time.time() self.last_read_time = time.time()
self._new_message_event.set() # 触发新消息事件,打断 wait
# 总是执行一次思考迭代(不管有没有新消息) # 总是执行一次思考迭代(不管有没有新消息)
# wait 动作会在其内部等待,不需要在这里处理 # wait 动作会在其内部等待,不需要在这里处理
@ -434,6 +436,9 @@ class BrainChatting:
last_check_time = self.last_read_time last_check_time = self.last_read_time
check_interval = 1.0 # 每秒检查一次 check_interval = 1.0 # 每秒检查一次
# 清除事件状态,准备等待新消息
self._new_message_event.clear()
while self.running: while self.running:
# 检查是否有新消息 # 检查是否有新消息
recent_messages_list = message_api.get_messages_by_time_in_chat( recent_messages_list = message_api.get_messages_by_time_in_chat(
@ -453,8 +458,15 @@ class BrainChatting:
logger.info(f"{self.log_prefix} 检测到新消息,恢复循环") logger.info(f"{self.log_prefix} 检测到新消息,恢复循环")
return 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( async def _handle_action(
self, self,
@ -674,7 +686,10 @@ class BrainChatting:
logger.warning(f"{self.log_prefix} wait_seconds 参数格式错误,使用默认值 5 秒") logger.warning(f"{self.log_prefix} wait_seconds 参数格式错误,使用默认值 5 秒")
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( await database_api.store_action_info(
@ -687,8 +702,17 @@ class BrainChatting:
action_name="wait", 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 动作完成,继续下一次思考") logger.info(f"{self.log_prefix} wait 动作完成,继续下一次思考")
@ -707,7 +731,10 @@ class BrainChatting:
# 使用默认等待时间 # 使用默认等待时间
wait_seconds = 3 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( await database_api.store_action_info(
@ -720,8 +747,17 @@ class BrainChatting:
action_name="listening", 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 动作完成,继续下一次思考") logger.info(f"{self.log_prefix} listening 动作完成,继续下一次思考")

View File

@ -402,7 +402,7 @@ class BrainPlanner:
moderation_prompt=moderation_prompt_block, moderation_prompt=moderation_prompt_block,
name_block=name_block, name_block=name_block,
interest=interest, interest=interest,
plan_style=global_config.personality.private_plan_style, plan_style=global_config.experimental.private_plan_style,
) )
return prompt, message_id_list return prompt, message_id_list

View File

@ -57,7 +57,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template")
# 考虑到实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 # 考虑到实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
# 对该字段的更新请严格参照语义化版本规范https://semver.org/lang/zh-CN/ # 对该字段的更新请严格参照语义化版本规范https://semver.org/lang/zh-CN/
MMC_VERSION = "0.12.1" MMC_VERSION = "0.12.2"
def get_key_comment(toml_table, key): def get_key_comment(toml_table, key):

View File

@ -57,9 +57,6 @@ class PersonalityConfig(ConfigBase):
visual_style: str = "" visual_style: str = ""
"""图片提示词""" """图片提示词"""
private_plan_style: str = ""
"""私聊说话规则,行为风格"""
states: list[str] = field(default_factory=lambda: []) states: list[str] = field(default_factory=lambda: [])
"""状态列表用于随机替换personality""" """状态列表用于随机替换personality"""
@ -713,8 +710,8 @@ class DebugConfig(ConfigBase):
class ExperimentalConfig(ConfigBase): class ExperimentalConfig(ConfigBase):
"""实验功能配置类""" """实验功能配置类"""
enable_friend_chat: bool = False private_plan_style: str = ""
"""是否启用好友聊天""" """私聊说话规则,行为风格(实验性功能)"""
chat_prompts: list[str] = field(default_factory=lambda: []) chat_prompts: list[str] = field(default_factory=lambda: [])
""" """
@ -904,6 +901,13 @@ class DreamConfig(ConfigBase):
return False return False
dream_visible: bool = False
"""
做梦结果是否存储到上下文
- True: 将梦境发送给配置的用户后也会存储到聊天上下文中在后续对话中可见
- False: 仅发送梦境但不存储不在后续对话上下文中出现
"""
def __post_init__(self): def __post_init__(self):
"""验证配置值""" """验证配置值"""
if self.interval_minutes < 1: if self.interval_minutes < 1:

View File

@ -215,11 +215,12 @@ async def generate_dream_summary(
f"platform={platform!r}, user_id={user_id!r}" f"platform={platform!r}, user_id={user_id!r}"
) )
else: else:
dream_visible = global_config.dream.dream_visible
ok = await send_api.text_to_stream( ok = await send_api.text_to_stream(
dream_content, dream_content,
stream_id=stream_id, stream_id=stream_id,
typing=False, typing=False,
storage_message=True, storage_message=dream_visible,
) )
if ok: if ok:
logger.info( logger.info(

View File

@ -1,5 +1,5 @@
[inner] [inner]
version = "7.3.3" version = "7.3.5"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
# 如果你想要修改配置文件请递增version的值 # 如果你想要修改配置文件请递增version的值
@ -40,20 +40,11 @@ multiple_reply_style = [
multiple_probability = 0.3 multiple_probability = 0.3
# 麦麦的说话规则和行为规则: # 麦麦的说话规则和行为规则:
plan_style = """ plan_style = """1.****action****使使
1.****action****使使 2.actionaction
2. 3.
3. 4.
4. 5."""
5.
6."""
# 麦麦私聊的说话规则,行为风格:
private_plan_style = """
1.****action****使使
2.
3."""
# 多重人格,根据概率随机选择人格 # 多重人格,根据概率随机选择人格
states = [ states = [
@ -153,6 +144,7 @@ first_delay_seconds = 1800 # 程序启动后首次做梦前的延迟时间(秒
# 为空字符串时不推送。 # 为空字符串时不推送。
dream_send = "" dream_send = ""
dream_visible = false # 做梦结果是否存储到上下文True: 将梦境发送给配置的用户后也会存储到聊天上下文中在后续对话中可见False: 仅发送梦境但不存储,不在后续对话上下文中出现
# 做梦时间段配置,格式:["HH:MM-HH:MM", ...] # 做梦时间段配置,格式:["HH:MM-HH:MM", ...]
# 如果列表为空,则表示全天允许做梦。 # 如果列表为空,则表示全天允许做梦。
# 如果配置了时间段,则只有在这些时间段内才会实际执行做梦流程。 # 如果配置了时间段,则只有在这些时间段内才会实际执行做梦流程。
@ -298,6 +290,12 @@ trust_xff = false # 是否启用X-Forwarded-For代理解析默认false
secure_cookie = false # 是否启用安全Cookie仅通过HTTPS传输默认false secure_cookie = false # 是否启用安全Cookie仅通过HTTPS传输默认false
[experimental] #实验性功能 [experimental] #实验性功能
# 麦麦私聊的说话规则,行为风格(实验性功能)
private_plan_style = """
1.****action****使使
2.
3."""
# 为指定聊天添加额外的prompt配置 # 为指定聊天添加额外的prompt配置
# 格式: ["platform:id:type:prompt内容", ...] # 格式: ["platform:id:type:prompt内容", ...]
# 示例: # 示例: