Merge branch 'dev' into dev

This commit is contained in:
UnCLAS-Prommer
2025-04-20 17:20:17 +08:00
committed by GitHub
22 changed files with 796 additions and 1113 deletions

View File

@@ -6,7 +6,6 @@ from .chat_stream import chat_manager
from ..chat_module.only_process.only_message_process import MessageProcessor
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
from ..chat_module.think_flow_chat.think_flow_chat import ThinkFlowChat
from ..chat_module.reasoning_chat.reasoning_chat import ReasoningChat
from ..chat_module.heartFC_chat.heartFC_processor import HeartFC_Processor
from ..utils.prompt_builder import Prompt, global_prompt_manager
@@ -29,7 +28,6 @@ class ChatBot:
self._started = False
self.mood_manager = MoodManager.get_instance() # 获取情绪管理器单例
self.mood_manager.start_mood_update() # 启动情绪更新
self.think_flow_chat = ThinkFlowChat()
self.reasoning_chat = ReasoningChat()
self.heartFC_processor = HeartFC_Processor() # 新增
@@ -79,10 +77,17 @@ class ChatBot:
# 确保所有任务已启动
await self._ensure_started()
if message_data["message_info"]["group_info"] is not None:
message_data["message_info"]["group_info"]["group_id"] = str(
message_data["message_info"]["group_info"]["group_id"]
)
message_data["message_info"]["group_info"]["group_id"] = str(
message_data["message_info"]["group_info"]["group_id"]
)
logger.trace(f"处理消息:{str(message_data)[:120]}...")
message = MessageRecv(message_data)
groupinfo = message.message_info.group_info
userinfo = message.message_info.user_info
logger.trace(f"处理消息:{str(message_data)[:120]}...")
if userinfo.user_id in global_config.ban_user_id:
logger.debug(f"用户{userinfo.user_id}被禁止回复")
@@ -118,11 +123,9 @@ class ChatBot:
else:
if groupinfo.group_id in global_config.talk_allowed_groups:
# logger.debug(f"开始群聊模式{str(message_data)[:50]}...")
if global_config.response_mode == "heart_FC":
if global_config.response_mode == "heart_flow":
# logger.info(f"启动最新最好的思维流FC模式{str(message_data)[:50]}...")
await self.heartFC_processor.process_message(message_data)
elif global_config.response_mode == "reasoning":
# logger.debug(f"开始推理模式{str(message_data)[:50]}...")
await self.reasoning_chat.process_message(message_data)
@@ -136,7 +139,7 @@ class ChatBot:
# 私聊处理流程
# await self._handle_private_chat(message)
if global_config.response_mode == "heart_flow":
await self.think_flow_chat.process_message(message_data)
await self.heartFC_processor.process_message(message_data)
elif global_config.response_mode == "reasoning":
await self.reasoning_chat.process_message(message_data)
else:
@@ -144,7 +147,7 @@ class ChatBot:
else: # 群聊处理
if groupinfo.group_id in global_config.talk_allowed_groups:
if global_config.response_mode == "heart_flow":
await self.think_flow_chat.process_message(message_data)
await self.heartFC_processor.process_message(message_data)
elif global_config.response_mode == "reasoning":
await self.reasoning_chat.process_message(message_data)
else:

View File

@@ -313,17 +313,21 @@ class MessageSending(MessageProcessBase):
def set_reply(self, reply: Optional["MessageRecv"] = None) -> None:
"""设置回复消息"""
if reply:
self.reply = reply
if self.reply:
self.reply_to_message_id = self.reply.message_info.message_id
self.message_segment = Seg(
type="seglist",
data=[
Seg(type="reply", data=self.reply.message_info.message_id),
self.message_segment,
],
)
if (
self.message_info.format_info.accept_format is not None
and "reply" in self.message_info.format_info.accept_format
):
if reply:
self.reply = reply
if self.reply:
self.reply_to_message_id = self.reply.message_info.message_id
self.message_segment = Seg(
type="seglist",
data=[
Seg(type="reply", data=self.reply.message_info.message_id),
self.message_segment,
],
)
return self
async def process(self) -> None:

View File

@@ -375,17 +375,21 @@ class ThinkFlowChat:
info_catcher.done_catch()
# 处理表情包
try:
with Timer("处理表情包", timing_results):
if global_config.emoji_chance == 1:
if send_emoji:
logger.info(f"麦麦决定发送表情包{send_emoji}")
await self._handle_emoji(message, chat, response_set, send_emoji)
else:
if random() < global_config.emoji_chance:
await self._handle_emoji(message, chat, response_set)
except Exception as e:
logger.error(f"心流处理表情包失败: {e}")
if (
message.message_info.format_info.accept_format is not None
and "emoji" in message.message_info.format_info.accept_format
):
try:
with Timer("处理表情包", timing_results):
if global_config.emoji_chance == 1:
if send_emoji:
logger.info(f"麦麦决定发送表情包{send_emoji}")
await self._handle_emoji(message, chat, response_set, send_emoji)
else:
if random() < global_config.emoji_chance:
await self._handle_emoji(message, chat, response_set)
except Exception as e:
logger.error(f"心流处理表情包失败: {e}")
# 思考后脑内状态更新
# try:

View File

@@ -1,534 +0,0 @@
import time
import traceback
from typing import List, Optional, Dict
import asyncio
from asyncio import Lock
from ...moods.moods import MoodManager
from ....config.config import global_config
from ...chat.emoji_manager import emoji_manager
from .heartFC__generator import ResponseGenerator
from ...chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet
from .messagesender import MessageManager
from ...chat.utils_image import image_path_to_base64
from ...message import UserInfo, Seg
from src.heart_flow.heartflow import heartflow
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
from ...person_info.relationship_manager import relationship_manager
from src.plugins.respon_info_catcher.info_catcher import info_catcher_manager
from ...utils.timer_calculater import Timer
from src.do_tool.tool_use import ToolUser
from .interest import InterestManager
from src.plugins.chat.chat_stream import chat_manager
from src.plugins.chat.message import BaseMessageInfo
from .pf_chatting import PFChatting
# 定义日志配置
chat_config = LogConfig(
console_format=CHAT_STYLE_CONFIG["console_format"],
file_format=CHAT_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("heartFC_chat", config=chat_config)
# 检测群聊兴趣的间隔时间
INTEREST_MONITOR_INTERVAL_SECONDS = 1
class HeartFC_Chat:
_instance = None # For potential singleton access if needed by MessageManager
def __init__(self):
# --- Updated Init ---
if HeartFC_Chat._instance is not None:
# Prevent re-initialization if used as a singleton
return
self.gpt = ResponseGenerator()
self.mood_manager = MoodManager.get_instance()
self.mood_manager.start_mood_update()
self.tool_user = ToolUser()
self.interest_manager = InterestManager()
self._interest_monitor_task: Optional[asyncio.Task] = None
# --- New PFChatting Management ---
self.pf_chatting_instances: Dict[str, PFChatting] = {}
self._pf_chatting_lock = Lock()
# --- End New PFChatting Management ---
HeartFC_Chat._instance = self # Register instance
# --- End Updated Init ---
# --- Added Class Method for Singleton Access ---
@classmethod
def get_instance(cls):
return cls._instance
# --- End Added Class Method ---
async def start(self):
"""启动异步任务,如回复启动器"""
logger.debug("HeartFC_Chat 正在启动异步任务...")
self._initialize_monitor_task()
logger.info("HeartFC_Chat 异步任务启动完成")
def _initialize_monitor_task(self):
"""启动后台兴趣监控任务,可以检查兴趣是否足以开启心流对话"""
if self._interest_monitor_task is None or self._interest_monitor_task.done():
try:
loop = asyncio.get_running_loop()
self._interest_monitor_task = loop.create_task(self._interest_monitor_loop())
except RuntimeError:
logger.error("创建兴趣监控任务失败:没有运行中的事件循环。")
raise
else:
logger.warning("跳过兴趣监控任务创建:任务已存在或正在运行。")
# --- Added PFChatting Instance Manager ---
async def _get_or_create_pf_chatting(self, stream_id: str) -> Optional[PFChatting]:
"""获取现有PFChatting实例或创建新实例。"""
async with self._pf_chatting_lock:
if stream_id not in self.pf_chatting_instances:
logger.info(f"为流 {stream_id} 创建新的PFChatting实例")
# 传递 self (HeartFC_Chat 实例) 进行依赖注入
instance = PFChatting(stream_id, self)
# 执行异步初始化
if not await instance._initialize():
logger.error(f"为流 {stream_id} 初始化PFChatting失败")
return None
self.pf_chatting_instances[stream_id] = instance
return self.pf_chatting_instances[stream_id]
# --- End Added PFChatting Instance Manager ---
async def _interest_monitor_loop(self):
"""后台任务,定期检查兴趣度变化并触发回复"""
logger.info("兴趣监控循环开始...")
while True:
await asyncio.sleep(INTEREST_MONITOR_INTERVAL_SECONDS)
try:
# 从心流中获取活跃流
active_stream_ids = list(heartflow.get_all_subheartflows_streams_ids())
for stream_id in active_stream_ids:
stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
sub_hf = heartflow.get_subheartflow(stream_id)
if not sub_hf:
logger.warning(f"监控循环: 无法获取活跃流 {stream_name} 的 sub_hf")
continue
should_trigger = False
try:
interest_chatting = self.interest_manager.get_interest_chatting(stream_id)
if interest_chatting:
should_trigger = interest_chatting.should_evaluate_reply()
else:
logger.trace(
f"[{stream_name}] 没有找到对应的 InterestChatting 实例,跳过基于兴趣的触发检查。"
)
except Exception as e:
logger.error(f"检查兴趣触发器时出错 流 {stream_name}: {e}")
logger.error(traceback.format_exc())
if should_trigger:
# 启动一次麦麦聊天
pf_instance = await self._get_or_create_pf_chatting(stream_id)
if pf_instance:
asyncio.create_task(pf_instance.add_time())
else:
logger.error(f"[{stream_name}] 无法获取或创建PFChatting实例。跳过触发。")
except asyncio.CancelledError:
logger.info("兴趣监控循环已取消。")
break
except Exception as e:
logger.error(f"兴趣监控循环错误: {e}")
logger.error(traceback.format_exc())
await asyncio.sleep(5) # 发生错误时等待
async def _create_thinking_message(self, anchor_message: Optional[MessageRecv]):
"""创建思考消息 (尝试锚定到 anchor_message)"""
if not anchor_message or not anchor_message.chat_stream:
logger.error("无法创建思考消息,缺少有效的锚点消息或聊天流。")
return None
chat = anchor_message.chat_stream
messageinfo = anchor_message.message_info
bot_user_info = UserInfo(
user_id=global_config.BOT_QQ,
user_nickname=global_config.BOT_NICKNAME,
platform=messageinfo.platform,
)
thinking_time_point = round(time.time(), 2)
thinking_id = "mt" + str(thinking_time_point)
thinking_message = MessageThinking(
message_id=thinking_id,
chat_stream=chat,
bot_user_info=bot_user_info,
reply=anchor_message, # 回复的是锚点消息
thinking_start_time=thinking_time_point,
)
MessageManager().add_message(thinking_message)
return thinking_id
async def _send_response_messages(
self, anchor_message: Optional[MessageRecv], response_set: List[str], thinking_id
) -> Optional[MessageSending]:
"""发送回复消息 (尝试锚定到 anchor_message)"""
if not anchor_message or not anchor_message.chat_stream:
logger.error("无法发送回复,缺少有效的锚点消息或聊天流。")
return None
chat = anchor_message.chat_stream
container = MessageManager().get_container(chat.stream_id)
thinking_message = None
for msg in container.messages:
if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id:
thinking_message = msg
container.messages.remove(msg)
break
if not thinking_message:
stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id # 获取流名称
logger.warning(f"[{stream_name}] 未找到对应的思考消息 {thinking_id},可能已超时被移除")
return None
thinking_start_time = thinking_message.thinking_start_time
message_set = MessageSet(chat, thinking_id)
mark_head = False
first_bot_msg = None
for msg_text in response_set:
message_segment = Seg(type="text", data=msg_text)
bot_message = MessageSending(
message_id=thinking_id, # 使用 thinking_id 作为批次标识
chat_stream=chat,
bot_user_info=UserInfo(
user_id=global_config.BOT_QQ,
user_nickname=global_config.BOT_NICKNAME,
platform=anchor_message.message_info.platform,
),
sender_info=anchor_message.message_info.user_info, # 发送给锚点消息的用户
message_segment=message_segment,
reply=anchor_message, # 回复锚点消息
is_head=not mark_head,
is_emoji=False,
thinking_start_time=thinking_start_time,
)
if not mark_head:
mark_head = True
first_bot_msg = bot_message
message_set.add_message(bot_message)
if message_set.messages: # 确保有消息才添加
MessageManager().add_message(message_set)
return first_bot_msg
else:
stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id # 获取流名称
logger.warning(f"[{stream_name}] 没有生成有效的回复消息集,无法发送。")
return None
async def _handle_emoji(self, anchor_message: Optional[MessageRecv], response_set, send_emoji=""):
"""处理表情包 (尝试锚定到 anchor_message)"""
if not anchor_message or not anchor_message.chat_stream:
logger.error("无法处理表情包,缺少有效的锚点消息或聊天流。")
return
chat = anchor_message.chat_stream
if send_emoji:
emoji_raw = await emoji_manager.get_emoji_for_text(send_emoji)
else:
emoji_text_source = "".join(response_set) if response_set else ""
emoji_raw = await emoji_manager.get_emoji_for_text(emoji_text_source)
if emoji_raw:
emoji_path, description = emoji_raw
emoji_cq = image_path_to_base64(emoji_path)
# 使用当前时间戳,因为没有原始消息的时间戳
thinking_time_point = round(time.time(), 2)
message_segment = Seg(type="emoji", data=emoji_cq)
bot_message = MessageSending(
message_id="me" + str(thinking_time_point), # 使用不同的 ID 前缀?
chat_stream=chat,
bot_user_info=UserInfo(
user_id=global_config.BOT_QQ,
user_nickname=global_config.BOT_NICKNAME,
platform=anchor_message.message_info.platform,
),
sender_info=anchor_message.message_info.user_info,
message_segment=message_segment,
reply=anchor_message, # 回复锚点消息
is_head=False,
is_emoji=True,
)
MessageManager().add_message(bot_message)
async def _update_relationship(self, anchor_message: Optional[MessageRecv], response_set):
"""更新关系情绪 (尝试基于 anchor_message)"""
if not anchor_message or not anchor_message.chat_stream:
logger.error("无法更新关系情绪,缺少有效的锚点消息或聊天流。")
return
# 关系更新依赖于理解回复是针对谁的,以及原始消息的上下文
# 这里的实现可能需要调整,取决于关系管理器如何工作
ori_response = ",".join(response_set)
# 注意anchor_message.processed_plain_text 是锚点消息的文本,不一定是思考的全部上下文
stance, emotion = await self.gpt._get_emotion_tags(ori_response, anchor_message.processed_plain_text)
await relationship_manager.calculate_update_relationship_value(
chat_stream=anchor_message.chat_stream, # 使用锚点消息的流
label=emotion,
stance=stance,
)
self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor)
# 暂不使用
async def trigger_reply_generation(self, stream_id: str, observed_messages: List[dict]):
"""根据 SubHeartflow 的触发信号生成回复 (基于观察)"""
stream_name = chat_manager.get_stream_name(stream_id) or stream_id # <--- 在开始时获取名称
chat = None
sub_hf = None
anchor_message: Optional[MessageRecv] = None # <--- 重命名,用于锚定回复的消息对象
userinfo: Optional[UserInfo] = None
messageinfo: Optional[BaseMessageInfo] = None
timing_results = {}
current_mind = None
response_set = None
thinking_id = None
info_catcher = None
try:
# --- 1. 获取核心对象ChatStream 和 SubHeartflow ---
try:
with Timer("获取聊天流和子心流", timing_results):
chat = chat_manager.get_stream(stream_id)
if not chat:
logger.error(f"[{stream_name}] 无法找到聊天流对象,无法生成回复。")
return
sub_hf = heartflow.get_subheartflow(stream_id)
if not sub_hf:
logger.error(f"[{stream_name}] 无法找到子心流对象,无法生成回复。")
return
except Exception as e:
logger.error(f"[{stream_name}] 获取 ChatStream 或 SubHeartflow 时出错: {e}")
logger.error(traceback.format_exc())
return
# --- 2. 尝试从 observed_messages 重建最后一条消息作为锚点, 失败则创建占位符 --- #
try:
with Timer("获取或创建锚点消息", timing_results):
reconstruction_failed = False
if observed_messages:
try:
last_msg_dict = observed_messages[-1]
logger.debug(
f"[{stream_name}] Attempting to reconstruct MessageRecv from last observed message."
)
anchor_message = MessageRecv(last_msg_dict, chat_stream=chat)
if not (
anchor_message
and anchor_message.message_info
and anchor_message.message_info.message_id
and anchor_message.message_info.user_info
):
raise ValueError("Reconstructed MessageRecv missing essential info.")
userinfo = anchor_message.message_info.user_info
messageinfo = anchor_message.message_info
logger.debug(
f"[{stream_name}] Successfully reconstructed anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}"
)
except Exception as e_reconstruct:
logger.warning(
f"[{stream_name}] Reconstructing MessageRecv from observed message failed: {e_reconstruct}. Will create placeholder."
)
reconstruction_failed = True
else:
logger.warning(
f"[{stream_name}] observed_messages is empty. Will create placeholder anchor message."
)
reconstruction_failed = True # Treat empty observed_messages as a failure to reconstruct
# 如果重建失败或 observed_messages 为空,创建占位符
if reconstruction_failed:
placeholder_id = f"mid_{int(time.time() * 1000)}" # 使用毫秒时间戳增加唯一性
placeholder_user = UserInfo(user_id="system_trigger", user_nickname="系统触发")
placeholder_msg_info = BaseMessageInfo(
message_id=placeholder_id,
platform=chat.platform,
group_info=chat.group_info,
user_info=placeholder_user,
time=time.time(),
# 其他 BaseMessageInfo 可能需要的字段设为默认值或 None
)
# 创建 MessageRecv 实例,注意它需要消息字典结构,我们创建一个最小化的
placeholder_msg_dict = {
"message_info": placeholder_msg_info.to_dict(),
"processed_plain_text": "", # 提供空文本
"raw_message": "",
"time": placeholder_msg_info.time,
}
# 先只用字典创建实例
anchor_message = MessageRecv(placeholder_msg_dict)
# 然后调用方法更新 chat_stream
anchor_message.update_chat_stream(chat)
userinfo = anchor_message.message_info.user_info
messageinfo = anchor_message.message_info
logger.info(
f"[{stream_name}] Created placeholder anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}"
)
except Exception as e:
logger.error(f"[{stream_name}] 获取或创建锚点消息时出错: {e}")
logger.error(traceback.format_exc())
anchor_message = None # 确保出错时 anchor_message 为 None
# --- 4. 检查并发思考限制 (使用 anchor_message 简化获取) ---
try:
container = MessageManager().get_container(chat.stream_id)
thinking_count = container.count_thinking_messages()
max_thinking_messages = getattr(global_config, "max_concurrent_thinking_messages", 3)
if thinking_count >= max_thinking_messages:
logger.warning(f"聊天流 {stream_name} 已有 {thinking_count} 条思考消息,取消回复。")
return
except Exception as e:
logger.error(f"[{stream_name}] 检查并发思考限制时出错: {e}")
return
# --- 5. 创建思考消息 (使用 anchor_message) ---
try:
with Timer("创建思考消息", timing_results):
# 注意:这里传递 anchor_message 给 _create_thinking_message
thinking_id = await self._create_thinking_message(anchor_message)
except Exception as e:
logger.error(f"[{stream_name}] 创建思考消息失败: {e}")
return
if not thinking_id:
logger.error(f"[{stream_name}] 未能成功创建思考消息 ID无法继续回复流程。")
return
# --- 6. 信息捕捉器 (使用 anchor_message) ---
logger.trace(f"[{stream_name}] 创建捕捉器thinking_id:{thinking_id}")
info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
info_catcher.catch_decide_to_response(anchor_message)
# --- 7. 思考前使用工具 --- #
get_mid_memory_id = []
tool_result_info = {}
send_emoji = ""
observation_context_text = "" # 从 observation 获取上下文文本
try:
# --- 使用传入的 observed_messages 构建上下文文本 --- #
if observed_messages:
# 可以选择转换全部消息,或只转换最后几条
# 这里示例转换全部消息
context_texts = []
for msg_dict in observed_messages:
# 假设 detailed_plain_text 字段包含所需文本
# 你可能需要更复杂的逻辑来格式化,例如添加发送者和时间
text = msg_dict.get("detailed_plain_text", "")
if text:
context_texts.append(text)
observation_context_text = " ".join(context_texts)
else:
logger.warning(f"[{stream_name}] observed_messages 列表为空,无法为工具提供上下文。")
if observation_context_text:
with Timer("思考前使用工具", timing_results):
tool_result = await self.tool_user.use_tool(
message_txt=observation_context_text, # <--- 使用观察上下文
chat_stream=chat,
sub_heartflow=sub_hf,
)
if tool_result.get("used_tools", False):
if "structured_info" in tool_result:
tool_result_info = tool_result["structured_info"]
get_mid_memory_id = []
for tool_name, tool_data in tool_result_info.items():
if tool_name == "mid_chat_mem":
for mid_memory in tool_data:
get_mid_memory_id.append(mid_memory["content"])
if tool_name == "send_emoji":
send_emoji = tool_data[0]["content"]
except Exception as e:
logger.error(f"[{stream_name}] 思考前工具调用失败: {e}")
logger.error(traceback.format_exc())
# --- 8. 调用 SubHeartflow 进行思考 (不传递具体消息文本和发送者) ---
try:
with Timer("生成内心想法(SubHF)", timing_results):
# 不再传递 message_txt 和 sender_info, SubHeartflow 应基于其内部观察
current_mind, past_mind = await sub_hf.do_thinking_before_reply(
# sender_info=userinfo,
chat_stream=chat,
extra_info=tool_result_info,
obs_id=get_mid_memory_id,
)
logger.info(f"[{stream_name}] SubHeartflow 思考完成: {current_mind}")
except Exception as e:
logger.error(f"[{stream_name}] SubHeartflow 思考失败: {e}")
logger.error(traceback.format_exc())
if info_catcher:
info_catcher.done_catch()
return # 思考失败则不继续
if info_catcher:
info_catcher.catch_afer_shf_step(timing_results.get("生成内心想法(SubHF)"), past_mind, current_mind)
# --- 9. 调用 ResponseGenerator 生成回复 (使用 anchor_message 和 current_mind) ---
try:
with Timer("生成最终回复(GPT)", timing_results):
# response_set = await self.gpt.generate_response(anchor_message, thinking_id, current_mind=current_mind)
response_set = await self.gpt.generate_response(anchor_message, thinking_id)
except Exception as e:
logger.error(f"[{stream_name}] GPT 生成回复失败: {e}")
logger.error(traceback.format_exc())
if info_catcher:
info_catcher.done_catch()
return
if info_catcher:
info_catcher.catch_after_generate_response(timing_results.get("生成最终回复(GPT)"))
if not response_set:
logger.info(f"[{stream_name}] 回复生成失败或为空。")
if info_catcher:
info_catcher.done_catch()
return
# --- 10. 发送消息 (使用 anchor_message) ---
first_bot_msg = None
try:
with Timer("发送消息", timing_results):
first_bot_msg = await self._send_response_messages(anchor_message, response_set, thinking_id)
except Exception as e:
logger.error(f"[{stream_name}] 发送消息失败: {e}")
logger.error(traceback.format_exc())
if info_catcher:
info_catcher.catch_after_response(timing_results.get("发送消息"), response_set, first_bot_msg)
info_catcher.done_catch() # 完成捕捉
# --- 11. 处理表情包 (使用 anchor_message) ---
try:
with Timer("处理表情包", timing_results):
if send_emoji:
logger.info(f"[{stream_name}] 决定发送表情包 {send_emoji}")
await self._handle_emoji(anchor_message, response_set, send_emoji)
except Exception as e:
logger.error(f"[{stream_name}] 处理表情包失败: {e}")
logger.error(traceback.format_exc())
# --- 12. 记录性能日志 --- #
timing_str = " | ".join([f"{step}: {duration:.2f}" for step, duration in timing_results.items()])
response_msg = " ".join(response_set) if response_set else "无回复"
logger.info(
f"[{stream_name}] 回复任务完成 (Observation Triggered): | 思维消息: {response_msg[:30]}... | 性能计时: {timing_str}"
)
# --- 13. 更新关系情绪 (使用 anchor_message) ---
if first_bot_msg: # 仅在成功发送消息后
try:
with Timer("更新关系情绪", timing_results):
await self._update_relationship(anchor_message, response_set)
except Exception as e:
logger.error(f"[{stream_name}] 更新关系情绪失败: {e}")
logger.error(traceback.format_exc())
except Exception as e:
logger.error(f"回复生成任务失败 (trigger_reply_generation V4 - Observation Triggered): {e}")
logger.error(traceback.format_exc())
finally:
# 可以在这里添加清理逻辑,如果有的话
pass

View File

@@ -0,0 +1,146 @@
import traceback
from typing import Optional, Dict
import asyncio
from asyncio import Lock
from ...moods.moods import MoodManager
from ....config.config import global_config
from ...chat.emoji_manager import emoji_manager
from .heartFC_generator import ResponseGenerator
from .messagesender import MessageManager
from src.heart_flow.heartflow import heartflow
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
from src.plugins.person_info.relationship_manager import relationship_manager
from src.do_tool.tool_use import ToolUser
from .interest import InterestManager
from src.plugins.chat.chat_stream import chat_manager
from .pf_chatting import PFChatting
# 定义日志配置
chat_config = LogConfig(
console_format=CHAT_STYLE_CONFIG["console_format"],
file_format=CHAT_STYLE_CONFIG["file_format"],
)
logger = get_module_logger("HeartFC_Controller", config=chat_config)
# 检测群聊兴趣的间隔时间
INTEREST_MONITOR_INTERVAL_SECONDS = 1
class HeartFC_Controller:
_instance = None # For potential singleton access if needed by MessageManager
def __init__(self):
# --- Updated Init ---
if HeartFC_Controller._instance is not None:
# Prevent re-initialization if used as a singleton
return
self.gpt = ResponseGenerator()
self.mood_manager = MoodManager.get_instance()
self.mood_manager.start_mood_update()
self.tool_user = ToolUser()
self.interest_manager = InterestManager()
self._interest_monitor_task: Optional[asyncio.Task] = None
# --- New PFChatting Management ---
self.pf_chatting_instances: Dict[str, PFChatting] = {}
self._pf_chatting_lock = Lock()
# --- End New PFChatting Management ---
HeartFC_Controller._instance = self # Register instance
# --- End Updated Init ---
# --- Make dependencies accessible for PFChatting ---
# These are accessed via the passed instance in PFChatting
self.emoji_manager = emoji_manager
self.relationship_manager = relationship_manager
self.global_config = global_config
self.MessageManager = MessageManager # Pass the class/singleton access
# --- End dependencies ---
# --- Added Class Method for Singleton Access ---
@classmethod
def get_instance(cls):
if cls._instance is None:
# This might indicate an issue if called before initialization
logger.warning("HeartFC_Controller get_instance called before initialization.")
# Optionally, initialize here if a strict singleton pattern is desired
# cls._instance = cls()
return cls._instance
# --- End Added Class Method ---
async def start(self):
"""启动异步任务,如回复启动器"""
logger.debug("HeartFC_Controller 正在启动异步任务...")
self._initialize_monitor_task()
logger.info("HeartFC_Controller 异步任务启动完成")
def _initialize_monitor_task(self):
"""启动后台兴趣监控任务,可以检查兴趣是否足以开启心流对话"""
if self._interest_monitor_task is None or self._interest_monitor_task.done():
try:
loop = asyncio.get_running_loop()
self._interest_monitor_task = loop.create_task(self._interest_monitor_loop())
except RuntimeError:
logger.error("创建兴趣监控任务失败:没有运行中的事件循环。")
raise
else:
logger.warning("跳过兴趣监控任务创建:任务已存在或正在运行。")
# --- Added PFChatting Instance Manager ---
async def _get_or_create_pf_chatting(self, stream_id: str) -> Optional[PFChatting]:
"""获取现有PFChatting实例或创建新实例。"""
async with self._pf_chatting_lock:
if stream_id not in self.pf_chatting_instances:
logger.info(f"为流 {stream_id} 创建新的PFChatting实例")
# 传递 self (HeartFC_Controller 实例) 进行依赖注入
instance = PFChatting(stream_id, self)
# 执行异步初始化
if not await instance._initialize():
logger.error(f"为流 {stream_id} 初始化PFChatting失败")
return None
self.pf_chatting_instances[stream_id] = instance
return self.pf_chatting_instances[stream_id]
# --- End Added PFChatting Instance Manager ---
async def _interest_monitor_loop(self):
"""后台任务,定期检查兴趣度变化并触发回复"""
logger.info("兴趣监控循环开始...")
while True:
await asyncio.sleep(INTEREST_MONITOR_INTERVAL_SECONDS)
try:
# 从心流中获取活跃流
active_stream_ids = list(heartflow.get_all_subheartflows_streams_ids())
for stream_id in active_stream_ids:
stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
sub_hf = heartflow.get_subheartflow(stream_id)
if not sub_hf:
logger.warning(f"监控循环: 无法获取活跃流 {stream_name} 的 sub_hf")
continue
should_trigger = False
try:
interest_chatting = self.interest_manager.get_interest_chatting(stream_id)
if interest_chatting:
should_trigger = interest_chatting.should_evaluate_reply()
else:
logger.trace(
f"[{stream_name}] 没有找到对应的 InterestChatting 实例,跳过基于兴趣的触发检查。"
)
except Exception as e:
logger.error(f"检查兴趣触发器时出错 流 {stream_name}: {e}")
logger.error(traceback.format_exc())
if should_trigger:
# 启动一次麦麦聊天
pf_instance = await self._get_or_create_pf_chatting(stream_id)
if pf_instance:
asyncio.create_task(pf_instance.add_time())
else:
logger.error(f"[{stream_name}] 无法获取或创建PFChatting实例。跳过触发。")
except asyncio.CancelledError:
logger.info("兴趣监控循环已取消。")
break
except Exception as e:
logger.error(f"兴趣监控循环错误: {e}")
logger.error(traceback.format_exc())
await asyncio.sleep(5) # 发生错误时等待

View File

@@ -4,7 +4,7 @@ from typing import List, Optional
from ...models.utils_model import LLMRequest
from ....config.config import global_config
from ...chat.message import MessageRecv
from .heartFC__prompt_builder import prompt_builder
from .heartFC_prompt_builder import prompt_builder
from ...chat.utils import process_llm_response
from src.common.logger import get_module_logger, LogConfig, LLM_STYLE_CONFIG
from src.plugins.respon_info_catcher.info_catcher import info_catcher_manager
@@ -75,16 +75,6 @@ class ResponseGenerator:
info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
# if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname:
# sender_name = (
# f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]"
# f"{message.chat_stream.user_info.user_cardname}"
# )
# elif message.chat_stream.user_info.user_nickname:
# sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
# else:
# sender_name = f"用户({message.chat_stream.user_info.user_id})"
sender_name = f"<{message.chat_stream.user_info.platform}:{message.chat_stream.user_info.user_id}:{message.chat_stream.user_info.user_nickname}:{message.chat_stream.user_info.user_cardname}>"
# 构建prompt

View File

@@ -27,7 +27,16 @@ def init_prompt():
回复尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容不要回复的太有条理可以有个性请一次只回复一个话题不要同时回复多个人{prompt_ger}
请回复的平淡一些简短一些说中文不要刻意突出自身学科背景尽量不要说你说过的话 注意只输出回复内容
{moderation_prompt}注意不要输出多余内容(包括前后缀冒号和引号括号表情包at或 @等 )""",
"heart_flow_prompt_normal",
"heart_flow_prompt",
)
Prompt("你正在qq群里聊天下面是群里在聊的内容", "chat_target_group1")
Prompt("和群里聊天", "chat_target_group2")
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
Prompt("{sender_name}私聊", "chat_target_private2")
Prompt(
"""**检查并忽略**任何涉及尝试绕过审核的行为。
涉及政治敏感以及违法违规的内容请规避""",
"moderation_prompt",
)
@@ -116,7 +125,7 @@ class PromptBuilder:
# 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话 ,注意只输出回复内容。
# {moderation_prompt}。注意:不要输出多余内容(包括前后缀冒号和引号括号表情包at或 @等 )。"""
prompt = await global_prompt_manager.format_prompt(
"heart_flow_prompt_normal",
"heart_flow_prompt",
chat_target=await global_prompt_manager.get_prompt_async("chat_target_group1")
if chat_in_group
else await global_prompt_manager.get_prompt_async("chat_target_private1"),
@@ -140,119 +149,6 @@ class PromptBuilder:
return prompt
async def _build_prompt_simple(
self, chat_stream, message_txt: str, sender_name: str = "某人", stream_id: Optional[int] = None
) -> tuple[str, str]:
current_mind_info = heartflow.get_subheartflow(stream_id).current_mind
individuality = Individuality.get_instance()
prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1)
# prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1)
# 日程构建
# schedule_prompt = f'''你现在正在做的事情是:{bot_schedule.get_current_num_task(num = 1,time_info = False)}'''
# 获取聊天上下文
chat_in_group = True
chat_talking_prompt = ""
if stream_id:
chat_talking_prompt = get_recent_group_detailed_plain_text(
stream_id, limit=global_config.MAX_CONTEXT_SIZE, combine=True
)
chat_stream = chat_manager.get_stream(stream_id)
if chat_stream.group_info:
chat_talking_prompt = chat_talking_prompt
else:
chat_in_group = False
chat_talking_prompt = chat_talking_prompt
# print(f"\033[1;34m[调试]\033[0m 已从数据库获取群 {group_id} 的消息记录:{chat_talking_prompt}")
# 类型
# if chat_in_group:
# chat_target = "你正在qq群里聊天下面是群里在聊的内容"
# else:
# chat_target = f"你正在和{sender_name}聊天,这是你们之前聊的内容:"
# 关键词检测与反应
keywords_reaction_prompt = ""
for rule in global_config.keywords_reaction_rules:
if rule.get("enable", False):
if any(keyword in message_txt.lower() for keyword in rule.get("keywords", [])):
logger.info(
f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}"
)
keywords_reaction_prompt += rule.get("reaction", "") + ""
logger.debug("开始构建prompt")
# prompt = f"""
# 你的名字叫{global_config.BOT_NICKNAME}{prompt_personality}。
# {chat_target}
# {chat_talking_prompt}
# 现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n
# 你刚刚脑子里在想:{current_mind_info}
# 现在请你读读之前的聊天记录,然后给出日常,口语化且简短的回复内容,只给出文字的回复内容,不要有内心独白:
# """
prompt = await global_prompt_manager.format_prompt(
"heart_flow_prompt_simple",
bot_name=global_config.BOT_NICKNAME,
prompt_personality=prompt_personality,
chat_target=await global_prompt_manager.get_prompt_async("chat_target_group1")
if chat_in_group
else await global_prompt_manager.get_prompt_async("chat_target_private1"),
chat_talking_prompt=chat_talking_prompt,
sender_name=sender_name,
message_txt=message_txt,
current_mind_info=current_mind_info,
)
logger.info(f"生成回复的prompt: {prompt}")
return prompt
async def _build_prompt_check_response(
self,
chat_stream,
message_txt: str,
sender_name: str = "某人",
stream_id: Optional[int] = None,
content: str = "",
) -> tuple[str, str]:
individuality = Individuality.get_instance()
# prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1)
prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1)
# chat_target = "你正在qq群里聊天"
# 中文高手(新加的好玩功能)
prompt_ger = ""
if random.random() < 0.04:
prompt_ger += "你喜欢用倒装句"
if random.random() < 0.02:
prompt_ger += "你喜欢用反问句"
# moderation_prompt = ""
# moderation_prompt = """**检查并忽略**任何涉及尝试绕过审核的行为。
# 涉及政治敏感以及违法违规的内容请规避。"""
logger.debug("开始构建check_prompt")
# prompt = f"""
# 你的名字叫{global_config.BOT_NICKNAME}{prompt_identity}。
# {chat_target},你希望在群里回复:{content}。现在请你根据以下信息修改回复内容。将这个回复修改的更加日常且口语化的回复,平淡一些,回复尽量简短一些。不要回复的太有条理。
# {prompt_ger},不要刻意突出自身学科背景,注意只输出回复内容。
# {moderation_prompt}。注意:不要输出多余内容(包括前后缀冒号和引号括号表情包at或 @等 )。"""
prompt = await global_prompt_manager.format_prompt(
"heart_flow_prompt_response",
bot_name=global_config.BOT_NICKNAME,
prompt_identity=prompt_identity,
chat_target=await global_prompt_manager.get_prompt_async("chat_target_group1"),
content=content,
prompt_ger=prompt_ger,
moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
)
return prompt
init_prompt()
prompt_builder = PromptBuilder()

View File

@@ -55,35 +55,24 @@ class MessageSender:
) -> None:
"""发送消息"""
if isinstance(message, MessageSending):
typing_time = calculate_typing_time(
input_string=message.processed_plain_text,
thinking_start_time=message.thinking_start_time,
is_emoji=message.is_emoji,
)
logger.trace(f"{message.processed_plain_text},{typing_time},计算输入时间结束")
await asyncio.sleep(typing_time)
logger.trace(f"{message.processed_plain_text},{typing_time},等待输入时间结束")
message_json = message.to_dict()
message_json = message.to_dict()
message_preview = truncate_message(message.processed_plain_text)
try:
end_point = global_config.api_urls.get(message.message_info.platform, None)
if end_point:
# logger.info(f"发送消息到{end_point}")
# logger.info(message_json)
try:
await global_api.send_message_rest(end_point, message_json)
except Exception as e:
logger.error(f"REST方式发送失败出现错误: {str(e)}")
logger.info("尝试使用ws发送")
await self.send_via_ws(message)
else:
message_preview = truncate_message(message.processed_plain_text)
try:
end_point = global_config.api_urls.get(message.message_info.platform, None)
if end_point:
try:
await global_api.send_message_rest(end_point, message_json)
except Exception as e:
logger.error(f"REST方式发送失败出现错误: {str(e)}")
logger.info("尝试使用ws发送")
await self.send_via_ws(message)
logger.success(f"发送消息 {message_preview} 成功")
except Exception as e:
logger.error(f"发送消息 {message_preview} 失败: {str(e)}")
else:
await self.send_via_ws(message)
logger.success(f"发送消息 {message_preview} 成功")
except Exception as e:
logger.error(f"发送消息 {message_preview} 失败: {str(e)}")
class MessageContainer:
@@ -173,6 +162,21 @@ class MessageManager:
container = self.get_container(chat_stream.stream_id)
container.add_message(message)
def check_if_sending_message_exist(self, chat_id, thinking_id):
"""检查指定聊天流的容器中是否存在具有特定 thinking_id 的 MessageSending 消息"""
container = self.get_container(chat_id)
if container.has_messages():
for message in container.get_all_messages():
# 首先确保是 MessageSending 类型
if isinstance(message, MessageSending):
# 然后再访问 message_info.message_id
# 检查 message_id 是否匹配 thinking_id 或以 "me" 开头
if message.message_info.message_id == thinking_id or message.message_info.message_id[:2] == "me":
print(f"检查到存在相同thinking_id的消息: {message.message_info.message_id}???{thinking_id}")
return True
return False
async def process_chat_messages(self, chat_id: str):
"""处理聊天流消息"""
container = self.get_container(chat_id)
@@ -204,26 +208,23 @@ class MessageManager:
thinking_messages_count, thinking_messages_length = count_messages_between(
start_time=thinking_start_time, end_time=now_time, stream_id=message_earliest.chat_stream.stream_id
)
# print(thinking_time)
# print(thinking_messages_count)
# print(thinking_messages_length)
if (
message_earliest.is_head
and (thinking_messages_count > 3 or thinking_messages_length > 200)
and not message_earliest.is_private_message() # 避免在私聊时插入reply
):
logger.debug(f"距离原始消息太长,设置回复消息{message_earliest.processed_plain_text}")
message_earliest.set_reply()
await message_earliest.process()
# print(f"message_earliest.thinking_start_tim22222e:{message_earliest.thinking_start_time}")
# 获取 MessageSender 的单例实例并发送消息
await MessageSender().send_message(message_earliest)
typing_time = calculate_typing_time(
input_string=message_earliest.processed_plain_text,
thinking_start_time=message_earliest.thinking_start_time,
is_emoji=message_earliest.is_emoji,
)
logger.trace(f"\n{message_earliest.processed_plain_text},{typing_time},计算输入时间结束\n")
await asyncio.sleep(typing_time)
logger.debug(f"\n{message_earliest.processed_plain_text},{typing_time},等待输入时间结束\n")
await self.storage.store_message(message_earliest, message_earliest.chat_stream)
await MessageSender().send_message(message_earliest)
container.remove_message(message_earliest)

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,100 @@
新写一个类叫做pfchating
这个类初始化时会输入一个chat_stream或者stream_id
这个类会包含对应的sub_hearflow和一个chat_stream
# PFChatting 与主动回复流程说明 (V2)
pfchating有以下几个组成部分:
规划器决定是否要进行回复根据sub_heartflow中的observe内容可以选择不回复回复文字或者回复表情包你可以使用llm的工具调用来实现
回复器可以根据信息产生回复这部分代码将大部分与trigger_reply_generation(stream_id, observed_messages)一模一样
(回复器可能同时运行多个(0-3个),这些回复器会根据不同时刻的规划器产生不同回复
检查器:由于生成回复需要时间,检查器会检查在有了新的消息内容之后,回复是否还适合,如果合适就转给发送器
如果一条消息被发送了,其他回复在检查时也要增加这条消息的信息,防止重复发送内容相近的回复
发送器将回复发送到聊天这部分主体不需要再pfcchating中实现只需要使用原有的self._send_response_messages(anchor_message, response_set, thinking_id)
本文档描述了 `PFChatting` 类及其在 `heartFC_controler` 模块中实现的主动、基于兴趣的回复流程。
## 1. `PFChatting` 类概述
* **目标**: 管理特定聊天流 (`stream_id`) 的主动回复逻辑,使其行为更像人类的自然交流。
* **创建时机**: 当 `HeartFC_Chat` 的兴趣监控任务 (`_interest_monitor_loop`) 检测到某个聊天流的兴趣度 (`InterestChatting`) 达到了触发回复评估的条件 (`should_evaluate_reply`) 时,会为该 `stream_id` 获取或创建唯一的 `PFChatting` 实例 (`_get_or_create_pf_chatting`)。
* **持有**:
* 对应的 `sub_heartflow` 实例引用 (通过 `heartflow.get_subheartflow(stream_id)`)。
* 对应的 `chat_stream` 实例引用。
*`HeartFC_Chat` 单例的引用 (用于调用发送消息、处理表情等辅助方法)。
* **初始化**: `PFChatting` 实例在创建后会执行异步初始化 (`_initialize`),这可能包括加载必要的上下文或历史信息(*待确认是否实现了读取历史消息*)。
## 2. 核心回复流程 (由 `HeartFC_Chat` 触发)
`HeartFC_Chat` 调用 `PFChatting` 实例的方法 (例如 `add_time`) 时,会启动内部的回复决策与执行流程:
1. **规划 (Planner):**
* **输入**: 从关联的 `sub_heartflow` 获取观察结果、思考链、记忆片段等上下文信息。
* **决策**:
* 判断当前是否适合进行回复。
* 决定回复的形式(纯文本、带表情包等)。
* 选择合适的回复时机和策略。
* **实现**: *此部分逻辑待详细实现,可能利用 LLM 的工具调用能力来增强决策的灵活性和智能性。需要考虑机器人的个性化设定。*
2. **回复生成 (Replier):**
* **输入**: Planner 的决策结果和必要的上下文。
* **执行**:
* 调用 `ResponseGenerator` (`self.gpt`) 或类似组件生成具体的回复文本内容。
* 可能根据 Planner 的策略生成多个候选回复。
* **并发**: 系统支持同时存在多个思考/生成任务(上限由 `global_config.max_concurrent_thinking_messages` 控制)。
3. **检查 (Checker):**
* **时机**: 在回复生成过程中或生成后、发送前执行。
* **目的**:
* 检查自开始生成回复以来,聊天流中是否出现了新的消息。
* 评估已生成的候选回复在新的上下文下是否仍然合适、相关。
* *需要实现相似度比较逻辑,防止发送与近期消息内容相近或重复的回复。*
* **处理**: 如果检查结果认为回复不合适,则该回复将被**抛弃**。
4. **发送协调:**
* **执行**: 如果 Checker 通过,`PFChatting` 会调用 `HeartFC_Chat` 实例提供的发送接口:
* `_create_thinking_message`: 通知 `MessageManager` 显示"正在思考"状态。
* `_send_response_messages`: 将最终的回复文本交给 `MessageManager` 进行排队和发送。
* `_handle_emoji`: 如果需要发送表情包,调用此方法处理表情包的获取和发送。
* **细节**: 实际的消息发送、排队、间隔控制由 `MessageManager``MessageSender` 负责。
## 3. 与其他模块的交互
* **`HeartFC_Chat`**:
* 创建、管理和触发 `PFChatting` 实例。
* 提供发送消息 (`_send_response_messages`)、处理表情 (`_handle_emoji`)、创建思考消息 (`_create_thinking_message`) 的接口给 `PFChatting` 调用。
* 运行兴趣监控循环 (`_interest_monitor_loop`)。
* **`InterestManager` / `InterestChatting`**:
* `InterestManager` 存储每个 `stream_id``InterestChatting` 实例。
* `InterestChatting` 负责计算兴趣衰减和回复概率。
* `HeartFC_Chat` 查询 `InterestChatting.should_evaluate_reply()` 来决定是否触发 `PFChatting`
* **`heartflow` / `sub_heartflow`**:
* `PFChatting` 从对应的 `sub_heartflow` 获取进行规划所需的核心上下文信息 (观察、思考链等)。
* **`MessageManager` / `MessageSender`**:
* 接收来自 `HeartFC_Chat` 的发送请求 (思考消息、文本消息、表情包消息)。
* 管理消息队列 (`MessageContainer`),处理消息发送间隔和实际发送 (`MessageSender`)。
* **`ResponseGenerator` (`gpt`)**:
*`PFChatting` 的 Replier 部分调用,用于生成回复文本。
* **`MessageStorage`**:
* 存储所有接收和发送的消息。
* **`HippocampusManager`**:
* `HeartFC_Processor` 使用它计算传入消息的记忆激活率,作为兴趣度计算的输入之一。
## 4. 原有问题与状态更新
1. **每个 `pfchating` 是否对应一个 `chat_stream`,是否是唯一的?**
* **是**`HeartFC_Chat._get_or_create_pf_chatting` 确保了每个 `stream_id` 只有一个 `PFChatting` 实例。 (已确认)
2. **`observe_text` 传入进来是纯 str是不是应该传进来 message 构成的 list?**
* **机制已改变**。当前的触发机制是基于 `InterestManager` 的概率判断。`PFChatting` 启动后,应从其关联的 `sub_heartflow` 获取更丰富的上下文信息,而非简单的 `observe_text`
3. **检查失败的回复应该怎么处理?**
* **暂定:抛弃**。这是当前 Checker 逻辑的基础设定。
4. **如何比较相似度?**
* **待实现**。Checker 需要具体的算法来比较候选回复与新消息的相似度。
5. **Planner 怎么写?**
* **待实现**。这是 `PFChatting` 的核心决策逻辑,需要结合 `sub_heartflow` 的输出、LLM 工具调用和个性化配置来设计。
当_process_triggered_reply(self, stream_id: str, observed_messages: List[dict]):触发时,并不会单独进行一次回复
## 6. 未来优化点
* 实现 Checker 中的相似度比较算法。
* 详细设计并实现 Planner 的决策逻辑,包括 LLM 工具调用和个性化。
* 确认并完善 `PFChatting._initialize()` 中的历史消息加载逻辑。
* 探索更优的检查失败回复处理策略(例如:重新规划、修改回复等)。
* 优化 `PFChatting``sub_heartflow` 的信息交互。
问题:
1.每个pfchating是否对应一个caht_stream是否是唯一的(fix)
2.observe_text传入进来是纯str是不是应该传进来message构成的list?(fix)
3.检查失败的回复应该怎么处理?(先抛弃)
4.如何比较相似度?
5.planner怎么写好像可以先不加入这部分
BUG:
1.第一条激活消息没有被读取进入pfc聊天委托时应该读取一下之前的上文
1.第一条激活消息没有被读取进入pfc聊天委托时应该读取一下之前的上文(fix)
2.复读可能是planner还未校准好
3.planner还未个性化需要加入bot个性信息且获取的聊天内容有问题
4.心流好像过短,而且有时候没有等待更新
5.表情包有可能会发两次
5.表情包有可能会发两次(fix)

View File

@@ -1,6 +1,6 @@
import time
from random import random
import traceback
from typing import List
from ...memory_system.Hippocampus import HippocampusManager
from ...moods.moods import MoodManager
@@ -255,7 +255,7 @@ class ReasoningChat:
info_catcher.catch_after_generate_response(timing_results["生成回复"])
except Exception as e:
logger.error(f"回复生成出现错误:{str(e)}")
logger.error(f"回复生成出现错误:{str(e)} {traceback.format_exc()}")
response_set = None
if not response_set:

View File

@@ -126,7 +126,7 @@ class RelationshipManager:
if all_person[person_id] is not None:
person_name = all_person[person_id]
print(f"将<{platform}:{user_id}:{nickname}:{cardname}>替换为{person_name}")
# print(f"将<{platform}:{user_id}:{nickname}:{cardname}>替换为{person_name}")
result_text = result_text.replace(f"<{platform}:{user_id}:{nickname}:{cardname}>", person_name)