refactor(chat): 异步化聊天系统并重构兴趣值计算机制

将同步调用改为异步调用以提升性能,重构兴趣值计算流程以支持更灵活的组件化架构。主要改进包括:

- 异步化ChatManager相关方法,避免阻塞主线程
- 重构兴趣值计算系统,从插件内部计算改为通过兴趣管理器统一处理
- 新增should_act字段支持更细粒度的动作决策
- 优化初始化逻辑,避免构造函数中的异步操作
- 扩展插件系统支持兴趣计算器组件注册
- 更新数据库模型以支持新的兴趣值相关字段

这些改进提升了系统的响应性能和可扩展性,同时保持了API的向后兼容性。
This commit is contained in:
Windpicker-owo
2025-10-05 01:25:52 +08:00
parent 49025a4973
commit 624298e1b8
38 changed files with 1493 additions and 259 deletions

View File

@@ -0,0 +1,220 @@
"""兴趣值计算组件基类
提供兴趣值计算的标准接口,确保只能有一个兴趣值计算组件实例运行
"""
import time
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from src.common.data_models.database_data_model import DatabaseMessages
from src.common.logger import get_logger
from src.plugin_system.base.component_types import ComponentType, InterestCalculatorInfo
logger = get_logger("base_interest_calculator")
class InterestCalculationResult:
"""兴趣值计算结果"""
def __init__(
self,
success: bool,
message_id: str,
interest_value: float,
should_take_action: bool = False,
should_reply: bool = False,
should_act: bool = False,
error_message: str | None = None,
calculation_time: float = 0.0
):
self.success = success
self.message_id = message_id
self.interest_value = max(0.0, min(1.0, interest_value)) # 确保在0-1范围内
self.should_take_action = should_take_action
self.should_reply = should_reply
self.should_act = should_act
self.error_message = error_message
self.calculation_time = calculation_time
self.timestamp = time.time()
def to_dict(self) -> dict:
"""转换为字典格式"""
return {
"success": self.success,
"message_id": self.message_id,
"interest_value": self.interest_value,
"should_take_action": self.should_take_action,
"should_reply": self.should_reply,
"should_act": self.should_act,
"error_message": self.error_message,
"calculation_time": self.calculation_time,
"timestamp": self.timestamp
}
def __repr__(self) -> str:
return (f"InterestCalculationResult("
f"success={self.success}, "
f"message_id={self.message_id}, "
f"interest_value={self.interest_value:.3f}, "
f"should_take_action={self.should_take_action}, "
f"should_reply={self.should_reply}, "
f"should_act={self.should_act})")
class BaseInterestCalculator(ABC):
"""兴趣值计算组件基类
所有兴趣值计算组件都必须继承此类,并实现 execute 方法
系统确保只能有一个兴趣值计算组件实例运行
"""
# 子类必须定义这些属性
component_name: str = ""
component_version: str = ""
component_description: str = ""
enabled_by_default: bool = True # 是否默认启用
def __init__(self):
self._enabled = False
self._last_calculation_time = 0.0
self._total_calculations = 0
self._failed_calculations = 0
self._average_calculation_time = 0.0
# 验证必须定义的属性
if not self.component_name:
raise ValueError("子类必须定义 component_name 属性")
if not self.component_version:
raise ValueError("子类必须定义 component_version 属性")
if not self.component_description:
raise ValueError("子类必须定义 component_description 属性")
@abstractmethod
async def execute(self, message: "DatabaseMessages") -> InterestCalculationResult:
"""执行兴趣值计算
Args:
message: 数据库消息对象
Returns:
InterestCalculationResult: 计算结果
"""
pass
async def initialize(self) -> bool:
"""初始化组件
Returns:
bool: 初始化是否成功
"""
try:
self._enabled = True
return True
except Exception as e:
self._enabled = False
return False
async def cleanup(self) -> bool:
"""清理组件资源
Returns:
bool: 清理是否成功
"""
try:
self._enabled = False
return True
except Exception:
return False
@property
def is_enabled(self) -> bool:
"""组件是否已启用"""
return self._enabled
def get_statistics(self) -> dict:
"""获取组件统计信息"""
return {
"component_name": self.component_name,
"component_version": self.component_version,
"enabled": self._enabled,
"total_calculations": self._total_calculations,
"failed_calculations": self._failed_calculations,
"success_rate": 1.0 - (self._failed_calculations / max(1, self._total_calculations)),
"average_calculation_time": self._average_calculation_time,
"last_calculation_time": self._last_calculation_time
}
def _update_statistics(self, result: InterestCalculationResult):
"""更新统计信息"""
self._total_calculations += 1
if not result.success:
self._failed_calculations += 1
# 更新平均计算时间
if self._total_calculations == 1:
self._average_calculation_time = result.calculation_time
else:
alpha = 0.1 # 指数移动平均的平滑因子
self._average_calculation_time = (
alpha * result.calculation_time +
(1 - alpha) * self._average_calculation_time
)
self._last_calculation_time = result.timestamp
async def _safe_execute(self, message: "DatabaseMessages") -> InterestCalculationResult:
"""安全执行计算,包含统计和错误处理"""
if not self._enabled:
return InterestCalculationResult(
success=False,
message_id=getattr(message, 'message_id', ''),
interest_value=0.0,
error_message="组件未启用"
)
start_time = time.time()
try:
result = await self.execute(message)
result.calculation_time = time.time() - start_time
self._update_statistics(result)
return result
except Exception as e:
result = InterestCalculationResult(
success=False,
message_id=getattr(message, 'message_id', ''),
interest_value=0.0,
error_message=f"计算执行失败: {str(e)}",
calculation_time=time.time() - start_time
)
self._update_statistics(result)
return result
@classmethod
def get_interest_calculator_info(cls) -> "InterestCalculatorInfo":
"""从类属性生成InterestCalculatorInfo
遵循BaseCommand和BaseAction的设计模式从类属性自动生成组件信息
Returns:
InterestCalculatorInfo: 生成的兴趣计算器信息对象
"""
name = getattr(cls, 'component_name', cls.__name__.lower().replace('calculator', ''))
if "." in name:
logger.error(f"InterestCalculator名称 '{name}' 包含非法字符 '.',请使用下划线替代")
raise ValueError(f"InterestCalculator名称 '{name}' 包含非法字符 '.',请使用下划线替代")
return InterestCalculatorInfo(
name=name,
component_type=ComponentType.INTEREST_CALCULATOR,
description=getattr(cls, 'component_description', cls.__doc__ or "兴趣度计算器"),
enabled_by_default=getattr(cls, 'enabled_by_default', True),
)
def __repr__(self) -> str:
return (f"{self.__class__.__name__}("
f"name={self.component_name}, "
f"version={self.component_version}, "
f"enabled={self._enabled})")

View File

@@ -1,11 +1,20 @@
from abc import abstractmethod
from src.common.logger import get_logger
from src.plugin_system.base.component_types import ActionInfo, CommandInfo, EventHandlerInfo, PlusCommandInfo, ToolInfo
from src.plugin_system.base.component_types import (
ActionInfo,
CommandInfo,
ComponentType,
EventHandlerInfo,
InterestCalculatorInfo,
PlusCommandInfo,
ToolInfo,
)
from .base_action import BaseAction
from .base_command import BaseCommand
from .base_events_handler import BaseEventHandler
from .base_interest_calculator import BaseInterestCalculator
from .base_tool import BaseTool
from .plugin_base import PluginBase
from .plus_command import PlusCommand
@@ -21,6 +30,72 @@ class BasePlugin(PluginBase):
- Command组件处理命令请求
- 未来可扩展Scheduler、Listener等
"""
@classmethod
def _get_component_info_from_class(cls, component_class: type, component_type: ComponentType):
"""从组件类自动生成组件信息
Args:
component_class: 组件类
component_type: 组件类型
Returns:
对应类型的ComponentInfo对象
"""
if component_type == ComponentType.COMMAND:
if hasattr(component_class, 'get_command_info'):
return component_class.get_command_info()
else:
logger.warning(f"Command类 {component_class.__name__} 缺少 get_command_info 方法")
return None
elif component_type == ComponentType.ACTION:
if hasattr(component_class, 'get_action_info'):
return component_class.get_action_info()
else:
logger.warning(f"Action类 {component_class.__name__} 缺少 get_action_info 方法")
return None
elif component_type == ComponentType.INTEREST_CALCULATOR:
if hasattr(component_class, 'get_interest_calculator_info'):
return component_class.get_interest_calculator_info()
else:
logger.warning(f"InterestCalculator类 {component_class.__name__} 缺少 get_interest_calculator_info 方法")
return None
elif component_type == ComponentType.PLUS_COMMAND:
# PlusCommand的get_info逻辑可以在这里实现
logger.warning("PlusCommand的get_info逻辑尚未实现")
return None
elif component_type == ComponentType.TOOL:
# Tool的get_info逻辑可以在这里实现
logger.warning("Tool的get_info逻辑尚未实现")
return None
elif component_type == ComponentType.EVENT_HANDLER:
# EventHandler的get_info逻辑可以在这里实现
logger.warning("EventHandler的get_info逻辑尚未实现")
return None
else:
logger.error(f"不支持的组件类型: {component_type}")
return None
@classmethod
def get_component_info(cls, component_class: type, component_type: ComponentType):
"""获取组件信息的通用方法
这是一个便捷方法内部调用_get_component_info_from_class
Args:
component_class: 组件类
component_type: 组件类型
Returns:
对应类型的ComponentInfo对象
"""
return cls._get_component_info_from_class(component_class, component_type)
@abstractmethod
def get_plugin_components(
self,
@@ -30,6 +105,7 @@ class BasePlugin(PluginBase):
| tuple[PlusCommandInfo, type[PlusCommand]]
| tuple[EventHandlerInfo, type[BaseEventHandler]]
| tuple[ToolInfo, type[BaseTool]]
| tuple[InterestCalculatorInfo, type[BaseInterestCalculator]]
]:
"""获取插件包含的组件列表

View File

@@ -19,6 +19,7 @@ class ComponentType(Enum):
SCHEDULER = "scheduler" # 定时任务组件(预留)
EVENT_HANDLER = "event_handler" # 事件处理组件
CHATTER = "chatter" # 聊天处理器组件
INTEREST_CALCULATOR = "interest_calculator" # 兴趣度计算组件
def __str__(self) -> str:
return self.value
@@ -229,6 +230,17 @@ class ChatterInfo(ComponentInfo):
self.component_type = ComponentType.CHATTER
@dataclass
class InterestCalculatorInfo(ComponentInfo):
"""兴趣度计算组件信息(单例模式)"""
enabled_by_default: bool = True # 是否默认启用
def __post_init__(self):
super().__post_init__()
self.component_type = ComponentType.INTEREST_CALCULATOR
@dataclass
class EventInfo(ComponentInfo):
"""事件组件信息"""