refactor(chat): 重构关系系统并优化消息打断处理机制
- 移除独立的RelationshipConfig,将关系追踪参数整合到AffinityFlowConfig - 实现消息打断后立即重新处理流程,提升交互响应性 - 优化关系追踪系统,添加概率筛选和超时保护机制 - 改进机器人自引用处理,确保消息内容正确显示 - 增强用户信息提取逻辑,兼容多种消息格式 - 添加异步后台任务处理,避免阻塞主回复流程 - 调整兴趣评分阈值和权重参数,优化消息匹配精度
This commit is contained in:
@@ -380,11 +380,11 @@ class MessageManager:
|
||||
else:
|
||||
logger.warning(f"消息打断未能取消任何任务: {chat_stream.stream_id}")
|
||||
|
||||
# 增加打断计数并应用afc阈值降低
|
||||
# 增加打断计数
|
||||
await chat_stream.context_manager.context.increment_interruption_count()
|
||||
chat_stream.context_manager.context.apply_interruption_afc_reduction(
|
||||
global_config.chat.interruption_afc_reduction
|
||||
)
|
||||
|
||||
# 🚀 新增:打断后立即重新进入聊天流程
|
||||
await self._trigger_immediate_reprocess(chat_stream)
|
||||
|
||||
# 检查是否已达到最大次数
|
||||
if chat_stream.context_manager.context.interruption_count >= global_config.chat.interruption_max_limit:
|
||||
@@ -393,11 +393,64 @@ class MessageManager:
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"聊天流 {chat_stream.stream_id} 已打断,当前打断次数: {chat_stream.context_manager.context.interruption_count}/{global_config.chat.interruption_max_limit}, afc阈值调整: {chat_stream.context_manager.context.get_afc_threshold_adjustment()}"
|
||||
f"聊天流 {chat_stream.stream_id} 已打断并重新进入处理流程,当前打断次数: {chat_stream.context_manager.context.interruption_count}/{global_config.chat.interruption_max_limit}"
|
||||
)
|
||||
else:
|
||||
logger.debug(f"聊天流 {chat_stream.stream_id} 未触发打断,打断概率: {interruption_probability:.2f},检测到 {len(all_processing_tasks)} 个任务")
|
||||
|
||||
async def _trigger_immediate_reprocess(self, chat_stream: ChatStream):
|
||||
"""打断后立即重新进入聊天流程"""
|
||||
try:
|
||||
stream_id = chat_stream.stream_id
|
||||
|
||||
logger.info(f"🚀 打断后立即重新处理聊天流: {stream_id}")
|
||||
|
||||
# 等待一小段时间确保当前消息已经添加到未读消息中
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# 获取当前的stream context
|
||||
context = chat_stream.stream_context
|
||||
|
||||
# 确保有未读消息需要处理
|
||||
unread_messages = context.get_unread_messages()
|
||||
if not unread_messages:
|
||||
logger.debug(f"💭 聊天流 {stream_id} 没有未读消息,跳过重新处理")
|
||||
return
|
||||
|
||||
logger.info(f"💬 开始重新处理 {len(unread_messages)} 条未读消息: {stream_id}")
|
||||
|
||||
# 创建新的处理任务
|
||||
task = asyncio.create_task(
|
||||
self.chatter_manager.process_stream_context(stream_id, context),
|
||||
name=f"reprocess_{stream_id}_{int(time.time())}"
|
||||
)
|
||||
|
||||
# 设置处理任务
|
||||
self.chatter_manager.set_processing_task(stream_id, task)
|
||||
|
||||
# 等待处理完成(使用超时防止无限等待)
|
||||
try:
|
||||
result = await asyncio.wait_for(task, timeout=30.0)
|
||||
success = result.get("success", False)
|
||||
actions_count = result.get("actions_count", 0)
|
||||
|
||||
if success:
|
||||
logger.info(f"✅ 聊天流 {stream_id} 重新处理成功: 执行了 {actions_count} 个动作")
|
||||
else:
|
||||
logger.warning(f"❌ 聊天流 {stream_id} 重新处理失败")
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"⏰ 聊天流 {stream_id} 重新处理超时")
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
except Exception as e:
|
||||
logger.error(f"💥 聊天流 {stream_id} 重新处理出错: {e}")
|
||||
if not task.done():
|
||||
task.cancel()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"🚨 触发重新处理时出错: {e}")
|
||||
|
||||
async def clear_all_unread_messages(self, stream_id: str):
|
||||
"""清除指定上下文中的所有未读消息,在消息处理完成后调用"""
|
||||
try:
|
||||
|
||||
@@ -903,9 +903,21 @@ class DefaultReplyer:
|
||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
||||
person_info_manager = get_person_info_manager()
|
||||
sender_name = await person_info_manager.get_value(person_id, "person_name") or "未知用户"
|
||||
|
||||
# 检查是否是机器人自己,如果是则显示为(你)
|
||||
if user_id == str(global_config.bot.qq_account):
|
||||
sender_name = f"{global_config.bot.nickname}(你)"
|
||||
else:
|
||||
sender_name = "未知用户"
|
||||
|
||||
# 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示
|
||||
from src.chat.utils.chat_message_builder import replace_user_references_sync
|
||||
msg_content = replace_user_references_sync(
|
||||
msg_content,
|
||||
platform,
|
||||
replace_bot_name=True
|
||||
)
|
||||
|
||||
# 添加兴趣度信息
|
||||
interest_score = interest_scores.get(msg_id, 0.0)
|
||||
interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else ""
|
||||
@@ -1002,9 +1014,21 @@ class DefaultReplyer:
|
||||
person_id = PersonInfoManager.get_person_id(platform, user_id)
|
||||
person_info_manager = get_person_info_manager()
|
||||
sender_name = await person_info_manager.get_value(person_id, "person_name") or "未知用户"
|
||||
|
||||
# 检查是否是机器人自己,如果是则显示为(你)
|
||||
if user_id == str(global_config.bot.qq_account):
|
||||
sender_name = f"{global_config.bot.nickname}(你)"
|
||||
else:
|
||||
sender_name = "未知用户"
|
||||
|
||||
# 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示
|
||||
from src.chat.utils.chat_message_builder import replace_user_references_sync
|
||||
msg_content = replace_user_references_sync(
|
||||
msg_content,
|
||||
platform,
|
||||
replace_bot_name=True
|
||||
)
|
||||
|
||||
# 添加兴趣度信息
|
||||
interest_score = interest_scores.get(msg_id, 0.0)
|
||||
interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else ""
|
||||
@@ -1678,7 +1702,7 @@ class DefaultReplyer:
|
||||
return ""
|
||||
|
||||
async def build_relation_info(self, sender: str, target: str):
|
||||
if not global_config.relationship.enable_relationship:
|
||||
if not global_config.affinity_flow.enable_relationship_tracking:
|
||||
return ""
|
||||
|
||||
# 获取用户ID
|
||||
|
||||
@@ -979,7 +979,7 @@ class Prompt:
|
||||
Returns:
|
||||
str: 关系信息字符串
|
||||
"""
|
||||
if not global_config.relationship.enable_relationship:
|
||||
if not global_config.affinity_flow.enable_relationship_tracking:
|
||||
return ""
|
||||
|
||||
from src.person_info.relationship_fetcher import relationship_fetcher_manager
|
||||
|
||||
@@ -42,7 +42,6 @@ class StreamContext(BaseDataModel):
|
||||
processing_task: asyncio.Task | None = 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) # 下次检查时间
|
||||
@@ -140,22 +139,13 @@ class StreamContext(BaseDataModel):
|
||||
await self._sync_interruption_count_to_stream()
|
||||
|
||||
async def reset_interruption_count(self):
|
||||
"""重置打断计数和afc阈值调整"""
|
||||
"""重置打断计数"""
|
||||
self.interruption_count = 0
|
||||
self.last_interruption_time = 0.0
|
||||
self.afc_threshold_adjustment = 0.0
|
||||
|
||||
# 同步打断计数到ChatStream
|
||||
await self._sync_interruption_count_to_stream()
|
||||
|
||||
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
|
||||
|
||||
async def _sync_interruption_count_to_stream(self):
|
||||
"""同步打断计数到ChatStream"""
|
||||
|
||||
@@ -37,7 +37,6 @@ from src.config.official_configs import (
|
||||
PersonalityConfig,
|
||||
PlanningSystemConfig,
|
||||
ProactiveThinkingConfig,
|
||||
RelationshipConfig,
|
||||
ResponsePostProcessConfig,
|
||||
ResponseSplitterConfig,
|
||||
SleepSystemConfig,
|
||||
@@ -377,7 +376,6 @@ class Config(ValidatedConfigBase):
|
||||
database: DatabaseConfig = Field(..., description="数据库配置")
|
||||
bot: BotConfig = Field(..., description="机器人基本配置")
|
||||
personality: PersonalityConfig = Field(..., description="个性配置")
|
||||
relationship: RelationshipConfig = Field(..., description="关系配置")
|
||||
chat: ChatConfig = Field(..., description="聊天配置")
|
||||
message_receive: MessageReceiveConfig = Field(..., description="消息接收配置")
|
||||
normal_chat: NormalChatConfig = Field(..., description="普通聊天配置")
|
||||
|
||||
@@ -99,12 +99,6 @@ class PersonalityConfig(ValidatedConfigBase):
|
||||
)
|
||||
|
||||
|
||||
class RelationshipConfig(ValidatedConfigBase):
|
||||
"""关系配置类"""
|
||||
|
||||
enable_relationship: bool = Field(default=True, description="是否启用关系")
|
||||
relation_frequency: float = Field(default=1.0, description="关系频率")
|
||||
|
||||
|
||||
class ChatConfig(ValidatedConfigBase):
|
||||
"""聊天配置类"""
|
||||
@@ -132,7 +126,6 @@ class ChatConfig(ValidatedConfigBase):
|
||||
interruption_min_probability: float = Field(
|
||||
default=0.1, ge=0.0, le=1.0, description="最低打断概率(即使达到较高打断次数,也保证有此概率的打断机会)"
|
||||
)
|
||||
interruption_afc_reduction: float = Field(default=0.05, ge=0.0, le=1.0, description="每次连续打断降低的afc阈值数值")
|
||||
|
||||
# DEPRECATED: interruption_probability_factor (已废弃的配置项)
|
||||
# 新的线性概率模型不再需要复杂的概率因子
|
||||
@@ -698,6 +691,12 @@ class AffinityFlowConfig(ValidatedConfigBase):
|
||||
mention_bot_interest_score: float = Field(default=0.6, description="提及bot的兴趣分")
|
||||
base_relationship_score: float = Field(default=0.5, description="基础人物关系分")
|
||||
|
||||
# 关系追踪系统参数
|
||||
enable_relationship_tracking: bool = Field(default=True, description="是否启用关系追踪系统")
|
||||
relationship_tracking_probability: float = Field(default=0.7, description="关系追踪执行概率 (0.0-1.0),用于减少API调用压力")
|
||||
relationship_tracking_interval_min: int = Field(default=300, description="关系追踪最小间隔时间(秒)")
|
||||
relationship_tracking_cooldown_hours: float = Field(default=1.0, description="同一用户关系追踪冷却时间(小时)")
|
||||
|
||||
|
||||
class ProactiveThinkingConfig(ValidatedConfigBase):
|
||||
"""主动思考(主动发起对话)功能配置"""
|
||||
|
||||
@@ -154,7 +154,7 @@ class PromptBuilder:
|
||||
)
|
||||
|
||||
relation_prompt = ""
|
||||
if global_config.relationship.enable_relationship and who_chat_in_group:
|
||||
if global_config.affinity_flow.enable_relationship_tracking and who_chat_in_group:
|
||||
relationship_fetcher = relationship_fetcher_manager.get_fetcher(chat_stream.stream_id)
|
||||
|
||||
# 将 (platform, user_id, nickname) 转换为 person_id
|
||||
|
||||
@@ -26,7 +26,7 @@ SEGMENT_CLEANUP_CONFIG = {
|
||||
"cleanup_interval_hours": 0.5, # 清理间隔(小时)
|
||||
}
|
||||
|
||||
MAX_MESSAGE_COUNT = int(80 / global_config.relationship.relation_frequency)
|
||||
MAX_MESSAGE_COUNT = 80 # 默认消息计数,旧的relation_frequency配置已移除
|
||||
|
||||
|
||||
class RelationshipBuilder:
|
||||
|
||||
@@ -228,10 +228,18 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
||||
is_at = getattr(message, "is_at", False)
|
||||
processed_plain_text = getattr(message, "processed_plain_text", "")
|
||||
|
||||
# 判断是否为私聊
|
||||
chat_info_group_id = getattr(message, "chat_info_group_id", None)
|
||||
is_private_chat = not chat_info_group_id # 如果没有group_id则是私聊
|
||||
|
||||
logger.debug(f"[提及分计算] is_mentioned={is_mentioned}, is_at={is_at}, is_private_chat={is_private_chat}")
|
||||
|
||||
if is_mentioned:
|
||||
if is_at:
|
||||
logger.debug(f"[提及分计算] 直接@机器人,返回1.0")
|
||||
return 1.0 # 直接@机器人,最高分
|
||||
else:
|
||||
logger.debug(f"[提及分计算] 提及机器人,返回0.8")
|
||||
return 0.8 # 提及机器人名字,高分
|
||||
else:
|
||||
# 检查是否被提及(文本匹配)
|
||||
@@ -239,9 +247,14 @@ class AffinityInterestCalculator(BaseInterestCalculator):
|
||||
is_text_mentioned = any(alias in processed_plain_text for alias in bot_aliases if alias)
|
||||
|
||||
# 如果被提及或是私聊,都视为提及了bot
|
||||
if is_text_mentioned or not hasattr(message, "chat_info_group_id"):
|
||||
if is_text_mentioned:
|
||||
logger.debug(f"[提及分计算] 文本提及机器人,返回提及分")
|
||||
return global_config.affinity_flow.mention_bot_interest_score
|
||||
elif is_private_chat:
|
||||
logger.debug(f"[提及分计算] 私聊消息,返回提及分")
|
||||
return global_config.affinity_flow.mention_bot_interest_score
|
||||
else:
|
||||
logger.debug(f"[提及分计算] 未提及机器人,返回0.0")
|
||||
return 0.0 # 未提及机器人
|
||||
|
||||
def _apply_no_reply_boost(self, base_score: float) -> float:
|
||||
|
||||
@@ -184,9 +184,16 @@ class ChatterPlanExecutor:
|
||||
|
||||
# 获取用户ID - 兼容对象和字典
|
||||
if hasattr(action_info.action_message, "user_info"):
|
||||
# DatabaseMessages对象情况
|
||||
user_id = action_info.action_message.user_info.user_id
|
||||
else:
|
||||
user_id = action_info.action_message.get("user_info", {}).get("user_id")
|
||||
# 字典情况(向后兼容)- 适配扁平化消息字典结构
|
||||
# 首先尝试从扁平化结构直接获取用户信息
|
||||
user_id = action_info.action_message.get("user_id")
|
||||
|
||||
# 如果扁平化结构中没有用户信息,再尝试从嵌套的user_info获取
|
||||
if not user_id:
|
||||
user_id = action_info.action_message.get("user_info", {}).get("user_id")
|
||||
|
||||
if user_id == str(global_config.bot.qq_account):
|
||||
logger.warning("尝试回复自己,跳过此动作以防止死循环。")
|
||||
@@ -231,11 +238,19 @@ class ChatterPlanExecutor:
|
||||
except Exception as e:
|
||||
error_message = str(e)
|
||||
logger.error(f"执行回复动作失败: {action_info.action_type}, 错误: {error_message}")
|
||||
"""
|
||||
# 记录用户关系追踪
|
||||
# 记录用户关系追踪 - 使用后台异步执行,防止阻塞主流程
|
||||
if success and action_info.action_message:
|
||||
await self._track_user_interaction(action_info, plan, reply_content)
|
||||
"""
|
||||
logger.debug(f"准备执行关系追踪: success={success}, action_message存在={bool(action_info.action_message)}")
|
||||
logger.debug(f"关系追踪器状态: {self.relationship_tracker is not None}")
|
||||
|
||||
# 直接使用后台异步任务执行关系追踪,避免阻塞主回复流程
|
||||
import asyncio
|
||||
asyncio.create_task(self._track_user_interaction(action_info, plan, reply_content))
|
||||
logger.debug("关系追踪已启动为后台异步任务")
|
||||
else:
|
||||
logger.debug(f"跳过关系追踪: success={success}, action_message存在={bool(action_info.action_message)}")
|
||||
# 将机器人回复添加到已读消息中
|
||||
await self._add_bot_reply_to_read_messages(action_info, plan, reply_content)
|
||||
execution_time = time.time() - start_time
|
||||
self.execution_stats["execution_times"].append(execution_time)
|
||||
|
||||
@@ -344,29 +359,54 @@ class ChatterPlanExecutor:
|
||||
async def _track_user_interaction(self, action_info: ActionPlannerInfo, plan: Plan, reply_content: str):
|
||||
"""追踪用户交互 - 集成回复后关系追踪"""
|
||||
try:
|
||||
logger.debug("🔍 开始执行用户交互追踪")
|
||||
|
||||
if not action_info.action_message:
|
||||
logger.debug("❌ 跳过追踪:action_message为空")
|
||||
return
|
||||
|
||||
# 获取用户信息 - 处理对象和字典两种情况
|
||||
if hasattr(action_info.action_message, "user_info"):
|
||||
# 对象情况
|
||||
user_info = action_info.action_message.user_info
|
||||
user_id = user_info.user_id
|
||||
user_name = user_info.user_nickname or user_id
|
||||
user_message = action_info.action_message.content
|
||||
# 获取用户信息 - 处理DatabaseMessages对象
|
||||
if hasattr(action_info.action_message, "user_id"):
|
||||
# DatabaseMessages对象情况
|
||||
user_id = action_info.action_message.user_id
|
||||
user_name = action_info.action_message.user_nickname or user_id
|
||||
# 使用processed_plain_text作为消息内容,如果没有则使用display_message
|
||||
user_message = (
|
||||
action_info.action_message.processed_plain_text
|
||||
or action_info.action_message.display_message
|
||||
or ""
|
||||
)
|
||||
logger.debug(f"📝 从DatabaseMessages获取用户信息: user_id={user_id}, user_name={user_name}")
|
||||
else:
|
||||
# 字典情况
|
||||
user_info = action_info.action_message.get("user_info", {})
|
||||
user_id = user_info.get("user_id")
|
||||
user_name = user_info.get("user_nickname") or user_id
|
||||
user_message = action_info.action_message.get("content", "")
|
||||
# 字典情况(向后兼容)- 适配扁平化消息字典结构
|
||||
# 首先尝试从扁平化结构直接获取用户信息
|
||||
user_id = action_info.action_message.get("user_id")
|
||||
user_name = action_info.action_message.get("user_nickname") or user_id
|
||||
|
||||
# 如果扁平化结构中没有用户信息,再尝试从嵌套的user_info获取
|
||||
if not user_id:
|
||||
user_info = action_info.action_message.get("user_info", {})
|
||||
user_id = user_info.get("user_id")
|
||||
user_name = user_info.get("user_nickname") or user_id
|
||||
logger.debug(f"📝 从嵌套user_info获取用户信息: user_id={user_id}, user_name={user_name}")
|
||||
else:
|
||||
logger.debug(f"📝 从扁平化结构获取用户信息: user_id={user_id}, user_name={user_name}")
|
||||
|
||||
# 获取消息内容,优先使用processed_plain_text
|
||||
user_message = (
|
||||
action_info.action_message.get("processed_plain_text", "")
|
||||
or action_info.action_message.get("display_message", "")
|
||||
or action_info.action_message.get("content", "")
|
||||
)
|
||||
|
||||
if not user_id:
|
||||
logger.debug("跳过追踪:缺少用户ID")
|
||||
logger.debug("❌ 跳过追踪:缺少用户ID")
|
||||
return
|
||||
|
||||
# 如果有设置关系追踪器,执行回复后关系追踪
|
||||
if self.relationship_tracker:
|
||||
logger.debug(f"✅ 关系追踪器存在,开始为用户 {user_id} 执行追踪")
|
||||
|
||||
# 记录基础交互信息(保持向后兼容)
|
||||
self.relationship_tracker.add_interaction(
|
||||
user_id=user_id,
|
||||
@@ -375,19 +415,102 @@ class ChatterPlanExecutor:
|
||||
bot_reply=reply_content,
|
||||
reply_timestamp=time.time(),
|
||||
)
|
||||
logger.debug(f"📊 已添加基础交互信息: {user_name}({user_id})")
|
||||
|
||||
# 执行新的回复后关系追踪
|
||||
await self.relationship_tracker.track_reply_relationship(
|
||||
user_id=user_id, user_name=user_name, bot_reply_content=reply_content, reply_timestamp=time.time()
|
||||
)
|
||||
logger.debug(f"🎯 已执行回复后关系追踪: {user_id}")
|
||||
|
||||
logger.debug(f"已执行用户交互追踪: {user_id}")
|
||||
else:
|
||||
logger.debug("❌ 关系追踪器不存在,跳过追踪")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"追踪用户交互时出错: {e}")
|
||||
logger.debug(f"action_message类型: {type(action_info.action_message)}")
|
||||
logger.debug(f"action_message内容: {action_info.action_message}")
|
||||
|
||||
async def _add_bot_reply_to_read_messages(self, action_info: ActionPlannerInfo, plan: Plan, reply_content: str):
|
||||
"""将机器人回复添加到已读消息中"""
|
||||
try:
|
||||
if not reply_content or not plan.chat_id:
|
||||
logger.debug("跳过添加已读消息:回复内容为空或缺少chat_id")
|
||||
return
|
||||
|
||||
# 获取chat_stream对象
|
||||
from src.plugin_system.apis.chat_api import get_chat_manager
|
||||
|
||||
chat_manager = get_chat_manager()
|
||||
chat_stream = await chat_manager.get_stream(plan.chat_id)
|
||||
|
||||
if not chat_stream:
|
||||
logger.warning(f"无法获取chat_stream: {plan.chat_id}")
|
||||
return
|
||||
|
||||
# 构建机器人回复的DatabaseMessages对象
|
||||
from src.common.data_models.database_data_model import DatabaseMessages
|
||||
|
||||
current_time = time.time()
|
||||
|
||||
# 构建用户信息
|
||||
bot_user_id = str(global_config.bot.qq_account)
|
||||
bot_nickname = global_config.bot.nickname
|
||||
|
||||
# 创建机器人回复消息
|
||||
bot_message = DatabaseMessages(
|
||||
message_id=f"bot_reply_{int(current_time * 1000)}", # 生成唯一ID
|
||||
time=current_time,
|
||||
chat_id=plan.chat_id,
|
||||
reply_to=None, # 不是回复消息
|
||||
interest_value=None, # 机器人回复不需要兴趣值
|
||||
processed_plain_text=reply_content,
|
||||
display_message=reply_content,
|
||||
is_read=True, # 标记为已读
|
||||
is_emoji=False,
|
||||
is_picid=False,
|
||||
is_command=False,
|
||||
is_notify=False,
|
||||
|
||||
# 用户信息
|
||||
user_id=bot_user_id,
|
||||
user_nickname=bot_nickname,
|
||||
user_cardname=bot_nickname,
|
||||
user_platform="qq",
|
||||
|
||||
# 聊天上下文信息
|
||||
chat_info_user_id=chat_stream.user_info.user_id if chat_stream.user_info else bot_user_id,
|
||||
chat_info_user_nickname=chat_stream.user_info.user_nickname if chat_stream.user_info else bot_nickname,
|
||||
chat_info_user_cardname=chat_stream.user_info.user_cardname if chat_stream.user_info else bot_nickname,
|
||||
chat_info_user_platform=chat_stream.platform,
|
||||
chat_info_stream_id=chat_stream.stream_id,
|
||||
chat_info_platform=chat_stream.platform,
|
||||
chat_info_create_time=chat_stream.create_time,
|
||||
chat_info_last_active_time=chat_stream.last_active_time,
|
||||
|
||||
# 群组信息(如果是群聊)
|
||||
chat_info_group_id=chat_stream.group_info.group_id if chat_stream.group_info else None,
|
||||
chat_info_group_name=chat_stream.group_info.group_name if chat_stream.group_info else None,
|
||||
chat_info_group_platform=chat_stream.group_info.group_platform if chat_stream.group_info else None,
|
||||
|
||||
# 动作信息
|
||||
actions=["bot_reply"],
|
||||
should_reply=False,
|
||||
should_act=False
|
||||
)
|
||||
|
||||
# 添加到chat_stream的已读消息中
|
||||
if hasattr(chat_stream, 'stream_context') and chat_stream.stream_context:
|
||||
chat_stream.stream_context.history_messages.append(bot_message)
|
||||
logger.debug(f"机器人回复已添加到已读消息: {reply_content[:50]}...")
|
||||
else:
|
||||
logger.warning("chat_stream没有stream_context,无法添加已读消息")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"添加机器人回复到已读消息时出错: {e}")
|
||||
logger.debug(f"plan.chat_id: {plan.chat_id}")
|
||||
logger.debug(f"reply_content: {reply_content[:100] if reply_content else 'None'}")
|
||||
|
||||
def get_execution_stats(self) -> dict[str, Any]:
|
||||
"""获取执行统计信息"""
|
||||
stats = self.execution_stats.copy()
|
||||
|
||||
@@ -50,6 +50,16 @@ class ChatterActionPlanner:
|
||||
self.generator = ChatterPlanGenerator(chat_id)
|
||||
self.executor = ChatterPlanExecutor(action_manager)
|
||||
|
||||
# 初始化关系追踪器
|
||||
if global_config.affinity_flow.enable_relationship_tracking:
|
||||
from .relationship_tracker import ChatterRelationshipTracker
|
||||
self.relationship_tracker = ChatterRelationshipTracker()
|
||||
self.executor.set_relationship_tracker(self.relationship_tracker)
|
||||
logger.info(f"关系追踪器已初始化 (chat_id: {chat_id})")
|
||||
else:
|
||||
self.relationship_tracker = None
|
||||
logger.info(f"关系系统已禁用,跳过关系追踪器初始化 (chat_id: {chat_id})")
|
||||
|
||||
# 使用新的统一兴趣度管理系统
|
||||
|
||||
# 规划器统计
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
支持数据库持久化存储和回复后自动关系更新
|
||||
"""
|
||||
|
||||
import random
|
||||
import time
|
||||
|
||||
from sqlalchemy import desc, select
|
||||
@@ -142,26 +143,40 @@ class ChatterRelationshipTracker:
|
||||
1. 加分必须符合现实关系发展逻辑 - 不能因为对方态度好就盲目加分到不符合当前关系档次的分数
|
||||
2. 关系提升需要足够的互动积累和时间验证
|
||||
3. 即使是朋友关系,单次互动加分通常不超过0.05-0.1
|
||||
4. 关系描述要详细具体,包括:
|
||||
- 用户性格特点观察
|
||||
- 印象深刻的互动记忆
|
||||
- 你们关系的具体状态描述
|
||||
4. 人物印象描述应该是泛化的、整体的理解,从你的视角对用户整体性格特质的描述:
|
||||
- 描述用户的整体性格特点(如:温柔、幽默、理性、感性等)
|
||||
- 用户给你的整体感觉和印象
|
||||
- 你们关系的整体状态和氛围
|
||||
- 避免描述具体事件或对话内容,而是基于这些事件形成的整体认知
|
||||
|
||||
根据你的人设性格,思考:
|
||||
1. 以你的性格,你会如何看待这次互动?
|
||||
2. 用户的行为是否符合你性格的喜好?
|
||||
3. 这次互动是否真的让你们的关系提升了一个档次?为什么?
|
||||
4. 有什么特别值得记住的互动细节?
|
||||
1. 从你的性格视角,这个用户给你什么样的整体印象?
|
||||
2. 用户的性格特质和行为模式是否符合你的喜好?
|
||||
3. 基于这次互动,你对用户的整体认知有什么变化?
|
||||
4. 这个用户在你心中的整体形象是怎样的?
|
||||
|
||||
请以JSON格式返回更新结果:
|
||||
{{
|
||||
"new_relationship_score": 0.0~1.0的数值(必须符合现实逻辑),
|
||||
"reasoning": "从你的性格角度说明更新理由,重点说明是否符合现实关系发展逻辑",
|
||||
"interaction_summary": "基于你性格的交互总结,包含印象深刻的互动记忆"
|
||||
"interaction_summary": "基于你性格的用户整体印象描述,包含用户的整体性格特质、给你的整体感觉,避免具体事件描述"
|
||||
}}
|
||||
"""
|
||||
|
||||
llm_response, _ = await self.relationship_llm.generate_response_async(prompt=prompt)
|
||||
# 调用LLM进行分析 - 添加超时保护
|
||||
import asyncio
|
||||
try:
|
||||
llm_response, _ = await asyncio.wait_for(
|
||||
self.relationship_llm.generate_response_async(prompt=prompt),
|
||||
timeout=30.0 # 30秒超时
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"初次见面LLM调用超时: user_id={user_id}, 跳过此次追踪")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error(f"初次见面LLM调用失败: user_id={user_id}, 错误: {e}")
|
||||
return
|
||||
|
||||
if llm_response:
|
||||
import json
|
||||
|
||||
@@ -382,15 +397,35 @@ class ChatterRelationshipTracker:
|
||||
):
|
||||
"""回复后关系追踪 - 主要入口点"""
|
||||
try:
|
||||
logger.info(f"🔄 [RelationshipTracker] 开始回复后关系追踪: {user_id}")
|
||||
# 首先检查是否启用关系追踪
|
||||
if not global_config.affinity_flow.enable_relationship_tracking:
|
||||
logger.debug(f"🚫 [RelationshipTracker] 关系追踪系统已禁用,跳过用户 {user_id}")
|
||||
return
|
||||
|
||||
# 检查上次追踪时间
|
||||
last_tracked_time = self._get_last_tracked_time(user_id)
|
||||
# 概率筛选 - 减少API调用压力
|
||||
tracking_probability = global_config.affinity_flow.relationship_tracking_probability
|
||||
if random.random() > tracking_probability:
|
||||
logger.debug(
|
||||
f"🎲 [RelationshipTracker] 概率筛选未通过 ({tracking_probability:.2f}),跳过用户 {user_id} 的关系追踪"
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(f"🔄 [RelationshipTracker] 开始回复后关系追踪: {user_id} (概率通过: {tracking_probability:.2f})")
|
||||
|
||||
# 检查上次追踪时间 - 使用配置的冷却时间
|
||||
last_tracked_time = await self._get_last_tracked_time(user_id)
|
||||
cooldown_hours = global_config.affinity_flow.relationship_tracking_cooldown_hours
|
||||
cooldown_seconds = cooldown_hours * 3600
|
||||
time_diff = reply_timestamp - last_tracked_time
|
||||
|
||||
if time_diff < 5 * 60: # 5分钟内不重复追踪
|
||||
# 使用配置的最小间隔时间
|
||||
min_interval = global_config.affinity_flow.relationship_tracking_interval_min
|
||||
required_interval = max(min_interval, cooldown_seconds)
|
||||
|
||||
if time_diff < required_interval:
|
||||
logger.debug(
|
||||
f"⏱️ [RelationshipTracker] 用户 {user_id} 距离上次追踪时间不足5分钟 ({time_diff:.2f}s),跳过"
|
||||
f"⏱️ [RelationshipTracker] 用户 {user_id} 距离上次追踪时间不足 {required_interval/60:.1f} 分钟 "
|
||||
f"(实际: {time_diff/60:.1f} 分钟),跳过"
|
||||
)
|
||||
return
|
||||
|
||||
@@ -563,30 +598,42 @@ class ChatterRelationshipTracker:
|
||||
1. 加分必须符合现实关系发展逻辑 - 不能因为用户反应好就盲目加分
|
||||
2. 关系提升需要足够的互动积累和时间验证,单次互动加分通常不超过0.05-0.1
|
||||
3. 必须考虑当前关系档次,不能跳跃式提升(比如从0.3直接到0.7)
|
||||
4. 关系印象描述要详细具体(100-200字),包括:
|
||||
- 用户性格特点和交流风格观察
|
||||
- 印象深刻的互动记忆和对话片段
|
||||
- 你们关系的具体状态描述和发展阶段
|
||||
- 根据你的性格,你对用户的真实感受
|
||||
4. 人物印象描述应该是泛化的、整体的理解(100-200字),从你的视角对用户整体性格特质的描述:
|
||||
- 描述用户的整体性格特点和行为模式(如:温柔体贴、幽默风趣、理性稳重等)
|
||||
- 用户给你的整体感觉和印象氛围
|
||||
- 你们关系的整体状态和发展阶段
|
||||
- 基于所有互动形成的用户整体形象认知
|
||||
- 避免提及具体事件或对话内容,而是总结形成的整体印象
|
||||
|
||||
性格视角深度分析:
|
||||
1. 以你的性格特点,用户这次的反应给你什么感受?
|
||||
2. 用户的情绪和行为符合你性格的喜好吗?具体哪些方面?
|
||||
1. 从你的性格视角,基于这次互动,你对用户的整体印象有什么新的认识?
|
||||
2. 用户的整体性格特质和行为模式符合你的喜好吗?
|
||||
3. 从现实角度看,这次互动是否足以让关系提升到下一个档次?为什么?
|
||||
4. 有什么特别值得记住的互动细节或对话内容?
|
||||
5. 基于你们的互动历史,用户给你留下了哪些深刻印象?
|
||||
4. 基于你们的互动历史,用户在你心中的整体形象是怎样的?
|
||||
5. 这个用户给你带来的整体感受和情绪体验是怎样的?
|
||||
|
||||
请以JSON格式返回更新结果:
|
||||
{{
|
||||
"relationship_text": "详细的关系印象描述(100-200字),包含用户性格观察、印象深刻记忆、关系状态描述",
|
||||
"relationship_text": "泛化的用户整体印象描述(100-200字),包含用户的整体性格特质、给你的整体感觉和印象氛围,避免具体事件描述",
|
||||
"relationship_score": 0.0~1.0的新分数(必须严格符合现实逻辑),
|
||||
"analysis_reasoning": "从你性格角度的深度分析,重点说明分数调整的现实合理性",
|
||||
"interaction_quality": "high/medium/low"
|
||||
}}
|
||||
"""
|
||||
|
||||
# 调用LLM进行分析
|
||||
llm_response, _ = await self.relationship_llm.generate_response_async(prompt=prompt)
|
||||
# 调用LLM进行分析 - 添加超时保护
|
||||
import asyncio
|
||||
try:
|
||||
llm_response, _ = await asyncio.wait_for(
|
||||
self.relationship_llm.generate_response_async(prompt=prompt),
|
||||
timeout=30.0 # 30秒超时
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"关系追踪LLM调用超时: user_id={user_id}, 跳过此次追踪")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error(f"关系追踪LLM调用失败: user_id={user_id}, 错误: {e}")
|
||||
return
|
||||
|
||||
if llm_response:
|
||||
import json
|
||||
@@ -650,7 +697,15 @@ class ChatterRelationshipTracker:
|
||||
async def _handle_first_interaction(self, user_id: str, user_name: str, bot_reply_content: str):
|
||||
"""处理与用户的初次交互"""
|
||||
try:
|
||||
logger.info(f"✨ [RelationshipTracker] 正在处理与用户 {user_id} 的初次交互")
|
||||
# 初次交互也进行概率检查,但使用更高的通过率
|
||||
first_interaction_probability = min(1.0, global_config.affinity_flow.relationship_tracking_probability * 1.5)
|
||||
if random.random() > first_interaction_probability:
|
||||
logger.debug(
|
||||
f"🎲 [RelationshipTracker] 初次交互概率筛选未通过 ({first_interaction_probability:.2f}),跳过用户 {user_id}"
|
||||
)
|
||||
return
|
||||
|
||||
logger.info(f"✨ [RelationshipTracker] 正在处理与用户 {user_id} 的初次交互 (概率通过: {first_interaction_probability:.2f})")
|
||||
|
||||
# 获取bot人设信息
|
||||
from src.individuality.individuality import Individuality
|
||||
@@ -671,14 +726,15 @@ class ChatterRelationshipTracker:
|
||||
|
||||
【严格要求】:
|
||||
1. 建立一个初始关系分数,通常在0.2-0.4之间(普通网友)。
|
||||
2. 关系印象描述要简洁地记录你对用户的初步看法(50-100字)。
|
||||
- 用户名给你的感觉?
|
||||
- 你的回复是基于什么考虑?
|
||||
- 你对接下来与TA的互动有什么期待?
|
||||
2. 初始关系印象描述要简洁地记录你对用户的整体初步看法(50-100字):
|
||||
- 基于用户名和初次互动,用户给你的整体感觉
|
||||
- 你感受到的用户整体性格特质倾向
|
||||
- 你对与这个用户建立关系的整体期待和感觉
|
||||
- 避免描述具体的事件细节,而是整体的直觉印象
|
||||
|
||||
请以JSON格式返回结果:
|
||||
{{
|
||||
"relationship_text": "简洁的初始关系印象描述(50-100字)",
|
||||
"relationship_text": "简洁的用户整体初始印象描述(50-100字),基于第一印象的整体性格感觉",
|
||||
"relationship_score": 0.2~0.4的新分数,
|
||||
"analysis_reasoning": "从你性格角度说明建立此初始印象的理由"
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[inner]
|
||||
version = "7.2.4"
|
||||
version = "7.2.6"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了MoFox-Bot,不需要阅读----
|
||||
#如果你想要修改配置文件,请递增version的值
|
||||
@@ -126,13 +126,6 @@ thinking_timeout = 40 # MoFox-Bot一次回复最长思考规划时间,超过
|
||||
interruption_enabled = true # 是否启用消息打断系统
|
||||
interruption_max_limit = 5 # 每个聊天流的最大打断次数
|
||||
interruption_min_probability = 0.05 # 最低打断概率(反比例函数趋近的下限值)
|
||||
interruption_afc_reduction = 0.05 # 每次连续打断降低的afc阈值数值
|
||||
|
||||
# DEPRECATED: interruption_probability_factor (已废弃的配置项)
|
||||
# 新的反比例函数概率模型不再需要复杂的概率因子
|
||||
# 公式:打断概率 = 1.4 / (当前打断次数 + 2.0) + 最低概率
|
||||
# 特性:第1次80% → 第2次35% → 第3次15% → 趋近于最低概率
|
||||
# interruption_probability_factor = 0.8 # 此配置已废弃,请删除
|
||||
|
||||
# 动态消息分发系统配置
|
||||
dynamic_distribution_enabled = true # 是否启用动态消息分发周期调整
|
||||
@@ -143,10 +136,6 @@ dynamic_distribution_jitter_factor = 0.2 # 分发间隔随机扰动因子
|
||||
max_concurrent_distributions = 10 # 最大并发处理的消息流数量,可以根据API性能和服务器负载调整
|
||||
|
||||
|
||||
[relationship]
|
||||
enable_relationship = true # 是否启用关系系统
|
||||
relation_frequency = 1 # 关系频率,MoFox-Bot构建关系的频率
|
||||
|
||||
|
||||
[message_receive]
|
||||
# 以下是消息过滤,可以根据规则过滤特定消息,将不会读取这些消息
|
||||
@@ -554,21 +543,21 @@ chat_ids = [
|
||||
|
||||
[affinity_flow]
|
||||
# 兴趣评分系统参数
|
||||
reply_action_interest_threshold = 0.62 # 回复动作兴趣阈值
|
||||
non_reply_action_interest_threshold = 0.48 # 非回复动作兴趣阈值
|
||||
high_match_interest_threshold = 0.6 # 高匹配兴趣阈值
|
||||
reply_action_interest_threshold = 1.1 # 回复动作兴趣阈值
|
||||
non_reply_action_interest_threshold = 0.9 # 非回复动作兴趣阈值
|
||||
high_match_interest_threshold = 0.7 # 高匹配兴趣阈值
|
||||
medium_match_interest_threshold = 0.4 # 中匹配兴趣阈值
|
||||
low_match_interest_threshold = 0.2 # 低匹配兴趣阈值
|
||||
high_match_keyword_multiplier = 4.5 # 高匹配关键词兴趣倍率
|
||||
medium_match_keyword_multiplier = 2.75 # 中匹配关键词兴趣倍率
|
||||
low_match_keyword_multiplier = 1.15 # 低匹配关键词兴趣倍率
|
||||
high_match_keyword_multiplier = 5 # 高匹配关键词兴趣倍率
|
||||
medium_match_keyword_multiplier = 3.75 # 中匹配关键词兴趣倍率
|
||||
low_match_keyword_multiplier = 1.3 # 低匹配关键词兴趣倍率
|
||||
match_count_bonus = 0.02 # 匹配数关键词加成值
|
||||
max_match_bonus = 0.25 # 最大匹配数加成值
|
||||
|
||||
# 回复决策系统参数
|
||||
no_reply_threshold_adjustment = 0.03 # 不回复兴趣阈值调整值
|
||||
reply_cooldown_reduction = 3 # 回复后减少的不回复计数
|
||||
max_no_reply_count = 5 # 最大不回复计数次数
|
||||
no_reply_threshold_adjustment = 0.01 # 不回复兴趣阈值调整值
|
||||
reply_cooldown_reduction = 5 # 回复后减少的不回复计数
|
||||
max_no_reply_count = 20 # 最大不回复计数次数
|
||||
|
||||
# 综合评分权重
|
||||
keyword_match_weight = 0.4 # 兴趣关键词匹配度权重
|
||||
@@ -576,10 +565,16 @@ mention_bot_weight = 0.3 # 提及bot分数权重
|
||||
relationship_weight = 0.3 # 人物关系分数权重
|
||||
|
||||
# 提及bot相关参数
|
||||
mention_bot_adjustment_threshold = 0.3 # 提及bot后的调整阈值
|
||||
mention_bot_interest_score = 0.6 # 提及bot的兴趣分
|
||||
mention_bot_adjustment_threshold = 0.5 # 提及bot后的调整阈值
|
||||
mention_bot_interest_score = 2.5 # 提及bot的兴趣分
|
||||
base_relationship_score = 0.3 # 基础人物关系分
|
||||
|
||||
# 关系追踪系统参数
|
||||
enable_relationship_tracking = true # 是否启用关系追踪系统
|
||||
relationship_tracking_probability = 0.7 # 关系追踪执行概率 (0.0-1.0),用于减少API调用压力
|
||||
relationship_tracking_interval_min = 300 # 关系追踪最小间隔时间(秒)
|
||||
relationship_tracking_cooldown_hours = 1.0 # 同一用户关系追踪冷却时间(小时)
|
||||
|
||||
[proactive_thinking] # 主动思考(主动发起对话)功能配置
|
||||
# --- 总开关 ---
|
||||
enable = true # 是否启用主动发起对话功能
|
||||
|
||||
Reference in New Issue
Block a user