mirror of https://github.com/Mai-with-u/MaiBot.git
Merge branch 'PFC-test' of https://github.com/Plutor-05/MaiBot.git into PFC-test
commit
18f3864693
|
|
@ -6,56 +6,6 @@ from types import ModuleType
|
|||
from pathlib import Path
|
||||
from dotenv import load_dotenv
|
||||
|
||||
"""
|
||||
日志颜色说明:
|
||||
|
||||
1. 主程序(Main)
|
||||
浅黄色标题 | 浅黄色消息
|
||||
|
||||
2. 海马体(Memory)
|
||||
浅黄色标题 | 浅黄色消息
|
||||
|
||||
3. PFC(前额叶皮质)
|
||||
浅绿色标题 | 浅绿色消息
|
||||
|
||||
4. 心情(Mood)
|
||||
品红色标题 | 品红色消息
|
||||
|
||||
5. 工具使用(Tool)
|
||||
品红色标题 | 品红色消息
|
||||
|
||||
6. 关系(Relation)
|
||||
浅品红色标题 | 浅品红色消息
|
||||
|
||||
7. 配置(Config)
|
||||
浅青色标题 | 浅青色消息
|
||||
|
||||
8. 麦麦大脑袋
|
||||
浅绿色标题 | 浅绿色消息
|
||||
|
||||
9. 在干嘛
|
||||
青色标题 | 青色消息
|
||||
|
||||
10. 麦麦组织语言
|
||||
浅绿色标题 | 浅绿色消息
|
||||
|
||||
11. 见闻(Chat)
|
||||
浅蓝色标题 | 绿色消息
|
||||
|
||||
12. 表情包(Emoji)
|
||||
橙色标题 | 橙色消息 fg #FFD700
|
||||
|
||||
13. 子心流
|
||||
|
||||
13. 其他模块
|
||||
模块名标题 | 对应颜色消息
|
||||
|
||||
|
||||
注意:
|
||||
1. 级别颜色遵循loguru默认配置
|
||||
2. 可通过环境变量修改日志级别
|
||||
"""
|
||||
|
||||
|
||||
# 加载 .env 文件
|
||||
env_path = Path(__file__).resolve().parent.parent.parent / ".env"
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ class BotConfig:
|
|||
enable_pfc_reply_checker: bool = True # 是否开启PFC回复检查
|
||||
|
||||
# idle_conversation
|
||||
enable_idle_conversation: bool = False # 是否启用 pfc 主动发言(未完善)
|
||||
enable_idle_conversation: bool = False # 是否启用 pfc 主动发言
|
||||
idle_check_interval: int = 10 # 检查间隔,10分钟检查一次
|
||||
min_idle_time: int = 7200 # 最短无活动时间,2小时 (7200秒)
|
||||
max_idle_time: int = 18000 # 最长无活动时间,5小时 (18000秒)
|
||||
|
|
@ -493,7 +493,6 @@ class BotConfig:
|
|||
"llm_heartflow",
|
||||
"llm_PFC_action_planner",
|
||||
"llm_PFC_chat",
|
||||
"llm_PFC_reply_checker",
|
||||
"llm_PFC_relationship_eval",
|
||||
]
|
||||
|
||||
|
|
@ -666,7 +665,7 @@ class BotConfig:
|
|||
config.talk_allowed_private = set(str(user) for user in experimental_config.get("talk_allowed_private", []))
|
||||
if config.INNER_VERSION in SpecifierSet(">=1.1.0"):
|
||||
config.enable_pfc_chatting = experimental_config.get("pfc_chatting", config.enable_pfc_chatting)
|
||||
if config.INNER_VERSION in SpecifierSet(">=1.1.0"):
|
||||
if config.INNER_VERSION in SpecifierSet(">=1.6.2"):
|
||||
config.enable_pfc_reply_checker = experimental_config.get(
|
||||
"enable_pfc_reply_checker", config.enable_pfc_reply_checker
|
||||
)
|
||||
|
|
@ -674,7 +673,7 @@ class BotConfig:
|
|||
|
||||
def idle_conversation(parent: dict):
|
||||
idle_conversation_config = parent["idle_conversation"]
|
||||
if config.INNER_VERSION in SpecifierSet(">=1.6.3"):
|
||||
if config.INNER_VERSION in SpecifierSet(">=1.6.2"):
|
||||
config.enable_idle_conversation = idle_conversation_config.get(
|
||||
"enable_idle_conversation", config.enable_idle_conversation
|
||||
)
|
||||
|
|
@ -717,7 +716,7 @@ class BotConfig:
|
|||
"chat": {"func": chat, "support": ">=1.6.0", "necessary": False},
|
||||
"normal_chat": {"func": normal_chat, "support": ">=1.6.0", "necessary": False},
|
||||
"focus_chat": {"func": focus_chat, "support": ">=1.6.0", "necessary": False},
|
||||
"idle_conversation": {"func": idle_conversation, "support": ">=1.6.3", "necessary": False},
|
||||
"idle_conversation": {"func": idle_conversation, "support": ">=1.6.2", "necessary": False},
|
||||
}
|
||||
|
||||
# 原地修改,将 字符串版本表达式 转换成 版本对象
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Optional, Dict, List, Set
|
||||
from typing import Optional, Dict, Set
|
||||
import asyncio
|
||||
import time
|
||||
import random
|
||||
|
|
@ -7,11 +7,10 @@ from datetime import datetime
|
|||
from src.common.logger_manager import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.plugins.models.utils_model import LLMRequest
|
||||
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
from src.plugins.utils.prompt_builder import global_prompt_manager
|
||||
from src.plugins.person_info.person_info import person_info_manager
|
||||
from src.plugins.utils.chat_message_builder import build_readable_messages
|
||||
from ...schedule.schedule_generator import bot_schedule
|
||||
from ....config.config import global_config
|
||||
from ..chat_observer import ChatObserver
|
||||
from ..message_sender import DirectMessageSender
|
||||
from src.plugins.chat.chat_stream import ChatStream
|
||||
|
|
@ -23,32 +22,33 @@ install(extra_lines=3)
|
|||
|
||||
logger = get_logger("pfc_idle_chat")
|
||||
|
||||
|
||||
class IdleChat:
|
||||
"""主动聊天组件(测试中)
|
||||
|
||||
|
||||
在以下条件都满足时触发主动聊天:
|
||||
1. 当前没有任何活跃的对话实例
|
||||
2. 在指定的活动时间内(7:00-23:00)
|
||||
3. 根据关系值动态调整触发概率
|
||||
4. 上次触发后已经过了足够的冷却时间
|
||||
"""
|
||||
|
||||
|
||||
# 单例模式实现
|
||||
_instances: Dict[str, 'IdleChat'] = {}
|
||||
|
||||
_instances: Dict[str, "IdleChat"] = {}
|
||||
|
||||
# 全局共享状态,用于跟踪未回复的用户
|
||||
_pending_replies: Dict[str, float] = {} # 用户名 -> 发送时间
|
||||
_tried_users: Set[str] = set() # 已尝试过的用户集合
|
||||
_global_lock = asyncio.Lock() # 保护共享状态的全局锁
|
||||
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, stream_id: str, private_name: str) -> 'IdleChat':
|
||||
def get_instance(cls, stream_id: str, private_name: str) -> "IdleChat":
|
||||
"""获取IdleChat实例(单例模式)
|
||||
|
||||
|
||||
Args:
|
||||
stream_id: 聊天流ID
|
||||
private_name: 私聊用户名称
|
||||
|
||||
|
||||
Returns:
|
||||
IdleChat: IdleChat实例
|
||||
"""
|
||||
|
|
@ -59,13 +59,13 @@ class IdleChat:
|
|||
cls._instances[key].start()
|
||||
logger.info(f"[私聊][{private_name}]创建新的IdleChat实例并启动")
|
||||
return cls._instances[key]
|
||||
|
||||
|
||||
@classmethod
|
||||
async def register_user_response(cls, private_name: str) -> None:
|
||||
"""注册用户已回复
|
||||
|
||||
|
||||
当用户回复消息时调用此方法,将用户从待回复列表中移除
|
||||
|
||||
|
||||
Args:
|
||||
private_name: 私聊用户名称
|
||||
"""
|
||||
|
|
@ -73,52 +73,52 @@ class IdleChat:
|
|||
if private_name in cls._pending_replies:
|
||||
del cls._pending_replies[private_name]
|
||||
logger.info(f"[私聊][{private_name}]已回复主动聊天消息,从待回复列表中移除")
|
||||
|
||||
|
||||
@classmethod
|
||||
async def get_next_available_user(cls) -> Optional[str]:
|
||||
"""获取下一个可用于主动聊天的用户
|
||||
|
||||
|
||||
优先选择未尝试过的用户,其次是已尝试但超时未回复的用户
|
||||
|
||||
|
||||
Returns:
|
||||
Optional[str]: 下一个可用的用户名,如果没有则返回None
|
||||
"""
|
||||
async with cls._global_lock:
|
||||
current_time = time.time()
|
||||
timeout_threshold = 7200 # 2小时未回复视为超时
|
||||
|
||||
|
||||
# 清理超时未回复的用户
|
||||
for user, send_time in list(cls._pending_replies.items()):
|
||||
if current_time - send_time > timeout_threshold:
|
||||
logger.info(f"[私聊][{user}]超过{timeout_threshold}秒未回复,标记为超时")
|
||||
del cls._pending_replies[user]
|
||||
|
||||
|
||||
# 获取所有实例中的用户
|
||||
all_users = set()
|
||||
for key in cls._instances:
|
||||
user = key.split(':', 1)[0]
|
||||
user = key.split(":", 1)[0]
|
||||
all_users.add(user)
|
||||
|
||||
|
||||
# 优先选择未尝试过的用户
|
||||
untried_users = all_users - cls._tried_users
|
||||
if untried_users:
|
||||
next_user = random.choice(list(untried_users))
|
||||
cls._tried_users.add(next_user)
|
||||
return next_user
|
||||
|
||||
|
||||
# 如果所有用户都已尝试过,重置尝试集合,从头开始
|
||||
if len(cls._tried_users) >= len(all_users):
|
||||
cls._tried_users.clear()
|
||||
logger.info(f"[私聊]所有用户都已尝试过,重置尝试列表")
|
||||
logger.info("[私聊]所有用户都已尝试过,重置尝试列表")
|
||||
# 随机选择一个不在待回复列表中的用户
|
||||
available_users = all_users - set(cls._pending_replies.keys())
|
||||
if available_users:
|
||||
next_user = random.choice(list(available_users))
|
||||
cls._tried_users.add(next_user)
|
||||
return next_user
|
||||
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def __init__(self, stream_id: str, private_name: str):
|
||||
"""初始化主动聊天组件
|
||||
|
||||
|
|
@ -130,24 +130,19 @@ class IdleChat:
|
|||
self.private_name = private_name
|
||||
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
||||
self.message_sender = DirectMessageSender(private_name)
|
||||
|
||||
|
||||
# 添加异步锁,保护对共享变量的访问
|
||||
self._lock: asyncio.Lock = asyncio.Lock()
|
||||
|
||||
|
||||
# LLM请求对象,用于生成主动对话内容
|
||||
self.llm = LLMRequest(
|
||||
model=global_config.llm_normal,
|
||||
temperature=0.5,
|
||||
max_tokens=500,
|
||||
request_type="idle_chat"
|
||||
)
|
||||
|
||||
self.llm = LLMRequest(model=global_config.llm_normal, temperature=0.5, max_tokens=500, request_type="idle_chat")
|
||||
|
||||
# 工作状态
|
||||
self.active_instances_count: int = 0
|
||||
self.last_trigger_time: float = time.time() - 1500 # 初始化时减少等待时间
|
||||
self._running: bool = False
|
||||
self._task: Optional[asyncio.Task] = None
|
||||
|
||||
|
||||
# 配置参数 - 从global_config加载
|
||||
self.min_cooldown = getattr(global_config, "min_idle_time", 7200) # 最短冷却时间(默认2小时)
|
||||
self.max_cooldown = getattr(global_config, "max_idle_time", 18000) # 最长冷却时间(默认5小时)
|
||||
|
|
@ -157,71 +152,70 @@ class IdleChat:
|
|||
|
||||
# 关系值相关
|
||||
self.base_trigger_probability = 0.3 # 基础触发概率
|
||||
self.relationship_factor = 0.0003 # 关系值影响因子
|
||||
|
||||
self.relationship_factor = 0.0003 # 关系值影响因子
|
||||
|
||||
def start(self) -> None:
|
||||
"""启动主动聊天检测"""
|
||||
# 检查是否启用了主动聊天功能
|
||||
if not getattr(global_config, "ENABLE_IDLE_CONVERSATION", False):
|
||||
logger.info(f"[私聊][{self.private_name}]主动聊天功能已禁用(配置ENABLE_IDLE_CONVERSATION=False)")
|
||||
return
|
||||
|
||||
|
||||
if self._running:
|
||||
logger.debug(f"[私聊][{self.private_name}]主动聊天功能已在运行中")
|
||||
return
|
||||
|
||||
|
||||
self._running = True
|
||||
self._task = asyncio.create_task(self._check_idle_loop())
|
||||
logger.info(f"[私聊][{self.private_name}]启动主动聊天检测")
|
||||
|
||||
|
||||
def stop(self) -> None:
|
||||
"""停止主动聊天检测
|
||||
"""
|
||||
"""停止主动聊天检测"""
|
||||
if not self._running:
|
||||
return
|
||||
|
||||
|
||||
self._running = False
|
||||
if self._task:
|
||||
self._task.cancel()
|
||||
self._task = None
|
||||
logger.info(f"[私聊][{self.private_name}]停止主动聊天检测")
|
||||
|
||||
|
||||
async def increment_active_instances(self) -> None:
|
||||
"""增加活跃实例计数
|
||||
|
||||
|
||||
当创建新的对话实例时调用此方法
|
||||
"""
|
||||
async with self._lock:
|
||||
self.active_instances_count += 1
|
||||
logger.debug(f"[私聊][{self.private_name}]活跃实例数+1,当前:{self.active_instances_count}")
|
||||
|
||||
|
||||
async def decrement_active_instances(self) -> None:
|
||||
"""减少活跃实例计数
|
||||
|
||||
|
||||
当对话实例结束时调用此方法
|
||||
"""
|
||||
async with self._lock:
|
||||
self.active_instances_count = max(0, self.active_instances_count - 1)
|
||||
logger.debug(f"[私聊][{self.private_name}]活跃实例数-1,当前:{self.active_instances_count}")
|
||||
|
||||
|
||||
async def update_last_message_time(self, message_time: Optional[float] = None) -> None:
|
||||
"""更新最后一条消息的时间
|
||||
|
||||
|
||||
Args:
|
||||
message_time: 消息时间戳,如果为None则使用当前时间
|
||||
"""
|
||||
async with self._lock:
|
||||
self.last_trigger_time = message_time or time.time()
|
||||
logger.debug(f"[私聊][{self.private_name}]更新最后消息时间: {self.last_trigger_time:.2f}")
|
||||
|
||||
|
||||
# 当用户发送消息时,也应该注册响应
|
||||
await self.__class__.register_user_response(self.private_name)
|
||||
|
||||
|
||||
def _is_active_hours(self) -> bool:
|
||||
"""检查是否在活动时间内"""
|
||||
current_hour = datetime.now().hour
|
||||
return self.active_hours_start <= current_hour < self.active_hours_end
|
||||
|
||||
|
||||
async def _should_trigger(self) -> bool:
|
||||
"""检查是否应该触发主动聊天"""
|
||||
async with self._lock:
|
||||
|
|
@ -229,25 +223,27 @@ class IdleChat:
|
|||
if self.active_instances_count < 0:
|
||||
logger.warning(f"[私聊][{self.private_name}]检测到活跃实例数为负数,重置为0")
|
||||
self.active_instances_count = 0
|
||||
|
||||
|
||||
# 检查是否有活跃实例
|
||||
if self.active_instances_count > 0:
|
||||
logger.debug(f"[私聊][{self.private_name}]存在活跃实例({self.active_instances_count}),不触发主动聊天")
|
||||
return False
|
||||
|
||||
|
||||
# 检查是否在活动时间内
|
||||
if not self._is_active_hours():
|
||||
logger.debug(f"[私聊][{self.private_name}]不在活动时间内,不触发主动聊天")
|
||||
return False
|
||||
|
||||
|
||||
# 检查冷却时间
|
||||
current_time = time.time()
|
||||
time_since_last_trigger = current_time - self.last_trigger_time
|
||||
if time_since_last_trigger < self.min_cooldown:
|
||||
time_left = self.min_cooldown - time_since_last_trigger
|
||||
logger.debug(f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天")
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}]冷却时间未到(已过{time_since_last_trigger:.0f}秒/需要{self.min_cooldown}秒),还需等待{time_left:.0f}秒,不触发主动聊天"
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
# 强制触发检查 - 如果超过最大冷却时间,增加触发概率
|
||||
force_trigger = False
|
||||
if time_since_last_trigger > self.max_cooldown * 2: # 如果超过最大冷却时间的两倍
|
||||
|
|
@ -255,9 +251,11 @@ class IdleChat:
|
|||
random_force = random.random()
|
||||
force_trigger = random_force < force_probability
|
||||
if force_trigger:
|
||||
logger.info(f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天")
|
||||
logger.info(
|
||||
f"[私聊][{self.private_name}]超过最大冷却时间({time_since_last_trigger:.0f}秒),强制触发主动聊天"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
# 获取关系值
|
||||
relationship_value = 0
|
||||
try:
|
||||
|
|
@ -270,10 +268,10 @@ class IdleChat:
|
|||
# 先尝试通过昵称获取person_id
|
||||
platform = "qq" # 默认平台
|
||||
person_id = person_info_manager.get_person_id(platform, self.private_name)
|
||||
|
||||
|
||||
# 如果通过昵称获取失败,尝试通过stream_id解析
|
||||
if not person_id:
|
||||
parts = self.stream_id.split('_')
|
||||
parts = self.stream_id.split("_")
|
||||
if len(parts) >= 2 and parts[0] == "private":
|
||||
user_id = parts[1]
|
||||
platform = parts[2] if len(parts) >= 3 else "qq"
|
||||
|
|
@ -284,7 +282,7 @@ class IdleChat:
|
|||
person_id = person_info_manager.get_person_id(platform, user_id)
|
||||
except Exception as e2:
|
||||
logger.warning(f"[私聊][{self.private_name}]尝试获取person_id失败: {str(e2)}")
|
||||
|
||||
|
||||
# 获取关系值
|
||||
if person_id:
|
||||
raw_value = await person_info_manager.get_value(person_id, "relationship_value")
|
||||
|
|
@ -292,51 +290,61 @@ class IdleChat:
|
|||
logger.debug(f"[私聊][{self.private_name}]成功获取关系值: {relationship_value}")
|
||||
else:
|
||||
logger.warning(f"[私聊][{self.private_name}]无法获取person_id,使用默认关系值0")
|
||||
|
||||
|
||||
# 使用PfcRepationshipTranslator获取关系描述
|
||||
relationship_translator = PfcRepationshipTranslator(self.private_name)
|
||||
relationship_level = relationship_translator._calculate_relationship_level_num(relationship_value, self.private_name)
|
||||
|
||||
relationship_level = relationship_translator._calculate_relationship_level_num(
|
||||
relationship_value, self.private_name
|
||||
)
|
||||
|
||||
# 基于关系等级调整触发概率
|
||||
# 关系越好,主动聊天概率越高
|
||||
level_probability_factors = [0.05, 0.1, 0.2, 0.3, 0.4, 0.5] # 每个等级对应的基础概率因子
|
||||
base_probability = level_probability_factors[relationship_level]
|
||||
|
||||
|
||||
# 基础概率因子
|
||||
trigger_probability = base_probability
|
||||
trigger_probability = max(0.05, min(0.6, trigger_probability)) # 限制在0.05-0.6之间
|
||||
|
||||
|
||||
# 最大冷却时间调整 - 随着冷却时间增加,逐渐增加触发概率
|
||||
if time_since_last_trigger > self.max_cooldown:
|
||||
# 计算额外概率 - 每超过最大冷却时间的10%,增加1%的概率,最多增加30%
|
||||
extra_time_factor = min(0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10))
|
||||
extra_time_factor = min(
|
||||
0.3, (time_since_last_trigger - self.max_cooldown) / (self.max_cooldown * 10)
|
||||
)
|
||||
trigger_probability += extra_time_factor
|
||||
logger.debug(f"[私聊][{self.private_name}]超过标准冷却时间,额外增加概率: +{extra_time_factor:.2f}")
|
||||
|
||||
|
||||
# 随机判断是否触发
|
||||
random_value = random.random()
|
||||
should_trigger = random_value < trigger_probability
|
||||
logger.debug(f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}")
|
||||
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}]触发概率计算: 基础({base_probability:.2f}) + 关系值({relationship_value})影响 = {trigger_probability:.2f},随机值={random_value:.2f}, 结果={should_trigger}"
|
||||
)
|
||||
|
||||
# 如果决定触发,记录详细日志
|
||||
if should_trigger:
|
||||
logger.info(f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒")
|
||||
|
||||
logger.info(
|
||||
f"[私聊][{self.private_name}]决定触发主动聊天: 触发概率={trigger_probability:.2f}, 距上次已过{time_since_last_trigger:.0f}秒"
|
||||
)
|
||||
|
||||
return should_trigger
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}]获取关系值失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
# 即使获取关系值失败,仍有一个基础的几率触发
|
||||
# 这确保即使数据库有问题,主动聊天功能仍然可用
|
||||
base_fallback_probability = 0.1 # 较低的基础几率
|
||||
random_fallback = random.random()
|
||||
fallback_trigger = random_fallback < base_fallback_probability
|
||||
if fallback_trigger:
|
||||
logger.info(f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}")
|
||||
logger.info(
|
||||
f"[私聊][{self.private_name}]获取关系值失败,使用后备触发机制: 概率={base_fallback_probability:.2f}, 决定={fallback_trigger}"
|
||||
)
|
||||
return fallback_trigger
|
||||
|
||||
|
||||
async def _check_idle_loop(self) -> None:
|
||||
"""检查空闲状态的循环"""
|
||||
try:
|
||||
|
|
@ -346,16 +354,16 @@ class IdleChat:
|
|||
# 如果禁用了功能,等待一段时间后再次检查配置
|
||||
await asyncio.sleep(60) # 每分钟检查一次配置变更
|
||||
continue
|
||||
|
||||
|
||||
# 检查当前用户是否应该触发主动聊天
|
||||
should_trigger = await self._should_trigger()
|
||||
|
||||
|
||||
# 如果当前用户不触发,检查是否有其他用户已经超时未回复
|
||||
if not should_trigger:
|
||||
async with self.__class__._global_lock:
|
||||
current_time = time.time()
|
||||
pending_timeout = 1800 # 30分钟未回复检查
|
||||
|
||||
|
||||
# 检查此用户是否在等待回复列表中
|
||||
if self.private_name in self.__class__._pending_replies:
|
||||
logger.debug(f"[私聊][{self.private_name}]当前用户在等待回复列表中,不进行额外检查")
|
||||
|
|
@ -365,12 +373,12 @@ class IdleChat:
|
|||
for user, send_time in self.__class__._pending_replies.items():
|
||||
if current_time - send_time > pending_timeout:
|
||||
timed_out_users.append(user)
|
||||
|
||||
|
||||
# 如果有超时未回复的用户,尝试找下一个用户
|
||||
if timed_out_users:
|
||||
logger.info(f"[私聊]发现{len(timed_out_users)}个用户超过{pending_timeout}秒未回复")
|
||||
next_user = await self.__class__.get_next_available_user()
|
||||
|
||||
|
||||
if next_user and next_user != self.private_name:
|
||||
logger.info(f"[私聊]选择下一个用户[{next_user}]进行主动聊天")
|
||||
# 查找该用户的实例并触发聊天
|
||||
|
|
@ -380,7 +388,7 @@ class IdleChat:
|
|||
# 触发该实例的主动聊天
|
||||
asyncio.create_task(instance._initiate_chat())
|
||||
break
|
||||
|
||||
|
||||
# 如果当前用户应该触发主动聊天
|
||||
if should_trigger:
|
||||
try:
|
||||
|
|
@ -388,7 +396,7 @@ class IdleChat:
|
|||
# 更新上次触发时间
|
||||
async with self._lock:
|
||||
self.last_trigger_time = time.time()
|
||||
|
||||
|
||||
# 将此用户添加到等待回复列表中
|
||||
async with self.__class__._global_lock:
|
||||
self.__class__._pending_replies[self.private_name] = time.time()
|
||||
|
|
@ -397,12 +405,12 @@ class IdleChat:
|
|||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}]执行主动聊天过程出错: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
# 等待下一次检查
|
||||
check_interval = self.check_interval # 使用配置的检查间隔
|
||||
logger.debug(f"[私聊][{self.private_name}]等待{check_interval}秒后进行下一次主动聊天检查")
|
||||
await asyncio.sleep(check_interval)
|
||||
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.debug(f"[私聊][{self.private_name}]主动聊天检测任务被取消")
|
||||
except Exception as e:
|
||||
|
|
@ -412,24 +420,25 @@ class IdleChat:
|
|||
if self._running:
|
||||
logger.info(f"[私聊][{self.private_name}]尝试重新启动主动聊天检测")
|
||||
self._task = asyncio.create_task(self._check_idle_loop())
|
||||
|
||||
|
||||
async def _get_chat_stream(self) -> Optional[ChatStream]:
|
||||
"""获取聊天流实例"""
|
||||
try:
|
||||
# 尝试从全局聊天管理器获取现有的聊天流
|
||||
from src.plugins.chat.chat_stream import chat_manager
|
||||
|
||||
existing_chat_stream = chat_manager.get_stream(self.stream_id)
|
||||
if existing_chat_stream:
|
||||
logger.debug(f"[私聊][{self.private_name}]从chat_manager找到现有聊天流")
|
||||
return existing_chat_stream
|
||||
|
||||
|
||||
# 如果没有现有聊天流,则创建新的
|
||||
logger.debug(f"[私聊][{self.private_name}]未找到现有聊天流,创建新聊天流")
|
||||
# 创建用户信息对象
|
||||
user_info = UserInfo(
|
||||
user_id=self.private_name, # 使用私聊用户的ID
|
||||
user_nickname=self.private_name, # 使用私聊用户的名称
|
||||
platform="qq"
|
||||
platform="qq",
|
||||
)
|
||||
# 创建聊天流
|
||||
new_stream = ChatStream(self.stream_id, "qq", user_info)
|
||||
|
|
@ -441,23 +450,19 @@ class IdleChat:
|
|||
logger.error(f"[私聊][{self.private_name}]创建/获取聊天流失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return None
|
||||
|
||||
|
||||
async def _initiate_chat(self) -> None:
|
||||
"""生成并发送主动聊天消息"""
|
||||
try:
|
||||
# 获取聊天历史记录
|
||||
messages = self.chat_observer.get_cached_messages(limit=12)
|
||||
chat_history_text = await build_readable_messages(
|
||||
messages,
|
||||
replace_bot_name=True,
|
||||
merge_messages=False,
|
||||
timestamp_mode="relative",
|
||||
read_mark=0.0
|
||||
messages, replace_bot_name=True, merge_messages=False, timestamp_mode="relative", read_mark=0.0
|
||||
)
|
||||
|
||||
|
||||
# 获取关系信息
|
||||
from src.plugins.person_info.relationship_manager import relationship_manager
|
||||
|
||||
|
||||
# 获取关系值
|
||||
relationship_value = 0
|
||||
try:
|
||||
|
|
@ -468,23 +473,25 @@ class IdleChat:
|
|||
relationship_value = relationship_manager.ensure_float(raw_value, person_id)
|
||||
except Exception as e:
|
||||
logger.warning(f"[私聊][{self.private_name}]获取关系值失败,使用默认值: {e}")
|
||||
|
||||
|
||||
# 使用PfcRepationshipTranslator获取关系描述
|
||||
relationship_translator = PfcRepationshipTranslator(self.private_name)
|
||||
full_relationship_text = await relationship_translator.translate_relationship_value_to_text(relationship_value)
|
||||
|
||||
full_relationship_text = await relationship_translator.translate_relationship_value_to_text(
|
||||
relationship_value
|
||||
)
|
||||
|
||||
# 提取纯关系描述(去掉"你们的关系是:"前缀)
|
||||
relationship_description = "普通" # 默认值
|
||||
if ":" in full_relationship_text:
|
||||
relationship_description = full_relationship_text.split(":")[1].replace("。", "")
|
||||
|
||||
|
||||
if global_config.ENABLE_SCHEDULE_GEN:
|
||||
schedule_prompt = await global_prompt_manager.format_prompt(
|
||||
"schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False)
|
||||
)
|
||||
"schedule_prompt", schedule_info=bot_schedule.get_current_num_task(num=1, time_info=False)
|
||||
)
|
||||
else:
|
||||
schedule_prompt = ""
|
||||
|
||||
|
||||
# 构建提示词
|
||||
current_time = datetime.now().strftime("%H:%M")
|
||||
prompt = f"""你是{global_config.BOT_NICKNAME}。
|
||||
|
|
@ -500,14 +507,11 @@ class IdleChat:
|
|||
请直接输出一条消息,不要有任何额外的解释或引导文字
|
||||
消息内容尽量简短
|
||||
"""
|
||||
|
||||
|
||||
# 生成回复
|
||||
logger.debug(f"[私聊][{self.private_name}]开始生成主动聊天内容")
|
||||
try:
|
||||
content, _ = await asyncio.wait_for(
|
||||
self.llm.generate_response_async(prompt),
|
||||
timeout=30
|
||||
)
|
||||
content, _ = await asyncio.wait_for(self.llm.generate_response_async(prompt), timeout=30)
|
||||
logger.debug(f"[私聊][{self.private_name}]成功生成主动聊天内容: {content}")
|
||||
except asyncio.TimeoutError:
|
||||
logger.error(f"[私聊][{self.private_name}]生成主动聊天内容超时")
|
||||
|
|
@ -516,34 +520,30 @@ class IdleChat:
|
|||
logger.error(f"[私聊][{self.private_name}]生成主动聊天内容失败: {str(llm_err)}")
|
||||
logger.error(traceback.format_exc())
|
||||
return
|
||||
|
||||
|
||||
# 清理结果
|
||||
content = content.strip()
|
||||
content = content.strip("\"'")
|
||||
|
||||
|
||||
if not content:
|
||||
logger.error(f"[私聊][{self.private_name}]生成的主动聊天内容为空")
|
||||
return
|
||||
|
||||
|
||||
# 获取聊天流
|
||||
chat_stream = await self._get_chat_stream()
|
||||
if not chat_stream:
|
||||
logger.error(f"[私聊][{self.private_name}]无法获取有效的聊天流,取消发送主动消息")
|
||||
return
|
||||
|
||||
|
||||
# 发送消息
|
||||
try:
|
||||
logger.debug(f"[私聊][{self.private_name}]准备发送主动聊天消息: {content}")
|
||||
await self.message_sender.send_message(
|
||||
chat_stream=chat_stream,
|
||||
content=content,
|
||||
reply_to_message=None
|
||||
)
|
||||
await self.message_sender.send_message(chat_stream=chat_stream, content=content, reply_to_message=None)
|
||||
logger.info(f"[私聊][{self.private_name}]成功主动发起聊天: {content}")
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}]发送主动聊天消息失败: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}]主动发起聊天过程中发生未预期的错误: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error(traceback.format_exc())
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ import traceback
|
|||
from typing import Tuple, Optional, Dict, Any, List
|
||||
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.individuality.individuality import Individuality
|
||||
# from src.individuality.individuality import Individuality
|
||||
from src.plugins.utils.chat_message_builder import build_readable_messages
|
||||
from ..models.utils_model import LLMRequest
|
||||
from ...config.config import global_config
|
||||
from src.config.config import global_config
|
||||
|
||||
# 确保导入路径正确
|
||||
from .pfc_utils import get_items_from_json, retrieve_contextual_info
|
||||
from .pfc_utils import get_items_from_json
|
||||
from .chat_observer import ChatObserver
|
||||
from .observation_info import ObservationInfo
|
||||
from .conversation_info import ConversationInfo
|
||||
|
|
@ -22,26 +22,21 @@ logger = get_logger("pfc_action_planner")
|
|||
# Prompt(1): 首次回复或非连续回复时的决策 Prompt
|
||||
PROMPT_INITIAL_REPLY = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}
|
||||
现在你正在和{sender_name}在QQ上私聊
|
||||
你和对方的关系是:{relationship_text}
|
||||
你现在的心情是:{current_emotion_text}
|
||||
请根据以下【所有信息】审慎且灵活的决策下一步行动,可以回复,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
现在{persona_text}正在与{sender_name}在qq上私聊
|
||||
他们的关系是:{relationship_text}
|
||||
{persona_text}现在的心情是是:{current_emotion_text}
|
||||
你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以回复,可以倾听,甚至可以屏蔽对方:
|
||||
|
||||
【当前对话目标】
|
||||
{goals_str}
|
||||
【最近行动历史概要】
|
||||
{action_history_summary}
|
||||
【你想起来的相关知识】
|
||||
{retrieved_knowledge_str}
|
||||
【上一次行动的详细情况和结果】
|
||||
{last_action_context}
|
||||
【时间和超时提示】
|
||||
{time_since_last_bot_message_info}{timeout_context}
|
||||
【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息)
|
||||
{chat_history_text}
|
||||
【你的回忆】
|
||||
{retrieved_memory_str}
|
||||
|
||||
{spam_warning_info}
|
||||
|
||||
|
|
@ -56,7 +51,7 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
请以JSON格式输出你的决策:
|
||||
{{
|
||||
"action": "选择的行动类型 (必须是上面列表中的一个)",
|
||||
"reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的)"
|
||||
"reason": "选择该行动的原因 "
|
||||
}}
|
||||
|
||||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
|
@ -64,26 +59,21 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
# Prompt(2): 上一次成功回复后,决定继续发言时的决策 Prompt
|
||||
PROMPT_FOLLOW_UP = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}
|
||||
现在你正在和{sender_name}在QQ上私聊,**并且刚刚你已经回复了对方**
|
||||
你与对方的关系是:{relationship_text}
|
||||
你现在的心情是:{current_emotion_text}
|
||||
请根据以下【所有信息】审慎且灵活的决策下一步行动,可以继续发送新消息,可以等待,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
现在{persona_text}正在与{sender_name}在qq上私聊,**并且刚刚{persona_text}已经回复了对方**
|
||||
他们的关系是:{relationship_text}
|
||||
{persona_text}现在的心情是是:{current_emotion_text}
|
||||
你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以发送新消息,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方:
|
||||
|
||||
【当前对话目标】
|
||||
{goals_str}
|
||||
【最近行动历史概要】
|
||||
{action_history_summary}
|
||||
【你想起来的相关知识】
|
||||
{retrieved_knowledge_str}
|
||||
【上一次行动的详细情况和结果】
|
||||
{last_action_context}
|
||||
【时间和超时提示】
|
||||
{time_since_last_bot_message_info}{timeout_context}
|
||||
【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息)
|
||||
{chat_history_text}
|
||||
【你的回忆】
|
||||
{retrieved_memory_str}
|
||||
|
||||
{spam_warning_info}
|
||||
|
||||
|
|
@ -99,7 +89,7 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
请以JSON格式输出你的决策:
|
||||
{{
|
||||
"action": "选择的行动类型 (必须是上面列表中的一个)",
|
||||
"reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的。)"
|
||||
"reason": "选择该行动的原因"
|
||||
}}
|
||||
|
||||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
|
@ -107,19 +97,22 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
# 新增:Prompt(3): 决定是否在结束对话前发送告别语
|
||||
PROMPT_END_DECISION = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。刚刚你决定结束一场 QQ 私聊。
|
||||
现在{persona_text}与{sender_name}刚刚结束了一场qq私聊
|
||||
他们的关系是:{relationship_text}
|
||||
你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程:
|
||||
|
||||
【你们之前的聊天记录】
|
||||
|
||||
【他们之前的聊天记录】
|
||||
{chat_history_text}
|
||||
|
||||
你觉得你们的对话已经完整结束了吗?有时候,在对话自然结束后再说点什么可能会有点奇怪,但有时也可能需要一条简短的消息来圆满结束。
|
||||
如果觉得确实有必要再发一条简短、自然、符合你人设的告别消息(比如 "好,下次再聊~" 或 "嗯,先这样吧"),就输出 "yes"。
|
||||
你觉得他们的对话已经完整结束了吗?有时候,在对话自然结束后再说点什么可能会有点奇怪,但有时也可能需要一条简短的消息来圆满结束。
|
||||
如果觉得确实有必要再发一条简短、自然的告别消息(比如 "好,下次再聊~" 或 "嗯,先这样吧"),就输出 "yes"。
|
||||
如果觉得当前状态下直接结束对话更好,没有必要再发消息,就输出 "no"。
|
||||
|
||||
请以 JSON 格式输出你的选择:
|
||||
{{
|
||||
"say_bye": "yes/no",
|
||||
"reason": "选择 yes 或 no 的原因和内心想法 (简要说明)"
|
||||
"reason": "选择 yes 或 no 的原因和 (简要说明)"
|
||||
}}
|
||||
|
||||
注意:请严格按照 JSON 格式输出,不要包含任何其他内容。"""
|
||||
|
|
@ -127,27 +120,21 @@ PROMPT_END_DECISION = """
|
|||
# Prompt(4): 当 reply_generator 决定不发送消息后的反思决策 Prompt
|
||||
PROMPT_REFLECT_AND_ACT = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}
|
||||
现在你正在和{sender_name}在QQ上私聊
|
||||
你与对方的关系是:{relationship_text}
|
||||
你现在的心情是:{current_emotion_text}
|
||||
刚刚你本来想发一条新消息,但是想了想,你决定不发了。
|
||||
请根据以下【所有信息】审慎且灵活的决策下一步行动,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方:
|
||||
现在{persona_text}正在与{sender_name}在qq上私聊,刚刚{persona_text}打算发一条新消息,想了想还是不发了
|
||||
他们的关系是:{relationship_text}
|
||||
{persona_text}现在的心情是是:{current_emotion_text}
|
||||
你现在需要操控{persona_text},根据以下【所有信息】灵活,合理的决策{persona_text}的下一步行动,需要符合正常人的社交流程,可以等待,可以倾听,可以结束对话,甚至可以屏蔽对方:
|
||||
|
||||
【当前对话目标】
|
||||
{goals_str}
|
||||
【最近行动历史概要】
|
||||
{action_history_summary}
|
||||
【你想起来的相关知识】
|
||||
{retrieved_knowledge_str}
|
||||
【上一次行动的详细情况和结果】
|
||||
{last_action_context}
|
||||
【时间和超时提示】
|
||||
{time_since_last_bot_message_info}{timeout_context}
|
||||
【最近的对话记录】(包括你已成功发送的消息 和 新收到的消息)
|
||||
{chat_history_text}
|
||||
【你的回忆】
|
||||
{retrieved_memory_str}
|
||||
|
||||
{spam_warning_info}
|
||||
|
||||
|
|
@ -162,12 +149,11 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
请以JSON格式输出你的决策:
|
||||
{{
|
||||
"action": "选择的行动类型 (必须是上面列表中的一个)",
|
||||
"reason": "选择该行动的详细原因 (必须有解释你是如何根据“上一次行动结果”、“对话记录”和自身设定人设做出合理判断的。)"
|
||||
"reason": "选择该行动的原因"
|
||||
}}
|
||||
|
||||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
||||
|
||||
class ActionPlanner:
|
||||
"""行动规划器"""
|
||||
|
||||
|
|
@ -195,7 +181,7 @@ class ActionPlanner:
|
|||
raise
|
||||
|
||||
# 获取个性化信息和机器人名称
|
||||
self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
|
||||
# self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
|
||||
self.name = global_config.BOT_NICKNAME
|
||||
# 获取 ChatObserver 实例 (单例模式)
|
||||
self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
|
||||
|
|
@ -228,21 +214,21 @@ class ActionPlanner:
|
|||
goals_str = self._build_goals_string(conversation_info)
|
||||
chat_history_text = await self._build_chat_history_text(observation_info)
|
||||
# 获取 sender_name, relationship_text, current_emotion_text
|
||||
sender_name_str = getattr(observation_info, "sender_name", "对方") # 从 observation_info 获取
|
||||
if not sender_name_str:
|
||||
sender_name_str = "对方" # 再次确保有默认值
|
||||
sender_name_str = getattr(observation_info, 'sender_name', '对方') # 从 observation_info 获取
|
||||
if not sender_name_str: sender_name_str = '对方' # 再次确保有默认值
|
||||
|
||||
relationship_text_str = getattr(conversation_info, "relationship_text", "你们还不熟悉。")
|
||||
current_emotion_text_str = getattr(conversation_info, "current_emotion_text", "心情平静。")
|
||||
relationship_text_str = getattr(conversation_info, 'relationship_text', '你们还不熟悉。')
|
||||
current_emotion_text_str = getattr(conversation_info, 'current_emotion_text', '心情平静。')
|
||||
|
||||
persona_text = f"你的名字是{self.name},{self.personality_info}。"
|
||||
|
||||
persona_text = f"{self.name}。"
|
||||
action_history_summary, last_action_context = self._build_action_history_context(conversation_info)
|
||||
retrieved_memory_str, retrieved_knowledge_str = await retrieve_contextual_info(
|
||||
chat_history_text, self.private_name
|
||||
)
|
||||
logger.info(
|
||||
f"[私聊][{self.private_name}] (ActionPlanner) 检索完成。记忆: {'有' if '回忆起' in retrieved_memory_str else '无'} / 知识: {'有' if retrieved_knowledge_str and '无相关知识' not in retrieved_knowledge_str and '出错' not in retrieved_knowledge_str else '无'}"
|
||||
)
|
||||
# retrieved_memory_str, retrieved_knowledge_str = await retrieve_contextual_info(
|
||||
# chat_history_text, self.private_name
|
||||
# )
|
||||
# logger.info(
|
||||
# f"[私聊][{self.private_name}] (ActionPlanner) 检索完成。记忆: {'有' if '回忆起' in retrieved_memory_str else '无'} / 知识: {'有' if retrieved_knowledge_str and '无相关知识' not in retrieved_knowledge_str and '出错' not in retrieved_knowledge_str else '无'}"
|
||||
# )
|
||||
except Exception as prep_err:
|
||||
logger.error(f"[私聊][{self.private_name}] 准备 Prompt 输入时出错: {prep_err}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
|
@ -250,16 +236,14 @@ class ActionPlanner:
|
|||
|
||||
# --- 2. 选择并格式化 Prompt ---
|
||||
try:
|
||||
if use_reflect_prompt: # 新增的判断
|
||||
if use_reflect_prompt: # 新增的判断
|
||||
prompt_template = PROMPT_REFLECT_AND_ACT
|
||||
log_msg = "使用 PROMPT_REFLECT_AND_ACT (反思决策)"
|
||||
# 对于 PROMPT_REFLECT_AND_ACT,它不包含 send_new_message 选项,所以 spam_warning_message 中的相关提示可以调整或省略
|
||||
# 但为了保持占位符填充的一致性,我们仍然计算它
|
||||
spam_warning_message = ""
|
||||
if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少
|
||||
spam_warning_message = (
|
||||
f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**"
|
||||
)
|
||||
if conversation_info.my_message_count > 5: # 这里的 my_message_count 仍有意义,表示之前连续发送了多少
|
||||
spam_warning_message = f"⚠️【警告】**你之前已连续发送{str(conversation_info.my_message_count)}条消息!请谨慎决策。**"
|
||||
elif conversation_info.my_message_count > 2:
|
||||
spam_warning_message = f"💬【提示】**你之前已连续发送{str(conversation_info.my_message_count)}条消息。请注意保持对话平衡。**"
|
||||
|
||||
|
|
@ -275,12 +259,12 @@ class ActionPlanner:
|
|||
else:
|
||||
prompt_template = PROMPT_INITIAL_REPLY
|
||||
log_msg = "使用 PROMPT_INITIAL_REPLY (首次/非连续回复决策)"
|
||||
spam_warning_message = "" # 初始回复时通常不需要刷屏警告
|
||||
spam_warning_message = "" # 初始回复时通常不需要刷屏警告
|
||||
|
||||
logger.debug(f"[私聊][{self.private_name}] {log_msg}")
|
||||
|
||||
current_time_value = "获取时间失败"
|
||||
if observation_info and hasattr(observation_info, "current_time_str") and observation_info.current_time_str:
|
||||
if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str:
|
||||
current_time_value = observation_info.current_time_str
|
||||
|
||||
if spam_warning_message:
|
||||
|
|
@ -294,13 +278,13 @@ class ActionPlanner:
|
|||
time_since_last_bot_message_info=time_since_last_bot_message_info,
|
||||
timeout_context=timeout_context,
|
||||
chat_history_text=chat_history_text if chat_history_text.strip() else "还没有聊天记录。",
|
||||
retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。",
|
||||
retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。",
|
||||
# retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。",
|
||||
# retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。",
|
||||
current_time_str=current_time_value,
|
||||
spam_warning_info=spam_warning_message,
|
||||
sender_name=sender_name_str,
|
||||
relationship_text=relationship_text_str,
|
||||
current_emotion_text=current_emotion_text_str,
|
||||
current_emotion_text=current_emotion_text_str
|
||||
)
|
||||
logger.debug(f"[私聊][{self.private_name}] 发送到LLM的最终提示词:\n------\n{prompt}\n------")
|
||||
except KeyError as fmt_key_err:
|
||||
|
|
@ -347,7 +331,11 @@ class ActionPlanner:
|
|||
):
|
||||
time_str_for_end_decision = observation_info.current_time_str
|
||||
final_action, final_reason = await self._handle_end_conversation_decision(
|
||||
persona_text, chat_history_text, initial_reason, time_str_for_end_decision
|
||||
persona_text,
|
||||
chat_history_text, initial_reason,
|
||||
time_str_for_end_decision,
|
||||
sender_name_str=sender_name_str,
|
||||
relationship_text_str=relationship_text_str
|
||||
)
|
||||
except Exception as end_dec_err:
|
||||
logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}")
|
||||
|
|
@ -372,7 +360,7 @@ class ActionPlanner:
|
|||
"block_and_ignore",
|
||||
"say_goodbye",
|
||||
]
|
||||
valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作
|
||||
valid_actions_reflect = [ # PROMPT_REFLECT_AND_ACT 的动作
|
||||
"wait",
|
||||
"listening",
|
||||
"rethink_goal",
|
||||
|
|
@ -513,7 +501,9 @@ class ActionPlanner:
|
|||
)
|
||||
logger.debug(f"[私聊][{self.private_name}] 向 LLM 追加了 {other_unread_count} 条未读消息。")
|
||||
else:
|
||||
chat_history_text += "\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n"
|
||||
chat_history_text += (
|
||||
f"\n--- 以上均为已读消息,未读消息均已处理完毕 ---\n"
|
||||
)
|
||||
except AttributeError as e:
|
||||
logger.warning(f"[私聊][{self.private_name}] 构建聊天记录文本时属性错误: {e}")
|
||||
chat_history_text = "[获取聊天记录时出错]\n"
|
||||
|
|
@ -571,13 +561,11 @@ class ActionPlanner:
|
|||
# --- Helper method for handling end_conversation decision ---
|
||||
|
||||
async def _handle_end_conversation_decision(
|
||||
self, persona_text: str, chat_history_text: str, initial_reason: str, current_time_str: str
|
||||
self, persona_text: str, chat_history_text: str, initial_reason: str, current_time_str: str, sender_name_str: str, relationship_text_str: str
|
||||
) -> Tuple[str, str]:
|
||||
"""处理结束对话前的告别决策"""
|
||||
logger.info(f"[私聊][{self.private_name}] 初步规划结束对话,进入告别决策...")
|
||||
end_decision_prompt = PROMPT_END_DECISION.format(
|
||||
persona_text=persona_text, chat_history_text=chat_history_text, current_time_str=current_time_str
|
||||
)
|
||||
end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str,sender_name = sender_name_str, relationship_text = relationship_text_str)
|
||||
logger.debug(f"[私聊][{self.private_name}] 发送到LLM的结束决策提示词:\n------\n{end_decision_prompt}\n------")
|
||||
llm_start_time = time.time()
|
||||
end_content, _ = await self.llm.generate_response_async(end_decision_prompt)
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ async def handle_action(
|
|||
is_suitable = True
|
||||
check_reason = "ReplyChecker 已通过配置关闭"
|
||||
need_replan_from_checker = False
|
||||
logger.info(f"{log_prefix} [配置关闭] ReplyChecker 已跳过,默认回复为合适。")
|
||||
logger.debug(f"{log_prefix} [配置关闭] ReplyChecker 已跳过,默认回复为合适。")
|
||||
|
||||
# 处理检查结果
|
||||
if not is_suitable:
|
||||
|
|
@ -295,7 +295,7 @@ async def handle_action(
|
|||
# 后续的 plan 循环会检测到这个 "done_no_reply" 状态并使用反思 prompt
|
||||
|
||||
elif is_suitable: # 适用于 direct_reply 或 (send_new_message 且 RG决定发送并通过检查)
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 找到合适的回复,准备发送。")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 找到合适的回复,准备发送。")
|
||||
# conversation_info.last_reply_rejection_reason = None # 已在循环内清除
|
||||
# conversation_info.last_rejected_reply_content = None
|
||||
conversation_instance.generated_reply = generated_content_for_check_or_send # 使用检查通过的内容
|
||||
|
|
@ -311,7 +311,7 @@ async def handle_action(
|
|||
action_successful = True
|
||||
final_status = "done" # 明确设置 final_status
|
||||
final_reason = "成功发送" # 明确设置 final_reason
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 成功发送回复.")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] 动作 '{action}': 成功发送回复.")
|
||||
|
||||
# --- 新增:将机器人发送的消息添加到 ObservationInfo 的 chat_history ---
|
||||
if (
|
||||
|
|
@ -334,7 +334,7 @@ async def handle_action(
|
|||
observation_info.chat_history.append(bot_message_dict)
|
||||
observation_info.chat_history_count = len(observation_info.chat_history)
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] 机器人发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}"
|
||||
f"[私聊][{conversation_instance.private_name}] {global_config.BOT_NICKNAME}发送的消息已添加到 chat_history。当前历史数: {observation_info.chat_history_count}"
|
||||
)
|
||||
|
||||
# 可选:如果 chat_history 过长,进行修剪 (例如,保留最近N条)
|
||||
|
|
@ -398,13 +398,13 @@ async def handle_action(
|
|||
|
||||
# 如果是 direct_reply 且规划期间有他人新消息,则下次不追问
|
||||
if other_new_msg_count_during_planning > 0 and action == "direct_reply":
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] 因规划期间收到 {other_new_msg_count_during_planning} 条他人新消息,下一轮强制使用【初始回复】逻辑。"
|
||||
)
|
||||
conversation_info.last_successful_reply_action = None
|
||||
# conversation_info.my_message_count 不在此处重置,因为它刚发了一条
|
||||
elif action == "direct_reply" or action == "send_new_message": # 成功发送后
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] 成功执行 '{action}', 下一轮【允许】使用追问逻辑。"
|
||||
)
|
||||
conversation_info.last_successful_reply_action = action
|
||||
|
|
@ -413,7 +413,7 @@ async def handle_action(
|
|||
if conversation_info: # 再次确认
|
||||
conversation_info.current_instance_message_count += 1
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] 实例消息计数(机器人发送后)增加到: {conversation_info.current_instance_message_count}"
|
||||
f"[私聊][{conversation_instance.private_name}] 实例消息计数({global_config.BOT_NICKNAME}发送后)增加到: {conversation_info.current_instance_message_count}"
|
||||
)
|
||||
|
||||
if conversation_instance.relationship_updater: # 确保存在
|
||||
|
|
|
|||
|
|
@ -114,11 +114,11 @@ class ChatObserver:
|
|||
logger.debug(
|
||||
f"[私聊][{self.private_name}] 消息已添加到 ChatObserver 缓存,当前缓存大小: {len(self.message_cache)}"
|
||||
)
|
||||
|
||||
|
||||
# 检查是否用户发送的消息(而非机器人自己)
|
||||
try:
|
||||
from .PFC_idle.idle_chat import IdleChat
|
||||
|
||||
|
||||
# 获取消息的发送者
|
||||
user_info = message.get("user_info", {})
|
||||
if user_info and str(user_info.get("user_id")) != str(global_config.BOT_QQ):
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class Conversation:
|
|||
observation_info=self.observation_info,
|
||||
chat_observer_for_history=self.chat_observer,
|
||||
)
|
||||
logger.info(f"[私聊][{self.private_name}] 最终关系评估已调用。")
|
||||
logger.debug(f"[私聊][{self.private_name}] 最终关系评估已调用。")
|
||||
except Exception as e_final_rel:
|
||||
logger.error(f"[私聊][{self.private_name}] 调用最终关系评估时出错: {e_final_rel}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
|
@ -145,12 +145,12 @@ class Conversation:
|
|||
if self.idle_chat:
|
||||
# 减少活跃实例计数,而不是停止IdleChat
|
||||
await self.idle_chat.decrement_active_instances()
|
||||
logger.info(f"[私聊][{self.private_name}] 已减少IdleChat活跃实例计数")
|
||||
logger.debug(f"[私聊][{self.private_name}] 已减少IdleChat活跃实例计数")
|
||||
if self.observation_info and self.chat_observer:
|
||||
self.observation_info.unbind_from_chat_observer()
|
||||
if self.mood_mng and hasattr(self.mood_mng, "stop_mood_update") and self.mood_mng._running: # type: ignore
|
||||
self.mood_mng.stop_mood_update() # type: ignore
|
||||
logger.info(f"[私聊][{self.private_name}] MoodManager 后台更新已停止。")
|
||||
logger.debug(f"[私聊][{self.private_name}] MoodManager 后台更新已停止。")
|
||||
|
||||
self._initialized = False # 标记为未初始化
|
||||
logger.info(f"[私聊][{self.private_name}] 对话实例 {self.stream_id} 已停止。")
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ async def load_initial_history(conversation_instance: "Conversation"):
|
|||
return
|
||||
|
||||
try:
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] 为 {conversation_instance.stream_id} 加载初始聊天记录..."
|
||||
)
|
||||
# 从聊天核心获取原始消息列表
|
||||
|
|
@ -99,10 +99,7 @@ async def load_initial_history(conversation_instance: "Conversation"):
|
|||
conversation_instance.chat_observer.last_message_time = (
|
||||
conversation_instance.observation_info.last_message_time
|
||||
)
|
||||
if (
|
||||
conversation_instance.idle_chat
|
||||
and conversation_instance.observation_info.last_message_time
|
||||
):
|
||||
if conversation_instance.idle_chat and conversation_instance.observation_info.last_message_time:
|
||||
# 更新空闲计时器的起始时间
|
||||
await conversation_instance.idle_chat.update_last_message_time(
|
||||
conversation_instance.observation_info.last_message_time
|
||||
|
|
@ -135,7 +132,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
# return
|
||||
|
||||
# conversation_instance._initializing_flag_from_manager = True # 标记开始初始化
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) 开始初始化对话实例核心组件: {conversation_instance.stream_id}"
|
||||
)
|
||||
|
||||
|
|
@ -152,12 +149,12 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
conversation_instance.relationship_translator = PfcRepationshipTranslator(
|
||||
private_name=conversation_instance.private_name
|
||||
)
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcRelationship 初始化完成。")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcRelationship 初始化完成。")
|
||||
|
||||
conversation_instance.emotion_updater = PfcEmotionUpdater(
|
||||
private_name=conversation_instance.private_name, bot_name=global_config.BOT_NICKNAME
|
||||
)
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcEmotion 初始化完成。")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) PfcEmotion 初始化完成。")
|
||||
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化 GoalAnalyzer...")
|
||||
conversation_instance.goal_analyzer = GoalAnalyzer(
|
||||
|
|
@ -197,7 +194,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
conversation_instance.stream_id, conversation_instance.private_name
|
||||
)
|
||||
await conversation_instance.idle_chat.increment_active_instances()
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已获取并增加活跃计数")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已获取并增加活跃计数")
|
||||
|
||||
# 2. 初始化信息存储和观察组件
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 获取 ChatObserver 实例...")
|
||||
|
|
@ -238,7 +235,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
conversation_instance.conversation_info.person_id = person_id_tuple[0] # 第一个元素是 person_id
|
||||
private_platform_str = person_id_tuple[1]
|
||||
private_user_id_str = person_id_tuple[2]
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) 获取到 person_id: {conversation_instance.conversation_info.person_id} for {private_platform_str}:{private_user_id_str}"
|
||||
)
|
||||
else:
|
||||
|
|
@ -254,7 +251,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
if conversation_instance.idle_chat:
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) 启动 IdleChat...")
|
||||
# 不需要再次启动,只需确保已初始化
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已初始化")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) IdleChat实例已初始化")
|
||||
|
||||
if (
|
||||
conversation_instance.mood_mng
|
||||
|
|
@ -262,11 +259,11 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
and not conversation_instance.mood_mng._running
|
||||
): # type: ignore
|
||||
conversation_instance.mood_mng.start_mood_update(update_interval=global_config.mood_update_interval) # type: ignore
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已启动后台更新,间隔: {global_config.mood_update_interval} 秒。"
|
||||
)
|
||||
elif conversation_instance.mood_mng and conversation_instance.mood_mng._running: # type: ignore
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已在运行中。")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 已在运行中。")
|
||||
else:
|
||||
logger.warning(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) MoodManager 未能启动,相关功能可能受限。"
|
||||
|
|
@ -294,7 +291,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
numeric_relationship_value
|
||||
)
|
||||
)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载关系文本: {conversation_instance.conversation_info.relationship_text}"
|
||||
)
|
||||
except Exception as e_init_rel:
|
||||
|
|
@ -308,7 +305,7 @@ async def initialize_core_components(conversation_instance: "Conversation"):
|
|||
conversation_instance.conversation_info.current_emotion_text = (
|
||||
conversation_instance.mood_mng.get_prompt()
|
||||
) # type: ignore
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{conversation_instance.private_name}] (Initializer) 初始化时加载情绪文本: {conversation_instance.conversation_info.current_emotion_text}"
|
||||
)
|
||||
except Exception as e_init_emo:
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ async def run_conversation_loop(conversation_instance: "Conversation"):
|
|||
核心的规划与行动循环 (PFC Loop)。
|
||||
之前是 Conversation 类中的 _plan_and_action_loop 方法。
|
||||
"""
|
||||
logger.info(f"[私聊][{conversation_instance.private_name}] 进入 run_conversation_loop 循环。")
|
||||
logger.debug(f"[私聊][{conversation_instance.private_name}] 进入 run_conversation_loop 循环。")
|
||||
|
||||
if not conversation_instance._initialized:
|
||||
logger.error(f"[私聊][{conversation_instance.private_name}] 尝试在未初始化状态下运行规划循环,退出。")
|
||||
|
|
@ -69,10 +69,7 @@ async def run_conversation_loop(conversation_instance: "Conversation"):
|
|||
conversation_instance.ignore_until_timestamp
|
||||
and loop_iter_start_time < conversation_instance.ignore_until_timestamp
|
||||
):
|
||||
if (
|
||||
conversation_instance.idle_chat
|
||||
and conversation_instance.idle_chat._running
|
||||
):
|
||||
if conversation_instance.idle_chat and conversation_instance.idle_chat._running:
|
||||
# 不直接停止服务,改为暂时忽略此用户
|
||||
# 虽然我们仍然可以通过active_instances_count来决定是否触发主动聊天
|
||||
# 但为了安全起见,我们只记录一个日志
|
||||
|
|
|
|||
|
|
@ -297,7 +297,7 @@ class ObservationInfo:
|
|||
|
||||
if new_count < original_count:
|
||||
self.new_messages_count = new_count
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] 移除了未处理的消息 (ID: {message_id_to_delete}), 当前未处理数: {self.new_messages_count}"
|
||||
)
|
||||
self.update_changed()
|
||||
|
|
@ -384,7 +384,7 @@ class ObservationInfo:
|
|||
self.new_messages_count = len(self.unprocessed_messages)
|
||||
self.chat_history_count = len(self.chat_history)
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] 已清理 {cleared_count} 条消息 (IDs: {message_ids_to_clear}),剩余未处理 {self.new_messages_count} 条,当前历史记录 {self.chat_history_count} 条。"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class PfcEmotionUpdater:
|
|||
# LLM 实例 (根据 global_config.llm_summary 配置)
|
||||
llm_config_summary = getattr(global_config, "llm_summary", None)
|
||||
if llm_config_summary and isinstance(llm_config_summary, dict):
|
||||
logger.info(f"[私聊][{self.private_name}] 使用 llm_summary 配置初始化情绪判断LLM。")
|
||||
logger.debug(f"[私聊][{self.private_name}] 使用 llm_summary 配置初始化情绪判断LLM。")
|
||||
self.llm = LLMRequest(
|
||||
model=llm_config_summary,
|
||||
temperature=llm_config_summary.get(
|
||||
|
|
@ -102,11 +102,11 @@ class PfcEmotionUpdater:
|
|||
and detected_emotion_word in self.mood_mng.emotion_map
|
||||
):
|
||||
self.mood_mng.update_mood_from_emotion(detected_emotion_word, intensity=self.EMOTION_UPDATE_INTENSITY)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] 基于事件 '{event_description}',情绪已更新为倾向于 '{detected_emotion_word}'。当前心情: {self.mood_mng.current_mood.text}"
|
||||
)
|
||||
elif detected_emotion_word == "无变化":
|
||||
logger.info(f"[私聊][{self.private_name}] 基于事件 '{event_description}',LLM判断情绪无显著变化。")
|
||||
logger.debug(f"[私聊][{self.private_name}] 基于事件 '{event_description}',LLM判断情绪无显著变化。")
|
||||
else:
|
||||
logger.warning(
|
||||
f"[私聊][{self.private_name}] LLM返回了未知的情绪词 '{detected_emotion_word}' 或未返回有效词,情绪未主动更新。"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from src.plugins.person_info.relationship_manager import (
|
|||
from src.plugins.utils.chat_message_builder import build_readable_messages
|
||||
from src.plugins.PFC.observation_info import ObservationInfo
|
||||
from src.plugins.PFC.conversation_info import ConversationInfo
|
||||
from src.plugins.PFC.pfc_utils import get_items_from_json
|
||||
from src.plugins.PFC.pfc_utils import get_items_from_json, adjust_relationship_value_nonlinear
|
||||
from src.config.config import global_config # 导入全局配置 (向上两级到 src/, 再到 config)
|
||||
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ class PfcRelationshipUpdater:
|
|||
请输出一个JSON对象,包含一个 "adjustment" 字段,其值为一个介于 -{self.REL_INCREMENTAL_MAX_CHANGE} 和 +{self.REL_INCREMENTAL_MAX_CHANGE} 之间的整数,代表关系值的变化。
|
||||
例如:{{ "adjustment": 3 }}。如果对话内容不明确或难以判断,请倾向于输出较小的调整值(如0, 1, -1)。"""
|
||||
|
||||
adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE
|
||||
raw_adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE
|
||||
try:
|
||||
logger.debug(f"[私聊][{self.private_name}] 增量关系评估Prompt:\n{relationship_prompt}")
|
||||
content, _ = await self.llm.generate_response_async(relationship_prompt)
|
||||
|
|
@ -136,13 +136,17 @@ class PfcRelationshipUpdater:
|
|||
)
|
||||
raw_adjustment = result.get("adjustment", self.REL_INCREMENTAL_DEFAULT_CHANGE)
|
||||
if not isinstance(raw_adjustment, (int, float)):
|
||||
adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE
|
||||
raw_adjustment_val = self.REL_INCREMENTAL_DEFAULT_CHANGE
|
||||
else:
|
||||
adjustment_val = float(raw_adjustment)
|
||||
adjustment_val = max(-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, adjustment_val))
|
||||
raw_adjustment_val = float(raw_adjustment)
|
||||
raw_adjustment_val = max(
|
||||
-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}] 增量关系评估LLM调用或解析失败: {e}")
|
||||
|
||||
adjustment_val = await adjust_relationship_value_nonlinear(current_relationship_value, raw_adjustment_val)
|
||||
|
||||
new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val))
|
||||
await self.person_info_mng.update_one_field(
|
||||
conversation_info.person_id, "relationship_value", new_relationship_value
|
||||
|
|
@ -202,7 +206,7 @@ class PfcRelationshipUpdater:
|
|||
请输出一个JSON对象,包含一个 "final_adjustment" 字段,其值为一个整数,代表关系值的变化量(例如,可以是 -{self.REL_FINAL_MAX_CHANGE} 到 +{self.REL_FINAL_MAX_CHANGE} 之间的一个值)。
|
||||
请大胆评估,但也要合理。"""
|
||||
|
||||
adjustment_val = self.REL_FINAL_DEFAULT_CHANGE
|
||||
raw_adjustment_val = self.REL_FINAL_DEFAULT_CHANGE
|
||||
try:
|
||||
logger.debug(f"[私聊][{self.private_name}] 最终关系评估Prompt:\n{relationship_prompt}")
|
||||
content, _ = await self.llm.generate_response_async(relationship_prompt)
|
||||
|
|
@ -217,13 +221,17 @@ class PfcRelationshipUpdater:
|
|||
)
|
||||
raw_adjustment = result.get("final_adjustment", self.REL_FINAL_DEFAULT_CHANGE)
|
||||
if not isinstance(raw_adjustment, (int, float)):
|
||||
adjustment_val = self.REL_FINAL_DEFAULT_CHANGE
|
||||
raw_adjustment_val = self.REL_FINAL_DEFAULT_CHANGE
|
||||
else:
|
||||
adjustment_val = float(raw_adjustment)
|
||||
adjustment_val = max(-self.REL_FINAL_MAX_CHANGE, min(self.REL_FINAL_MAX_CHANGE, adjustment_val))
|
||||
raw_adjustment_val = float(raw_adjustment)
|
||||
raw_adjustment_val = max(
|
||||
-self.REL_INCREMENTAL_MAX_CHANGE, min(self.REL_INCREMENTAL_MAX_CHANGE, raw_adjustment_val)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"[私聊][{self.private_name}] 最终关系评估LLM调用或解析失败: {e}")
|
||||
|
||||
adjustment_val = await adjust_relationship_value_nonlinear(current_relationship_value, raw_adjustment_val)
|
||||
|
||||
new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val))
|
||||
await self.person_info_mng.update_one_field(
|
||||
conversation_info.person_id, "relationship_value", new_relationship_value
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from src.plugins.memory_system.Hippocampus import HippocampusManager
|
|||
from src.plugins.heartFC_chat.heartflow_prompt_builder import prompt_builder # 确认 prompt_builder 的导入路径
|
||||
from src.plugins.chat.chat_stream import ChatStream
|
||||
from ..person_info.person_info import person_info_manager
|
||||
import math
|
||||
|
||||
logger = get_logger("pfc_utils")
|
||||
|
||||
|
|
@ -273,7 +274,7 @@ async def get_person_id(private_name: str, chat_stream: ChatStream):
|
|||
if chat_stream.user_info:
|
||||
private_user_id_str = str(chat_stream.user_info.user_id)
|
||||
private_platform_str = chat_stream.user_info.platform
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{private_name}] 从 ChatStream 获取到私聊对象信息: ID={private_user_id_str}, Platform={private_platform_str}, Name={private_nickname_str}"
|
||||
)
|
||||
elif chat_stream.group_info is None and private_name:
|
||||
|
|
@ -308,3 +309,33 @@ async def get_person_id(private_name: str, chat_stream: ChatStream):
|
|||
f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform,无法获取 person_id。将在收到消息后尝试。"
|
||||
)
|
||||
return None # 返回 None 表示失败
|
||||
|
||||
|
||||
async def adjust_relationship_value_nonlinear(old_value: float, raw_adjustment: float) -> float:
|
||||
# 限制 old_value 范围
|
||||
old_value = max(-1000, min(1000, old_value))
|
||||
value = raw_adjustment
|
||||
|
||||
if old_value >= 0:
|
||||
if value >= 0:
|
||||
value = value * math.cos(math.pi * old_value / 2000)
|
||||
if old_value > 500:
|
||||
rdict = await person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700)
|
||||
high_value_count = len(rdict)
|
||||
if old_value > 700:
|
||||
value *= 3 / (high_value_count + 2)
|
||||
else:
|
||||
value *= 3 / (high_value_count + 3)
|
||||
elif value < 0:
|
||||
value = value * math.exp(old_value / 2000)
|
||||
else:
|
||||
value = 0
|
||||
else:
|
||||
if value >= 0:
|
||||
value = value * math.exp(old_value / 2000)
|
||||
elif value < 0:
|
||||
value = value * math.cos(math.pi * old_value / 2000)
|
||||
else:
|
||||
value = 0
|
||||
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ class ReplyChecker:
|
|||
对于非重复消息: (True, "消息内容未与机器人历史发言重复。", False)
|
||||
"""
|
||||
if not self.bot_qq_str:
|
||||
logger.error(f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查机器人自身消息。")
|
||||
logger.error(
|
||||
f"[私聊][{self.private_name}] ReplyChecker: BOT_QQ 未配置,无法检查{global_config.BOT_NICKNAME}自身消息。"
|
||||
)
|
||||
return True, "BOT_QQ未配置,跳过重复检查。", False # 无法检查则默认通过
|
||||
|
||||
if len(reply) <= 4:
|
||||
|
|
@ -64,11 +66,13 @@ class ReplyChecker:
|
|||
historical_message_text = msg_dict.get("processed_plain_text", "")
|
||||
# <--- 新增详细对比日志 --- START --->
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] ReplyChecker: 历史记录 #{i} (机器人): '{historical_message_text}' (长度 {len(historical_message_text)})"
|
||||
f"[私聊][{self.private_name}] ReplyChecker: 历史记录 #{i} ({global_config.BOT_NICKNAME}): '{historical_message_text}' (长度 {len(historical_message_text)})"
|
||||
)
|
||||
if reply == historical_message_text:
|
||||
logger.warning(f"[私聊][{self.private_name}] ReplyChecker: !!! 精确匹配成功 !!!")
|
||||
logger.warning(f"[私聊][{self.private_name}] ReplyChecker 检测到机器人自身重复消息: '{reply}'")
|
||||
logger.warning(
|
||||
f"[私聊][{self.private_name}] ReplyChecker 检测到{global_config.BOT_NICKNAME}自身重复消息: '{reply}'"
|
||||
)
|
||||
match_found = True # <--- 标记找到
|
||||
return (False, "机器人尝试发送重复消息", False)
|
||||
# <--- 新增详细对比日志 --- END --->
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ class ReplyGenerator:
|
|||
f"请根据此提示调整你的新回复,确保内容新颖,不要重复你已经说过的话。\n"
|
||||
f"------\n"
|
||||
)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] (ReplyGenerator) 检测到自身复读,将加入特定警告到 Prompt:\n"
|
||||
f" 内容: {last_content}"
|
||||
)
|
||||
|
|
@ -273,7 +273,7 @@ class ReplyGenerator:
|
|||
f"请根据【消息内容】和【失败原因】调整你的新回复,避免重复之前的错误。\n"
|
||||
f"------\n"
|
||||
)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"[私聊][{self.private_name}] (ReplyGenerator) 检测到上次回复失败信息,将加入 Prompt:\n"
|
||||
f" 内容: {last_content}\n"
|
||||
f" 原因: {last_reason}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[inner]
|
||||
version = "1.6.5"
|
||||
version = "1.6.2"
|
||||
|
||||
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
|
||||
#如果你想要修改配置文件,请在修改后将version的值进行变更
|
||||
|
|
@ -191,7 +191,7 @@ pfc_chatting = false # 是否启用PFC聊天,该功能仅作用于私聊,与
|
|||
enable_pfc_reply_checker = true # 是否启用 PFC 的回复检查器
|
||||
|
||||
[idle_conversation]
|
||||
enable_idle_conversation = false
|
||||
enable_idle_conversation = false # 是否启用 pfc 主动发言
|
||||
idle_check_interval = 10 # 检查间隔,10分钟检查一次
|
||||
min_idle_time = 7200 # 最短无活动时间,2小时 (7200秒)
|
||||
max_idle_time = 18000 # 最长无活动时间,5小时 (18000秒)
|
||||
|
|
@ -291,21 +291,13 @@ temp = 0.3
|
|||
pri_in = 2
|
||||
pri_out = 8
|
||||
|
||||
#PFC检查模型
|
||||
[model.llm_PFC_reply_checker]
|
||||
name = "Pro/deepseek-ai/DeepSeek-V3"
|
||||
provider = "SILICONFLOW"
|
||||
pri_in = 2
|
||||
pri_out = 8
|
||||
|
||||
# PFC 关系评估LLM
|
||||
[model.llm_PFC_relationship_eval]
|
||||
name = "Pro/deepseek-ai/DeepSeek-V3" # 或者其他你认为适合判断任务的模型
|
||||
provider = "SILICONFLOW"
|
||||
temp = 0.4 # 判断任务建议温度稍低
|
||||
max_tokens = 512 # 根据Prompt长度和期望输出来调整
|
||||
pri_in = 2 # 价格信息(可选)
|
||||
pri_out = 8 # 价格信息(可选)
|
||||
temp = 0.4
|
||||
pri_in = 2
|
||||
pri_out = 8
|
||||
|
||||
#以下模型暂时没有使用!!
|
||||
#以下模型暂时没有使用!!
|
||||
|
|
|
|||
Loading…
Reference in New Issue