feat(message-manager): 用流循环管理器替换调度器/分派器

- 移除 scheduler_dispatcher 模块,并用 distribution_manager 替换
- 实现StreamLoopManager,以改进消息分发和中断处理
- 将消息缓存系统直接添加到StreamContext中,并配置缓存设置
- 使用具有缓存感知的消息处理来增强SingleStreamContextManager
- 更新`message_manager`,使用`stream_loop_manager`替代`scheduler_dispatcher`
- 在StreamContext数据模型中添加缓存统计和刷新方法
- 通过适当的任务取消和重新处理来改进中断处理
- 为ChatManager添加get_all_stream方法,以实现更优的流管理
- 更新亲和聊天规划器,以更可靠地处理专注/正常模式切换
This commit is contained in:
Windpicker-owo
2025-11-08 10:46:44 +08:00
parent 78a3a192bf
commit d4824e35ad
9 changed files with 1178 additions and 908 deletions

View File

@@ -87,7 +87,7 @@ class AffinityChatter(BaseChatter):
self.stats["successful_executions"] += 1
self.last_activity_time = time.time()
result: ClassVar = {
result = {
"success": True,
"stream_id": self.stream_id,
"plan_created": True,

View File

@@ -84,14 +84,12 @@ class ChatterActionPlanner:
return [], None
async def _enhanced_plan_flow(self, context: "StreamContext | None") -> tuple[list[dict[str, Any]], Any | None]:
"""执行增强版规划流程"""
"""执行增强版规划流程,根据模式分发到对应的处理函数"""
try:
# 1. 生成初始 Plan
# 1. 确定当前模式
chat_mode = context.chat_mode if context else ChatMode.FOCUS
# 如果禁用了Normal模式则强制将任何处于Normal模式的会话切换回Focus模式
# 2. 如果禁用了Normal模式则强制切换回Focus模式
if not global_config.affinity_flow.enable_normal_mode and chat_mode == ChatMode.NORMAL:
logger.info("Normal模式已禁用强制切换回Focus模式")
chat_mode = ChatMode.FOCUS
@@ -99,12 +97,29 @@ class ChatterActionPlanner:
context.chat_mode = ChatMode.FOCUS
await self._sync_chat_mode_to_stream(context)
# Normal模式下使用简化流程
# 3. 根据模式分发到对应的处理流程
if chat_mode == ChatMode.NORMAL:
return await self._normal_mode_flow(context)
else:
return await self._focus_mode_flow(context)
except Exception as e:
logger.error(f"增强版规划流程出错: {e}")
self.planner_stats["failed_plans"] += 1
# 清理处理标记
if context:
context.processing_message_id = None
return [], None
async def _focus_mode_flow(self, context: "StreamContext | None") -> tuple[list[dict[str, Any]], Any | None]:
"""Focus模式下的完整plan流程
执行完整的生成→筛选→执行流程,支持所有类型的动作,包括非回复动作。
"""
try:
unread_messages = context.get_unread_messages() if context else []
# 2. 使用新的兴趣度管理系统进行评分
# 1. 使用新的兴趣度管理系统进行评分
max_message_interest = 0.0
reply_not_available = True
aggregate_should_act = False
@@ -123,7 +138,7 @@ class ChatterActionPlanner:
message_should_act = getattr(message, "should_act", False)
logger.debug(
f"消息 {message.message_id} 预计算标志: interest={message_interest:.3f}, "
f"Focus模式 - 消息 {message.message_id} 预计算标志: interest={message_interest:.3f}, "
f"should_reply={message_should_reply}, should_act={message_should_act}"
)
@@ -136,22 +151,22 @@ class ChatterActionPlanner:
aggregate_should_act = True
except Exception as e:
logger.warning(f"处理消息 {message.message_id} 失败: {e}")
logger.warning(f"Focus模式 - 处理消息 {message.message_id} 失败: {e}")
message.interest_value = 0.0
message.should_reply = False
message.should_act = False
# 检查兴趣度是否达到非回复动作阈值
# 2. 检查兴趣度是否达到非回复动作阈值
non_reply_action_interest_threshold = global_config.affinity_flow.non_reply_action_interest_threshold
if not aggregate_should_act:
logger.info("所有未读消息低于兴趣度阈值,不执行动作")
logger.info("Focus模式 - 所有未读消息低于兴趣度阈值,不执行动作")
# 直接返回 no_action
from src.common.data_models.info_data_model import ActionPlannerInfo
no_action = ActionPlannerInfo(
action_type="no_action",
reasoning=(
"所有未读消息兴趣度未达阈值 "
"Focus模式 - 所有未读消息兴趣度未达阈值 "
f"{non_reply_action_interest_threshold:.3f}"
f"(最高兴趣度 {max_message_interest:.3f}"
),
@@ -162,28 +177,27 @@ class ChatterActionPlanner:
# 更新连续不回复计数
await self._update_interest_calculator_state(replied=False)
initial_plan = await self.generator.generate(chat_mode)
initial_plan = await self.generator.generate(ChatMode.FOCUS)
filtered_plan = initial_plan
filtered_plan.decided_actions = [no_action]
else:
# 在规划前,先进行动作修改
# 3. 在规划前,先进行动作修改
from src.chat.planner_actions.action_modifier import ActionModifier
action_modifier = ActionModifier(self.action_manager, self.chat_id)
await action_modifier.modify_actions()
# 生成初始计划前,刷新缓存消息到未读列表
await self._flush_cached_messages_to_unread(context)
# 4. 生成初始计划
initial_plan = await self.generator.generate(ChatMode.FOCUS)
initial_plan = await self.generator.generate(chat_mode)
# 确保Plan中包含所有当前可用的动作
# 5. 确保Plan中包含所有当前可用的动作
initial_plan.available_actions = self.action_manager.get_using_actions()
# 4. 筛选 Plan
# 6. 筛选 Plan
available_actions = list(initial_plan.available_actions.keys())
plan_filter = ChatterPlanFilter(self.chat_id, available_actions)
filtered_plan = await plan_filter.filter(reply_not_available, initial_plan)
# 4.5 检查是否正在处理相同的目标消息,防止重复回复
# 7. 检查是否正在处理相同的目标消息,防止重复回复
target_message_id = None
if filtered_plan and filtered_plan.decided_actions:
for action in filtered_plan.decided_actions:
@@ -195,17 +209,17 @@ class ChatterActionPlanner:
target_message_id = action.action_message.get("message_id")
break
# 如果找到目标消息ID检查是否已经在处理中
# 8. 如果找到目标消息ID检查是否已经在处理中
if target_message_id and context:
if context.processing_message_id == target_message_id:
logger.warning(
f"目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
f"Focus模式 - 目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
)
# 返回 no_action避免重复处理
from src.common.data_models.info_data_model import ActionPlannerInfo
no_action = ActionPlannerInfo(
action_type="no_action",
reasoning=f"目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
reasoning=f"Focus模式 - 目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
action_data={},
action_message=None,
)
@@ -213,15 +227,15 @@ class ChatterActionPlanner:
else:
# 记录当前正在处理的消息ID
context.processing_message_id = target_message_id
logger.debug(f"开始处理目标消息: {target_message_id}")
logger.debug(f"Focus模式 - 开始处理目标消息: {target_message_id}")
# 5. 使用 PlanExecutor 执行 Plan
# 9. 使用 PlanExecutor 执行 Plan
execution_result = await self.executor.execute(filtered_plan)
# 6. 根据执行结果更新统计信息
# 10. 根据执行结果更新统计信息
self._update_stats_from_execution_result(execution_result)
# 7. 更新兴趣计算器状态
# 11. 更新兴趣计算器状态
if filtered_plan.decided_actions:
has_reply = any(
action.action_type in ["reply", "proactive_reply"]
@@ -231,21 +245,20 @@ class ChatterActionPlanner:
has_reply = False
await self._update_interest_calculator_state(replied=has_reply)
# 8. Focus模式下如果执行了reply动作根据focus_energy概率切换到Normal模式
if chat_mode == ChatMode.FOCUS and context:
if has_reply and global_config.affinity_flow.enable_normal_mode:
await self._check_enter_normal_mode(context)
# 12. Focus模式下如果执行了reply动作根据focus_energy概率切换到Normal模式
if has_reply and context and global_config.affinity_flow.enable_normal_mode:
await self._check_enter_normal_mode(context)
# 9. 清理处理标记
# 13. 清理处理标记
if context:
context.processing_message_id = None
logger.debug("已清理处理标记,完成规划流程")
logger.debug("Focus模式 - 已清理处理标记,完成规划流程")
# 10. 返回结果
# 14. 返回结果
return self._build_return_result(filtered_plan)
except Exception as e:
logger.error(f"增强版规划流程出错: {e}")
logger.error(f"Focus模式流程出错: {e}")
self.planner_stats["failed_plans"] += 1
# 清理处理标记
if context:
@@ -258,32 +271,33 @@ class ChatterActionPlanner:
只计算兴趣值并判断是否达到reply阈值不执行完整的plan流程。
根据focus_energy决定退出normal模式回到focus模式的概率。
"""
# 最后的保障措施,以防意外进入此流程
# 安全检查确保Normal模式已启用
if not global_config.affinity_flow.enable_normal_mode:
logger.warning("意外进入了Normal模式流程但该模式已被禁用将强制切换回Focus模式进行完整规划。")
logger.warning("Normal模式 - 意外进入了Normal模式流程但该模式已被禁用将强制切换回Focus模式进行完整规划。")
if context:
context.chat_mode = ChatMode.FOCUS
await self._sync_chat_mode_to_stream(context)
# 重新运行主规划流程这次将正确使用Focus模式
return await self._enhanced_plan_flow(context)
try:
# Normal模式开始时刷新缓存消息到未读列表
await self._flush_cached_messages_to_unread(context)
try:
unread_messages = context.get_unread_messages() if context else []
# 1. 检查是否有未读消息
if not unread_messages:
logger.debug("Normal模式: 没有未读消息")
logger.debug("Normal模式 - 没有未读消息")
from src.common.data_models.info_data_model import ActionPlannerInfo
no_action = ActionPlannerInfo(
action_type="no_action",
reasoning="Normal模式: 没有未读消息",
reasoning="Normal模式 - 没有未读消息",
action_data={},
action_message=None,
)
# 检查是否需要退出Normal模式
await self._check_exit_normal_mode(context)
return [asdict(no_action)], None
# 检查是否有消息达到reply阈值
# 2. 检查是否有消息达到reply阈值
should_reply = False
target_message = None
@@ -292,32 +306,34 @@ class ChatterActionPlanner:
if message_should_reply:
should_reply = True
target_message = message
logger.info(f"Normal模式: 消息 {message.message_id} 达到reply阈值")
logger.info(f"Normal模式 - 消息 {message.message_id} 达到reply阈值,准备回复")
break
if should_reply and target_message:
# 检查是否正在处理相同的目标消息,防止重复回复
# 3. 防重复检查:检查是否正在处理相同的目标消息
target_message_id = target_message.message_id
if context and context.processing_message_id == target_message_id:
logger.warning(
f"Normal模式: 目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
f"Normal模式 - 目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
)
# 返回 no_action避免重复处理
from src.common.data_models.info_data_model import ActionPlannerInfo
no_action = ActionPlannerInfo(
action_type="no_action",
reasoning=f"目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
reasoning=f"Normal模式 - 目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
action_data={},
action_message=None,
)
# 检查是否需要退出Normal模式
await self._check_exit_normal_mode(context)
return [asdict(no_action)], None
# 记录当前正在处理的消息ID
if context:
context.processing_message_id = target_message_id
logger.debug(f"Normal模式: 开始处理目标消息: {target_message_id}")
logger.debug(f"Normal模式 - 开始处理目标消息: {target_message_id}")
# 达到reply阈值直接进入回复流程
# 4. 构建回复动作Normal模式的简化流程
from src.common.data_models.info_data_model import ActionPlannerInfo, Plan
from src.plugin_system.base.component_types import ChatType
@@ -326,7 +342,7 @@ class ChatterActionPlanner:
reply_action = ActionPlannerInfo(
action_type="reply",
reasoning="Normal模式: 兴趣度达到阈值,直接回复",
reasoning="Normal模式 - 兴趣度达到阈值,直接回复(简化流程)",
action_data={"target_message_id": target_message.message_id},
action_message=target_message,
should_quote_reply=False, # Normal模式默认不引用回复保持对话流畅
@@ -341,31 +357,31 @@ class ChatterActionPlanner:
decided_actions=[reply_action],
)
# 执行reply动作
# 5. 执行reply动作
execution_result = await self.executor.execute(minimal_plan)
self._update_stats_from_execution_result(execution_result)
logger.info("Normal模式: 执行reply动作完成")
logger.info("Normal模式 - 执行reply动作完成")
# 更新兴趣计算器状态(回复成功,重置不回复计数)
# 6. 更新兴趣计算器状态(回复成功,重置不回复计数)
await self._update_interest_calculator_state(replied=True)
# 清理处理标记
# 7. 清理处理标记
if context:
context.processing_message_id = None
logger.debug("Normal模式: 已清理处理标记")
logger.debug("Normal模式 - 已清理处理标记")
# 无论是否回复,都进行退出normal模式的判定
# 8. 检查是否需要退出Normal模式
await self._check_exit_normal_mode(context)
return [asdict(reply_action)], target_message_dict
else:
# 未达到reply阈值
logger.debug("Normal模式: 未达到reply阈值")
logger.debug("Normal模式 - 未达到reply阈值,不执行回复")
from src.common.data_models.info_data_model import ActionPlannerInfo
no_action = ActionPlannerInfo(
action_type="no_action",
reasoning="Normal模式: 兴趣度未达到阈值",
reasoning="Normal模式 - 兴趣度未达到阈值",
action_data={},
action_message=None,
)
@@ -373,13 +389,13 @@ class ChatterActionPlanner:
# 更新连续不回复计数
await self._update_interest_calculator_state(replied=False)
# 无论是否回复,都进行退出normal模式的判定
# 检查是否需要退出Normal模式
await self._check_exit_normal_mode(context)
return [asdict(no_action)], None
except Exception as e:
logger.error(f"Normal模式流程出错: {e}")
logger.error(f"Normal模式 - 流程出错: {e}")
self.planner_stats["failed_plans"] += 1
# 清理处理标记
if context: