refactor(chat): 重构消息管理器以使用集中式上下文管理和能量系统

- 将流上下文管理从MessageManager迁移到专门的ContextManager
- 使用统一的能量系统计算focus_energy和分发间隔
- 重构ChatStream的消息数据转换逻辑,支持更完整的数据字段
- 更新数据库模型,移除interest_degree字段,统一使用interest_value
- 集成新的兴趣度管理系统替代原有的评分系统
- 添加消息存储的interest_value修复功能
This commit is contained in:
Windpicker-owo
2025-09-27 14:23:48 +08:00
parent 5962b44294
commit 1177b81285
15 changed files with 3520 additions and 506 deletions

View File

@@ -1,14 +1,26 @@
"""
消息管理模块
管理每个聊天流的上下文信息,包含历史记录和未读消息,定期检查并处理新消息
消息管理模块
提供统一的消息管理、上下文管理和分发调度功能
"""
from .message_manager import MessageManager, message_manager
from src.common.data_models.message_manager_data_model import (
StreamContext,
MessageStatus,
MessageManagerStats,
StreamStats,
from .context_manager import StreamContextManager, context_manager
from .distribution_manager import (
DistributionManager,
DistributionPriority,
DistributionTask,
StreamDistributionState,
distribution_manager
)
__all__ = ["MessageManager", "message_manager", "StreamContext", "MessageStatus", "MessageManagerStats", "StreamStats"]
__all__ = [
"MessageManager",
"message_manager",
"StreamContextManager",
"context_manager",
"DistributionManager",
"DistributionPriority",
"DistributionTask",
"StreamDistributionState",
"distribution_manager"
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ from src.chat.planner_actions.action_manager import ChatterActionManager
from .sleep_manager.sleep_manager import SleepManager
from .sleep_manager.wakeup_manager import WakeUpManager
from src.config.config import global_config
from . import context_manager
if TYPE_CHECKING:
from src.common.data_models.message_manager_data_model import StreamContext
@@ -28,7 +29,6 @@ class MessageManager:
"""消息管理器"""
def __init__(self, check_interval: float = 5.0):
self.stream_contexts: Dict[str, StreamContext] = {}
self.check_interval = check_interval # 检查间隔(秒)
self.is_running = False
self.manager_task: Optional[asyncio.Task] = None
@@ -44,6 +44,9 @@ class MessageManager:
self.sleep_manager = SleepManager()
self.wakeup_manager = WakeUpManager(self.sleep_manager)
# 初始化上下文管理器
self.context_manager = context_manager.context_manager
async def start(self):
"""启动消息管理器"""
if self.is_running:
@@ -53,6 +56,7 @@ class MessageManager:
self.is_running = True
self.manager_task = asyncio.create_task(self._manager_loop())
await self.wakeup_manager.start()
await self.context_manager.start()
logger.info("消息管理器已启动")
async def stop(self):
@@ -63,48 +67,44 @@ class MessageManager:
self.is_running = False
# 停止所有流处理任务
for context in self.stream_contexts.values():
if context.processing_task and not context.processing_task.done():
context.processing_task.cancel()
# 停止管理器任务
# 注意context_manager 会自己清理任务
if self.manager_task and not self.manager_task.done():
self.manager_task.cancel()
await self.wakeup_manager.stop()
await self.context_manager.stop()
logger.info("消息管理器已停止")
def add_message(self, stream_id: str, message: DatabaseMessages):
"""添加消息到指定聊天流"""
# 获取或创建流上下文
if stream_id not in self.stream_contexts:
self.stream_contexts[stream_id] = StreamContext(stream_id=stream_id)
self.stats.total_streams += 1
# 使用 context_manager 添加消息
success = self.context_manager.add_message_to_context(stream_id, message)
context = self.stream_contexts[stream_id]
context.set_chat_mode(ChatMode.FOCUS)
context.add_message(message)
logger.debug(f"添加消息到聊天流 {stream_id}: {message.message_id}")
if success:
logger.debug(f"添加消息到聊天流 {stream_id}: {message.message_id}")
else:
logger.warning(f"添加消息到聊天流 {stream_id} 失败")
def update_message_and_refresh_energy(
self,
stream_id: str,
message_id: str,
interest_degree: float = None,
interest_value: float = None,
actions: list = None,
should_reply: bool = None,
):
"""更新消息信息"""
if stream_id in self.stream_contexts:
context = self.stream_contexts[stream_id]
context.update_message_info(message_id, interest_degree, actions, should_reply)
# 使用 context_manager 更新消息信息
context = self.context_manager.get_stream_context(stream_id)
if context:
context.update_message_info(message_id, interest_value, actions, should_reply)
def add_action_and_refresh_energy(self, stream_id: str, message_id: str, action: str):
"""添加动作到消息"""
if stream_id in self.stream_contexts:
context = self.stream_contexts[stream_id]
# 使用 context_manager 添加动作到消息
context = self.context_manager.get_stream_context(stream_id)
if context:
context.add_action_to_message(message_id, action)
async def _manager_loop(self):
@@ -135,19 +135,23 @@ class MessageManager:
active_streams = 0
total_unread = 0
for stream_id, context in self.stream_contexts.items():
if not context.is_active:
# 使用 context_manager 获取活跃的流
active_stream_ids = self.context_manager.get_active_streams()
for stream_id in active_stream_ids:
context = self.context_manager.get_stream_context(stream_id)
if not context:
continue
active_streams += 1
# 检查是否有未读消息
unread_messages = context.get_unread_messages()
unread_messages = self.context_manager.get_unread_messages(stream_id)
if unread_messages:
total_unread += len(unread_messages)
# 如果没有处理任务,创建一个
if not context.processing_task or context.processing_task.done():
if not hasattr(context, 'processing_task') or not context.processing_task or context.processing_task.done():
context.processing_task = asyncio.create_task(self._process_stream_messages(stream_id))
# 更新统计
@@ -156,14 +160,13 @@ class MessageManager:
async def _process_stream_messages(self, stream_id: str):
"""处理指定聊天流的消息"""
if stream_id not in self.stream_contexts:
context = self.context_manager.get_stream_context(stream_id)
if not context:
return
context = self.stream_contexts[stream_id]
try:
# 获取未读消息
unread_messages = context.get_unread_messages()
unread_messages = self.context_manager.get_unread_messages(stream_id)
if not unread_messages:
return
@@ -204,7 +207,7 @@ class MessageManager:
# 处理结果,标记消息为已读
if results.get("success", False):
self._clear_all_unread_messages(context)
self._clear_all_unread_messages(stream_id)
logger.debug(f"聊天流 {stream_id} 处理成功,清除了 {len(unread_messages)} 条未读消息")
else:
logger.warning(f"聊天流 {stream_id} 处理失败: {results.get('error_message', '未知错误')}")
@@ -212,7 +215,7 @@ class MessageManager:
except Exception as e:
logger.error(f"处理聊天流 {stream_id} 时发生异常,将清除所有未读消息: {e}")
# 出现异常时也清除未读消息,避免重复处理
self._clear_all_unread_messages(context)
self._clear_all_unread_messages(stream_id)
raise
logger.debug(f"聊天流 {stream_id} 消息处理完成")
@@ -225,35 +228,36 @@ class MessageManager:
def deactivate_stream(self, stream_id: str):
"""停用聊天流"""
if stream_id in self.stream_contexts:
context = self.stream_contexts[stream_id]
context = self.context_manager.get_stream_context(stream_id)
if context:
context.is_active = False
# 取消处理任务
if context.processing_task and not context.processing_task.done():
if hasattr(context, 'processing_task') and context.processing_task and not context.processing_task.done():
context.processing_task.cancel()
logger.info(f"停用聊天流: {stream_id}")
def activate_stream(self, stream_id: str):
"""激活聊天流"""
if stream_id in self.stream_contexts:
self.stream_contexts[stream_id].is_active = True
context = self.context_manager.get_stream_context(stream_id)
if context:
context.is_active = True
logger.info(f"激活聊天流: {stream_id}")
def get_stream_stats(self, stream_id: str) -> Optional[StreamStats]:
"""获取聊天流统计"""
if stream_id not in self.stream_contexts:
context = self.context_manager.get_stream_context(stream_id)
if not context:
return None
context = self.stream_contexts[stream_id]
return StreamStats(
stream_id=stream_id,
is_active=context.is_active,
unread_count=len(context.get_unread_messages()),
unread_count=len(self.context_manager.get_unread_messages(stream_id)),
history_count=len(context.history_messages),
last_check_time=context.last_check_time,
has_active_task=bool(context.processing_task and not context.processing_task.done()),
has_active_task=bool(hasattr(context, 'processing_task') and context.processing_task and not context.processing_task.done()),
)
def get_manager_stats(self) -> Dict[str, Any]:
@@ -269,18 +273,9 @@ class MessageManager:
def cleanup_inactive_streams(self, max_inactive_hours: int = 24):
"""清理不活跃的聊天流"""
current_time = time.time()
max_inactive_seconds = max_inactive_hours * 3600
inactive_streams = []
for stream_id, context in self.stream_contexts.items():
if current_time - context.last_check_time > max_inactive_seconds and not context.get_unread_messages():
inactive_streams.append(stream_id)
for stream_id in inactive_streams:
self.deactivate_stream(stream_id)
del self.stream_contexts[stream_id]
logger.info(f"清理不活跃聊天流: {stream_id}")
# 使用 context_manager 的自动清理功能
self.context_manager.cleanup_inactive_contexts(max_inactive_hours * 3600)
logger.info("已启动不活跃聊天流清理")
async def _check_and_handle_interruption(self, context: StreamContext, stream_id: str):
"""检查并处理消息打断"""
@@ -329,90 +324,29 @@ class MessageManager:
logger.debug(f"聊天流 {stream_id} 未触发打断,打断概率: {interruption_probability:.2f}")
def _calculate_stream_distribution_interval(self, context: StreamContext) -> float:
"""计算单个聊天流的分发周期 - 基于阈值感知的focus_energy"""
"""计算单个聊天流的分发周期 - 使用重构后的能量管理器"""
if not global_config.chat.dynamic_distribution_enabled:
return self.check_interval # 使用固定间隔
from src.plugin_system.apis.chat_api import get_chat_manager
try:
from src.chat.energy_system import energy_manager
from src.plugin_system.apis.chat_api import get_chat_manager
chat_stream = get_chat_manager().get_stream(context.stream_id)
# 获取该流的focus_energy新的阈值感知版本
focus_energy = 0.5 # 默认值
avg_message_interest = 0.5 # 默认平均兴趣度
# 获取聊天流和能量
chat_stream = get_chat_manager().get_stream(context.stream_id)
if chat_stream:
focus_energy = chat_stream.focus_energy
# 使用能量管理器获取分发周期
interval = energy_manager.get_distribution_interval(focus_energy)
logger.debug(f"{context.stream_id} 分发周期: {interval:.2f}s (能量: {focus_energy:.3f})")
return interval
else:
# 默认间隔
return self.check_interval
if chat_stream:
focus_energy = chat_stream.focus_energy
# 获取平均消息兴趣度用于更精确的计算 - 从StreamContext获取
history_messages = context.get_history_messages(limit=100)
unread_messages = context.get_unread_messages()
all_messages = history_messages + unread_messages
if all_messages:
message_interests = [msg.interest_degree for msg in all_messages if hasattr(msg, "interest_degree")]
avg_message_interest = sum(message_interests) / len(message_interests) if message_interests else 0.5
# 获取AFC阈值用于参考添加None值检查
reply_threshold = getattr(global_config.affinity_flow, "reply_action_interest_threshold", 0.4)
non_reply_threshold = getattr(global_config.affinity_flow, "non_reply_action_interest_threshold", 0.2)
high_match_threshold = getattr(global_config.affinity_flow, "high_match_interest_threshold", 0.8)
# 使用配置参数
base_interval = global_config.chat.dynamic_distribution_base_interval
min_interval = global_config.chat.dynamic_distribution_min_interval
max_interval = global_config.chat.dynamic_distribution_max_interval
jitter_factor = global_config.chat.dynamic_distribution_jitter_factor
# 基于阈值感知的智能分发周期计算
if avg_message_interest >= high_match_threshold:
# 超高兴趣度:极快响应 (1-2秒)
interval_multiplier = 0.3 + (focus_energy - 0.7) * 2.0
elif avg_message_interest >= reply_threshold:
# 高兴趣度:快速响应 (2-6秒)
gap_from_reply = (avg_message_interest - reply_threshold) / (high_match_threshold - reply_threshold)
interval_multiplier = 0.6 + gap_from_reply * 0.4
elif avg_message_interest >= non_reply_threshold:
# 中等兴趣度:正常响应 (6-15秒)
gap_from_non_reply = (avg_message_interest - non_reply_threshold) / (reply_threshold - non_reply_threshold)
interval_multiplier = 1.2 + gap_from_non_reply * 1.8
else:
# 低兴趣度:缓慢响应 (15-30秒)
gap_ratio = max(0, avg_message_interest / non_reply_threshold)
interval_multiplier = 3.0 + (1.0 - gap_ratio) * 3.0
# 应用focus_energy微调
energy_adjustment = 1.0 + (focus_energy - 0.5) * 0.5
interval = base_interval * interval_multiplier * energy_adjustment
# 添加随机扰动避免同步
import random
jitter = random.uniform(1.0 - jitter_factor, 1.0 + jitter_factor)
final_interval = interval * jitter
# 限制在合理范围内
final_interval = max(min_interval, min(max_interval, final_interval))
# 根据兴趣度级别调整日志级别
if avg_message_interest >= high_match_threshold:
log_level = "info"
elif avg_message_interest >= reply_threshold:
log_level = "info"
else:
log_level = "debug"
log_msg = (
f"{context.stream_id} 分发周期: {final_interval:.2f}s | "
f"focus_energy: {focus_energy:.3f} | "
f"avg_interest: {avg_message_interest:.3f} | "
f"阈值参考: {non_reply_threshold:.2f}/{reply_threshold:.2f}/{high_match_threshold:.2f}"
)
if log_level == "info":
logger.info(log_msg)
else:
logger.debug(log_msg)
return final_interval
except Exception as e:
logger.error(f"计算分发周期失败: {e}")
return self.check_interval
def _calculate_next_manager_delay(self) -> float:
"""计算管理器下次检查的延迟时间"""
@@ -420,8 +354,10 @@ class MessageManager:
min_delay = float("inf")
# 找到最近需要检查的流
for context in self.stream_contexts.values():
if not context.is_active:
active_stream_ids = self.context_manager.get_active_streams()
for stream_id in active_stream_ids:
context = self.context_manager.get_stream_context(stream_id)
if not context or not context.is_active:
continue
time_until_check = context.next_check_time - current_time
@@ -443,8 +379,12 @@ class MessageManager:
current_time = time.time()
processed_streams = 0
for stream_id, context in self.stream_contexts.items():
if not context.is_active:
# 使用 context_manager 获取活跃的流
active_stream_ids = self.context_manager.get_active_streams()
for stream_id in active_stream_ids:
context = self.context_manager.get_stream_context(stream_id)
if not context or not context.is_active:
continue
# 检查是否达到检查时间
@@ -462,7 +402,7 @@ class MessageManager:
context.next_check_time = current_time + context.distribution_interval
# 检查未读消息
unread_messages = context.get_unread_messages()
unread_messages = self.context_manager.get_unread_messages(stream_id)
if unread_messages:
processed_streams += 1
self.stats.total_unread_messages = len(unread_messages)
@@ -492,7 +432,7 @@ class MessageManager:
context.processing_task = asyncio.create_task(self._process_stream_messages(stream_id))
# 更新活跃流计数
active_count = sum(1 for ctx in self.stream_contexts.values() if ctx.is_active)
active_count = len(self.context_manager.get_active_streams())
self.stats.active_streams = active_count
if processed_streams > 0:
@@ -500,13 +440,16 @@ class MessageManager:
async def _check_all_streams_with_priority(self):
"""按优先级检查所有聊天流高focus_energy的流优先处理"""
if not self.stream_contexts:
if not self.context_manager.get_active_streams():
return
# 获取活跃的聊天流并按focus_energy排序
active_streams = []
for stream_id, context in self.stream_contexts.items():
if not context.is_active:
active_stream_ids = self.context_manager.get_active_streams()
for stream_id in active_stream_ids:
context = self.context_manager.get_stream_context(stream_id)
if not context or not context.is_active:
continue
# 获取focus_energy如果不存在则使用默认值
@@ -532,12 +475,12 @@ class MessageManager:
active_stream_count += 1
# 检查是否有未读消息
unread_messages = context.get_unread_messages()
unread_messages = self.context_manager.get_unread_messages(stream_id)
if unread_messages:
total_unread += len(unread_messages)
# 如果没有处理任务,创建一个
if not context.processing_task or context.processing_task.done():
if not hasattr(context, 'processing_task') or not context.processing_task or context.processing_task.done():
context.processing_task = asyncio.create_task(self._process_stream_messages(stream_id))
# 高优先级流的额外日志
@@ -553,63 +496,40 @@ class MessageManager:
self.stats.total_unread_messages = total_unread
def _calculate_stream_priority(self, context: StreamContext, focus_energy: float) -> float:
"""计算聊天流的优先级分数"""
from src.plugin_system.apis.chat_api import get_chat_manager
chat_stream = get_chat_manager().get_stream(context.stream_id)
# 基础优先级focus_energy
"""计算聊天流的优先级分数 - 简化版本主要使用focus_energy"""
# 使用重构后的能量管理器主要依赖focus_energy
base_priority = focus_energy
# 未读消息数量加权
# 简单的未读消息加权
unread_count = len(context.get_unread_messages())
message_count_bonus = min(unread_count * 0.1, 0.3) # 最多30%加成
message_bonus = min(unread_count * 0.05, 0.2) # 最多20%加成
# 时间加权:最近活跃的流优先级更高
# 简单的时间加权
current_time = time.time()
time_since_active = current_time - context.last_check_time
time_penalty = max(0, 1.0 - time_since_active / 3600.0) # 1小时内无惩罚
# 连续无回复惩罚 - 从StreamContext历史消息计算
if chat_stream:
# 计算连续无回复次数
consecutive_no_reply = 0
all_messages = context.get_history_messages(limit=50) + context.get_unread_messages()
for msg in reversed(all_messages):
if hasattr(msg, "should_reply") and msg.should_reply:
if not (hasattr(msg, "actions") and "reply" in (msg.actions or [])):
consecutive_no_reply += 1
else:
break
no_reply_penalty = max(0, 1.0 - consecutive_no_reply * 0.05) # 每次无回复降低5%
else:
no_reply_penalty = 1.0
# 综合优先级计算
final_priority = (
base_priority * 0.6 # 基础兴趣度权重60%
+ message_count_bonus * 0.2 # 消息数量权重20%
+ time_penalty * 0.1 # 时间权重10%
+ no_reply_penalty * 0.1 # 回复状态权重10%
)
time_bonus = max(0, 1.0 - time_since_active / 7200.0) * 0.1 # 2小时内衰减
final_priority = base_priority + message_bonus + time_bonus
return max(0.0, min(1.0, final_priority))
def _clear_all_unread_messages(self, context: StreamContext):
def _clear_all_unread_messages(self, stream_id: str):
"""清除指定上下文中的所有未读消息,防止意外情况导致消息一直未读"""
unread_messages = context.get_unread_messages()
unread_messages = self.context_manager.get_unread_messages(stream_id)
if not unread_messages:
return
logger.warning(f"正在清除 {len(unread_messages)} 条未读消息")
# 将所有未读消息标记为已读并移动到历史记录
for msg in unread_messages[:]: # 使用切片复制避免迭代时修改列表
try:
context.mark_message_as_read(msg.message_id)
self.stats.total_processed_messages += 1
logger.debug(f"强制清除消息 {msg.message_id},标记为已读")
except Exception as e:
logger.error(f"清除消息 {msg.message_id} 时出错: {e}")
# 将所有未读消息标记为已读
context = self.context_manager.get_stream_context(stream_id)
if context:
for msg in unread_messages[:]: # 使用切片复制避免迭代时修改列表
try:
context.mark_message_as_read(msg.message_id)
self.stats.total_processed_messages += 1
logger.debug(f"强制清除消息 {msg.message_id},标记为已读")
except Exception as e:
logger.error(f"清除消息 {msg.message_id} 时出错: {e}")
# 创建全局消息管理器实例