feat(chat): 实现动态消息分发系统和消息打断机制

添加基于focus_energy的动态消息分发周期调整功能,根据聊天流兴趣度智能调整检查间隔
实现消息打断系统,允许高优先级消息打断正在处理的任务
重构ChatStream类,引入动态兴趣度计算系统,包括消息兴趣度统计和用户关系评分
扩展数据库模型和配置系统以支持新功能,增加相关配置项
更新版本号至0.11.0-alpha-1以反映重大功能更新
This commit is contained in:
Windpicker-owo
2025-09-25 17:14:01 +08:00
parent 4cbd86aa96
commit a2baec088e
15 changed files with 703 additions and 73 deletions

View File

@@ -4,6 +4,7 @@
""" """
import asyncio import asyncio
import random
import time import time
import traceback import traceback
from typing import Dict, Optional, Any, TYPE_CHECKING from typing import Dict, Optional, Any, TYPE_CHECKING
@@ -16,6 +17,7 @@ from src.chat.planner_actions.action_manager import ChatterActionManager
from src.plugin_system.base.component_types import ChatMode from src.plugin_system.base.component_types import ChatMode
from .sleep_manager.sleep_manager import SleepManager from .sleep_manager.sleep_manager import SleepManager
from .sleep_manager.wakeup_manager import WakeUpManager from .sleep_manager.wakeup_manager import WakeUpManager
from src.config.config import global_config
if TYPE_CHECKING: if TYPE_CHECKING:
from src.common.data_models.message_manager_data_model import StreamContext from src.common.data_models.message_manager_data_model import StreamContext
@@ -69,7 +71,7 @@ class MessageManager:
# 停止管理器任务 # 停止管理器任务
if self.manager_task and not self.manager_task.done(): if self.manager_task and not self.manager_task.done():
self.manager_task.cancel() self.manager_task.cancel()
await self.wakeup_manager.stop() await self.wakeup_manager.stop()
logger.info("消息管理器已停止") logger.info("消息管理器已停止")
@@ -88,14 +90,22 @@ class MessageManager:
logger.debug(f"添加消息到聊天流 {stream_id}: {message.message_id}") logger.debug(f"添加消息到聊天流 {stream_id}: {message.message_id}")
async def _manager_loop(self): async def _manager_loop(self):
"""管理器主循环""" """管理器主循环 - 独立聊天流分发周期版本"""
while self.is_running: while self.is_running:
try: try:
# 更新睡眠状态 # 更新睡眠状态
await self.sleep_manager.update_sleep_state(self.wakeup_manager) await self.sleep_manager.update_sleep_state(self.wakeup_manager)
await self._check_all_streams() # 执行独立分发周期的检查
await asyncio.sleep(self.check_interval) await self._check_streams_with_individual_intervals()
# 计算下次检查时间(使用最小间隔或固定间隔)
if global_config.chat.dynamic_distribution_enabled:
next_check_delay = self._calculate_next_manager_delay()
else:
next_check_delay = self.check_interval
await asyncio.sleep(next_check_delay)
except asyncio.CancelledError: except asyncio.CancelledError:
break break
except Exception as e: except Exception as e:
@@ -138,12 +148,14 @@ class MessageManager:
unread_messages = context.get_unread_messages() unread_messages = context.get_unread_messages()
if not unread_messages: if not unread_messages:
return return
# 检查是否需要打断现有处理
await self._check_and_handle_interruption(context, stream_id)
# --- 睡眠状态检查 --- # --- 睡眠状态检查 ---
from .sleep_manager.sleep_state import SleepState
if self.sleep_manager.is_sleeping(): if self.sleep_manager.is_sleeping():
logger.info(f"Bot正在睡觉检查聊天流 {stream_id} 是否有唤醒触发器。") logger.info(f"Bot正在睡觉检查聊天流 {stream_id} 是否有唤醒触发器。")
was_woken_up = False was_woken_up = False
is_private = context.is_private_chat() is_private = context.is_private_chat()
@@ -152,12 +164,12 @@ class MessageManager:
if is_private or is_mentioned: if is_private or is_mentioned:
if self.wakeup_manager.add_wakeup_value(is_private, is_mentioned): if self.wakeup_manager.add_wakeup_value(is_private, is_mentioned):
was_woken_up = True was_woken_up = True
break # 一旦被吵醒,就跳出循环并处理消息 break # 一旦被吵醒,就跳出循环并处理消息
if not was_woken_up: if not was_woken_up:
logger.debug(f"聊天流 {stream_id} 中没有唤醒触发器,保持消息未读状态。") logger.debug(f"聊天流 {stream_id} 中没有唤醒触发器,保持消息未读状态。")
return # 退出,不处理消息 return # 退出,不处理消息
logger.info(f"Bot被聊天流 {stream_id} 中的消息吵醒,继续处理。") logger.info(f"Bot被聊天流 {stream_id} 中的消息吵醒,继续处理。")
# --- 睡眠状态检查结束 --- # --- 睡眠状态检查结束 ---
@@ -252,6 +264,345 @@ class MessageManager:
del self.stream_contexts[stream_id] del self.stream_contexts[stream_id]
logger.info(f"清理不活跃聊天流: {stream_id}") logger.info(f"清理不活跃聊天流: {stream_id}")
async def _check_and_handle_interruption(self, context: StreamContext, stream_id: str):
"""检查并处理消息打断"""
if not global_config.chat.interruption_enabled:
return
# 检查是否有正在进行的处理任务
if context.processing_task and not context.processing_task.done():
# 计算打断概率
interruption_probability = context.calculate_interruption_probability(
global_config.chat.interruption_max_limit, global_config.chat.interruption_probability_factor
)
# 根据概率决定是否打断
if random.random() < interruption_probability:
logger.info(f"聊天流 {stream_id} 触发消息打断,打断概率: {interruption_probability:.2f}")
# 取消现有任务
context.processing_task.cancel()
try:
await context.processing_task
except asyncio.CancelledError:
pass
# 增加打断计数并应用afc阈值降低
context.increment_interruption_count()
context.apply_interruption_afc_reduction(global_config.chat.interruption_afc_reduction)
logger.info(
f"聊天流 {stream_id} 已打断,当前打断次数: {context.interruption_count}/{global_config.chat.interruption_max_limit}, afc阈值调整: {context.get_afc_threshold_adjustment()}"
)
else:
logger.debug(f"聊天流 {stream_id} 未触发打断,打断概率: {interruption_probability:.2f}")
def _calculate_dynamic_distribution_interval(self) -> float:
"""根据所有活跃聊天流的focus_energy动态计算分发周期"""
if not global_config.chat.dynamic_distribution_enabled:
return self.check_interval # 使用固定间隔
if not self.stream_contexts:
return self.check_interval # 默认间隔
# 计算活跃流的平均focus_energy
active_streams = [ctx for ctx in self.stream_contexts.values() if ctx.is_active]
if not active_streams:
return self.check_interval
total_focus_energy = 0.0
max_focus_energy = 0.0
stream_count = 0
for context in active_streams:
if hasattr(context, 'chat_stream') and context.chat_stream:
focus_energy = context.chat_stream.focus_energy
total_focus_energy += focus_energy
max_focus_energy = max(max_focus_energy, focus_energy)
stream_count += 1
if stream_count == 0:
return self.check_interval
avg_focus_energy = total_focus_energy / stream_count
# 使用配置参数
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_focus_energy >= 0.7:
# 高兴趣度1-5秒
interval = base_interval * (1.0 - (avg_focus_energy - 0.7) * 2.0)
elif avg_focus_energy >= 0.4:
# 中等兴趣度5-15秒
interval = base_interval * (1.0 + (avg_focus_energy - 0.4) * 3.33)
else:
# 低兴趣度15-30秒
interval = base_interval * (3.0 + (0.4 - avg_focus_energy) * 5.0)
# 添加随机扰动避免同步
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))
logger.debug(
f"动态分发周期: {final_interval:.2f}s | "
f"平均兴趣度: {avg_focus_energy:.3f} | "
f"活跃流数: {stream_count}"
)
return final_interval
def _calculate_stream_distribution_interval(self, context: StreamContext) -> float:
"""计算单个聊天流的分发周期 - 基于阈值感知的focus_energy"""
if not global_config.chat.dynamic_distribution_enabled:
return self.check_interval # 使用固定间隔
# 获取该流的focus_energy新的阈值感知版本
focus_energy = 0.5 # 默认值
avg_message_interest = 0.5 # 默认平均兴趣度
if hasattr(context, 'chat_stream') and context.chat_stream:
focus_energy = context.chat_stream.focus_energy
# 获取平均消息兴趣度用于更精确的计算
if context.chat_stream.message_count > 0:
avg_message_interest = context.chat_stream.message_interest_total / context.chat_stream.message_count
# 获取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
def _calculate_next_manager_delay(self) -> float:
"""计算管理器下次检查的延迟时间"""
current_time = time.time()
min_delay = float('inf')
# 找到最近需要检查的流
for context in self.stream_contexts.values():
if not context.is_active:
continue
time_until_check = context.next_check_time - current_time
if time_until_check > 0:
min_delay = min(min_delay, time_until_check)
else:
min_delay = 0.1 # 立即检查
break
# 如果没有活跃流,使用默认间隔
if min_delay == float('inf'):
return self.check_interval
# 确保最小延迟
return max(0.1, min(min_delay, self.check_interval))
async def _check_streams_with_individual_intervals(self):
"""检查所有达到检查时间的聊天流"""
current_time = time.time()
processed_streams = 0
for stream_id, context in self.stream_contexts.items():
if not context.is_active:
continue
# 检查是否达到检查时间
if current_time >= context.next_check_time:
# 更新检查时间
context.last_check_time = current_time
# 计算下次检查时间和分发周期
if global_config.chat.dynamic_distribution_enabled:
context.distribution_interval = self._calculate_stream_distribution_interval(context)
else:
context.distribution_interval = self.check_interval
# 设置下次检查时间
context.next_check_time = current_time + context.distribution_interval
# 检查未读消息
unread_messages = context.get_unread_messages()
if unread_messages:
processed_streams += 1
self.stats.total_unread_messages = len(unread_messages)
# 如果没有处理任务,创建一个
if not context.processing_task or context.processing_task.done():
focus_energy = context.chat_stream.focus_energy if hasattr(context, 'chat_stream') and context.chat_stream else 0.5
# 根据优先级记录日志
if focus_energy >= 0.7:
logger.info(
f"高优先级流 {stream_id} 开始处理 | "
f"focus_energy: {focus_energy:.3f} | "
f"分发周期: {context.distribution_interval:.2f}s | "
f"未读消息: {len(unread_messages)}"
)
else:
logger.debug(
f"{stream_id} 开始处理 | "
f"focus_energy: {focus_energy:.3f} | "
f"分发周期: {context.distribution_interval:.2f}s"
)
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)
self.stats.active_streams = active_count
if processed_streams > 0:
logger.debug(
f"本次循环处理了 {processed_streams} 个流 | "
f"活跃流总数: {active_count}"
)
async def _check_all_streams_with_priority(self):
"""按优先级检查所有聊天流高focus_energy的流优先处理"""
if not self.stream_contexts:
return
# 获取活跃的聊天流并按focus_energy排序
active_streams = []
for stream_id, context in self.stream_contexts.items():
if not context.is_active:
continue
# 获取focus_energy如果不存在则使用默认值
focus_energy = 0.5
if hasattr(context, 'chat_stream') and context.chat_stream:
focus_energy = context.chat_stream.focus_energy
# 计算流优先级分数
priority_score = self._calculate_stream_priority(context, focus_energy)
active_streams.append((priority_score, stream_id, context))
# 按优先级降序排序
active_streams.sort(reverse=True, key=lambda x: x[0])
# 处理排序后的流
active_stream_count = 0
total_unread = 0
for priority_score, stream_id, context in active_streams:
active_stream_count += 1
# 检查是否有未读消息
unread_messages = context.get_unread_messages()
if unread_messages:
total_unread += len(unread_messages)
# 如果没有处理任务,创建一个
if not context.processing_task or context.processing_task.done():
context.processing_task = asyncio.create_task(self._process_stream_messages(stream_id))
# 高优先级流的额外日志
if priority_score > 0.7:
logger.info(
f"高优先级流 {stream_id} 开始处理 | "
f"优先级: {priority_score:.3f} | "
f"未读消息: {len(unread_messages)}"
)
# 更新统计
self.stats.active_streams = active_stream_count
self.stats.total_unread_messages = total_unread
def _calculate_stream_priority(self, context: StreamContext, focus_energy: float) -> float:
"""计算聊天流的优先级分数"""
# 基础优先级focus_energy
base_priority = focus_energy
# 未读消息数量加权
unread_count = len(context.get_unread_messages())
message_count_bonus = min(unread_count * 0.1, 0.3) # 最多30%加成
# 时间加权:最近活跃的流优先级更高
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小时内无惩罚
# 连续无回复惩罚
if hasattr(context, 'chat_stream') and context.chat_stream:
consecutive_no_reply = context.chat_stream.consecutive_no_reply
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%
)
return max(0.0, min(1.0, final_priority))
def _clear_all_unread_messages(self, context: StreamContext): def _clear_all_unread_messages(self, context: StreamContext):
"""清除指定上下文中的所有未读消息,防止意外情况导致消息一直未读""" """清除指定上下文中的所有未读消息,防止意外情况导致消息一直未读"""
unread_messages = context.get_unread_messages() unread_messages = context.get_unread_messages()

View File

@@ -360,19 +360,7 @@ class ChatBot:
return return
async def message_process(self, message_data: Dict[str, Any]) -> None: async def message_process(self, message_data: Dict[str, Any]) -> None:
"""处理转化后的统一格式消息 """处理转化后的统一格式消息"""
这个函数本质是预处理一些数据,根据配置信息和消息内容,预处理消息,并分发到合适的消息处理器中
heart_flow模式使用思维流系统进行回复
- 包含思维流状态管理
- 在回复前进行观察和状态更新
- 回复后更新思维流状态
- 消息过滤
- 记忆激活
- 意愿计算
- 消息生成和发送
- 表情包处理
- 性能计时
"""
try: try:
# 首先处理可能的切片消息重组 # 首先处理可能的切片消息重组
from src.utils.message_chunker import reassembler from src.utils.message_chunker import reassembler
@@ -464,7 +452,8 @@ class ChatBot:
result = await event_manager.trigger_event(EventType.ON_MESSAGE, permission_group="SYSTEM", message=message) result = await event_manager.trigger_event(EventType.ON_MESSAGE, permission_group="SYSTEM", message=message)
if not result.all_continue_process(): if not result.all_continue_process():
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于消息到达时取消了消息处理") raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于消息到达时取消了消息处理")
# TODO:暂不可用
# 确认从接口发来的message是否有自定义的prompt模板信息 # 确认从接口发来的message是否有自定义的prompt模板信息
if message.message_info.template_info and not message.message_info.template_info.template_default: if message.message_info.template_info and not message.message_info.template_info.template_default:
template_group_name: Optional[str] = message.message_info.template_info.template_name # type: ignore template_group_name: Optional[str] = message.message_info.template_info.template_name # type: ignore

View File

@@ -83,10 +83,18 @@ class ChatStream:
self.sleep_pressure = data.get("sleep_pressure", 0.0) if data else 0.0 self.sleep_pressure = data.get("sleep_pressure", 0.0) if data else 0.0
self.saved = False self.saved = False
self.context: ChatMessageContext = None # type: ignore # 用于存储该聊天的上下文信息 self.context: ChatMessageContext = None # type: ignore # 用于存储该聊天的上下文信息
# 从配置文件中读取focus_value如果没有则使用默认值1.0
self.focus_energy = ( # 动态兴趣度系统 - 重构后的focus_energy
data.get("focus_energy", global_config.chat.focus_value) if data else global_config.chat.focus_value self.base_interest_energy = data.get("base_interest_energy", 0.5) if data else 0.5
) self.message_interest_total = data.get("message_interest_total", 0.0) if data else 0.0
self.message_count = data.get("message_count", 0) if data else 0
self.action_count = data.get("action_count", 0) if data else 0
self.reply_count = data.get("reply_count", 0) if data else 0
self.last_interaction_time = data.get("last_interaction_time", time.time()) if data else time.time()
self.consecutive_no_reply = data.get("consecutive_no_reply", 0) if data else 0
# 计算动态focus_energy
self.focus_energy = self._calculate_dynamic_focus_energy()
self.no_reply_consecutive = 0 self.no_reply_consecutive = 0
self.breaking_accumulated_interest = 0.0 self.breaking_accumulated_interest = 0.0
@@ -103,6 +111,14 @@ class ChatStream:
"sleep_pressure": self.sleep_pressure, "sleep_pressure": self.sleep_pressure,
"focus_energy": self.focus_energy, "focus_energy": self.focus_energy,
"breaking_accumulated_interest": self.breaking_accumulated_interest, "breaking_accumulated_interest": self.breaking_accumulated_interest,
# 新增动态兴趣度系统字段
"base_interest_energy": self.base_interest_energy,
"message_interest_total": self.message_interest_total,
"message_count": self.message_count,
"action_count": self.action_count,
"reply_count": self.reply_count,
"last_interaction_time": self.last_interaction_time,
"consecutive_no_reply": self.consecutive_no_reply,
} }
@classmethod @classmethod
@@ -128,6 +144,148 @@ class ChatStream:
"""设置聊天消息上下文""" """设置聊天消息上下文"""
self.context = ChatMessageContext(message) self.context = ChatMessageContext(message)
def _calculate_dynamic_focus_energy(self) -> float:
"""动态计算聊天流的总体兴趣度"""
try:
# 基础分:平均消息兴趣度
avg_message_interest = self.message_interest_total / max(self.message_count, 1)
# 动作参与度:动作执行率
action_rate = self.action_count / max(self.message_count, 1)
# 回复活跃度:回复率
reply_rate = self.reply_count / max(self.message_count, 1)
# 获取用户关系分(对于私聊,群聊无效)
relationship_factor = self._get_user_relationship_score()
# 时间衰减因子:最近活跃度
current_time = time.time()
if not self.last_interaction_time:
self.last_interaction_time = current_time
time_since_interaction = current_time - self.last_interaction_time
time_decay = max(0.3, 1.0 - min(time_since_interaction / (7 * 24 * 3600), 0.7)) # 7天衰减
# 连续无回复惩罚
no_reply_penalty = max(0.1, 1.0 - self.consecutive_no_reply * 0.1)
# 获取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)
# 计算与不同阈值的差距比例
reply_gap_ratio = max(0, (avg_message_interest - reply_threshold) / max(0.1, (1.0 - reply_threshold)))
non_reply_gap_ratio = max(0, (avg_message_interest - non_reply_threshold) / max(0.1, (1.0 - non_reply_threshold)))
high_match_gap_ratio = max(0, (avg_message_interest - high_match_threshold) / max(0.1, (1.0 - high_match_threshold)))
# 基于阈值差距比例的基础分计算
threshold_based_score = (
reply_gap_ratio * 0.6 + # 回复阈值差距权重60%
non_reply_gap_ratio * 0.2 + # 非回复阈值差距权重20%
high_match_gap_ratio * 0.2 # 高匹配阈值差距权重20%
)
# 动态权重调整:根据平均兴趣度水平调整权重分配
if avg_message_interest >= high_match_threshold:
# 高兴趣度:更注重阈值差距
threshold_weight = 0.7
activity_weight = 0.2
relationship_weight = 0.1
elif avg_message_interest >= reply_threshold:
# 中等兴趣度:平衡权重
threshold_weight = 0.5
activity_weight = 0.3
relationship_weight = 0.2
else:
# 低兴趣度:更注重活跃度提升
threshold_weight = 0.3
activity_weight = 0.5
relationship_weight = 0.2
# 计算活跃度得分
activity_score = (action_rate * 0.6 + reply_rate * 0.4)
# 综合计算:基于阈值的动态加权
focus_energy = (
threshold_based_score * threshold_weight + # 阈值差距基础分
activity_score * activity_weight + # 活跃度得分
relationship_factor * relationship_weight + # 关系得分
self.base_interest_energy * 0.05 # 基础兴趣微调
) * time_decay * no_reply_penalty
# 确保在合理范围内
focus_energy = max(0.1, min(1.0, focus_energy))
# 应用非线性变换增强区分度
if focus_energy >= 0.7:
# 高兴趣度区域:指数增强,更敏感
focus_energy = 0.7 + (focus_energy - 0.7) ** 0.8
elif focus_energy >= 0.4:
# 中等兴趣度区域:线性保持
pass
else:
# 低兴趣度区域:对数压缩,减少区分度
focus_energy = 0.4 * (focus_energy / 0.4) ** 1.2
return max(0.1, min(1.0, focus_energy))
except Exception as e:
logger.error(f"计算动态focus_energy失败: {e}")
return self.base_interest_energy
def _get_user_relationship_score(self) -> float:
"""从外部系统获取用户关系分"""
try:
# 尝试从兴趣评分系统获取用户关系分
from src.plugins.built_in.affinity_flow_chatter.interest_scoring import (
chatter_interest_scoring_system,
)
if self.user_info and hasattr(self.user_info, 'user_id'):
return chatter_interest_scoring_system.get_user_relationship(str(self.user_info.user_id))
except Exception:
pass
# 默认基础分
return 0.3
def add_message_interest(self, interest_score: float):
"""添加消息兴趣值并更新focus_energy"""
self.message_interest_total += interest_score
self.message_count += 1
self.last_interaction_time = time.time()
self.focus_energy = self._calculate_dynamic_focus_energy()
def record_action(self, is_reply: bool = False):
"""记录动作执行"""
self.action_count += 1
if is_reply:
self.reply_count += 1
self.consecutive_no_reply = max(0, self.consecutive_no_reply - 1)
self.last_interaction_time = time.time()
self.focus_energy = self._calculate_dynamic_focus_energy()
def record_no_reply(self):
"""记录无回复动作"""
self.consecutive_no_reply += 1
self.last_interaction_time = time.time()
self.focus_energy = self._calculate_dynamic_focus_energy()
def get_adjusted_focus_energy(self) -> float:
"""获取应用了afc调整的focus_energy"""
try:
from src.chat.message_manager.message_manager import message_manager
if self.stream_id in message_manager.stream_contexts:
context = message_manager.stream_contexts[self.stream_id]
afc_adjustment = context.get_afc_threshold_adjustment()
# 对动态计算的focus_energy应用AFC调整
adjusted_energy = max(0.0, self.focus_energy - afc_adjustment)
return adjusted_energy
except Exception:
pass
return self.focus_energy
class ChatManager: class ChatManager:
"""聊天管理器,管理所有聊天流""" """聊天管理器,管理所有聊天流"""
@@ -364,7 +522,16 @@ class ChatManager:
"group_name": group_info_d["group_name"] if group_info_d else "", "group_name": group_info_d["group_name"] if group_info_d else "",
"energy_value": s_data_dict.get("energy_value", 5.0), "energy_value": s_data_dict.get("energy_value", 5.0),
"sleep_pressure": s_data_dict.get("sleep_pressure", 0.0), "sleep_pressure": s_data_dict.get("sleep_pressure", 0.0),
"focus_energy": s_data_dict.get("focus_energy", global_config.chat.focus_value), "focus_energy": s_data_dict.get("focus_energy", 0.5),
# 新增动态兴趣度系统字段
"base_interest_energy": s_data_dict.get("base_interest_energy", 0.5),
"message_interest_total": s_data_dict.get("message_interest_total", 0.0),
"message_count": s_data_dict.get("message_count", 0),
"action_count": s_data_dict.get("action_count", 0),
"reply_count": s_data_dict.get("reply_count", 0),
"last_interaction_time": s_data_dict.get("last_interaction_time", time.time()),
"relationship_score": s_data_dict.get("relationship_score", 0.3),
"consecutive_no_reply": s_data_dict.get("consecutive_no_reply", 0),
} }
# 根据数据库类型选择插入语句 # 根据数据库类型选择插入语句
@@ -426,7 +593,16 @@ class ChatManager:
"last_active_time": model_instance.last_active_time, "last_active_time": model_instance.last_active_time,
"energy_value": model_instance.energy_value, "energy_value": model_instance.energy_value,
"sleep_pressure": model_instance.sleep_pressure, "sleep_pressure": model_instance.sleep_pressure,
"focus_energy": getattr(model_instance, "focus_energy", global_config.chat.focus_value), "focus_energy": getattr(model_instance, "focus_energy", 0.5),
# 新增动态兴趣度系统字段 - 使用getattr提供默认值
"base_interest_energy": getattr(model_instance, "base_interest_energy", 0.5),
"message_interest_total": getattr(model_instance, "message_interest_total", 0.0),
"message_count": getattr(model_instance, "message_count", 0),
"action_count": getattr(model_instance, "action_count", 0),
"reply_count": getattr(model_instance, "reply_count", 0),
"last_interaction_time": getattr(model_instance, "last_interaction_time", time.time()),
"relationship_score": getattr(model_instance, "relationship_score", 0.3),
"consecutive_no_reply": getattr(model_instance, "consecutive_no_reply", 0),
} }
loaded_streams_data.append(data_for_from_dict) loaded_streams_data.append(data_for_from_dict)
session.commit() session.commit()

View File

@@ -15,6 +15,7 @@ from src.plugin_system.base.component_types import ComponentType, ActionInfo
from src.plugin_system.base.base_action import BaseAction from src.plugin_system.base.base_action import BaseAction
from src.plugin_system.apis import generator_api, database_api, send_api, message_api from src.plugin_system.apis import generator_api, database_api, send_api, message_api
logger = get_logger("action_manager") logger = get_logger("action_manager")
@@ -161,6 +162,7 @@ class ChatterActionManager:
Returns: Returns:
执行结果 执行结果
""" """
from src.chat.message_manager.message_manager import message_manager
try: try:
logger.debug(f"🎯 [ActionManager] execute_action接收到 target_message: {target_message}") logger.debug(f"🎯 [ActionManager] execute_action接收到 target_message: {target_message}")
# 通过chat_id获取chat_stream # 通过chat_id获取chat_stream
@@ -207,6 +209,11 @@ class ChatterActionManager:
thinking_id, thinking_id,
target_message, target_message,
) )
# 如果动作执行成功且不是no_action重置打断计数
if success:
await self._reset_interruption_count_after_action(chat_stream.stream_id)
return { return {
"action_type": action_name, "action_type": action_name,
"success": success, "success": success,
@@ -244,6 +251,10 @@ class ChatterActionManager:
thinking_id, thinking_id,
[], # actions [], # actions
) )
# 回复成功,重置打断计数
await self._reset_interruption_count_after_action(chat_stream.stream_id)
return {"action_type": "reply", "success": True, "reply_text": reply_text, "loop_info": loop_info} return {"action_type": "reply", "success": True, "reply_text": reply_text, "loop_info": loop_info}
except Exception as e: except Exception as e:
@@ -257,6 +268,20 @@ class ChatterActionManager:
"error": str(e), "error": str(e),
} }
async def _reset_interruption_count_after_action(self, stream_id: str):
"""在动作执行成功后重置打断计数"""
from src.chat.message_manager.message_manager import message_manager
try:
if stream_id in message_manager.stream_contexts:
context = message_manager.stream_contexts[stream_id]
if context.interruption_count > 0:
old_count = context.interruption_count
old_afc_adjustment = context.get_afc_threshold_adjustment()
context.reset_interruption_count()
logger.debug(f"动作执行成功,重置聊天流 {stream_id} 的打断计数: {old_count} -> 0, afc调整: {old_afc_adjustment} -> 0")
except Exception as e:
logger.warning(f"重置打断计数时出错: {e}")
async def _handle_action( async def _handle_action(
self, chat_stream, action, reasoning, action_data, cycle_timers, thinking_id, action_message self, chat_stream, action, reasoning, action_data, cycle_timers, thinking_id, action_message
) -> tuple[bool, str, str]: ) -> tuple[bool, str, str]:

View File

@@ -11,10 +11,13 @@ from typing import List, Optional, TYPE_CHECKING
from . import BaseDataModel from . import BaseDataModel
from src.plugin_system.base.component_types import ChatMode, ChatType from src.plugin_system.base.component_types import ChatMode, ChatType
from src.common.logger import get_logger
if TYPE_CHECKING: if TYPE_CHECKING:
from .database_data_model import DatabaseMessages from .database_data_model import DatabaseMessages
logger = get_logger("stream_context")
class MessageStatus(Enum): class MessageStatus(Enum):
"""消息状态枚举""" """消息状态枚举"""
@@ -36,6 +39,13 @@ class StreamContext(BaseDataModel):
last_check_time: float = field(default_factory=time.time) last_check_time: float = field(default_factory=time.time)
is_active: bool = True is_active: bool = True
processing_task: Optional[asyncio.Task] = None processing_task: Optional[asyncio.Task] = None
interruption_count: int = 0 # 打断计数器
last_interruption_time: float = 0.0 # 上次打断时间
afc_threshold_adjustment: float = 0.0 # afc阈值调整量
# 独立分发周期字段
next_check_time: float = field(default_factory=time.time) # 下次检查时间
distribution_interval: float = 5.0 # 当前分发周期(秒)
def add_message(self, message: "DatabaseMessages"): def add_message(self, message: "DatabaseMessages"):
"""添加消息到上下文""" """添加消息到上下文"""
@@ -50,9 +60,9 @@ class StreamContext(BaseDataModel):
# 只有在第一次添加消息时才检测聊天类型,避免后续消息改变类型 # 只有在第一次添加消息时才检测聊天类型,避免后续消息改变类型
if len(self.unread_messages) == 1: # 只有这条消息 if len(self.unread_messages) == 1: # 只有这条消息
# 如果消息包含群组信息,则为群聊 # 如果消息包含群组信息,则为群聊
if hasattr(message, 'chat_info_group_id') and message.chat_info_group_id: if hasattr(message, "chat_info_group_id") and message.chat_info_group_id:
self.chat_type = ChatType.GROUP self.chat_type = ChatType.GROUP
elif hasattr(message, 'chat_info_group_name') and message.chat_info_group_name: elif hasattr(message, "chat_info_group_name") and message.chat_info_group_name:
self.chat_type = ChatType.GROUP self.chat_type = ChatType.GROUP
else: else:
self.chat_type = ChatType.PRIVATE self.chat_type = ChatType.PRIVATE
@@ -64,7 +74,7 @@ class StreamContext(BaseDataModel):
def set_chat_mode(self, chat_mode: ChatMode): def set_chat_mode(self, chat_mode: ChatMode):
"""设置聊天模式""" """设置聊天模式"""
self.chat_mode = chat_mode self.chat_mode = chat_mode
def is_group_chat(self) -> bool: def is_group_chat(self) -> bool:
"""检查是否为群聊""" """检查是否为群聊"""
return self.chat_type == ChatType.GROUP return self.chat_type == ChatType.GROUP
@@ -82,10 +92,6 @@ class StreamContext(BaseDataModel):
else: else:
return "未知类型" return "未知类型"
def get_unread_messages(self) -> List["DatabaseMessages"]:
"""获取未读消息"""
return [msg for msg in self.unread_messages if not msg.is_read]
def mark_message_as_read(self, message_id: str): def mark_message_as_read(self, message_id: str):
"""标记消息为已读""" """标记消息为已读"""
for msg in self.unread_messages: for msg in self.unread_messages:
@@ -94,13 +100,56 @@ class StreamContext(BaseDataModel):
self.history_messages.append(msg) self.history_messages.append(msg)
self.unread_messages.remove(msg) self.unread_messages.remove(msg)
break break
def get_unread_messages(self) -> List["DatabaseMessages"]:
"""获取未读消息"""
return [msg for msg in self.unread_messages if not msg.is_read]
def get_history_messages(self, limit: int = 20) -> List["DatabaseMessages"]: def get_history_messages(self, limit: int = 20) -> List["DatabaseMessages"]:
"""获取历史消息""" """获取历史消息"""
# 优先返回最近的历史消息和所有未读消息 # 优先返回最近的历史消息和所有未读消息
recent_history = self.history_messages[-limit:] if len(self.history_messages) > limit else self.history_messages recent_history = self.history_messages[-limit:] if len(self.history_messages) > limit else self.history_messages
return recent_history return recent_history
def calculate_interruption_probability(self, max_limit: int, probability_factor: float) -> float:
"""计算打断概率"""
if max_limit <= 0:
return 0.0
# 计算打断比例
interruption_ratio = self.interruption_count / max_limit
# 如果超过概率因子,概率下降
if interruption_ratio > probability_factor:
# 使用指数衰减,超过限制越多,概率越低
excess_ratio = interruption_ratio - probability_factor
probability = 1.0 * (0.5**excess_ratio) # 基础概率0.5,指数衰减
else:
# 在限制内,保持较高概率
probability = 0.8
return max(0.0, min(1.0, probability))
def increment_interruption_count(self):
"""增加打断计数"""
self.interruption_count += 1
self.last_interruption_time = time.time()
def reset_interruption_count(self):
"""重置打断计数和afc阈值调整"""
self.interruption_count = 0
self.last_interruption_time = 0.0
self.afc_threshold_adjustment = 0.0
def apply_interruption_afc_reduction(self, reduction_value: float):
"""应用打断导致的afc阈值降低"""
self.afc_threshold_adjustment += reduction_value
logger.debug(f"应用afc阈值降低: {reduction_value}, 总调整量: {self.afc_threshold_adjustment}")
def get_afc_threshold_adjustment(self) -> float:
"""获取当前的afc阈值调整量"""
return self.afc_threshold_adjustment
@dataclass @dataclass
class MessageManagerStats(BaseDataModel): class MessageManagerStats(BaseDataModel):

View File

@@ -53,7 +53,15 @@ class ChatStreams(Base):
user_cardname = Column(Text, nullable=True) user_cardname = Column(Text, nullable=True)
energy_value = Column(Float, nullable=True, default=5.0) energy_value = Column(Float, nullable=True, default=5.0)
sleep_pressure = Column(Float, nullable=True, default=0.0) sleep_pressure = Column(Float, nullable=True, default=0.0)
focus_energy = Column(Float, nullable=True, default=1.0) focus_energy = Column(Float, nullable=True, default=0.5)
# 动态兴趣度系统字段
base_interest_energy = Column(Float, nullable=True, default=0.5)
message_interest_total = Column(Float, nullable=True, default=0.0)
message_count = Column(Integer, nullable=True, default=0)
action_count = Column(Integer, nullable=True, default=0)
reply_count = Column(Integer, nullable=True, default=0)
last_interaction_time = Column(Float, nullable=True, default=None)
consecutive_no_reply = Column(Integer, nullable=True, default=0)
__table_args__ = ( __table_args__ = (
Index("idx_chatstreams_stream_id", "stream_id"), Index("idx_chatstreams_stream_id", "stream_id"),

View File

@@ -350,6 +350,10 @@ MODULE_COLORS = {
"memory": "\033[38;5;117m", # 天蓝色 "memory": "\033[38;5;117m", # 天蓝色
"hfc": "\033[38;5;81m", # 稍微暗一些的青色,保持可读 "hfc": "\033[38;5;81m", # 稍微暗一些的青色,保持可读
"action_manager": "\033[38;5;208m", # 橙色不与replyer重复 "action_manager": "\033[38;5;208m", # 橙色不与replyer重复
"message_manager": "\033[38;5;27m", # 深蓝色,消息管理器
"chatter_manager": "\033[38;5;129m", # 紫色,聊天管理器
"chatter_interest_scoring": "\033[38;5;214m", # 橙黄色,兴趣评分
"plan_executor": "\033[38;5;172m", # 橙褐色,计划执行器
# 关系系统 # 关系系统
"relation": "\033[38;5;139m", # 柔和的紫色,不刺眼 "relation": "\033[38;5;139m", # 柔和的紫色,不刺眼
# 聊天相关模块 # 聊天相关模块
@@ -551,6 +555,10 @@ MODULE_ALIASES = {
"llm_models": "模型", "llm_models": "模型",
"person_info": "人物", "person_info": "人物",
"chat_stream": "聊天流", "chat_stream": "聊天流",
"message_manager": "消息管理",
"chatter_manager": "聊天管理",
"chatter_interest_scoring": "兴趣评分",
"plan_executor": "计划执行",
"planner": "规划器", "planner": "规划器",
"replyer": "言语", "replyer": "言语",
"config": "配置", "config": "配置",

View File

@@ -67,7 +67,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.10.0-alpha-2" MMC_VERSION = "0.11.0-alpha-1"
def get_key_comment(toml_table, key): def get_key_comment(toml_table, key):

View File

@@ -104,6 +104,31 @@ class ChatConfig(ValidatedConfigBase):
) )
delta_sigma: int = Field(default=120, description="采用正态分布随机时间间隔") delta_sigma: int = Field(default=120, description="采用正态分布随机时间间隔")
# 消息打断系统配置
interruption_enabled: bool = Field(default=True, description="是否启用消息打断系统")
interruption_max_limit: int = Field(default=3, ge=0, description="每个聊天流的最大打断次数")
interruption_probability_factor: float = Field(
default=0.8, ge=0.0, le=1.0, description="打断概率因子,当前打断次数/最大打断次数超过此值时触发概率下降"
)
interruption_afc_reduction: float = Field(
default=0.05, ge=0.0, le=1.0, description="每次连续打断降低的afc阈值数值"
)
# 动态消息分发系统配置
dynamic_distribution_enabled: bool = Field(default=True, description="是否启用动态消息分发周期调整")
dynamic_distribution_base_interval: float = Field(
default=5.0, ge=1.0, le=60.0, description="基础分发间隔(秒)"
)
dynamic_distribution_min_interval: float = Field(
default=1.0, ge=0.5, le=10.0, description="最小分发间隔(秒)"
)
dynamic_distribution_max_interval: float = Field(
default=30.0, ge=5.0, le=300.0, description="最大分发间隔(秒)"
)
dynamic_distribution_jitter_factor: float = Field(
default=0.2, ge=0.0, le=0.5, description="分发间隔随机扰动因子"
)
def get_current_talk_frequency(self, chat_stream_id: Optional[str] = None) -> float: def get_current_talk_frequency(self, chat_stream_id: Optional[str] = None) -> float:
""" """
根据当前时间和聊天流获取对应的 talk_frequency 根据当前时间和聊天流获取对应的 talk_frequency

View File

@@ -4,6 +4,7 @@ import time
import signal import signal
import sys import sys
from functools import partial from functools import partial
import traceback
from typing import Dict, Any from typing import Dict, Any
from maim_message import MessageServer from maim_message import MessageServer

View File

@@ -150,6 +150,11 @@ class ChatMood:
self.last_change_time = message_time self.last_change_time = message_time
# 更新ChatStream的兴趣度数据
if hasattr(self, 'chat_stream') and self.chat_stream:
self.chat_stream.add_message_interest(interested_rate)
logger.debug(f"{self.log_prefix} 已更新ChatStream兴趣度当前focus_energy: {self.chat_stream.focus_energy:.3f}")
async def regress_mood(self): async def regress_mood(self):
message_time = time.time() message_time = time.time()
message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive(

View File

@@ -125,11 +125,17 @@ class ChatterActionPlanner:
logger.info(f"兴趣度不足 ({latest_score.total_score:.2f}),移除回复") logger.info(f"兴趣度不足 ({latest_score.total_score:.2f}),移除回复")
reply_not_available = True reply_not_available = True
# 更新情绪状态 - 使用最新消息的兴趣度 # 更新情绪状态和ChatStream兴趣度数据
if latest_message and score > 0: if latest_message and score > 0:
chat_mood = mood_manager.get_mood_by_chat_id(self.chat_id) chat_mood = mood_manager.get_mood_by_chat_id(self.chat_id)
await chat_mood.update_mood_by_message(latest_message, score) await chat_mood.update_mood_by_message(latest_message, score)
logger.debug(f"已更新聊天 {self.chat_id} 的情绪状态,兴趣度: {score:.3f}") logger.debug(f"已更新聊天 {self.chat_id} 的情绪状态,兴趣度: {score:.3f}")
elif latest_message:
# 即使不更新情绪状态也要更新ChatStream的兴趣度数据
chat_mood = mood_manager.get_mood_by_chat_id(self.chat_id)
if hasattr(chat_mood, 'chat_stream') and chat_mood.chat_stream:
chat_mood.chat_stream.add_message_interest(score)
logger.debug(f"已更新聊天 {self.chat_id} 的ChatStream兴趣度分数: {score:.3f}")
# base_threshold = self.interest_scoring.reply_threshold # base_threshold = self.interest_scoring.reply_threshold
# 检查兴趣度是否达到非回复动作阈值 # 检查兴趣度是否达到非回复动作阈值

View File

@@ -11,7 +11,7 @@
"host_application": { "host_application": {
"min_version": "0.10.0", "min_version": "0.10.0",
"max_version": "0.10.0" "max_version": "0.11.0"
}, },
"homepage_url": "https://github.com/Windpicker-owo/InternetSearchPlugin", "homepage_url": "https://github.com/Windpicker-owo/InternetSearchPlugin",
"repository_url": "https://github.com/Windpicker-owo/InternetSearchPlugin", "repository_url": "https://github.com/Windpicker-owo/InternetSearchPlugin",

View File

@@ -299,7 +299,7 @@ class NapcatAdapterPlugin(BasePlugin):
"name": ConfigField(type=str, default="napcat_adapter_plugin", description="插件名称"), "name": ConfigField(type=str, default="napcat_adapter_plugin", description="插件名称"),
"version": ConfigField(type=str, default="1.1.0", description="插件版本"), "version": ConfigField(type=str, default="1.1.0", description="插件版本"),
"config_version": ConfigField(type=str, default="1.3.1", description="配置文件版本"), "config_version": ConfigField(type=str, default="1.3.1", description="配置文件版本"),
"enabled": ConfigField(type=bool, default=False, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
}, },
"inner": { "inner": {
"version": ConfigField(type=str, default="0.2.1", description="配置版本号,请勿修改"), "version": ConfigField(type=str, default="0.2.1", description="配置版本号,请勿修改"),

View File

@@ -1,5 +1,5 @@
[inner] [inner]
version = "6.9.6" version = "7.0.2"
#----以下是给开发人员阅读的如果你只是部署了MoFox-Bot不需要阅读---- #----以下是给开发人员阅读的如果你只是部署了MoFox-Bot不需要阅读----
#如果你想要修改配置文件请递增version的值 #如果你想要修改配置文件请递增version的值
@@ -114,36 +114,23 @@ learn_expression = false
learning_strength = 0.5 learning_strength = 0.5
[chat] #MoFox-Bot的聊天通用设置 [chat] #MoFox-Bot的聊天通用设置
# 群聊聊天模式设置
group_chat_mode = "auto" # 群聊聊天模式auto-自动切换normal-强制普通模式focus-强制专注模式
talk_frequency = 1
# MoFox-Bot活跃度越高麦麦回复越多
# 专注时能更好把握发言时机,能够进行持久的连续对话
focus_value = 1
# MoFox-Bot的专注思考能力越高越容易持续连续对话
# 在专注模式下,只在被艾特或提及时才回复的群组列表
# 这可以让你在某些群里保持“高冷”,只在被需要时才发言
# 格式为: ["platform:group_id1", "platform:group_id2"]
# 例如: ["qq:123456789", "qq:987654321"]
focus_mode_quiet_groups = []
# breaking模式配置
enable_breaking_mode = true # 是否启用自动进入breaking模式关闭后不会自动进入breaking形式
# 强制私聊回复
force_reply_private = false # 是否强制私聊回复,开启后私聊将强制回复
allow_reply_self = false # 是否允许回复自己说的话 allow_reply_self = false # 是否允许回复自己说的话
max_context_size = 25 # 上下文长度 max_context_size = 25 # 上下文长度
thinking_timeout = 40 # MoFox-Bot一次回复最长思考规划时间超过这个时间的思考会放弃往往是api反应太慢 thinking_timeout = 40 # MoFox-Bot一次回复最长思考规划时间超过这个时间的思考会放弃往往是api反应太慢
replyer_random_probability = 0.5 # 首要replyer模型被选择的概率
mentioned_bot_inevitable_reply = true # 提及 bot 必然回复 # 消息打断系统配置
at_bot_inevitable_reply = true # @bot 或 提及bot 必然回复 interruption_enabled = true # 是否启用消息打断系统
# 兼容normal、focus在focus模式下为强制移除no_reply动作 interruption_max_limit = 3 # 每个聊天流的最大打断次数
interruption_probability_factor = 0.8 # 打断概率因子,当前打断次数/最大打断次数超过此值时触发概率下降
interruption_afc_reduction = 0.05 # 每次连续打断降低的afc阈值数值
# 动态消息分发系统配置
dynamic_distribution_enabled = true # 是否启用动态消息分发周期调整
dynamic_distribution_base_interval = 5.0 # 基础分发间隔(秒)
dynamic_distribution_min_interval = 1.0 # 最小分发间隔(秒)
dynamic_distribution_max_interval = 30.0 # 最大分发间隔(秒)
dynamic_distribution_jitter_factor = 0.2 # 分发间隔随机扰动因子
talk_frequency_adjust = [ talk_frequency_adjust = [
["", "8:00,1", "12:00,1.2", "18:00,1.5", "01:00,0.6"], ["", "8:00,1", "12:00,1.2", "18:00,1.5", "01:00,0.6"],
@@ -288,7 +275,7 @@ enable_vector_instant_memory = true # 是否启用基于向量的瞬时记忆
memory_ban_words = [ "表情包", "图片", "回复", "聊天记录" ] memory_ban_words = [ "表情包", "图片", "回复", "聊天记录" ]
[voice] [voice]
enable_asr = false # 是否启用语音识别启用后MoFox-Bot可以识别语音消息启用该功能需要配置语音识别模型[model.voice] enable_asr = true # 是否启用语音识别启用后MoFox-Bot可以识别语音消息启用该功能需要配置语音识别模型[model.voice]
[lpmm_knowledge] # lpmm知识库配置 [lpmm_knowledge] # lpmm知识库配置
enable = false # 是否启用lpmm知识库 enable = false # 是否启用lpmm知识库
@@ -341,7 +328,7 @@ enable = true # 是否启用回复分割器
split_mode = "punctuation" # 分割模式: "llm" - 由语言模型决定, "punctuation" - 基于标点符号 split_mode = "punctuation" # 分割模式: "llm" - 由语言模型决定, "punctuation" - 基于标点符号
max_length = 512 # 回复允许的最大长度 max_length = 512 # 回复允许的最大长度
max_sentence_num = 8 # 回复允许的最大句子数 max_sentence_num = 8 # 回复允许的最大句子数
enable_kaomoji_protection = false # 是否启用颜文字保护 enable_kaomoji_protection = true # 是否启用颜文字保护
[log] [log]
date_style = "m-d H:i:s" # 日期格式 date_style = "m-d H:i:s" # 日期格式