refactor(chat): 重构关系系统并优化消息打断处理机制

- 移除独立的RelationshipConfig,将关系追踪参数整合到AffinityFlowConfig
- 实现消息打断后立即重新处理流程,提升交互响应性
- 优化关系追踪系统,添加概率筛选和超时保护机制
- 改进机器人自引用处理,确保消息内容正确显示
- 增强用户信息提取逻辑,兼容多种消息格式
- 添加异步后台任务处理,避免阻塞主回复流程
- 调整兴趣评分阈值和权重参数,优化消息匹配精度
This commit is contained in:
Windpicker-owo
2025-10-08 22:33:10 +08:00
parent b3ae8e4f1a
commit dd1444cc41
13 changed files with 368 additions and 107 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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"""

View File

@@ -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="普通聊天配置")

View File

@@ -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):
"""主动思考(主动发起对话)功能配置"""

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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()

View File

@@ -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})")
# 使用新的统一兴趣度管理系统
# 规划器统计

View File

@@ -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": "从你性格角度说明建立此初始印象的理由"
}}

View File

@@ -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 # 是否启用主动发起对话功能