FEAT:PPPPfc in群聊,优化聊天流相关功能,新增获取聊天流名称的方法,调整日志输出以包含流名称,改进心流对话的提示信息,移除冗余代码,增强代码可读性。

This commit is contained in:
SengokuCola
2025-04-18 00:26:44 +08:00
parent 920aa5ed84
commit 3c50c6fb46
5 changed files with 190 additions and 187 deletions

View File

@@ -44,10 +44,9 @@ def init_prompt():
prompt += "现在是{time_now}你正在上网和qq群里的网友们聊天群里正在聊的话题是\n{chat_observe_info}\n" prompt += "现在是{time_now}你正在上网和qq群里的网友们聊天群里正在聊的话题是\n{chat_observe_info}\n"
prompt += "你现在{mood_info}\n" prompt += "你现在{mood_info}\n"
# prompt += "你注意到{sender_name}刚刚说:{message_txt}\n" # prompt += "你注意到{sender_name}刚刚说:{message_txt}\n"
prompt += "现在你接下去继续思考,产生新的想法,不要分点输出,输出连贯的内心独白" prompt += "思考时可以想想如何对群聊内容进行回复,关注新话题,大家正在说的话才是聊天的主题。回复的要求是:平淡一些,简短一些,说中文,尽量不要说你说过的话。如果你要回复,最好只回复一个人的一个话题\n"
prompt += "思考时可以想想如何对群聊内容进行回复。回复的要求是:平淡一些,简短一些,说中文,尽量不要说你说过的话。如果你要回复,最好只回复一个人的一个话题\n"
prompt += "请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要带有括号和动作描写" prompt += "请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要带有括号和动作描写"
prompt += "记得结合上述的消息,生成内心想法,文字不要浮夸,注意{bot_name}指的就是你。" prompt += "记得结合上述的消息,不要分点输出,生成内心想法,文字不要浮夸,注意{bot_name}指的就是你。"
Prompt(prompt, "sub_heartflow_prompt_before") Prompt(prompt, "sub_heartflow_prompt_before")
prompt = "" prompt = ""
# prompt += f"你现在正在做的事情是:{schedule_info}\n" # prompt += f"你现在正在做的事情是:{schedule_info}\n"
@@ -246,33 +245,6 @@ class SubHeartflow:
identity_detail = individuality.identity.identity_detail identity_detail = individuality.identity.identity_detail
if identity_detail: random.shuffle(identity_detail); prompt_personality += f",{identity_detail[0]}" if identity_detail: random.shuffle(identity_detail); prompt_personality += f",{identity_detail[0]}"
# who_chat_in_group = [
# (chat_stream.platform, sender_info.user_id, sender_info.user_nickname) # 先添加当前发送者
# ]
# # 获取最近发言者,排除当前发送者,避免重复
# recent_speakers = get_recent_group_speaker(
# chat_stream.stream_id,
# (chat_stream.platform, sender_info.user_id),
# limit=global_config.MAX_CONTEXT_SIZE -1 # 减去当前发送者
# )
# who_chat_in_group.extend(recent_speakers)
# relation_prompt = ""
# unique_speakers = set() # 确保人物信息不重复
# for person_tuple in who_chat_in_group:
# person_key = (person_tuple[0], person_tuple[1]) # 使用 platform+id 作为唯一标识
# if person_key not in unique_speakers:
# relation_prompt += await relationship_manager.build_relationship_info(person_tuple)
# unique_speakers.add(person_key)
# relation_prompt_all = (await global_prompt_manager.get_prompt_async("relationship_prompt")).format(
# relation_prompt, sender_info.user_nickname
# )
# sender_name_sign = (
# f"<{chat_stream.platform}:{sender_info.user_id}:{sender_info.user_nickname}:{sender_info.user_cardname or 'NoCard'}>"
# )
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_before")).format( prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_before")).format(

View File

@@ -188,6 +188,20 @@ class ChatManager:
stream_id = self._generate_stream_id(platform, user_info, group_info) stream_id = self._generate_stream_id(platform, user_info, group_info)
return self.streams.get(stream_id) return self.streams.get(stream_id)
def get_stream_name(self, stream_id: str) -> Optional[str]:
"""根据 stream_id 获取聊天流名称"""
stream = self.get_stream(stream_id)
if not stream:
return None
if stream.group_info and stream.group_info.group_name:
return stream.group_info.group_name
elif stream.user_info and stream.user_info.user_nickname:
return f"{stream.user_info.user_nickname}的私聊"
else:
# 如果没有群名或用户昵称,返回 None 或其他默认值
return None
async def _save_stream(self, stream: ChatStream): async def _save_stream(self, stream: ChatStream):
"""保存聊天流到数据库""" """保存聊天流到数据库"""
if not stream.saved: if not stream.saved:

View File

@@ -105,12 +105,13 @@ class HeartFC_Chat:
await asyncio.sleep(INTEREST_MONITOR_INTERVAL_SECONDS) await asyncio.sleep(INTEREST_MONITOR_INTERVAL_SECONDS)
try: try:
active_stream_ids = list(heartflow.get_all_subheartflows_streams_ids()) active_stream_ids = list(heartflow.get_all_subheartflows_streams_ids())
logger.trace(f"检查 {len(active_stream_ids)} 个活跃流是否足以开启心流对话...") # logger.trace(f"检查 {len(active_stream_ids)} 个活跃流是否足以开启心流对话...") # 调试日志
for stream_id in active_stream_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) sub_hf = heartflow.get_subheartflow(stream_id)
if not sub_hf: if not sub_hf:
logger.warning(f"监控循环: 无法获取活跃流 {stream_id} 的 sub_hf") logger.warning(f"监控循环: 无法获取活跃流 {stream_name} 的 sub_hf")
continue continue
should_trigger = False should_trigger = False
@@ -118,24 +119,21 @@ class HeartFC_Chat:
interest_chatting = self.interest_manager.get_interest_chatting(stream_id) interest_chatting = self.interest_manager.get_interest_chatting(stream_id)
if interest_chatting: if interest_chatting:
should_trigger = interest_chatting.should_evaluate_reply() should_trigger = interest_chatting.should_evaluate_reply()
if should_trigger: # if should_trigger:
logger.info(f"[{stream_id}] 基于兴趣概率决定启动交流模式 (概率: {interest_chatting.current_reply_probability:.4f})。") # logger.info(f"[{stream_name}] 基于兴趣概率决定启动交流模式 (概率: {interest_chatting.current_reply_probability:.4f})。")
else: else:
logger.trace(f"[{stream_id}] 没有找到对应的 InterestChatting 实例,跳过基于兴趣的触发检查。") logger.trace(f"[{stream_name}] 没有找到对应的 InterestChatting 实例,跳过基于兴趣的触发检查。")
except Exception as e: except Exception as e:
logger.error(f"检查兴趣触发器时出错 流 {stream_id}: {e}") logger.error(f"检查兴趣触发器时出错 流 {stream_name}: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
if should_trigger: if should_trigger:
logger.info(f"[{stream_id}] 触发条件满足, 委托给PFChatting.")
# --- 修改: 获取 PFChatting 实例并调用 add_time (无参数,时间由内部衰减逻辑决定) ---
pf_instance = await self._get_or_create_pf_chatting(stream_id) pf_instance = await self._get_or_create_pf_chatting(stream_id)
if pf_instance: if pf_instance:
# 调用 add_time 启动或延长循环,时间由 PFChatting 内部决定 # logger.info(f"[{stream_name}] 触发条件满足, 委托给PFChatting.")
asyncio.create_task(pf_instance.add_time()) asyncio.create_task(pf_instance.add_time())
else: else:
logger.error(f"[{stream_id}] 无法获取或创建PFChatting实例。跳过触发。") logger.error(f"[{stream_name}] 无法获取或创建PFChatting实例。跳过触发。")
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info("兴趣监控循环已取消。") logger.info("兴趣监控循环已取消。")
@@ -187,7 +185,8 @@ class HeartFC_Chat:
container.messages.remove(msg) container.messages.remove(msg)
break break
if not thinking_message: if not thinking_message:
logger.warning(f"[{chat.stream_id}] 未找到对应的思考消息 {thinking_id},可能已超时被移除") stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id # 获取流名称
logger.warning(f"[{stream_name}] 未找到对应的思考消息 {thinking_id},可能已超时被移除")
return None return None
thinking_start_time = thinking_message.thinking_start_time thinking_start_time = thinking_message.thinking_start_time
@@ -220,7 +219,8 @@ class HeartFC_Chat:
MessageManager().add_message(message_set) MessageManager().add_message(message_set)
return first_bot_msg return first_bot_msg
else: else:
logger.warning(f"[{chat.stream_id}] 没有生成有效的回复消息集,无法发送。") stream_name = chat_manager.get_stream_name(chat.stream_id) or chat.stream_id # 获取流名称
logger.warning(f"[{stream_name}] 没有生成有效的回复消息集,无法发送。")
return None return None
async def _handle_emoji(self, anchor_message: Optional[MessageRecv], response_set, send_emoji=""): async def _handle_emoji(self, anchor_message: Optional[MessageRecv], response_set, send_emoji=""):
@@ -278,6 +278,7 @@ class HeartFC_Chat:
async def trigger_reply_generation(self, stream_id: str, observed_messages: List[dict]): async def trigger_reply_generation(self, stream_id: str, observed_messages: List[dict]):
"""根据 SubHeartflow 的触发信号生成回复 (基于观察)""" """根据 SubHeartflow 的触发信号生成回复 (基于观察)"""
stream_name = chat_manager.get_stream_name(stream_id) or stream_id # <--- 在开始时获取名称
chat = None chat = None
sub_hf = None sub_hf = None
anchor_message: Optional[MessageRecv] = None # <--- 重命名,用于锚定回复的消息对象 anchor_message: Optional[MessageRecv] = None # <--- 重命名,用于锚定回复的消息对象
@@ -296,14 +297,14 @@ class HeartFC_Chat:
with Timer("获取聊天流和子心流", timing_results): with Timer("获取聊天流和子心流", timing_results):
chat = chat_manager.get_stream(stream_id) chat = chat_manager.get_stream(stream_id)
if not chat: if not chat:
logger.error(f"[{stream_id}] 无法找到聊天流对象,无法生成回复。") logger.error(f"[{stream_name}] 无法找到聊天流对象,无法生成回复。")
return return
sub_hf = heartflow.get_subheartflow(stream_id) sub_hf = heartflow.get_subheartflow(stream_id)
if not sub_hf: if not sub_hf:
logger.error(f"[{stream_id}] 无法找到子心流对象,无法生成回复。") logger.error(f"[{stream_name}] 无法找到子心流对象,无法生成回复。")
return return
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 获取 ChatStream 或 SubHeartflow 时出错: {e}") logger.error(f"[{stream_name}] 获取 ChatStream 或 SubHeartflow 时出错: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return return
@@ -314,18 +315,18 @@ class HeartFC_Chat:
if observed_messages: if observed_messages:
try: try:
last_msg_dict = observed_messages[-1] last_msg_dict = observed_messages[-1]
logger.debug(f"[{stream_id}] Attempting to reconstruct MessageRecv from last observed message.") logger.debug(f"[{stream_name}] Attempting to reconstruct MessageRecv from last observed message.")
anchor_message = MessageRecv(last_msg_dict, chat_stream=chat) 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): 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.") raise ValueError("Reconstructed MessageRecv missing essential info.")
userinfo = anchor_message.message_info.user_info userinfo = anchor_message.message_info.user_info
messageinfo = anchor_message.message_info messageinfo = anchor_message.message_info
logger.debug(f"[{stream_id}] Successfully reconstructed anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}") logger.debug(f"[{stream_name}] Successfully reconstructed anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}")
except Exception as e_reconstruct: except Exception as e_reconstruct:
logger.warning(f"[{stream_id}] Reconstructing MessageRecv from observed message failed: {e_reconstruct}. Will create placeholder.") logger.warning(f"[{stream_name}] Reconstructing MessageRecv from observed message failed: {e_reconstruct}. Will create placeholder.")
reconstruction_failed = True reconstruction_failed = True
else: else:
logger.warning(f"[{stream_id}] observed_messages is empty. Will create placeholder anchor message.") 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 reconstruction_failed = True # Treat empty observed_messages as a failure to reconstruct
# 如果重建失败或 observed_messages 为空,创建占位符 # 如果重建失败或 observed_messages 为空,创建占位符
@@ -353,10 +354,10 @@ class HeartFC_Chat:
anchor_message.update_chat_stream(chat) anchor_message.update_chat_stream(chat)
userinfo = anchor_message.message_info.user_info userinfo = anchor_message.message_info.user_info
messageinfo = anchor_message.message_info messageinfo = anchor_message.message_info
logger.info(f"[{stream_id}] Created placeholder anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}") logger.info(f"[{stream_name}] Created placeholder anchor message: ID={messageinfo.message_id}, Sender={userinfo.user_nickname}")
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 获取或创建锚点消息时出错: {e}") logger.error(f"[{stream_name}] 获取或创建锚点消息时出错: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
anchor_message = None # 确保出错时 anchor_message 为 None anchor_message = None # 确保出错时 anchor_message 为 None
@@ -366,10 +367,10 @@ class HeartFC_Chat:
thinking_count = container.count_thinking_messages() thinking_count = container.count_thinking_messages()
max_thinking_messages = getattr(global_config, 'max_concurrent_thinking_messages', 3) max_thinking_messages = getattr(global_config, 'max_concurrent_thinking_messages', 3)
if thinking_count >= max_thinking_messages: if thinking_count >= max_thinking_messages:
logger.warning(f"聊天流 {chat.stream_id} 已有 {thinking_count} 条思考消息,取消回复。") logger.warning(f"聊天流 {stream_name} 已有 {thinking_count} 条思考消息,取消回复。")
return return
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 检查并发思考限制时出错: {e}") logger.error(f"[{stream_name}] 检查并发思考限制时出错: {e}")
return return
# --- 5. 创建思考消息 (使用 anchor_message) --- # --- 5. 创建思考消息 (使用 anchor_message) ---
@@ -378,14 +379,14 @@ class HeartFC_Chat:
# 注意:这里传递 anchor_message 给 _create_thinking_message # 注意:这里传递 anchor_message 给 _create_thinking_message
thinking_id = await self._create_thinking_message(anchor_message) thinking_id = await self._create_thinking_message(anchor_message)
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 创建思考消息失败: {e}") logger.error(f"[{stream_name}] 创建思考消息失败: {e}")
return return
if not thinking_id: if not thinking_id:
logger.error(f"[{stream_id}] 未能成功创建思考消息 ID无法继续回复流程。") logger.error(f"[{stream_name}] 未能成功创建思考消息 ID无法继续回复流程。")
return return
# --- 6. 信息捕捉器 (使用 anchor_message) --- # --- 6. 信息捕捉器 (使用 anchor_message) ---
logger.trace(f"[{stream_id}] 创建捕捉器thinking_id:{thinking_id}") logger.trace(f"[{stream_name}] 创建捕捉器thinking_id:{thinking_id}")
info_catcher = info_catcher_manager.get_info_catcher(thinking_id) info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
info_catcher.catch_decide_to_response(anchor_message) info_catcher.catch_decide_to_response(anchor_message)
@@ -406,9 +407,9 @@ class HeartFC_Chat:
text = msg_dict.get('detailed_plain_text', '') text = msg_dict.get('detailed_plain_text', '')
if text: context_texts.append(text) if text: context_texts.append(text)
observation_context_text = "\n".join(context_texts) observation_context_text = "\n".join(context_texts)
logger.debug(f"[{stream_id}] Context for tools:\n{observation_context_text[-200:]}...") # 打印部分上下文 logger.debug(f"[{stream_name}] Context for tools:\n{observation_context_text[-200:]}...") # 打印部分上下文
else: else:
logger.warning(f"[{stream_id}] observed_messages 列表为空,无法为工具提供上下文。") logger.warning(f"[{stream_name}] observed_messages 列表为空,无法为工具提供上下文。")
if observation_context_text: if observation_context_text:
with Timer("思考前使用工具", timing_results): with Timer("思考前使用工具", timing_results):
@@ -428,7 +429,7 @@ class HeartFC_Chat:
if tool_name == "send_emoji": if tool_name == "send_emoji":
send_emoji = tool_data[0]["content"] send_emoji = tool_data[0]["content"]
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 思考前工具调用失败: {e}") logger.error(f"[{stream_name}] 思考前工具调用失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# --- 8. 调用 SubHeartflow 进行思考 (不传递具体消息文本和发送者) --- # --- 8. 调用 SubHeartflow 进行思考 (不传递具体消息文本和发送者) ---
@@ -441,9 +442,9 @@ class HeartFC_Chat:
extra_info=tool_result_info, extra_info=tool_result_info,
obs_id=get_mid_memory_id, obs_id=get_mid_memory_id,
) )
logger.info(f"[{stream_id}] SubHeartflow 思考完成: {current_mind}") logger.info(f"[{stream_name}] SubHeartflow 思考完成: {current_mind}")
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] SubHeartflow 思考失败: {e}") logger.error(f"[{stream_name}] SubHeartflow 思考失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
if info_catcher: info_catcher.done_catch() if info_catcher: info_catcher.done_catch()
return # 思考失败则不继续 return # 思考失败则不继续
@@ -456,14 +457,14 @@ class HeartFC_Chat:
# 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, current_mind=current_mind)
response_set = await self.gpt.generate_response(anchor_message, thinking_id) response_set = await self.gpt.generate_response(anchor_message, thinking_id)
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] GPT 生成回复失败: {e}") logger.error(f"[{stream_name}] GPT 生成回复失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
if info_catcher: info_catcher.done_catch() if info_catcher: info_catcher.done_catch()
return return
if info_catcher: if info_catcher:
info_catcher.catch_after_generate_response(timing_results.get("生成最终回复(GPT)")) info_catcher.catch_after_generate_response(timing_results.get("生成最终回复(GPT)"))
if not response_set: if not response_set:
logger.info(f"[{stream_id}] 回复生成失败或为空。") logger.info(f"[{stream_name}] 回复生成失败或为空。")
if info_catcher: info_catcher.done_catch() if info_catcher: info_catcher.done_catch()
return return
@@ -473,7 +474,7 @@ class HeartFC_Chat:
with Timer("发送消息", timing_results): with Timer("发送消息", timing_results):
first_bot_msg = await self._send_response_messages(anchor_message, response_set, thinking_id) first_bot_msg = await self._send_response_messages(anchor_message, response_set, thinking_id)
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 发送消息失败: {e}") logger.error(f"[{stream_name}] 发送消息失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
if info_catcher: if info_catcher:
info_catcher.catch_after_response(timing_results.get("发送消息"), response_set, first_bot_msg) info_catcher.catch_after_response(timing_results.get("发送消息"), response_set, first_bot_msg)
@@ -483,16 +484,16 @@ class HeartFC_Chat:
try: try:
with Timer("处理表情包", timing_results): with Timer("处理表情包", timing_results):
if send_emoji: if send_emoji:
logger.info(f"[{stream_id}] 决定发送表情包 {send_emoji}") logger.info(f"[{stream_name}] 决定发送表情包 {send_emoji}")
await self._handle_emoji(anchor_message, response_set, send_emoji) await self._handle_emoji(anchor_message, response_set, send_emoji)
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 处理表情包失败: {e}") logger.error(f"[{stream_name}] 处理表情包失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# --- 12. 记录性能日志 --- # # --- 12. 记录性能日志 --- #
timing_str = " | ".join([f"{step}: {duration:.2f}" for step, duration in timing_results.items()]) timing_str = " | ".join([f"{step}: {duration:.2f}" for step, duration in timing_results.items()])
response_msg = " ".join(response_set) if response_set else "无回复" response_msg = " ".join(response_set) if response_set else "无回复"
logger.info(f"[{stream_id}] 回复任务完成 (Observation Triggered): | 思维消息: {response_msg[:30]}... | 性能计时: {timing_str}") logger.info(f"[{stream_name}] 回复任务完成 (Observation Triggered): | 思维消息: {response_msg[:30]}... | 性能计时: {timing_str}")
# --- 13. 更新关系情绪 (使用 anchor_message) --- # --- 13. 更新关系情绪 (使用 anchor_message) ---
if first_bot_msg: # 仅在成功发送消息后 if first_bot_msg: # 仅在成功发送消息后
@@ -500,7 +501,7 @@ class HeartFC_Chat:
with Timer("更新关系情绪", timing_results): with Timer("更新关系情绪", timing_results):
await self._update_relationship(anchor_message, response_set) await self._update_relationship(anchor_message, response_set)
except Exception as e: except Exception as e:
logger.error(f"[{stream_id}] 更新关系情绪失败: {e}") logger.error(f"[{stream_name}] 更新关系情绪失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
except Exception as e: except Exception as e:

View File

@@ -218,13 +218,13 @@ class InterestChatting:
if self.current_reply_probability > 0: if self.current_reply_probability > 0:
# 只有在阈值之上且概率大于0时才有可能触发 # 只有在阈值之上且概率大于0时才有可能触发
trigger = random.random() < self.current_reply_probability trigger = random.random() < self.current_reply_probability
if trigger: # if trigger:
logger.info(f"回复概率评估触发! 概率: {self.current_reply_probability:.4f}, 阈值: {self.trigger_threshold}, 兴趣: {self.interest_level:.2f}") # logger.info(f"回复概率评估触发! 概率: {self.current_reply_probability:.4f}, 阈值: {self.trigger_threshold}, 兴趣: {self.interest_level:.2f}")
# 可选:触发后是否重置/降低概率?根据需要决定 # # 可选:触发后是否重置/降低概率?根据需要决定
# self.current_reply_probability = self.base_reply_probability # 例如,触发后降回基础概率 # # self.current_reply_probability = self.base_reply_probability # 例如,触发后降回基础概率
# self.current_reply_probability *= 0.5 # 例如,触发后概率减半 # # self.current_reply_probability *= 0.5 # 例如,触发后概率减半
else: # else:
logger.debug(f"回复概率评估未触发。概率: {self.current_reply_probability:.4f}") # logger.debug(f"回复概率评估未触发。概率: {self.current_reply_probability:.4f}")
return trigger return trigger
else: else:
# logger.debug(f"Reply evaluation check: Below threshold or zero probability. Probability: {self.current_reply_probability:.4f}") # logger.debug(f"Reply evaluation check: Below threshold or zero probability. Probability: {self.current_reply_probability:.4f}")
@@ -282,7 +282,7 @@ class InterestManager:
"""后台日志记录任务的异步函数 (记录历史数据,包含 group_name)""" """后台日志记录任务的异步函数 (记录历史数据,包含 group_name)"""
while True: while True:
await asyncio.sleep(interval_seconds) await asyncio.sleep(interval_seconds)
logger.debug(f"运行定期历史记录 (间隔: {interval_seconds}秒)...") # logger.debug(f"运行定期历史记录 (间隔: {interval_seconds}秒)...")
try: try:
current_timestamp = time.time() current_timestamp = time.time()
all_states = self.get_all_interest_states() # 获取当前所有状态 all_states = self.get_all_interest_states() # 获取当前所有状态
@@ -435,7 +435,8 @@ class InterestManager:
interest_chatting = self._get_or_create_interest_chatting(stream_id) interest_chatting = self._get_or_create_interest_chatting(stream_id)
# 调用修改后的 increase_interest不再传入 message # 调用修改后的 increase_interest不再传入 message
interest_chatting.increase_interest(current_time, value) interest_chatting.increase_interest(current_time, value)
logger.debug(f"增加了聊天流 {stream_id} 的兴趣度 {value:.2f},当前值为 {interest_chatting.interest_level:.2f}") # 更新日志 stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
logger.debug(f"增加了聊天流 {stream_name} 的兴趣度 {value:.2f},当前值为 {interest_chatting.interest_level:.2f}") # 更新日志
def decrease_interest(self, stream_id: str, value: float): def decrease_interest(self, stream_id: str, value: float):
"""降低指定聊天流的兴趣度""" """降低指定聊天流的兴趣度"""
@@ -444,9 +445,11 @@ class InterestManager:
interest_chatting = self.get_interest_chatting(stream_id) interest_chatting = self.get_interest_chatting(stream_id)
if interest_chatting: if interest_chatting:
interest_chatting.decrease_interest(current_time, value) interest_chatting.decrease_interest(current_time, value)
logger.debug(f"降低了聊天流 {stream_id} 的兴趣度 {value:.2f},当前值为 {interest_chatting.interest_level:.2f}") stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
logger.debug(f"降低了聊天流 {stream_name} 的兴趣度 {value:.2f},当前值为 {interest_chatting.interest_level:.2f}")
else: else:
logger.warning(f"尝试降低不存在的聊天流 {stream_id} 的兴趣度") stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
logger.warning(f"尝试降低不存在的聊天流 {stream_name} 的兴趣度")
def cleanup_inactive_chats(self, max_age_seconds=INACTIVE_THRESHOLD_SECONDS): def cleanup_inactive_chats(self, max_age_seconds=INACTIVE_THRESHOLD_SECONDS):
""" """
@@ -474,7 +477,8 @@ class InterestManager:
if should_remove: if should_remove:
keys_to_remove.append(stream_id) keys_to_remove.append(stream_id)
logger.debug(f"Marking stream_id {stream_id} for removal. Reason: {reason}") stream_name = chat_manager.get_stream_name(stream_id) or stream_id # 获取流名称
logger.debug(f"Marking stream {stream_name} for removal. Reason: {reason}")
if keys_to_remove: if keys_to_remove:
logger.info(f"清理识别到 {len(keys_to_remove)} 个不活跃/低兴趣的流。") logger.info(f"清理识别到 {len(keys_to_remove)} 个不活跃/低兴趣的流。")
@@ -483,7 +487,8 @@ class InterestManager:
# 再次检查 key 是否存在,以防万一在迭代和删除之间状态改变 # 再次检查 key 是否存在,以防万一在迭代和删除之间状态改变
if key in self.interest_dict: if key in self.interest_dict:
del self.interest_dict[key] del self.interest_dict[key]
logger.debug(f"移除了流_id: {key}") stream_name = chat_manager.get_stream_name(key) or key # 获取流名称
logger.debug(f"移除了流: {stream_name}")
final_count = initial_count - len(keys_to_remove) final_count = initial_count - len(keys_to_remove)
logger.info(f"清理完成。移除了 {len(keys_to_remove)} 个流。当前数量: {final_count}") logger.info(f"清理完成。移除了 {len(keys_to_remove)} 个流。当前数量: {final_count}")
else: else:

View File

@@ -92,11 +92,18 @@ class PFChatting:
self._loop_active: bool = False # Is the loop currently running? self._loop_active: bool = False # Is the loop currently running?
self._loop_task: Optional[asyncio.Task] = None # Stores the main loop task self._loop_task: Optional[asyncio.Task] = None # Stores the main loop task
self._trigger_count_this_activation: int = 0 # Counts triggers within an active period self._trigger_count_this_activation: int = 0 # Counts triggers within an active period
self._initial_duration: float = 10.0 # 首次触发增加的时间
self._last_added_duration: float = self._initial_duration # <--- 新增:存储上次增加的时间
# Removed pending_replies as processing is now serial within the loop # Removed pending_replies as processing is now serial within the loop
# self.pending_replies: Dict[str, PendingReply] = {} # self.pending_replies: Dict[str, PendingReply] = {}
def _get_log_prefix(self) -> str:
"""获取日志前缀,包含可读的流名称"""
stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
return f"[{stream_name}]"
async def _initialize(self) -> bool: async def _initialize(self) -> bool:
""" """
Lazy initialization to resolve chat_stream and sub_hf using the provided identifier. Lazy initialization to resolve chat_stream and sub_hf using the provided identifier.
@@ -105,95 +112,97 @@ class PFChatting:
async with self._init_lock: async with self._init_lock:
if self._initialized: if self._initialized:
return True return True
log_prefix = self._get_log_prefix() # 获取前缀
try: try:
self.chat_stream = chat_manager.get_stream(self.stream_id) self.chat_stream = chat_manager.get_stream(self.stream_id)
if not self.chat_stream: if not self.chat_stream:
logger.error(f"PFChatting-{self.stream_id} 获取ChatStream失败。") logger.error(f"{log_prefix} 获取ChatStream失败。")
return False return False
# 子心流(SubHeartflow)可能初始不存在但后续会被创建 # 子心流(SubHeartflow)可能初始不存在但后续会被创建
# 在需要它的方法中应优雅处理其可能缺失的情况 # 在需要它的方法中应优雅处理其可能缺失的情况
self.sub_hf = heartflow.get_subheartflow(self.stream_id) self.sub_hf = heartflow.get_subheartflow(self.stream_id)
if not self.sub_hf: if not self.sub_hf:
logger.warning(f"PFChatting-{self.stream_id} 获取SubHeartflow失败。一些功能可能受限。") logger.warning(f"{log_prefix} 获取SubHeartflow失败。一些功能可能受限。")
# 决定是否继续初始化。目前允许初始化。 # 决定是否继续初始化。目前允许初始化。
self._initialized = True self._initialized = True
logger.info(f"PFChatting-{self.stream_id} 初始化成功。") logger.info(f"麦麦感觉到了,激发了PFChatting{log_prefix} 初始化成功。")
return True return True
except Exception as e: except Exception as e:
logger.error(f"PFChatting-{self.stream_id} 初始化失败: {e}") logger.error(f"{log_prefix} 初始化失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False return False
async def add_time(self): async def add_time(self):
""" """
Adds time to the loop timer with decay and starts the loop if it's not active. Adds time to the loop timer with decay and starts the loop if it's not active.
Called externally (e.g., by HeartFC_Chat) to trigger or extend activity. First trigger adds initial duration, subsequent triggers add 50% of the previous addition.
Durations: 1st trigger = 10s, 2nd = 5s, 3rd+ = 2s.
""" """
log_prefix = self._get_log_prefix()
if not self._initialized: if not self._initialized:
if not await self._initialize(): if not await self._initialize():
logger.error(f"PFChatting-{self.stream_id} 无法添加时间: 未初始化。") logger.error(f"{log_prefix} 无法添加时间: 未初始化。")
return return
async with self._timer_lock: async with self._timer_lock:
duration_to_add: float = 0.0 duration_to_add: float = 0.0
if not self._loop_active: # First trigger for this activation cycle if not self._loop_active: # First trigger for this activation cycle
duration_to_add = 10.0 duration_to_add = self._initial_duration # 使用初始值
self._trigger_count_this_activation = 1 # Start counting for this activation self._last_added_duration = duration_to_add # 更新上次增加的值
logger.info(f"[{self.stream_id}] First trigger in activation. Adding {duration_to_add:.1f}s.") self._trigger_count_this_activation = 1 # Start counting
else: # Loop is already active, apply decay logger.info(f"{log_prefix} First trigger in activation. Adding {duration_to_add:.2f}s.")
else: # Loop is already active, apply 50% reduction
self._trigger_count_this_activation += 1 self._trigger_count_this_activation += 1
if self._trigger_count_this_activation == 2: duration_to_add = self._last_added_duration * 0.5
duration_to_add = 5.0 self._last_added_duration = duration_to_add # 更新上次增加的值
logger.info(f"[{self.stream_id}] 2nd trigger in activation. Adding {duration_to_add:.1f}s.") logger.info(f"{log_prefix} Trigger #{self._trigger_count_this_activation}. Adding {duration_to_add:.2f}s (50% of previous). Timer was {self._loop_timer:.1f}s.")
else: # 3rd trigger or more
duration_to_add = 2.0
logger.info(f"[{self.stream_id}] {self._trigger_count_this_activation}rd/+ trigger in activation. Adding {duration_to_add:.1f}s.")
# 添加计算出的时间
new_timer_value = self._loop_timer + duration_to_add new_timer_value = self._loop_timer + duration_to_add
self._loop_timer = max(0, new_timer_value) # Ensure timer doesn't go negative conceptually self._loop_timer = max(0, new_timer_value)
logger.info(f"[{self.stream_id}] Timer is now {self._loop_timer:.1f}s.") logger.info(f"{log_prefix} Timer is now {self._loop_timer:.1f}s.")
# Start the loop if it wasn't active and timer is positive
if not self._loop_active and self._loop_timer > 0: if not self._loop_active and self._loop_timer > 0:
logger.info(f"[{self.stream_id}] Timer > 0 and loop not active. Starting PF loop.") logger.info(f"{log_prefix} Timer > 0 and loop not active. Starting PF loop.")
self._loop_active = True self._loop_active = True
# Cancel previous task just in case (shouldn't happen if logic is correct)
if self._loop_task and not self._loop_task.done(): if self._loop_task and not self._loop_task.done():
logger.warning(f"[{self.stream_id}] Found existing loop task unexpectedly during start. Cancelling it.") logger.warning(f"{log_prefix} Found existing loop task unexpectedly during start. Cancelling it.")
self._loop_task.cancel() self._loop_task.cancel()
self._loop_task = asyncio.create_task(self._run_pf_loop()) self._loop_task = asyncio.create_task(self._run_pf_loop())
# Add callback to reset state if loop finishes or errors out
self._loop_task.add_done_callback(self._handle_loop_completion) self._loop_task.add_done_callback(self._handle_loop_completion)
elif self._loop_active: elif self._loop_active:
logger.debug(f"[{self.stream_id}] Loop already active. Timer extended.") logger.debug(f"{log_prefix} Loop already active. Timer extended.")
def _handle_loop_completion(self, task: asyncio.Task): def _handle_loop_completion(self, task: asyncio.Task):
"""Callback executed when the _run_pf_loop task finishes.""" """Callback executed when the _run_pf_loop task finishes."""
log_prefix = self._get_log_prefix()
try: try:
# Check if the task raised an exception # Check if the task raised an exception
exception = task.exception() exception = task.exception()
if exception: if exception:
logger.error(f"[{self.stream_id}] PF loop task completed with error: {exception}") logger.error(f"{log_prefix} PF loop task completed with error: {exception}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
else: else:
logger.info(f"[{self.stream_id}] PF loop task completed normally (timer likely expired or cancelled).") logger.info(f"{log_prefix} PF loop task completed normally (timer likely expired or cancelled).")
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info(f"[{self.stream_id}] PF loop task was cancelled.") logger.info(f"{log_prefix} PF loop task was cancelled.")
finally: finally:
# Reset state regardless of how the task finished # Reset state regardless of how the task finished
self._loop_active = False self._loop_active = False
self._loop_task = None self._loop_task = None
self._last_added_duration = self._initial_duration # <--- 重置下次首次触发的增加时间
self._trigger_count_this_activation = 0 # 重置计数器
# Ensure lock is released if the loop somehow exited while holding it # Ensure lock is released if the loop somehow exited while holding it
if self._processing_lock.locked(): if self._processing_lock.locked():
logger.warning(f"[{self.stream_id}] Releasing processing lock after loop task completion.") logger.warning(f"{log_prefix} Releasing processing lock after loop task completion.")
self._processing_lock.release() self._processing_lock.release()
logger.info(f"[{self.stream_id}] Loop state reset.") logger.info(f"{log_prefix} Loop state reset.")
async def _run_pf_loop(self): async def _run_pf_loop(self):
@@ -201,14 +210,14 @@ class PFChatting:
主循环,当计时器>0时持续进行计划并可能回复消息 主循环,当计时器>0时持续进行计划并可能回复消息
管理每个循环周期的处理锁 管理每个循环周期的处理锁
""" """
logger.info(f"[{self.stream_id}] 开始执行PF循环") logger.info(f"{self._get_log_prefix()} 开始执行PF循环")
try: try:
while True: while True:
# 使用计时器锁安全地检查当前计时器值 # 使用计时器锁安全地检查当前计时器值
async with self._timer_lock: async with self._timer_lock:
current_timer = self._loop_timer current_timer = self._loop_timer
if current_timer <= 0: if current_timer <= 0:
logger.info(f"[{self.stream_id}] 计时器为零或负数({current_timer:.1f}秒)退出PF循环") logger.info(f"{self._get_log_prefix()} 计时器为零或负数({current_timer:.1f}秒)退出PF循环")
break # 退出条件:计时器到期 break # 退出条件:计时器到期
# 记录循环开始时间 # 记录循环开始时间
@@ -221,7 +230,7 @@ class PFChatting:
try: try:
await self._processing_lock.acquire() await self._processing_lock.acquire()
acquired_lock = True acquired_lock = True
logger.debug(f"[{self.stream_id}] 循环获取到处理锁") logger.debug(f"{self._get_log_prefix()} 循环获取到处理锁")
# --- Planner --- # --- Planner ---
# Planner decides action, reasoning, emoji_query, etc. # Planner decides action, reasoning, emoji_query, etc.
@@ -234,16 +243,16 @@ class PFChatting:
observed_messages = planner_result.get("observed_messages", []) # Planner needs to return this observed_messages = planner_result.get("observed_messages", []) # Planner needs to return this
if action == "text_reply": if action == "text_reply":
logger.info(f"[{self.stream_id}] 计划循环决定: 回复文本.") logger.info(f"{self._get_log_prefix()} 计划循环决定: 回复文本.")
action_taken_this_cycle = True action_taken_this_cycle = True
# --- 回复器 --- # --- 回复器 ---
anchor_message = await self._get_anchor_message(observed_messages) anchor_message = await self._get_anchor_message(observed_messages)
if not anchor_message: if not anchor_message:
logger.error(f"[{self.stream_id}] 循环: 无法获取锚点消息用于回复. 跳过周期.") logger.error(f"{self._get_log_prefix()} 循环: 无法获取锚点消息用于回复. 跳过周期.")
else: else:
thinking_id = await self.heartfc_chat._create_thinking_message(anchor_message) thinking_id = await self.heartfc_chat._create_thinking_message(anchor_message)
if not thinking_id: if not thinking_id:
logger.error(f"[{self.stream_id}] 循环: 无法创建思考ID. 跳过周期.") logger.error(f"{self._get_log_prefix()} 循环: 无法创建思考ID. 跳过周期.")
else: else:
replier_result = None replier_result = None
try: try:
@@ -256,7 +265,7 @@ class PFChatting:
send_emoji=send_emoji_from_tools send_emoji=send_emoji_from_tools
) )
except Exception as e_replier: except Exception as e_replier:
logger.error(f"[{self.stream_id}] 循环: 回复器工作失败: {e_replier}") logger.error(f"{self._get_log_prefix()} 循环: 回复器工作失败: {e_replier}")
self._cleanup_thinking_message(thinking_id) # 清理思考消息 self._cleanup_thinking_message(thinking_id) # 清理思考消息
# 继续循环, 视为非操作周期 # 继续循环, 视为非操作周期
@@ -264,61 +273,61 @@ class PFChatting:
# --- Sender --- # --- Sender ---
try: try:
await self._sender(thinking_id, anchor_message, replier_result) await self._sender(thinking_id, anchor_message, replier_result)
logger.info(f"[{self.stream_id}] 循环: 发送器完成成功.") logger.info(f"{self._get_log_prefix()} 循环: 发送器完成成功.")
except Exception as e_sender: except Exception as e_sender:
logger.error(f"[{self.stream_id}] 循环: 发送器失败: {e_sender}") logger.error(f"{self._get_log_prefix()} 循环: 发送器失败: {e_sender}")
self._cleanup_thinking_message(thinking_id) # 确保发送失败时清理 self._cleanup_thinking_message(thinking_id) # 确保发送失败时清理
# 继续循环, 视为非操作周期 # 继续循环, 视为非操作周期
else: else:
# Replier failed to produce result # Replier failed to produce result
logger.warning(f"[{self.stream_id}] 循环: 回复器未产生结果. 跳过发送.") logger.warning(f"{self._get_log_prefix()} 循环: 回复器未产生结果. 跳过发送.")
self._cleanup_thinking_message(thinking_id) # 清理思考消息 self._cleanup_thinking_message(thinking_id) # 清理思考消息
elif action == "emoji_reply": elif action == "emoji_reply":
logger.info(f"[{self.stream_id}] 计划循环决定: 回复表情 ('{emoji_query}').") logger.info(f"{self._get_log_prefix()} 计划循环决定: 回复表情 ('{emoji_query}').")
action_taken_this_cycle = True action_taken_this_cycle = True
anchor = await self._get_anchor_message(observed_messages) anchor = await self._get_anchor_message(observed_messages)
if anchor: if anchor:
try: try:
await self.heartfc_chat._handle_emoji(anchor, [], emoji_query) await self.heartfc_chat._handle_emoji(anchor, [], emoji_query)
except Exception as e_emoji: except Exception as e_emoji:
logger.error(f"[{self.stream_id}] 循环: 发送表情失败: {e_emoji}") logger.error(f"{self._get_log_prefix()} 循环: 发送表情失败: {e_emoji}")
else: else:
logger.warning(f"[{self.stream_id}] 循环: 无法发送表情, 无法获取锚点.") logger.warning(f"{self._get_log_prefix()} 循环: 无法发送表情, 无法获取锚点.")
elif action == "no_reply": elif action == "no_reply":
logger.info(f"[{self.stream_id}] 计划循环决定: 不回复. 原因: {reasoning}") logger.info(f"{self._get_log_prefix()} 计划循环决定: 不回复. 原因: {reasoning}")
# Do nothing else, action_taken_this_cycle remains False # Do nothing else, action_taken_this_cycle remains False
elif action == "error": elif action == "error":
logger.error(f"[{self.stream_id}] 计划循环返回错误或失败. 原因: {reasoning}") logger.error(f"{self._get_log_prefix()} 计划循环返回错误或失败. 原因: {reasoning}")
# 视为非操作周期 # 视为非操作周期
else: # Unknown action else: # Unknown action
logger.warning(f"[{self.stream_id}] 计划循环返回未知动作: {action}. 视为不回复.") logger.warning(f"{self._get_log_prefix()} 计划循环返回未知动作: {action}. 视为不回复.")
# 视为非操作周期 # 视为非操作周期
except Exception as e_cycle: except Exception as e_cycle:
# Catch errors occurring within the locked section (e.g., planner crash) # Catch errors occurring within the locked section (e.g., planner crash)
logger.error(f"[{self.stream_id}] 循环周期执行时发生错误: {e_cycle}") logger.error(f"{self._get_log_prefix()} 循环周期执行时发生错误: {e_cycle}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# Ensure lock is released if an error occurs before the finally block # Ensure lock is released if an error occurs before the finally block
if acquired_lock and self._processing_lock.locked(): if acquired_lock and self._processing_lock.locked():
self._processing_lock.release() self._processing_lock.release()
acquired_lock = False # 防止在 finally 块中重复释放 acquired_lock = False # 防止在 finally 块中重复释放
logger.warning(f"[{self.stream_id}] 由于循环周期中的错误释放了处理锁.") logger.warning(f"{self._get_log_prefix()} 由于循环周期中的错误释放了处理锁.")
finally: finally:
# Ensure the lock is always released after a cycle # Ensure the lock is always released after a cycle
if acquired_lock: if acquired_lock:
self._processing_lock.release() self._processing_lock.release()
logger.debug(f"[{self.stream_id}] 循环释放了处理锁.") logger.debug(f"{self._get_log_prefix()} 循环释放了处理锁.")
# --- Timer Decrement --- # --- Timer Decrement ---
cycle_duration = time.monotonic() - loop_cycle_start_time cycle_duration = time.monotonic() - loop_cycle_start_time
async with self._timer_lock: async with self._timer_lock:
self._loop_timer -= cycle_duration self._loop_timer -= cycle_duration
logger.debug(f"[{self.stream_id}] 循环周期耗时 {cycle_duration:.2f}s. 计时器剩余: {self._loop_timer:.1f}s.") logger.debug(f"{self._get_log_prefix()} 循环周期耗时 {cycle_duration:.2f}s. 计时器剩余: {self._loop_timer:.1f}s.")
# --- Delay --- # --- Delay ---
# Add a small delay, especially if no action was taken, to prevent busy-waiting # Add a small delay, especially if no action was taken, to prevent busy-waiting
@@ -329,21 +338,21 @@ class PFChatting:
elif cycle_duration < 0.2: # Minimum delay even if action was taken elif cycle_duration < 0.2: # Minimum delay even if action was taken
await asyncio.sleep(0.2) await asyncio.sleep(0.2)
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info(f"[{self.stream_id}] Sleep interrupted, likely loop cancellation.") logger.info(f"{self._get_log_prefix()} Sleep interrupted, likely loop cancellation.")
break # Exit loop if cancelled during sleep break # Exit loop if cancelled during sleep
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info(f"[{self.stream_id}] PF loop task received cancellation request.") logger.info(f"{self._get_log_prefix()} PF loop task received cancellation request.")
except Exception as e_loop_outer: except Exception as e_loop_outer:
# Catch errors outside the main cycle lock (should be rare) # Catch errors outside the main cycle lock (should be rare)
logger.error(f"[{self.stream_id}] PF loop encountered unexpected outer error: {e_loop_outer}") logger.error(f"{self._get_log_prefix()} PF loop encountered unexpected outer error: {e_loop_outer}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
finally: finally:
# Reset trigger count when loop finishes # Reset trigger count when loop finishes
async with self._timer_lock: async with self._timer_lock:
self._trigger_count_this_activation = 0 self._trigger_count_this_activation = 0
logger.debug(f"[{self.stream_id}] Trigger count reset to 0 as loop finishes.") logger.debug(f"{self._get_log_prefix()} Trigger count reset to 0 as loop finishes.")
logger.info(f"[{self.stream_id}] PF loop finished execution run.") logger.info(f"{self._get_log_prefix()} PF loop finished execution run.")
# State reset (_loop_active, _loop_task) is handled by _handle_loop_completion callback # State reset (_loop_active, _loop_task) is handled by _handle_loop_completion callback
async def _planner(self) -> Dict[str, Any]: async def _planner(self) -> Dict[str, Any]:
@@ -353,6 +362,7 @@ class PFChatting:
{'action': str, 'reasoning': str, 'emoji_query': str, 'current_mind': str, {'action': str, 'reasoning': str, 'emoji_query': str, 'current_mind': str,
'send_emoji_from_tools': str, 'observed_messages': List[dict]} 'send_emoji_from_tools': str, 'observed_messages': List[dict]}
""" """
log_prefix = self._get_log_prefix()
observed_messages: List[dict] = [] observed_messages: List[dict] = []
tool_result_info = {} tool_result_info = {}
get_mid_memory_id = [] get_mid_memory_id = []
@@ -363,14 +373,14 @@ class PFChatting:
try: try:
if self.sub_hf and self.sub_hf._get_primary_observation(): if self.sub_hf and self.sub_hf._get_primary_observation():
observation = self.sub_hf._get_primary_observation() observation = self.sub_hf._get_primary_observation()
logger.debug(f"[{self.stream_id}][Planner] 调用 observation.observe()...") logger.debug(f"{log_prefix}[Planner] 调用 observation.observe()...")
await observation.observe() # 主动观察以获取最新消息 await observation.observe() # 主动观察以获取最新消息
observed_messages = observation.talking_message # 获取更新后的消息列表 observed_messages = observation.talking_message # 获取更新后的消息列表
logger.debug(f"[{self.stream_id}][Planner] 获取到 {len(observed_messages)} 条观察消息。") logger.debug(f"{log_prefix}[Planner] 获取到 {len(observed_messages)} 条观察消息。")
else: else:
logger.warning(f"[{self.stream_id}][Planner] 无法获取 SubHeartflow 或 Observation 来获取消息。") logger.warning(f"{log_prefix}[Planner] 无法获取 SubHeartflow 或 Observation 来获取消息。")
except Exception as e: except Exception as e:
logger.error(f"[{self.stream_id}][Planner] 获取观察信息时出错: {e}") logger.error(f"{log_prefix}[Planner] 获取观察信息时出错: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# --- 结束获取观察信息 --- # --- 结束获取观察信息 ---
@@ -380,7 +390,7 @@ class PFChatting:
if observed_messages: if observed_messages:
context_texts = [msg.get('detailed_plain_text', '') for msg in observed_messages if msg.get('detailed_plain_text')] context_texts = [msg.get('detailed_plain_text', '') for msg in observed_messages if msg.get('detailed_plain_text')]
observation_context_text = "\n".join(context_texts) observation_context_text = "\n".join(context_texts)
logger.debug(f"[{self.stream_id}][Planner] Context for tools: {observation_context_text[:100]}...") logger.debug(f"{log_prefix}[Planner] Context for tools: {observation_context_text[:100]}...")
if observation_context_text and self.sub_hf: if observation_context_text and self.sub_hf:
# Ensure SubHeartflow exists for tool use context # Ensure SubHeartflow exists for tool use context
@@ -391,16 +401,16 @@ class PFChatting:
) )
if tool_result.get("used_tools", False): if tool_result.get("used_tools", False):
tool_result_info = tool_result.get("structured_info", {}) tool_result_info = tool_result.get("structured_info", {})
logger.debug(f"[{self.stream_id}][Planner] Tool results: {tool_result_info}") logger.debug(f"{log_prefix}[Planner] Tool results: {tool_result_info}")
if "mid_chat_mem" in tool_result_info: if "mid_chat_mem" in tool_result_info:
get_mid_memory_id = [mem["content"] for mem in tool_result_info["mid_chat_mem"] if "content" in mem] get_mid_memory_id = [mem["content"] for mem in tool_result_info["mid_chat_mem"] if "content" in mem]
if "send_emoji" in tool_result_info and tool_result_info["send_emoji"]: if "send_emoji" in tool_result_info and tool_result_info["send_emoji"]:
send_emoji_from_tools = tool_result_info["send_emoji"][0].get("content", "") # Use renamed var send_emoji_from_tools = tool_result_info["send_emoji"][0].get("content", "") # Use renamed var
elif not self.sub_hf: elif not self.sub_hf:
logger.warning(f"[{self.stream_id}][Planner] Skipping tool use because SubHeartflow is not available.") logger.warning(f"{log_prefix}[Planner] Skipping tool use because SubHeartflow is not available.")
except Exception as e_tool: except Exception as e_tool:
logger.error(f"[PFChatting-{self.stream_id}][Planner] Tool use failed: {e_tool}") logger.error(f"{log_prefix}[Planner] Tool use failed: {e_tool}")
# Continue even if tool use fails # Continue even if tool use fails
# --- 结束工具使用 --- # --- 结束工具使用 ---
@@ -413,13 +423,13 @@ class PFChatting:
extra_info=tool_result_info, extra_info=tool_result_info,
obs_id=get_mid_memory_id, obs_id=get_mid_memory_id,
) )
logger.info(f"[{self.stream_id}][Planner] SubHeartflow thought: {current_mind}") logger.info(f"{log_prefix}[Planner] SubHeartflow thought: {current_mind}")
else: else:
logger.warning(f"[{self.stream_id}][Planner] Skipping SubHeartflow thinking because it is not available.") logger.warning(f"{log_prefix}[Planner] Skipping SubHeartflow thinking because it is not available.")
current_mind = "[心流思考不可用]" # Set a default/indicator value current_mind = "[心流思考不可用]" # Set a default/indicator value
except Exception as e_shf: except Exception as e_shf:
logger.error(f"[PFChatting-{self.stream_id}][Planner] SubHeartflow thinking failed: {e_shf}") logger.error(f"{log_prefix}[Planner] SubHeartflow thinking failed: {e_shf}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
current_mind = "[心流思考出错]" current_mind = "[心流思考出错]"
@@ -433,7 +443,7 @@ class PFChatting:
try: try:
# 构建提示 (Now includes current_mind) # 构建提示 (Now includes current_mind)
prompt = self._build_planner_prompt(observed_messages, current_mind) prompt = self._build_planner_prompt(observed_messages, current_mind)
logger.trace(f"[{self.stream_id}][Planner] Prompt: {prompt}") logger.debug(f"{log_prefix}[Planner] Prompt: {prompt}")
# 准备 LLM 请求 Payload # 准备 LLM 请求 Payload
payload = { payload = {
@@ -443,7 +453,7 @@ class PFChatting:
"tool_choice": {"type": "function", "function": {"name": "decide_reply_action"}}, # 强制调用此工具 "tool_choice": {"type": "function", "function": {"name": "decide_reply_action"}}, # 强制调用此工具
} }
logger.debug(f"[{self.stream_id}][Planner] 发送 Planner LLM 请求...") logger.debug(f"{log_prefix}[Planner] 发送 Planner LLM 请求...")
# 调用 LLM # 调用 LLM
response = await self.planner_llm._execute_request( response = await self.planner_llm._execute_request(
endpoint="/chat/completions", payload=payload, prompt=prompt endpoint="/chat/completions", payload=payload, prompt=prompt
@@ -463,25 +473,25 @@ class PFChatting:
if action == "emoji_reply": if action == "emoji_reply":
# Planner's decision overrides tool's emoji if action is emoji_reply # Planner's decision overrides tool's emoji if action is emoji_reply
emoji_query = arguments.get("emoji_query", send_emoji_from_tools) # Use tool emoji as default if planner asks for emoji emoji_query = arguments.get("emoji_query", send_emoji_from_tools) # Use tool emoji as default if planner asks for emoji
logger.info(f"[{self.stream_id}][Planner] LLM 决策: {action}, 理由: {reasoning}, EmojiQuery: '{emoji_query}'") logger.info(f"{log_prefix}[Planner] LLM 决策: {action}, 理由: {reasoning}, EmojiQuery: '{emoji_query}'")
except json.JSONDecodeError as json_e: except json.JSONDecodeError as json_e:
logger.error(f"[{self.stream_id}][Planner] 解析工具参数失败: {json_e}. Arguments: {tool_call['function'].get('arguments')}") logger.error(f"{log_prefix}[Planner] 解析工具参数失败: {json_e}. Arguments: {tool_call['function'].get('arguments')}")
action = "error"; reasoning = "工具参数解析失败"; llm_error = True action = "error"; reasoning = "工具参数解析失败"; llm_error = True
except Exception as parse_e: except Exception as parse_e:
logger.error(f"[{self.stream_id}][Planner] 处理工具参数时出错: {parse_e}") logger.error(f"{log_prefix}[Planner] 处理工具参数时出错: {parse_e}")
action = "error"; reasoning = "处理工具参数时出错"; llm_error = True action = "error"; reasoning = "处理工具参数时出错"; llm_error = True
else: else:
logger.warning(f"[{self.stream_id}][Planner] LLM 未按预期调用 'decide_reply_action' 工具。Tool calls: {tool_calls}") logger.warning(f"{log_prefix}[Planner] LLM 未按预期调用 'decide_reply_action' 工具。Tool calls: {tool_calls}")
action = "error"; reasoning = "LLM未调用预期工具"; llm_error = True action = "error"; reasoning = "LLM未调用预期工具"; llm_error = True
else: else:
logger.warning(f"[{self.stream_id}][Planner] LLM 响应中未包含有效的工具调用。Tool calls: {tool_calls}") logger.warning(f"{log_prefix}[Planner] LLM 响应中未包含有效的工具调用。Tool calls: {tool_calls}")
action = "error"; reasoning = "LLM响应无工具调用"; llm_error = True action = "error"; reasoning = "LLM响应无工具调用"; llm_error = True
else: else:
logger.warning(f"[{self.stream_id}][Planner] LLM 未返回预期的工具调用响应。Response parts: {len(response)}") logger.warning(f"{log_prefix}[Planner] LLM 未返回预期的工具调用响应。Response parts: {len(response)}")
action = "error"; reasoning = "LLM响应格式错误"; llm_error = True action = "error"; reasoning = "LLM响应格式错误"; llm_error = True
except Exception as llm_e: except Exception as llm_e:
logger.error(f"[{self.stream_id}][Planner] Planner LLM 调用失败: {llm_e}") logger.error(f"{log_prefix}[Planner] Planner LLM 调用失败: {llm_e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
action = "error"; reasoning = f"LLM 调用失败: {llm_e}"; llm_error = True action = "error"; reasoning = f"LLM 调用失败: {llm_e}"; llm_error = True
@@ -503,7 +513,7 @@ class PFChatting:
如果重构失败或观察为空,则创建一个占位符。 如果重构失败或观察为空,则创建一个占位符。
""" """
if not self.chat_stream: if not self.chat_stream:
logger.error(f"[PFChatting-{self.stream_id}] 无法获取锚点消息: ChatStream 不可用.") logger.error(f"{self._get_log_prefix()} 无法获取锚点消息: ChatStream 不可用.")
return None return None
try: try:
@@ -518,12 +528,12 @@ class PFChatting:
# Basic validation # Basic validation
if not (anchor_message and anchor_message.message_info and anchor_message.message_info.message_id and anchor_message.message_info.user_info): 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("重构的 MessageRecv 缺少必要信息.") raise ValueError("重构的 MessageRecv 缺少必要信息.")
logger.debug(f"[{self.stream_id}] 重构的锚点消息: ID={anchor_message.message_info.message_id}") logger.debug(f"{self._get_log_prefix()} 重构的锚点消息: ID={anchor_message.message_info.message_id}")
return anchor_message return anchor_message
except Exception as e_reconstruct: except Exception as e_reconstruct:
logger.warning(f"[{self.stream_id}] 从观察到的消息重构 MessageRecv 失败: {e_reconstruct}. 创建占位符.") logger.warning(f"{self._get_log_prefix()} 从观察到的消息重构 MessageRecv 失败: {e_reconstruct}. 创建占位符.")
else: else:
logger.warning(f"[{self.stream_id}] observed_messages 为空. 创建占位符锚点消息.") logger.warning(f"{self._get_log_prefix()} observed_messages 为空. 创建占位符锚点消息.")
# --- Create Placeholder --- # --- Create Placeholder ---
placeholder_id = f"mid_pf_{int(time.time() * 1000)}" placeholder_id = f"mid_pf_{int(time.time() * 1000)}"
@@ -543,11 +553,11 @@ class PFChatting:
} }
anchor_message = MessageRecv(placeholder_msg_dict) anchor_message = MessageRecv(placeholder_msg_dict)
anchor_message.update_chat_stream(self.chat_stream) # Associate with the stream anchor_message.update_chat_stream(self.chat_stream) # Associate with the stream
logger.info(f"[{self.stream_id}] Created placeholder anchor message: ID={anchor_message.message_info.message_id}") logger.info(f"{self._get_log_prefix()} Created placeholder anchor message: ID={anchor_message.message_info.message_id}")
return anchor_message return anchor_message
except Exception as e: except Exception as e:
logger.error(f"[PFChatting-{self.stream_id}] Error getting/creating anchor message: {e}") logger.error(f"{self._get_log_prefix()} Error getting/creating anchor message: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None return None
@@ -556,9 +566,9 @@ class PFChatting:
try: try:
container = MessageManager().get_container(self.stream_id) container = MessageManager().get_container(self.stream_id)
container.remove_message(thinking_id, msg_type=MessageThinking) container.remove_message(thinking_id, msg_type=MessageThinking)
logger.debug(f"[{self.stream_id}] Cleaned up thinking message {thinking_id}.") logger.debug(f"{self._get_log_prefix()} Cleaned up thinking message {thinking_id}.")
except Exception as e: except Exception as e:
logger.error(f"[{self.stream_id}] Error cleaning up thinking message {thinking_id}: {e}") logger.error(f"{self._get_log_prefix()} Error cleaning up thinking message {thinking_id}: {e}")
async def _sender(self, thinking_id: str, anchor_message: MessageRecv, replier_result: Dict[str, Any]): async def _sender(self, thinking_id: str, anchor_message: MessageRecv, replier_result: Dict[str, Any]):
@@ -573,7 +583,7 @@ class PFChatting:
send_emoji = replier_result.get("send_emoji", "") # Emoji determined by tools, passed via replier send_emoji = replier_result.get("send_emoji", "") # Emoji determined by tools, passed via replier
if not response_set: if not response_set:
logger.error(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Called with empty response_set.") logger.error(f"{self._get_log_prefix()}[Sender-{thinking_id}] Called with empty response_set.")
# Clean up thinking message before raising error # Clean up thinking message before raising error
self._cleanup_thinking_message(thinking_id) self._cleanup_thinking_message(thinking_id)
raise ValueError("Sender called with no response_set") # Signal failure to loop raise ValueError("Sender called with no response_set") # Signal failure to loop
@@ -582,44 +592,44 @@ class PFChatting:
send_success = False send_success = False
try: try:
# --- Send the main text response --- # --- Send the main text response ---
logger.debug(f"[{self.stream_id}][Sender-{thinking_id}] Sending response messages...") logger.debug(f"{self._get_log_prefix()}[Sender-{thinking_id}] Sending response messages...")
# This call implicitly handles replacing the MessageThinking with MessageSending/MessageSet # This call implicitly handles replacing the MessageThinking with MessageSending/MessageSet
first_bot_msg = await self.heartfc_chat._send_response_messages(anchor_message, response_set, thinking_id) first_bot_msg = await self.heartfc_chat._send_response_messages(anchor_message, response_set, thinking_id)
if first_bot_msg: if first_bot_msg:
send_success = True # Mark success send_success = True # Mark success
logger.info(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Successfully sent reply.") logger.info(f"{self._get_log_prefix()}[Sender-{thinking_id}] Successfully sent reply.")
# --- Handle associated emoji (if determined by tools) --- # --- Handle associated emoji (if determined by tools) ---
if send_emoji: if send_emoji:
logger.info(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Sending associated emoji: {send_emoji}") logger.info(f"{self._get_log_prefix()}[Sender-{thinking_id}] Sending associated emoji: {send_emoji}")
try: try:
# Use first_bot_msg as anchor if available, otherwise fallback to original anchor # Use first_bot_msg as anchor if available, otherwise fallback to original anchor
emoji_anchor = first_bot_msg if first_bot_msg else anchor_message emoji_anchor = first_bot_msg if first_bot_msg else anchor_message
await self.heartfc_chat._handle_emoji(emoji_anchor, response_set, send_emoji) await self.heartfc_chat._handle_emoji(emoji_anchor, response_set, send_emoji)
except Exception as e_emoji: except Exception as e_emoji:
logger.error(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Failed to send associated emoji: {e_emoji}") logger.error(f"{self._get_log_prefix()}[Sender-{thinking_id}] Failed to send associated emoji: {e_emoji}")
# Log error but don't fail the whole send process for emoji failure # Log error but don't fail the whole send process for emoji failure
# --- Update relationship --- # --- Update relationship ---
try: try:
await self.heartfc_chat._update_relationship(anchor_message, response_set) await self.heartfc_chat._update_relationship(anchor_message, response_set)
logger.debug(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Updated relationship.") logger.debug(f"{self._get_log_prefix()}[Sender-{thinking_id}] Updated relationship.")
except Exception as e_rel: except Exception as e_rel:
logger.error(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Failed to update relationship: {e_rel}") logger.error(f"{self._get_log_prefix()}[Sender-{thinking_id}] Failed to update relationship: {e_rel}")
# Log error but don't fail the whole send process for relationship update failure # Log error but don't fail the whole send process for relationship update failure
else: else:
# Sending failed (e.g., _send_response_messages found thinking message already gone) # Sending failed (e.g., _send_response_messages found thinking message already gone)
send_success = False send_success = False
logger.warning(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Failed to send reply (maybe thinking message expired or was removed?).") logger.warning(f"{self._get_log_prefix()}[Sender-{thinking_id}] Failed to send reply (maybe thinking message expired or was removed?).")
# No need to clean up thinking message here, _send_response_messages implies it's gone or handled # No need to clean up thinking message here, _send_response_messages implies it's gone or handled
raise RuntimeError("Sending reply failed, _send_response_messages returned None.") # Signal failure raise RuntimeError("Sending reply failed, _send_response_messages returned None.") # Signal failure
except Exception as e: except Exception as e:
# Catch potential errors during sending or post-send actions # Catch potential errors during sending or post-send actions
logger.error(f"[PFChatting-{self.stream_id}][Sender-{thinking_id}] Error during sending process: {e}") logger.error(f"{self._get_log_prefix()}[Sender-{thinking_id}] Error during sending process: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
# Ensure thinking message is cleaned up if send failed mid-way and wasn't handled # Ensure thinking message is cleaned up if send failed mid-way and wasn't handled
if not send_success: if not send_success:
@@ -633,21 +643,21 @@ class PFChatting:
""" """
Gracefully shuts down the PFChatting instance by cancelling the active loop task. Gracefully shuts down the PFChatting instance by cancelling the active loop task.
""" """
logger.info(f"[{self.stream_id}] Shutting down PFChatting...") logger.info(f"{self._get_log_prefix()} Shutting down PFChatting...")
if self._loop_task and not self._loop_task.done(): if self._loop_task and not self._loop_task.done():
logger.info(f"[{self.stream_id}] Cancelling active PF loop task.") logger.info(f"{self._get_log_prefix()} Cancelling active PF loop task.")
self._loop_task.cancel() self._loop_task.cancel()
try: try:
# Wait briefly for the task to acknowledge cancellation # Wait briefly for the task to acknowledge cancellation
await asyncio.wait_for(self._loop_task, timeout=5.0) await asyncio.wait_for(self._loop_task, timeout=5.0)
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info(f"[{self.stream_id}] PF loop task cancelled successfully.") logger.info(f"{self._get_log_prefix()} PF loop task cancelled successfully.")
except asyncio.TimeoutError: except asyncio.TimeoutError:
logger.warning(f"[{self.stream_id}] Timeout waiting for PF loop task cancellation.") logger.warning(f"{self._get_log_prefix()} Timeout waiting for PF loop task cancellation.")
except Exception as e: except Exception as e:
logger.error(f"[{self.stream_id}] Error during loop task cancellation: {e}") logger.error(f"{self._get_log_prefix()} Error during loop task cancellation: {e}")
else: else:
logger.info(f"[{self.stream_id}] No active PF loop task found to cancel.") logger.info(f"{self._get_log_prefix()} No active PF loop task found to cancel.")
# Ensure loop state is reset even if task wasn't running or cancellation failed # Ensure loop state is reset even if task wasn't running or cancellation failed
self._loop_active = False self._loop_active = False
@@ -655,15 +665,14 @@ class PFChatting:
# Double-check lock state (should be released by loop completion/cancellation handler) # Double-check lock state (should be released by loop completion/cancellation handler)
if self._processing_lock.locked(): if self._processing_lock.locked():
logger.warning(f"[{self.stream_id}] Releasing processing lock during shutdown.") logger.warning(f"{self._get_log_prefix()} Releasing processing lock during shutdown.")
self._processing_lock.release() self._processing_lock.release()
logger.info(f"[{self.stream_id}] PFChatting shutdown complete.") logger.info(f"{self._get_log_prefix()} PFChatting shutdown complete.")
def _build_planner_prompt(self, observed_messages: List[dict], current_mind: Optional[str]) -> str: def _build_planner_prompt(self, observed_messages: List[dict], current_mind: Optional[str]) -> str:
"""构建 Planner LLM 的提示词 (现在包含 current_mind)""" """构建 Planner LLM 的提示词 (现在包含 current_mind)"""
prompt = "是一个聊天机器人助手,正在决定是否以及如何回应当前的聊天。\n" prompt = f"的名字是 {global_config.BOT_NICKNAME}。是一个女大二学生正在QQ聊天,正在决定是否以及如何回应当前的聊天。\n"
prompt += f"你的名字是 {global_config.BOT_NICKNAME}\n"
# Add current mind state if available # Add current mind state if available
if current_mind: if current_mind:
@@ -684,6 +693,7 @@ class PFChatting:
prompt += "1. 如果聊天内容无聊、与你无关、或者你的内部想法认为不适合回复,选择 'no_reply'\n" prompt += "1. 如果聊天内容无聊、与你无关、或者你的内部想法认为不适合回复,选择 'no_reply'\n"
prompt += "2. 如果聊天内容值得回应,且适合用文字表达(参考你的内部想法),选择 'text_reply'\n" prompt += "2. 如果聊天内容值得回应,且适合用文字表达(参考你的内部想法),选择 'text_reply'\n"
prompt += "3. 如果聊天内容或你的内部想法适合用一个表情来回应,选择 'emoji_reply' 并提供表情主题 'emoji_query'\n" prompt += "3. 如果聊天内容或你的内部想法适合用一个表情来回应,选择 'emoji_reply' 并提供表情主题 'emoji_query'\n"
prompt += "4. 如果你已经回复过消息,也没有人又回复你,选择'no_reply'"
prompt += "必须调用 'decide_reply_action' 工具并提供 'action''reasoning'" prompt += "必须调用 'decide_reply_action' 工具并提供 'action''reasoning'"
return prompt return prompt
@@ -695,12 +705,13 @@ class PFChatting:
被 _run_pf_loop 直接调用和 await。 被 _run_pf_loop 直接调用和 await。
Returns dict with 'response_set' and 'send_emoji' or None on failure. Returns dict with 'response_set' and 'send_emoji' or None on failure.
""" """
log_prefix = self._get_log_prefix()
response_set: Optional[List[str]] = None response_set: Optional[List[str]] = None
try: try:
# --- Tool Use and SubHF Thinking are now in _planner --- # --- Tool Use and SubHF Thinking are now in _planner ---
# --- Generate Response with LLM --- # --- Generate Response with LLM ---
logger.debug(f"[{self.stream_id}][Replier-{thinking_id}] Calling LLM to generate response...") logger.debug(f"{log_prefix}[Replier-{thinking_id}] Calling LLM to generate response...")
# 注意:实际的生成调用是在 self.heartfc_chat.gpt.generate_response 中 # 注意:实际的生成调用是在 self.heartfc_chat.gpt.generate_response 中
response_set = await self.heartfc_chat.gpt.generate_response( response_set = await self.heartfc_chat.gpt.generate_response(
anchor_message, anchor_message,
@@ -710,17 +721,17 @@ class PFChatting:
) )
if not response_set: if not response_set:
logger.warning(f"[{self.stream_id}][Replier-{thinking_id}] LLM生成了一个空回复集。") logger.warning(f"{log_prefix}[Replier-{thinking_id}] LLM生成了一个空回复集。")
return None # Indicate failure return None # Indicate failure
# --- 准备并返回结果 --- # --- 准备并返回结果 ---
logger.info(f"[{self.stream_id}][Replier-{thinking_id}] 成功生成了回复集: {' '.join(response_set)[:50]}...") logger.info(f"{log_prefix}[Replier-{thinking_id}] 成功生成了回复集: {' '.join(response_set)[:50]}...")
return { return {
"response_set": response_set, "response_set": response_set,
"send_emoji": send_emoji, # Pass through the emoji determined earlier (usually by tools) "send_emoji": send_emoji, # Pass through the emoji determined earlier (usually by tools)
} }
except Exception as e: except Exception as e:
logger.error(f"[PFChatting-{self.stream_id}][Replier-{thinking_id}] Unexpected error in replier_work: {e}") logger.error(f"{log_prefix}[Replier-{thinking_id}] Unexpected error in replier_work: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return None # Indicate failure return None # Indicate failure