Merge branch 'master' of https://github.com/MoFox-Studio/MoFox_Bot
This commit is contained in:
@@ -131,6 +131,11 @@ class CycleProcessor:
|
||||
|
||||
if ENABLE_S4U:
|
||||
await stop_typing()
|
||||
|
||||
# 在一轮动作执行完毕后,增加睡眠压力
|
||||
if self.context.energy_manager and global_config.wakeup_system.enable_insomnia_system:
|
||||
if action_type not in ["no_reply", "no_action"]:
|
||||
self.context.energy_manager.increase_sleep_pressure()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from src.common.logger import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.plugin_system.base.component_types import ChatMode
|
||||
from .hfc_context import HfcContext
|
||||
from src.schedule.schedule_manager import schedule_manager
|
||||
|
||||
logger = get_logger("hfc")
|
||||
|
||||
@@ -77,7 +78,7 @@ class EnergyManager:
|
||||
|
||||
async def _energy_loop(self):
|
||||
"""
|
||||
能量管理的主循环
|
||||
能量与睡眠压力管理的主循环
|
||||
|
||||
功能说明:
|
||||
- 每10秒执行一次能量更新
|
||||
@@ -92,24 +93,35 @@ class EnergyManager:
|
||||
if not self.context.chat_stream:
|
||||
continue
|
||||
|
||||
is_group_chat = self.context.chat_stream.group_info is not None
|
||||
if is_group_chat and global_config.chat.group_chat_mode != "auto":
|
||||
if global_config.chat.group_chat_mode == "focus":
|
||||
self.context.loop_mode = ChatMode.FOCUS
|
||||
self.context.energy_value = 35
|
||||
elif global_config.chat.group_chat_mode == "normal":
|
||||
self.context.loop_mode = ChatMode.NORMAL
|
||||
self.context.energy_value = 15
|
||||
continue
|
||||
# 判断当前是否为睡眠时间
|
||||
is_sleeping = schedule_manager.is_sleeping(self.context.wakeup_manager)
|
||||
|
||||
if self.context.loop_mode == ChatMode.NORMAL:
|
||||
self.context.energy_value -= 0.3
|
||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
||||
if self.context.loop_mode == ChatMode.FOCUS:
|
||||
self.context.energy_value -= 0.6
|
||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
||||
|
||||
self._log_energy_change("能量值衰减")
|
||||
if is_sleeping:
|
||||
# 睡眠中:减少睡眠压力
|
||||
decay_per_10s = global_config.wakeup_system.sleep_pressure_decay_rate / 6
|
||||
self.context.sleep_pressure -= decay_per_10s
|
||||
self.context.sleep_pressure = max(self.context.sleep_pressure, 0)
|
||||
self._log_sleep_pressure_change("睡眠压力释放")
|
||||
else:
|
||||
# 清醒时:处理能量衰减
|
||||
is_group_chat = self.context.chat_stream.group_info is not None
|
||||
if is_group_chat and global_config.chat.group_chat_mode != "auto":
|
||||
if global_config.chat.group_chat_mode == "focus":
|
||||
self.context.loop_mode = ChatMode.FOCUS
|
||||
self.context.energy_value = 35
|
||||
elif global_config.chat.group_chat_mode == "normal":
|
||||
self.context.loop_mode = ChatMode.NORMAL
|
||||
self.context.energy_value = 15
|
||||
continue
|
||||
|
||||
if self.context.loop_mode == ChatMode.NORMAL:
|
||||
self.context.energy_value -= 0.3
|
||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
||||
if self.context.loop_mode == ChatMode.FOCUS:
|
||||
self.context.energy_value -= 0.6
|
||||
self.context.energy_value = max(self.context.energy_value, 0.3)
|
||||
|
||||
self._log_energy_change("能量值衰减")
|
||||
|
||||
def _should_log_energy(self) -> bool:
|
||||
"""
|
||||
@@ -129,6 +141,15 @@ class EnergyManager:
|
||||
return True
|
||||
return False
|
||||
|
||||
def increase_sleep_pressure(self):
|
||||
"""
|
||||
在执行动作后增加睡眠压力
|
||||
"""
|
||||
increment = global_config.wakeup_system.sleep_pressure_increment
|
||||
self.context.sleep_pressure += increment
|
||||
self.context.sleep_pressure = min(self.context.sleep_pressure, 100.0) # 设置一个100的上限
|
||||
self._log_sleep_pressure_change("执行动作,睡眠压力累积")
|
||||
|
||||
def _log_energy_change(self, action: str, reason: str = ""):
|
||||
"""
|
||||
记录能量变化日志
|
||||
@@ -151,4 +172,14 @@ class EnergyManager:
|
||||
log_message = f"{self.context.log_prefix} {action},当前能量值:{self.context.energy_value:.1f}"
|
||||
if reason:
|
||||
log_message = f"{self.context.log_prefix} {action},{reason},当前能量值:{self.context.energy_value:.1f}"
|
||||
logger.debug(log_message)
|
||||
logger.debug(log_message)
|
||||
|
||||
def _log_sleep_pressure_change(self, action: str):
|
||||
"""
|
||||
记录睡眠压力变化日志
|
||||
"""
|
||||
# 使用与能量日志相同的频率控制
|
||||
if self._should_log_energy():
|
||||
logger.info(f"{self.context.log_prefix} {action},当前睡眠压力:{self.context.sleep_pressure:.1f}")
|
||||
else:
|
||||
logger.debug(f"{self.context.log_prefix} {action},当前睡眠压力:{self.context.sleep_pressure:.1f}")
|
||||
@@ -10,6 +10,7 @@ from src.chat.express.expression_learner import expression_learner_manager
|
||||
from src.plugin_system.base.component_types import ChatMode
|
||||
from src.schedule.schedule_manager import schedule_manager
|
||||
from src.plugin_system.apis import message_api
|
||||
from src.mood.mood_manager import mood_manager
|
||||
|
||||
from .hfc_context import HfcContext
|
||||
from .energy_manager import EnergyManager
|
||||
@@ -48,6 +49,7 @@ class HeartFChatting:
|
||||
|
||||
# 将唤醒度管理器设置到上下文中
|
||||
self.context.wakeup_manager = self.wakeup_manager
|
||||
self.context.energy_manager = self.energy_manager
|
||||
|
||||
self._loop_task: Optional[asyncio.Task] = None
|
||||
|
||||
@@ -196,8 +198,28 @@ class HeartFChatting:
|
||||
- NORMAL模式:检查进入FOCUS模式的条件,并通过normal_mode_handler处理消息
|
||||
"""
|
||||
is_sleeping = schedule_manager.is_sleeping(self.wakeup_manager)
|
||||
|
||||
# 核心修复:在睡眠模式下获取消息时,不过滤命令消息,以确保@消息能被接收
|
||||
|
||||
# --- 失眠状态管理 ---
|
||||
if self.context.is_in_insomnia and time.time() > self.context.insomnia_end_time:
|
||||
# 失眠状态结束
|
||||
self.context.is_in_insomnia = False
|
||||
await self.proactive_thinker.trigger_goodnight_thinking()
|
||||
|
||||
if is_sleeping and not self.context.was_sleeping:
|
||||
# 刚刚进入睡眠状态,进行一次入睡检查
|
||||
if self.wakeup_manager and self.wakeup_manager.check_for_insomnia():
|
||||
# 触发失眠
|
||||
self.context.is_in_insomnia = True
|
||||
duration = global_config.wakeup_system.insomnia_duration_minutes * 60
|
||||
self.context.insomnia_end_time = time.time() + duration
|
||||
|
||||
# 判断失眠原因并触发思考
|
||||
reason = "random"
|
||||
if self.context.sleep_pressure < global_config.wakeup_system.sleep_pressure_threshold:
|
||||
reason = "low_pressure"
|
||||
await self.proactive_thinker.trigger_insomnia_thinking(reason)
|
||||
|
||||
# 核心修复:在睡眠模式(包括失眠)下获取消息时,不过滤命令消息,以确保@消息能被接收
|
||||
filter_command_flag = not is_sleeping
|
||||
|
||||
recent_messages = message_api.get_messages_by_time_in_chat(
|
||||
@@ -220,8 +242,9 @@ class HeartFChatting:
|
||||
# 处理唤醒度逻辑
|
||||
if is_sleeping:
|
||||
self._handle_wakeup_messages(recent_messages)
|
||||
# 如果仍在睡眠状态,跳过正常处理但仍返回有新消息
|
||||
if schedule_manager.is_sleeping(self.wakeup_manager):
|
||||
# 如果处于失眠状态,则无视睡眠时间,继续处理消息
|
||||
# 否则,如果仍然在睡眠(没被吵醒),则跳过本轮处理
|
||||
if not self.context.is_in_insomnia and schedule_manager.is_sleeping(self.wakeup_manager):
|
||||
return has_new_messages
|
||||
|
||||
# 根据聊天模式处理新消息
|
||||
@@ -239,6 +262,9 @@ class HeartFChatting:
|
||||
self._check_focus_exit()
|
||||
elif self.context.loop_mode == ChatMode.NORMAL:
|
||||
self._check_focus_entry(0) # 传入0表示无新消息
|
||||
|
||||
# 更新上一帧的睡眠状态
|
||||
self.context.was_sleeping = is_sleeping
|
||||
|
||||
return has_new_messages
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from src.chat.chat_loop.hfc_utils import CycleDetail
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .wakeup_manager import WakeUpManager
|
||||
from .energy_manager import EnergyManager
|
||||
|
||||
class HfcContext:
|
||||
def __init__(self, chat_id: str):
|
||||
@@ -40,6 +41,12 @@ class HfcContext:
|
||||
|
||||
self.loop_mode = ChatMode.NORMAL
|
||||
self.energy_value = 5.0
|
||||
self.sleep_pressure = 0.0
|
||||
self.was_sleeping = False # 用于检测睡眠状态的切换
|
||||
|
||||
# 失眠状态
|
||||
self.is_in_insomnia: bool = False
|
||||
self.insomnia_end_time: float = 0.0
|
||||
|
||||
self.last_message_time = time.time()
|
||||
self.last_read_time = time.time() - 10
|
||||
@@ -53,4 +60,5 @@ class HfcContext:
|
||||
self.current_cycle_detail: Optional[CycleDetail] = None
|
||||
|
||||
# 唤醒度管理器 - 延迟初始化以避免循环导入
|
||||
self.wakeup_manager: Optional['WakeUpManager'] = None
|
||||
self.wakeup_manager: Optional['WakeUpManager'] = None
|
||||
self.energy_manager: Optional['EnergyManager'] = None
|
||||
@@ -279,3 +279,56 @@ class ProactiveThinker:
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 主动思考执行异常: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
async def trigger_insomnia_thinking(self, reason: str):
|
||||
"""
|
||||
由外部事件(如失眠)触发的一次性主动思考
|
||||
|
||||
Args:
|
||||
reason: 触发的原因 (e.g., "low_pressure", "random")
|
||||
"""
|
||||
logger.info(f"{self.context.log_prefix} 因“{reason}”触发失眠,开始深夜思考...")
|
||||
|
||||
# 1. 根据原因修改情绪
|
||||
try:
|
||||
from src.mood.mood_manager import mood_manager
|
||||
mood_obj = mood_manager.get_mood_by_chat_id(self.context.stream_id)
|
||||
if reason == "low_pressure":
|
||||
mood_obj.mood_state = "精力过剩,毫无睡意"
|
||||
elif reason == "random":
|
||||
mood_obj.mood_state = "深夜emo,胡思乱想"
|
||||
mood_obj.last_change_time = time.time() # 更新时间戳以允许后续的情绪回归
|
||||
logger.info(f"{self.context.log_prefix} 因失眠,情绪状态被强制更新为: {mood_obj.mood_state}")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 设置失眠情绪时出错: {e}")
|
||||
|
||||
# 2. 直接执行主动思考逻辑
|
||||
try:
|
||||
# 传入一个象征性的silence_duration,因为它在这里不重要
|
||||
await self._execute_proactive_thinking(silence_duration=1)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 失眠思考执行出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
async def trigger_goodnight_thinking(self):
|
||||
"""
|
||||
在失眠状态结束后,触发一次准备睡觉的主动思考
|
||||
"""
|
||||
logger.info(f"{self.context.log_prefix} 失眠状态结束,准备睡觉,触发告别思考...")
|
||||
|
||||
# 1. 设置一个准备睡觉的特定情绪
|
||||
try:
|
||||
from src.mood.mood_manager import mood_manager
|
||||
mood_obj = mood_manager.get_mood_by_chat_id(self.context.stream_id)
|
||||
mood_obj.mood_state = "有点困了,准备睡觉了"
|
||||
mood_obj.last_change_time = time.time()
|
||||
logger.info(f"{self.context.log_prefix} 情绪状态更新为: {mood_obj.mood_state}")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 设置睡前情绪时出错: {e}")
|
||||
|
||||
# 2. 直接执行主动思考逻辑
|
||||
try:
|
||||
await self._execute_proactive_thinking(silence_duration=1)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 睡前告别思考执行出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@@ -39,6 +39,13 @@ class WakeUpManager:
|
||||
self.angry_duration = wakeup_config.angry_duration
|
||||
self.enabled = wakeup_config.enable
|
||||
self.angry_prompt = wakeup_config.angry_prompt
|
||||
|
||||
# 失眠系统参数
|
||||
self.insomnia_enabled = wakeup_config.enable_insomnia_system
|
||||
self.sleep_pressure_threshold = wakeup_config.sleep_pressure_threshold
|
||||
self.deep_sleep_threshold = wakeup_config.deep_sleep_threshold
|
||||
self.insomnia_chance_low_pressure = wakeup_config.insomnia_chance_low_pressure
|
||||
self.insomnia_chance_normal_pressure = wakeup_config.insomnia_chance_normal_pressure
|
||||
|
||||
async def start(self):
|
||||
"""启动唤醒度管理器"""
|
||||
@@ -177,4 +184,36 @@ class WakeUpManager:
|
||||
"wakeup_threshold": self.wakeup_threshold,
|
||||
"is_angry": self.is_angry,
|
||||
"angry_remaining_time": max(0, self.angry_duration - (time.time() - self.angry_start_time)) if self.is_angry else 0
|
||||
}
|
||||
}
|
||||
|
||||
def check_for_insomnia(self) -> bool:
|
||||
"""
|
||||
在尝试入睡时检查是否会失眠
|
||||
|
||||
Returns:
|
||||
bool: 如果失眠则返回 True,否则返回 False
|
||||
"""
|
||||
if not self.insomnia_enabled:
|
||||
return False
|
||||
|
||||
import random
|
||||
|
||||
pressure = self.context.sleep_pressure
|
||||
|
||||
# 压力过高,深度睡眠,极难失眠
|
||||
if pressure > self.deep_sleep_threshold:
|
||||
return False
|
||||
|
||||
# 根据睡眠压力决定失眠概率
|
||||
if pressure < self.sleep_pressure_threshold:
|
||||
# 压力不足型失眠
|
||||
if random.random() < self.insomnia_chance_low_pressure:
|
||||
logger.info(f"{self.context.log_prefix} 睡眠压力不足 ({pressure:.1f}),触发失眠!")
|
||||
return True
|
||||
else:
|
||||
# 压力正常,随机失眠
|
||||
if random.random() < self.insomnia_chance_normal_pressure:
|
||||
logger.info(f"{self.context.log_prefix} 睡眠压力正常 ({pressure:.1f}),触发随机失眠!")
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -114,16 +114,27 @@ class ExpressionSelector:
|
||||
return None
|
||||
|
||||
def get_related_chat_ids(self, chat_id: str) -> List[str]:
|
||||
"""根据expression_groups配置,获取与当前chat_id相关的所有chat_id(包括自身)"""
|
||||
groups = global_config.expression.expression_groups
|
||||
for group in groups:
|
||||
group_chat_ids = []
|
||||
for stream_config_str in group:
|
||||
if chat_id_candidate := self._parse_stream_config_to_chat_id(stream_config_str):
|
||||
group_chat_ids.append(chat_id_candidate)
|
||||
if chat_id in group_chat_ids:
|
||||
return group_chat_ids
|
||||
return [chat_id]
|
||||
"""根据expression.rules配置,获取与当前chat_id相关的所有chat_id(包括自身)"""
|
||||
rules = global_config.expression.rules
|
||||
current_group = None
|
||||
|
||||
# 找到当前chat_id所在的组
|
||||
for rule in rules:
|
||||
if rule.chat_stream_id and self._parse_stream_config_to_chat_id(rule.chat_stream_id) == chat_id:
|
||||
current_group = rule.group
|
||||
break
|
||||
|
||||
if not current_group:
|
||||
return [chat_id]
|
||||
|
||||
# 找出同一组的所有chat_id
|
||||
related_chat_ids = []
|
||||
for rule in rules:
|
||||
if rule.group == current_group and rule.chat_stream_id:
|
||||
if chat_id_candidate := self._parse_stream_config_to_chat_id(rule.chat_stream_id):
|
||||
related_chat_ids.append(chat_id_candidate)
|
||||
|
||||
return related_chat_ids if related_chat_ids else [chat_id]
|
||||
|
||||
def get_random_expressions(
|
||||
self, chat_id: str, total_num: int, style_percentage: float, grammar_percentage: float
|
||||
|
||||
@@ -87,22 +87,38 @@ class VectorInstantMemoryV2:
|
||||
"""清理过期的聊天记录"""
|
||||
try:
|
||||
expire_time = time.time() - (self.retention_hours * 3600)
|
||||
|
||||
# 使用 where 条件来删除过期记录
|
||||
# 注意: ChromaDB 的 where 过滤器目前对 timestamp 的 $lt 操作支持可能有限
|
||||
# 一个更可靠的方法是 get() -> filter -> delete()
|
||||
# 但为了简化,我们先尝试直接 delete
|
||||
|
||||
# TODO: 确认 ChromaDB 对 $lt 在 metadata 上的支持。如果不支持,需要实现 get-filter-delete 模式。
|
||||
vector_db_service.delete(
|
||||
|
||||
# 采用 get -> filter -> delete 模式,避免复杂的 where 查询
|
||||
# 1. 获取当前 chat_id 的所有文档
|
||||
results = vector_db_service.get(
|
||||
collection_name=self.collection_name,
|
||||
where={
|
||||
"chat_id": self.chat_id,
|
||||
"timestamp": {"$lt": expire_time}
|
||||
}
|
||||
where={"chat_id": self.chat_id},
|
||||
include=["metadatas"]
|
||||
)
|
||||
logger.info(f"已为 chat_id '{self.chat_id}' 触发过期记录清理")
|
||||
|
||||
|
||||
if not results or not results.get('ids'):
|
||||
logger.info(f"chat_id '{self.chat_id}' 没有找到任何记录,无需清理")
|
||||
return
|
||||
|
||||
# 2. 在内存中过滤出过期的文档
|
||||
expired_ids = []
|
||||
metadatas = results.get('metadatas', [])
|
||||
ids = results.get('ids', [])
|
||||
|
||||
for i, metadata in enumerate(metadatas):
|
||||
if metadata and metadata.get('timestamp', float('inf')) < expire_time:
|
||||
expired_ids.append(ids[i])
|
||||
|
||||
# 3. 如果有过期文档,根据 ID 进行删除
|
||||
if expired_ids:
|
||||
vector_db_service.delete(
|
||||
collection_name=self.collection_name,
|
||||
ids=expired_ids
|
||||
)
|
||||
logger.info(f"为 chat_id '{self.chat_id}' 清理了 {len(expired_ids)} 条过期记录")
|
||||
else:
|
||||
logger.info(f"chat_id '{self.chat_id}' 没有需要清理的过期记录")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"清理过期记录失败: {e}")
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
from sqlalchemy import Column, String, Float, Integer, Boolean, Text, Index, create_engine, DateTime
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker, Session
|
||||
from sqlalchemy.orm import sessionmaker, Session, Mapped, mapped_column
|
||||
from sqlalchemy.pool import QueuePool
|
||||
import os
|
||||
import datetime
|
||||
import time
|
||||
from typing import Iterator, Optional
|
||||
from typing import Iterator, Optional, Any, Dict
|
||||
from src.common.logger import get_logger
|
||||
from contextlib import contextmanager
|
||||
|
||||
@@ -306,14 +306,14 @@ class Expression(Base):
|
||||
"""表达风格模型"""
|
||||
__tablename__ = 'expression'
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||
situation = Column(Text, nullable=False)
|
||||
style = Column(Text, nullable=False)
|
||||
count = Column(Float, nullable=False)
|
||||
last_active_time = Column(Float, nullable=False)
|
||||
chat_id = Column(get_string_field(64), nullable=False, index=True)
|
||||
type = Column(Text, nullable=False)
|
||||
create_date = Column(Float, nullable=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
situation: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
style: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
count: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
last_active_time: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
chat_id: Mapped[str] = mapped_column(get_string_field(64), nullable=False, index=True)
|
||||
type: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
create_date: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_expression_chat_id', 'chat_id'),
|
||||
@@ -589,7 +589,7 @@ def initialize_database():
|
||||
config = global_config.database
|
||||
|
||||
# 配置引擎参数
|
||||
engine_kwargs = {
|
||||
engine_kwargs: Dict[str, Any] = {
|
||||
'echo': False, # 生产环境关闭SQL日志
|
||||
'future': True,
|
||||
}
|
||||
@@ -642,7 +642,9 @@ def get_db_session() -> Iterator[Session]:
|
||||
"""数据库会话上下文管理器 - 推荐使用这个而不是get_session()"""
|
||||
session: Optional[Session] = None
|
||||
try:
|
||||
_, SessionLocal = initialize_database()
|
||||
engine, SessionLocal = initialize_database()
|
||||
if not SessionLocal:
|
||||
raise RuntimeError("Database session not initialized")
|
||||
session = SessionLocal()
|
||||
yield session
|
||||
#session.commit()
|
||||
|
||||
@@ -93,6 +93,34 @@ class VectorDBBase(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get(
|
||||
self,
|
||||
collection_name: str,
|
||||
ids: Optional[List[str]] = None,
|
||||
where: Optional[Dict[str, Any]] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
where_document: Optional[Dict[str, Any]] = None,
|
||||
include: Optional[List[str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
根据条件从集合中获取数据。
|
||||
|
||||
Args:
|
||||
collection_name (str): 目标集合的名称。
|
||||
ids (Optional[List[str]], optional): 要获取的条目的 ID 列表。Defaults to None.
|
||||
where (Optional[Dict[str, Any]], optional): 基于元数据的过滤条件。Defaults to None.
|
||||
limit (Optional[int], optional): 返回结果的数量限制。Defaults to None.
|
||||
offset (Optional[int], optional): 返回结果的偏移量。Defaults to None.
|
||||
where_document (Optional[Dict[str, Any]], optional): 基于文档内容的过滤条件。Defaults to None.
|
||||
include (Optional[List[str]], optional): 指定返回的数据字段 (e.g., ["metadatas", "documents"])。Defaults to None.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: 获取到的数据。
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def count(self, collection_name: str) -> int:
|
||||
"""
|
||||
|
||||
@@ -102,6 +102,32 @@ class ChromaDBImpl(VectorDBBase):
|
||||
logger.error(f"查询集合 '{collection_name}' 失败: {e}")
|
||||
return {}
|
||||
|
||||
def get(
|
||||
self,
|
||||
collection_name: str,
|
||||
ids: Optional[List[str]] = None,
|
||||
where: Optional[Dict[str, Any]] = None,
|
||||
limit: Optional[int] = None,
|
||||
offset: Optional[int] = None,
|
||||
where_document: Optional[Dict[str, Any]] = None,
|
||||
include: Optional[List[str]] = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""根据条件从集合中获取数据"""
|
||||
collection = self.get_or_create_collection(collection_name)
|
||||
if collection:
|
||||
try:
|
||||
return collection.get(
|
||||
ids=ids,
|
||||
where=where,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
where_document=where_document,
|
||||
include=include,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"从集合 '{collection_name}' 获取数据失败: {e}")
|
||||
return {}
|
||||
|
||||
def delete(
|
||||
self,
|
||||
collection_name: str,
|
||||
|
||||
@@ -263,11 +263,20 @@ class NormalChatConfig(ValidatedConfigBase):
|
||||
|
||||
|
||||
|
||||
class ExpressionRule(ValidatedConfigBase):
|
||||
"""表达学习规则"""
|
||||
|
||||
chat_stream_id: str = Field(..., description="聊天流ID,空字符串表示全局")
|
||||
use_expression: bool = Field(default=True, description="是否使用学到的表达")
|
||||
learn_expression: bool = Field(default=True, description="是否学习表达")
|
||||
learning_strength: float = Field(default=1.0, description="学习强度")
|
||||
group: Optional[str] = Field(default=None, description="表达共享组")
|
||||
|
||||
|
||||
class ExpressionConfig(ValidatedConfigBase):
|
||||
"""表达配置类"""
|
||||
|
||||
expression_learning: list[list] = Field(default_factory=lambda: [], description="表达学习")
|
||||
expression_groups: list[list[str]] = Field(default_factory=list, description="表达组")
|
||||
rules: List[ExpressionRule] = Field(default_factory=list, description="表达学习规则")
|
||||
|
||||
def _parse_stream_config_to_chat_id(self, stream_config_str: str) -> Optional[str]:
|
||||
"""
|
||||
@@ -314,86 +323,23 @@ class ExpressionConfig(ValidatedConfigBase):
|
||||
Returns:
|
||||
tuple: (是否使用表达, 是否学习表达, 学习间隔)
|
||||
"""
|
||||
if not self.expression_learning:
|
||||
# 如果没有配置,使用默认值:启用表达,启用学习,300秒间隔
|
||||
return True, True, 300
|
||||
if not self.rules:
|
||||
# 如果没有配置,使用默认值:启用表达,启用学习,强度1.0
|
||||
return True, True, 1.0
|
||||
|
||||
# 优先检查聊天流特定的配置
|
||||
if chat_stream_id:
|
||||
specific_config = self._get_stream_specific_config(chat_stream_id)
|
||||
if specific_config is not None:
|
||||
return specific_config
|
||||
for rule in self.rules:
|
||||
if rule.chat_stream_id and self._parse_stream_config_to_chat_id(rule.chat_stream_id) == chat_stream_id:
|
||||
return rule.use_expression, rule.learn_expression, rule.learning_strength
|
||||
|
||||
# 检查全局配置(第一个元素为空字符串的配置)
|
||||
global_config = self._get_global_config()
|
||||
if global_config is not None:
|
||||
return global_config
|
||||
# 检查全局配置(chat_stream_id为空字符串的配置)
|
||||
for rule in self.rules:
|
||||
if rule.chat_stream_id == "":
|
||||
return rule.use_expression, rule.learn_expression, rule.learning_strength
|
||||
|
||||
# 如果都没有匹配,返回默认值
|
||||
return True, True, 300
|
||||
|
||||
def _get_stream_specific_config(self, chat_stream_id: str) -> Optional[tuple[bool, bool, float]]:
|
||||
"""
|
||||
获取特定聊天流的表达配置
|
||||
|
||||
Args:
|
||||
chat_stream_id: 聊天流ID(哈希值)
|
||||
|
||||
Returns:
|
||||
tuple: (是否使用表达, 是否学习表达, 学习间隔),如果没有配置则返回 None
|
||||
"""
|
||||
for config_item in self.expression_learning:
|
||||
if not config_item or len(config_item) < 4:
|
||||
continue
|
||||
|
||||
stream_config_str = config_item[0] # 例如 "qq:1026294844:group"
|
||||
|
||||
# 如果是空字符串,跳过(这是全局配置)
|
||||
if stream_config_str == "":
|
||||
continue
|
||||
|
||||
# 解析配置字符串并生成对应的 chat_id
|
||||
config_chat_id = self._parse_stream_config_to_chat_id(stream_config_str)
|
||||
if config_chat_id is None:
|
||||
continue
|
||||
|
||||
# 比较生成的 chat_id
|
||||
if config_chat_id != chat_stream_id:
|
||||
continue
|
||||
|
||||
# 解析配置
|
||||
try:
|
||||
use_expression = config_item[1].lower() == "enable"
|
||||
enable_learning = config_item[2].lower() == "enable"
|
||||
learning_intensity = float(config_item[3])
|
||||
return use_expression, enable_learning, learning_intensity
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def _get_global_config(self) -> Optional[tuple[bool, bool, float]]:
|
||||
"""
|
||||
获取全局表达配置
|
||||
|
||||
Returns:
|
||||
tuple: (是否使用表达, 是否学习表达, 学习间隔),如果没有配置则返回 None
|
||||
"""
|
||||
for config_item in self.expression_learning:
|
||||
if not config_item or len(config_item) < 4:
|
||||
continue
|
||||
|
||||
# 检查是否为全局配置(第一个元素为空字符串)
|
||||
if config_item[0] == "":
|
||||
try:
|
||||
use_expression = config_item[1].lower() == "enable"
|
||||
enable_learning = config_item[2].lower() == "enable"
|
||||
learning_intensity = float(config_item[3])
|
||||
return use_expression, enable_learning, learning_intensity
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
|
||||
return None
|
||||
return True, True, 1.0
|
||||
|
||||
|
||||
class ToolHistoryConfig(ValidatedConfigBase):
|
||||
@@ -657,7 +603,7 @@ class PluginsConfig(ValidatedConfigBase):
|
||||
|
||||
|
||||
class WakeUpSystemConfig(ValidatedConfigBase):
|
||||
"""唤醒度系统配置类"""
|
||||
"""唤醒度与失眠系统配置类"""
|
||||
|
||||
enable: bool = Field(default=True, description="是否启用唤醒度系统")
|
||||
wakeup_threshold: float = Field(default=15.0, ge=1.0, description="唤醒阈值,达到此值时会被唤醒")
|
||||
@@ -668,6 +614,16 @@ class WakeUpSystemConfig(ValidatedConfigBase):
|
||||
angry_duration: float = Field(default=300.0, ge=10.0, description="愤怒状态持续时间(秒)")
|
||||
angry_prompt: str = Field(default="你被人吵醒了非常生气,说话带着怒气", description="被吵醒后的愤怒提示词")
|
||||
|
||||
# --- 失眠机制相关参数 ---
|
||||
enable_insomnia_system: bool = Field(default=True, description="是否启用失眠系统")
|
||||
insomnia_duration_minutes: int = Field(default=30, ge=1, description="单次失眠状态的持续时间(分钟)")
|
||||
sleep_pressure_threshold: float = Field(default=30.0, description="触发“压力不足型失眠”的睡眠压力阈值")
|
||||
deep_sleep_threshold: float = Field(default=80.0, description="进入“深度睡眠”的睡眠压力阈值")
|
||||
insomnia_chance_low_pressure: float = Field(default=0.6, ge=0.0, le=1.0, description="压力不足时的失眠基础概率")
|
||||
insomnia_chance_normal_pressure: float = Field(default=0.1, ge=0.0, le=1.0, description="压力正常时的失眠基础概率")
|
||||
sleep_pressure_increment: float = Field(default=1.5, ge=0.0, description="每次AI执行动作后,增加的睡眠压力值")
|
||||
sleep_pressure_decay_rate: float = Field(default=1.5, ge=0.0, description="睡眠时,每分钟衰减的睡眠压力值")
|
||||
|
||||
|
||||
class MonthlyPlanSystemConfig(ValidatedConfigBase):
|
||||
"""月度计划系统配置类"""
|
||||
|
||||
@@ -66,6 +66,11 @@ class ChatMood:
|
||||
self.last_change_time: float = 0
|
||||
|
||||
async def update_mood_by_message(self, message: MessageRecv, interested_rate: float):
|
||||
# 如果当前聊天处于失眠状态,则锁定情绪,不允许更新
|
||||
if self.chat_id in mood_manager.insomnia_chats:
|
||||
logger.debug(f"{self.log_prefix} 处于失眠状态,情绪已锁定,跳过更新。")
|
||||
return
|
||||
|
||||
self.regression_count = 0
|
||||
|
||||
during_last_time = message.message_info.time - self.last_change_time # type: ignore
|
||||
@@ -216,6 +221,7 @@ class MoodManager:
|
||||
self.mood_list: list[ChatMood] = []
|
||||
"""当前情绪状态"""
|
||||
self.task_started: bool = False
|
||||
self.insomnia_chats: set[str] = set() # 正在失眠的聊天ID列表
|
||||
|
||||
async def start(self):
|
||||
"""启动情绪回归后台任务"""
|
||||
@@ -262,6 +268,16 @@ class MoodManager:
|
||||
mood.mood_state = "感觉很平静"
|
||||
logger.info(f"{mood.log_prefix} 清除被吵醒的愤怒状态")
|
||||
|
||||
def start_insomnia(self, chat_id: str):
|
||||
"""开始一个聊天的失眠状态,锁定情绪更新"""
|
||||
logger.info(f"Chat [{chat_id}]进入失眠状态,情绪已锁定。")
|
||||
self.insomnia_chats.add(chat_id)
|
||||
|
||||
def stop_insomnia(self, chat_id: str):
|
||||
"""停止一个聊天的失眠状态,解锁情绪更新"""
|
||||
logger.info(f"Chat [{chat_id}]失眠状态结束,情绪已解锁。")
|
||||
self.insomnia_chats.discard(chat_id)
|
||||
|
||||
def get_angry_prompt_addition(self, chat_id: str) -> str:
|
||||
"""获取愤怒状态下的提示词补充"""
|
||||
mood = self.get_mood_by_chat_id(chat_id)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[inner]
|
||||
version = "6.5.3"
|
||||
version = "6.5.4"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||
#如果你想要修改配置文件,请递增version的值
|
||||
@@ -44,9 +44,7 @@ connection_timeout = 10 # 连接超时时间(秒)
|
||||
# Master用户配置(拥有最高权限,无视所有权限节点)
|
||||
# 格式:[[platform, user_id], ...]
|
||||
# 示例:[["qq", "123456"], ["telegram", "user789"]]
|
||||
master_users = [
|
||||
# ["qq", "123456789"], # 示例:QQ平台的Master用户
|
||||
]
|
||||
master_users = []# ["qq", "123456789"], # 示例:QQ平台的Master用户
|
||||
|
||||
[bot]
|
||||
platform = "qq"
|
||||
@@ -74,23 +72,37 @@ compress_identity = true # 是否压缩身份,压缩后会精简身份信息
|
||||
|
||||
[expression]
|
||||
# 表达学习配置
|
||||
expression_learning = [ # 表达学习配置列表,支持按聊天流配置
|
||||
["", "enable", "enable", 1.0], # 全局配置:使用表达,启用学习,学习强度1.0
|
||||
["qq:1919810:group", "enable", "enable", 1.5], # 特定群聊配置:使用表达,启用学习,学习强度1.5
|
||||
["qq:114514:private", "enable", "disable", 0.5], # 特定私聊配置:使用表达,禁用学习,学习强度0.5
|
||||
# 格式说明:
|
||||
# 第一位: chat_stream_id,空字符串表示全局配置
|
||||
# 第二位: 是否使用学到的表达 ("enable"/"disable")
|
||||
# 第三位: 是否学习表达 ("enable"/"disable")
|
||||
# 第四位: 学习强度(浮点数),影响学习频率,最短学习时间间隔 = 300/学习强度(秒)
|
||||
# 学习强度越高,学习越频繁;学习强度越低,学习越少
|
||||
]
|
||||
# rules是一个列表,每个元素都是一个学习规则
|
||||
# chat_stream_id: 聊天流ID,格式为 "platform:id:type",例如 "qq:123456:private"。空字符串""表示全局配置
|
||||
# use_expression: 是否使用学到的表达 (true/false)
|
||||
# learn_expression: 是否学习新的表达 (true/false)
|
||||
# learning_strength: 学习强度(浮点数),影响学习频率
|
||||
# group: 表达共享组的名称(字符串),相同组的聊天会共享学习到的表达方式
|
||||
[[expression.rules]]
|
||||
chat_stream_id = ""
|
||||
use_expression = true
|
||||
learn_expression = true
|
||||
learning_strength = 1.0
|
||||
|
||||
expression_groups = [
|
||||
["qq:1919810:private","qq:114514:private","qq:1111111:group"], # 在这里设置互通组,相同组的chat_id会共享学习到的表达方式
|
||||
# 格式:["qq:123456:private","qq:654321:group"]
|
||||
# 注意:如果为群聊,则需要设置为group,如果设置为私聊,则需要设置为private
|
||||
]
|
||||
[[expression.rules]]
|
||||
chat_stream_id = "qq:1919810:group"
|
||||
use_expression = true
|
||||
learn_expression = true
|
||||
learning_strength = 1.5
|
||||
|
||||
[[expression.rules]]
|
||||
chat_stream_id = "qq:114514:private"
|
||||
group = "group_A"
|
||||
use_expression = true
|
||||
learn_expression = false
|
||||
learning_strength = 0.5
|
||||
|
||||
[[expression.rules]]
|
||||
chat_stream_id = "qq:1919810:private"
|
||||
group = "group_A"
|
||||
use_expression = true
|
||||
learn_expression = true
|
||||
learning_strength = 1.0
|
||||
|
||||
|
||||
|
||||
@@ -427,7 +439,7 @@ guidelines = """
|
||||
"""
|
||||
|
||||
[wakeup_system]
|
||||
enable = true #"是否启用唤醒度系统"
|
||||
enable = false #"是否启用唤醒度系统"
|
||||
wakeup_threshold = 15.0 #唤醒阈值,达到此值时会被唤醒"
|
||||
private_message_increment = 3.0 #"私聊消息增加的唤醒度"
|
||||
group_mention_increment = 2.0 #"群聊艾特增加的唤醒度"
|
||||
@@ -436,6 +448,22 @@ decay_interval = 30.0 #"唤醒度衰减间隔(秒)"
|
||||
angry_duration = 300.0 #"愤怒状态持续时间(秒)"
|
||||
angry_prompt = "你被人吵醒了非常生气,说话带着怒气" # "被吵醒后的愤怒提示词"
|
||||
|
||||
# --- 失眠机制相关参数 ---
|
||||
enable_insomnia_system = false # 是否启用失眠系统
|
||||
# 触发“压力不足型失眠”的睡眠压力阈值
|
||||
sleep_pressure_threshold = 30.0
|
||||
# 进入“深度睡眠”的睡眠压力阈值
|
||||
deep_sleep_threshold = 80.0
|
||||
# 压力不足时的失眠基础概率 (0.0 to 1.0)
|
||||
insomnia_chance_low_pressure = 0.6
|
||||
# 压力正常时的失眠基础概率 (0.0 to 1.0)
|
||||
insomnia_chance_normal_pressure = 0.1
|
||||
# 每次AI执行动作后,增加的睡眠压力值
|
||||
sleep_pressure_increment = 1.5
|
||||
# 睡眠时,每分钟衰减的睡眠压力值
|
||||
sleep_pressure_decay_rate = 1.5
|
||||
insomnia_duration_minutes = 30 # 单次失眠状态的持续时间(分钟)
|
||||
|
||||
[cross_context] # 跨群聊上下文共享配置
|
||||
# 这是总开关,用于一键启用或禁用此功能
|
||||
enable = false
|
||||
|
||||
Reference in New Issue
Block a user