From 360406efde6c723bcea77fb35024232c38558a72 Mon Sep 17 00:00:00 2001 From: HexatomicRing <54496918+HexatomicRing@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:29:55 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E7=BB=99keywords=5Freaction=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=AD=A3=E5=88=99=E8=A1=A8=E8=BE=BE=E5=BC=8F=E5=8C=B9?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 顺便做了正则表达式预编译 --- .../chat_module/only_process/only_message_process.py | 3 +-- .../chat_module/reasoning_chat/reasoning_chat.py | 4 ++-- .../reasoning_chat/reasoning_prompt_builder.py | 11 +++++++++++ .../chat_module/think_flow_chat/think_flow_chat.py | 3 +-- .../think_flow_chat/think_flow_prompt_builder.py | 11 +++++++++++ src/plugins/config/config.py | 8 ++++++-- template/bot_config_template.toml | 5 +++++ 7 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/plugins/chat_module/only_process/only_message_process.py b/src/plugins/chat_module/only_process/only_message_process.py index 6da19efe7..a39b7f8b0 100644 --- a/src/plugins/chat_module/only_process/only_message_process.py +++ b/src/plugins/chat_module/only_process/only_message_process.py @@ -2,7 +2,6 @@ from src.common.logger import get_module_logger from src.plugins.chat.message import MessageRecv from src.plugins.storage.storage import MessageStorage from src.plugins.config.config import global_config -import re from datetime import datetime logger = get_module_logger("pfc_message_processor") @@ -28,7 +27,7 @@ class MessageProcessor: def _check_ban_regex(self, text: str, chat, userinfo) -> bool: """检查消息是否匹配过滤正则表达式""" for pattern in global_config.ban_msgs_regex: - if re.search(pattern, text): + if pattern.search(text): logger.info( f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" ) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index b9e94e4fe..357c9c87d 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -1,6 +1,6 @@ import time from random import random -import re + from typing import List from ...memory_system.Hippocampus import HippocampusManager from ...moods.moods import MoodManager @@ -302,7 +302,7 @@ class ReasoningChat: def _check_ban_regex(self, text: str, chat, userinfo) -> bool: """检查消息是否匹配过滤正则表达式""" for pattern in global_config.ban_msgs_regex: - if re.search(pattern, text): + if pattern.search(text): logger.info( f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" ) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py index a379fa6d5..045045bae 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py @@ -115,6 +115,17 @@ class PromptBuilder: f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}" ) keywords_reaction_prompt += rule.get("reaction", "") + "," + for pattern in rule.get("regex", []): + result = pattern.search(message_txt) + if result: + reaction = rule.get('reaction', '') + for name, content in result.groupdict().items(): + reaction = reaction.replace(f'[{name}]', content) + logger.info( + f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" + ) + keywords_reaction_prompt += reaction + "," + break # 中文高手(新加的好玩功能) prompt_ger = "" diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index 51bafcbcc..329619256 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -1,6 +1,5 @@ import time from random import random -import re import traceback from typing import List from ...memory_system.Hippocampus import HippocampusManager @@ -388,7 +387,7 @@ class ThinkFlowChat: def _check_ban_regex(self, text: str, chat, userinfo) -> bool: """检查消息是否匹配过滤正则表达式""" for pattern in global_config.ban_msgs_regex: - if re.search(pattern, text): + if pattern.search(text): logger.info( f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" ) diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index 5d701c6a2..8938e2e78 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -61,6 +61,17 @@ class PromptBuilder: f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}" ) keywords_reaction_prompt += rule.get("reaction", "") + "," + for pattern in rule.get("regex", []): + result = pattern.search(message_txt) + if result: + reaction = rule.get('reaction', '') + for name, content in result.groupdict().items(): + reaction = reaction.replace(f'[{name}]', content) + logger.info( + f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" + ) + keywords_reaction_prompt += reaction + "," + break # 中文高手(新加的好玩功能) prompt_ger = "" diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index 23e277498..be3343292 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -1,4 +1,5 @@ import os +import re from dataclasses import dataclass, field from typing import Dict, List, Optional from dateutil import tz @@ -545,8 +546,8 @@ class BotConfig: "response_interested_rate_amplifier", config.response_interested_rate_amplifier ) config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate) - config.ban_msgs_regex = msg_config.get("ban_msgs_regex", config.ban_msgs_regex) - + for r in msg_config.get("ban_msgs_regex", config.ban_msgs_regex): + config.ban_msgs_regex.add(re.compile(r)) if config.INNER_VERSION in SpecifierSet(">=0.0.11"): config.max_response_length = msg_config.get("max_response_length", config.max_response_length) if config.INNER_VERSION in SpecifierSet(">=1.1.4"): @@ -587,6 +588,9 @@ class BotConfig: keywords_reaction_config = parent["keywords_reaction"] if keywords_reaction_config.get("enable", False): config.keywords_reaction_rules = keywords_reaction_config.get("rules", config.keywords_reaction_rules) + for rule in config.keywords_reaction_rules: + if rule.get("enable", False) and "regex" in rule: + rule["regex"] = [re.compile(r) for r in rule.get("regex", [])] def chinese_typo(parent: dict): chinese_typo_config = parent["chinese_typo"] diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 84a70cd98..059a03ed2 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -149,6 +149,11 @@ enable = false # 仅作示例,不会触发 keywords = ["测试关键词回复","test",""] reaction = "回答“测试成功”" +[[keywords_reaction.rules]] # 使用正则表达式匹配句式 +enable = false # 仅作示例,不会触发 +regex = ["^(?P\\S{1,20})是这样的$"] # 将匹配到的词汇命名为n,反应中对应的[n]会被替换为匹配到的内容,若不了解正则表达式请勿编写 +reaction = "请按照以下模板造句:[n]是这样的,xx只要xx就可以,可是[n]要考虑的事情就很多了,比如什么时候xx,什么时候xx,什么时候xx。(请自由发挥替换xx部分,只需保持句式结构,同时表达一种将[n]过度重视的反讽意味)" + [chinese_typo] enable = true # 是否启用中文错别字生成器 error_rate=0.001 # 单字替换概率 From 68a60f7e7124f8e231747e4e84f5cb871838df70 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 11 Apr 2025 10:22:49 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fix:=20PFC=E4=B8=8D=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E8=81=8A=E5=A4=A9=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 14 ++- src/plugins/PFC/chat_observer.py | 110 ++++++------------ src/plugins/PFC/chat_states.py | 25 +++- src/plugins/PFC/conversation.py | 15 +-- src/plugins/PFC/notification_handler.py | 71 ----------- src/plugins/PFC/observation_info.py | 91 ++++++++------- src/plugins/PFC/pfc.py | 54 ++++++--- src/plugins/PFC/pfc_utils.py | 62 +++++++++- .../think_flow_chat/think_flow_generator.py | 4 +- 9 files changed, 222 insertions(+), 224 deletions(-) delete mode 100644 src/plugins/PFC/notification_handler.py diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 43b0749a1..6eb67b7d4 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -45,7 +45,19 @@ class ActionPlanner: # 构建对话目标 if conversation_info.goal_list: - goal, reasoning = conversation_info.goal_list[-1] + last_goal = conversation_info.goal_list[-1] + print(last_goal) + # 处理字典或元组格式 + if isinstance(last_goal, tuple) and len(last_goal) == 2: + goal, reasoning = last_goal + elif isinstance(last_goal, dict) and 'goal' in last_goal and 'reasoning' in last_goal: + # 处理字典格式 + goal = last_goal.get('goal', "目前没有明确对话目标") + reasoning = last_goal.get('reasoning', "目前没有明确对话目标,最好思考一个对话目标") + else: + # 处理未知格式 + goal = "目前没有明确对话目标" + reasoning = "目前没有明确对话目标,最好思考一个对话目标" else: goal = "目前没有明确对话目标" reasoning = "目前没有明确对话目标,最好思考一个对话目标" diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index c96bc47b1..b9f704917 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -1,5 +1,6 @@ import time import asyncio +import traceback from typing import Optional, Dict, Any, List, Tuple from src.common.logger import get_module_logger from ..message.message_base import UserInfo @@ -44,18 +45,14 @@ class ChatObserver: self.stream_id = stream_id self.message_storage = message_storage or MongoDBMessageStorage() - self.last_user_speak_time: Optional[float] = None # 对方上次发言时间 - self.last_bot_speak_time: Optional[float] = None # 机器人上次发言时间 - self.last_check_time: float = time.time() # 上次查看聊天记录时间 + # self.last_user_speak_time: Optional[float] = None # 对方上次发言时间 + # self.last_bot_speak_time: Optional[float] = None # 机器人上次发言时间 + # self.last_check_time: float = time.time() # 上次查看聊天记录时间 self.last_message_read: Optional[str] = None # 最后读取的消息ID self.last_message_time: Optional[float] = None # 最后一条消息的时间戳 self.waiting_start_time: float = time.time() # 等待开始时间,初始化为当前时间 - # 消息历史记录 - self.message_history: List[Dict[str, Any]] = [] # 所有消息历史 - self.last_message_id: Optional[str] = None # 最后一条消息的ID - self.message_count: int = 0 # 消息计数 # 运行状态 self._running: bool = False @@ -72,7 +69,7 @@ class ChatObserver: self.is_cold_chat_state: bool = False self.update_event = asyncio.Event() - self.update_interval = 5 # 更新间隔(秒) + self.update_interval = 2 # 更新间隔(秒) self.message_cache = [] self.update_running = False @@ -98,21 +95,17 @@ class ChatObserver: Args: message: 消息数据 """ - self.message_history.append(message) - self.last_message_id = message["message_id"] - self.last_message_time = message["time"] # 更新最后消息时间 - self.message_count += 1 + try: - # 更新说话时间 - user_info = UserInfo.from_dict(message.get("user_info", {})) - if user_info.user_id == global_config.BOT_QQ: - self.last_bot_speak_time = message["time"] - else: - self.last_user_speak_time = message["time"] - - # 发送新消息通知 - notification = create_new_message_notification(sender="chat_observer", target="pfc", message=message) - await self.notification_manager.send_notification(notification) + # 发送新消息通知 + # logger.info(f"发送新ccchandleer消息通知: {message}") + notification = create_new_message_notification(sender="chat_observer", target="observation_info", message=message) + # logger.info(f"发送新消ddddd息通知: {notification}") + # print(self.notification_manager) + await self.notification_manager.send_notification(notification) + except Exception as e: + logger.error(f"添加消息到历史记录时出错: {e}") + print(traceback.format_exc()) # 检查并更新冷场状态 await self._check_cold_chat() @@ -156,9 +149,6 @@ class ChatObserver: Returns: bool: 是否有新消息 """ - if time_point is None: - logger.warning("time_point 为 None,返回 False") - return False if self.last_message_time is None: logger.debug("没有最后消息时间,返回 False") @@ -214,6 +204,8 @@ class ChatObserver: if new_messages: self.last_message_read = new_messages[-1]["message_id"] + + print(f"获取111111111122222222新消息: {new_messages}") return new_messages @@ -230,6 +222,8 @@ class ChatObserver: if new_messages: self.last_message_read = new_messages[-1]["message_id"] + + logger.debug(f"获取指定时间点111之前的消息: {new_messages}") return new_messages @@ -237,20 +231,24 @@ class ChatObserver: async def _update_loop(self): """更新循环""" - try: - start_time = time.time() - messages = await self._fetch_new_messages_before(start_time) - for message in messages: - await self._add_message_to_history(message) - except Exception as e: - logger.error(f"缓冲消息出错: {e}") + # try: + # start_time = time.time() + # messages = await self._fetch_new_messages_before(start_time) + # for message in messages: + # await self._add_message_to_history(message) + # logger.debug(f"缓冲消息: {messages}") + # except Exception as e: + # logger.error(f"缓冲消息出错: {e}") while self._running: try: # 等待事件或超时(1秒) try: + # print("等待事件") await asyncio.wait_for(self._update_event.wait(), timeout=1) + except asyncio.TimeoutError: + # print("超时") pass # 超时后也执行一次检查 self._update_event.clear() # 重置触发事件 @@ -355,51 +353,6 @@ class ChatObserver: return time_info - def start_periodic_update(self): - """启动观察器的定期更新""" - if not self.update_running: - self.update_running = True - asyncio.create_task(self._periodic_update()) - - async def _periodic_update(self): - """定期更新消息历史""" - try: - while self.update_running: - await self._update_message_history() - await asyncio.sleep(self.update_interval) - except Exception as e: - logger.error(f"定期更新消息历史时出错: {str(e)}") - - async def _update_message_history(self) -> bool: - """更新消息历史 - - Returns: - bool: 是否有新消息 - """ - try: - messages = await self.message_storage.get_messages_for_stream(self.stream_id, limit=50) - - if not messages: - return False - - # 检查是否有新消息 - has_new_messages = False - if messages and ( - not self.message_cache or messages[0]["message_id"] != self.message_cache[0]["message_id"] - ): - has_new_messages = True - - self.message_cache = messages - - if has_new_messages: - self.update_event.set() - self.update_event.clear() - return True - return False - - except Exception as e: - logger.error(f"更新消息历史时出错: {str(e)}") - return False def get_cached_messages(self, limit: int = 50) -> List[Dict[str, Any]]: """获取缓存的消息历史 @@ -421,3 +374,6 @@ class ChatObserver: if not self.message_cache: return None return self.message_cache[0] + + def __str__(self): + return f"ChatObserver for {self.stream_id}" diff --git a/src/plugins/PFC/chat_states.py b/src/plugins/PFC/chat_states.py index b28ca69a6..373dfdb74 100644 --- a/src/plugins/PFC/chat_states.py +++ b/src/plugins/PFC/chat_states.py @@ -98,11 +98,17 @@ class NotificationManager: notification_type: 要处理的通知类型 handler: 处理器实例 """ + print(1145145511114445551111444) if target not in self._handlers: + print("没11有target") self._handlers[target] = {} if notification_type not in self._handlers[target]: + print("没11有notification_type") self._handlers[target][notification_type] = [] + print(self._handlers[target][notification_type]) + print(f"注册1111111111111111111111处理器: {target} {notification_type} {handler}") self._handlers[target][notification_type].append(handler) + print(self._handlers[target][notification_type]) def unregister_handler(self, target: str, notification_type: NotificationType, handler: NotificationHandler): """注销通知处理器 @@ -126,6 +132,7 @@ class NotificationManager: async def send_notification(self, notification: Notification): """发送通知""" self._notification_history.append(notification) + # print("kaishichul-----------------------------------i") # 如果是状态通知,更新活跃状态 if isinstance(notification, StateNotification): @@ -133,12 +140,16 @@ class NotificationManager: self._active_states.add(notification.type) else: self._active_states.discard(notification.type) + # 调用目标接收者的处理器 target = notification.target if target in self._handlers: handlers = self._handlers[target].get(notification.type, []) + # print(1111111) + print(handlers) for handler in handlers: + print(f"调用处理器: {handler}") await handler.handle_notification(notification) def get_active_states(self) -> Set[NotificationType]: @@ -170,6 +181,13 @@ class NotificationManager: history = history[-limit:] return history + + def __str__(self): + str = "" + for target, handlers in self._handlers.items(): + for notification_type, handler_list in handlers.items(): + str += f"NotificationManager for {target} {notification_type} {handler_list}" + return str # 一些常用的通知创建函数 @@ -182,8 +200,9 @@ def create_new_message_notification(sender: str, target: str, message: Dict[str, target=target, data={ "message_id": message.get("message_id"), - "content": message.get("content"), - "sender": message.get("sender"), + "processed_plain_text": message.get("processed_plain_text"), + "detailed_plain_text": message.get("detailed_plain_text"), + "user_info": message.get("user_info"), "time": message.get("time"), }, ) @@ -276,3 +295,5 @@ class ChatStateManager: current_time = datetime.now().timestamp() return (current_time - self.state_info.last_message_time) <= threshold + + diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index 40a729671..a5da3e48d 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -60,9 +60,10 @@ class Conversation: self.chat_observer = ChatObserver.get_instance(self.stream_id) self.chat_observer.start() self.observation_info = ObservationInfo() - self.observation_info.bind_to_chat_observer(self.stream_id) + self.observation_info.bind_to_chat_observer(self.chat_observer) + # print(self.chat_observer.get_cached_messages(limit=) - # 对话信息 + self.conversation_info = ConversationInfo() except Exception as e: logger.error(f"初始化对话实例:注册信息组件失败: {e}") @@ -140,6 +141,7 @@ class Conversation: if action == "direct_reply": self.state = ConversationState.GENERATING self.generated_reply = await self.reply_generator.generate(observation_info, conversation_info) + print(f"生成回复: {self.generated_reply}") # # 检查回复是否合适 # is_suitable, reason, need_replan = await self.reply_generator.check_reply( @@ -148,6 +150,7 @@ class Conversation: # ) if self._check_new_messages_after_planning(): + logger.info("333333发现新消息,重新考虑行动") return None await self._send_reply() @@ -212,15 +215,9 @@ class Conversation: logger.warning("没有生成回复") return - messages = self.chat_observer.get_cached_messages(limit=1) - if not messages: - logger.warning("没有最近的消息可以回复") - return - - latest_message = self._convert_to_message(messages[0]) try: await self.direct_sender.send_message( - chat_stream=self.chat_stream, content=self.generated_reply, reply_to_message=latest_message + chat_stream=self.chat_stream, content=self.generated_reply ) self.chat_observer.trigger_update() # 触发立即更新 if not await self.chat_observer.wait_for_update(): diff --git a/src/plugins/PFC/notification_handler.py b/src/plugins/PFC/notification_handler.py deleted file mode 100644 index 1131d18bf..000000000 --- a/src/plugins/PFC/notification_handler.py +++ /dev/null @@ -1,71 +0,0 @@ -from typing import TYPE_CHECKING -from src.common.logger import get_module_logger -from .chat_states import NotificationHandler, Notification, NotificationType - -if TYPE_CHECKING: - from .conversation import Conversation - -logger = get_module_logger("notification_handler") - - -class PFCNotificationHandler(NotificationHandler): - """PFC通知处理器""" - - def __init__(self, conversation: "Conversation"): - """初始化PFC通知处理器 - - Args: - conversation: 对话实例 - """ - self.conversation = conversation - - async def handle_notification(self, notification: Notification): - """处理通知 - - Args: - notification: 通知对象 - """ - logger.debug(f"收到通知: {notification.type.name}, 数据: {notification.data}") - - # 根据通知类型执行不同的处理 - if notification.type == NotificationType.NEW_MESSAGE: - # 新消息通知 - await self._handle_new_message(notification) - elif notification.type == NotificationType.COLD_CHAT: - # 冷聊天通知 - await self._handle_cold_chat(notification) - elif notification.type == NotificationType.COMMAND: - # 命令通知 - await self._handle_command(notification) - else: - logger.warning(f"未知的通知类型: {notification.type.name}") - - async def _handle_new_message(self, notification: Notification): - """处理新消息通知 - - Args: - notification: 通知对象 - """ - - # 更新决策信息 - observation_info = self.conversation.observation_info - observation_info.last_message_time = notification.data.get("time", 0) - observation_info.add_unprocessed_message(notification.data) - - # 手动触发观察器更新 - self.conversation.chat_observer.trigger_update() - - async def _handle_cold_chat(self, notification: Notification): - """处理冷聊天通知 - - Args: - notification: 通知对象 - """ - # 获取冷聊天信息 - cold_duration = notification.data.get("duration", 0) - - # 更新决策信息 - observation_info = self.conversation.observation_info - observation_info.conversation_cold_duration = cold_duration - - logger.info(f"对话已冷: {cold_duration}秒") diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index d0eee2236..947c3205d 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -6,7 +6,7 @@ import time from dataclasses import dataclass, field from src.common.logger import get_module_logger from .chat_observer import ChatObserver -from .chat_states import NotificationHandler +from .chat_states import NotificationHandler, NotificationType logger = get_module_logger("observation_info") @@ -22,63 +22,70 @@ class ObservationInfoHandler(NotificationHandler): """ self.observation_info = observation_info - async def handle_notification(self, notification: Dict[str, Any]): - """处理通知 - - Args: - notification: 通知数据 - """ - notification_type = notification.get("type") - data = notification.get("data", {}) - - if notification_type == "NEW_MESSAGE": + async def handle_notification(self, notification): + # 获取通知类型和数据 + notification_type = notification.type + data = notification.data + + if notification_type == NotificationType.NEW_MESSAGE: # 处理新消息通知 logger.debug(f"收到新消息通知data: {data}") - message = data.get("message", {}) + message_id = data.get("message_id") + processed_plain_text = data.get("processed_plain_text") + detailed_plain_text = data.get("detailed_plain_text") + user_info = data.get("user_info") + time_value = data.get("time") + + message = { + "message_id": message_id, + "processed_plain_text": processed_plain_text, + "detailed_plain_text": detailed_plain_text, + "user_info": user_info, + "time": time_value + } + self.observation_info.update_from_message(message) - # self.observation_info.has_unread_messages = True - # self.observation_info.new_unread_message.append(message.get("processed_plain_text", "")) - elif notification_type == "COLD_CHAT": + elif notification_type == NotificationType.COLD_CHAT: # 处理冷场通知 is_cold = data.get("is_cold", False) self.observation_info.update_cold_chat_status(is_cold, time.time()) - elif notification_type == "ACTIVE_CHAT": + elif notification_type == NotificationType.ACTIVE_CHAT: # 处理活跃通知 is_active = data.get("is_active", False) self.observation_info.is_cold = not is_active - elif notification_type == "BOT_SPEAKING": + elif notification_type == NotificationType.BOT_SPEAKING: # 处理机器人说话通知 self.observation_info.is_typing = False self.observation_info.last_bot_speak_time = time.time() - elif notification_type == "USER_SPEAKING": + elif notification_type == NotificationType.USER_SPEAKING: # 处理用户说话通知 self.observation_info.is_typing = False self.observation_info.last_user_speak_time = time.time() - elif notification_type == "MESSAGE_DELETED": + elif notification_type == NotificationType.MESSAGE_DELETED: # 处理消息删除通知 message_id = data.get("message_id") self.observation_info.unprocessed_messages = [ msg for msg in self.observation_info.unprocessed_messages if msg.get("message_id") != message_id ] - elif notification_type == "USER_JOINED": + elif notification_type == NotificationType.USER_JOINED: # 处理用户加入通知 user_id = data.get("user_id") if user_id: self.observation_info.active_users.add(user_id) - elif notification_type == "USER_LEFT": + elif notification_type == NotificationType.USER_LEFT: # 处理用户离开通知 user_id = data.get("user_id") if user_id: self.observation_info.active_users.discard(user_id) - elif notification_type == "ERROR": + elif notification_type == NotificationType.ERROR: # 处理错误通知 error_msg = data.get("error", "") logger.error(f"收到错误通知: {error_msg}") @@ -100,6 +107,7 @@ class ObservationInfo: last_message_content: str = "" last_message_sender: Optional[str] = None bot_id: Optional[str] = None + chat_history_count: int = 0 new_messages_count: int = 0 cold_chat_duration: float = 0.0 @@ -117,28 +125,37 @@ class ObservationInfo: self.chat_observer = None self.handler = ObservationInfoHandler(self) - def bind_to_chat_observer(self, stream_id: str): + def bind_to_chat_observer(self, chat_observer: ChatObserver): """绑定到指定的chat_observer Args: stream_id: 聊天流ID """ - self.chat_observer = ChatObserver.get_instance(stream_id) + self.chat_observer = chat_observer + print(f"1919810----------------------绑定-----------------------------") + print(self.chat_observer) + print(f"1919810--------------------绑定-----------------------------") + print(self.chat_observer.notification_manager) + print(f"1919810-------------------绑定-----------------------------") self.chat_observer.notification_manager.register_handler( - target="observation_info", notification_type="NEW_MESSAGE", handler=self.handler + target="observation_info", notification_type=NotificationType.NEW_MESSAGE, handler=self.handler ) self.chat_observer.notification_manager.register_handler( - target="observation_info", notification_type="COLD_CHAT", handler=self.handler + target="observation_info", notification_type=NotificationType.COLD_CHAT, handler=self.handler ) + print("1919810------------------------绑定-----------------------------") + print(f"1919810--------------------绑定-----------------------------") + print(self.chat_observer.notification_manager) + print(f"1919810-------------------绑定-----------------------------") def unbind_from_chat_observer(self): """解除与chat_observer的绑定""" if self.chat_observer: self.chat_observer.notification_manager.unregister_handler( - target="observation_info", notification_type="NEW_MESSAGE", handler=self.handler + target="observation_info", notification_type=NotificationType.NEW_MESSAGE, handler=self.handler ) self.chat_observer.notification_manager.unregister_handler( - target="observation_info", notification_type="COLD_CHAT", handler=self.handler + target="observation_info", notification_type=NotificationType.COLD_CHAT, handler=self.handler ) self.chat_observer = None @@ -148,8 +165,11 @@ class ObservationInfo: Args: message: 消息数据 """ + print("1919810-----------------------------------------------------") logger.debug(f"更新信息from_message: {message}") self.last_message_time = message["time"] + self.last_message_id = message["message_id"] + self.last_message_content = message.get("processed_plain_text", "") user_info = UserInfo.from_dict(message.get("user_info", {})) @@ -169,7 +189,6 @@ class ObservationInfo: def update_changed(self): """更新changed状态""" self.changed = True - # self.meta_plan_trigger = True def update_cold_chat_status(self, is_cold: bool, current_time: float): """更新冷场状态 @@ -223,17 +242,3 @@ class ObservationInfo: self.unprocessed_messages.clear() self.new_messages_count = 0 - def add_unprocessed_message(self, message: Dict[str, Any]): - """添加未处理的消息 - - Args: - message: 消息数据 - """ - # 防止重复添加同一消息 - message_id = message.get("message_id") - if message_id and not any(m.get("message_id") == message_id for m in self.unprocessed_messages): - self.unprocessed_messages.append(message) - self.new_messages_count += 1 - - # 同时更新其他消息相关信息 - self.update_from_message(message) diff --git a/src/plugins/PFC/pfc.py b/src/plugins/PFC/pfc.py index c88ed47d5..0a20812b9 100644 --- a/src/plugins/PFC/pfc.py +++ b/src/plugins/PFC/pfc.py @@ -99,19 +99,21 @@ class GoalAnalyzer: 3. 添加新目标 4. 删除不再相关的目标 -请以JSON格式输出当前的所有对话目标,包含以下字段: +请以JSON数组格式输出当前的所有对话目标,每个目标包含以下字段: 1. goal: 对话目标(简短的一句话) 2. reasoning: 对话原因,为什么设定这个目标(简要解释) 输出格式示例: -{{ -"goal": "回答用户关于Python编程的具体问题", -"reasoning": "用户提出了关于Python的技术问题,需要专业且准确的解答" -}}, -{{ -"goal": "回答用户关于python安装的具体问题", -"reasoning": "用户提出了关于Python的技术问题,需要专业且准确的解答" -}}""" +[ + {{ + "goal": "回答用户关于Python编程的具体问题", + "reasoning": "用户提出了关于Python的技术问题,需要专业且准确的解答" + }}, + {{ + "goal": "回答用户关于python安装的具体问题", + "reasoning": "用户提出了关于Python的技术问题,需要专业且准确的解答" + }} +]""" logger.debug(f"发送到LLM的提示词: {prompt}") try: @@ -120,13 +122,37 @@ class GoalAnalyzer: except Exception as e: logger.error(f"分析对话目标时出错: {str(e)}") content = "" - # 使用简化函数提取JSON内容 + + # 使用改进后的get_items_from_json函数处理JSON数组 success, result = get_items_from_json( - content, "goal", "reasoning", required_types={"goal": str, "reasoning": str} + content, "goal", "reasoning", + required_types={"goal": str, "reasoning": str}, + allow_array=True ) - # TODO - - conversation_info.goal_list.append(result) + + if success: + # 判断结果是单个字典还是字典列表 + if isinstance(result, list): + # 清空现有目标列表并添加新目标 + conversation_info.goal_list = [] + for item in result: + goal = item.get("goal", "") + reasoning = item.get("reasoning", "") + conversation_info.goal_list.append((goal, reasoning)) + + # 返回第一个目标作为当前主要目标(如果有) + if result: + first_goal = result[0] + return (first_goal.get("goal", ""), "", first_goal.get("reasoning", "")) + else: + # 单个目标的情况 + goal = result.get("goal", "") + reasoning = result.get("reasoning", "") + conversation_info.goal_list.append((goal, reasoning)) + return (goal, "", reasoning) + + # 如果解析失败,返回默认值 + return ("", "", "") async def _update_goals(self, new_goal: str, method: str, reasoning: str): """更新目标列表 diff --git a/src/plugins/PFC/pfc_utils.py b/src/plugins/PFC/pfc_utils.py index 633d9016e..f99b32a3d 100644 --- a/src/plugins/PFC/pfc_utils.py +++ b/src/plugins/PFC/pfc_utils.py @@ -1,6 +1,6 @@ import json import re -from typing import Dict, Any, Optional, Tuple +from typing import Dict, Any, Optional, Tuple, List, Union from src.common.logger import get_module_logger logger = get_module_logger("pfc_utils") @@ -11,7 +11,8 @@ def get_items_from_json( *items: str, default_values: Optional[Dict[str, Any]] = None, required_types: Optional[Dict[str, type]] = None, -) -> Tuple[bool, Dict[str, Any]]: + allow_array: bool = True, +) -> Tuple[bool, Union[Dict[str, Any], List[Dict[str, Any]]]]: """从文本中提取JSON内容并获取指定字段 Args: @@ -19,18 +20,69 @@ def get_items_from_json( *items: 要提取的字段名 default_values: 字段的默认值,格式为 {字段名: 默认值} required_types: 字段的必需类型,格式为 {字段名: 类型} + allow_array: 是否允许解析JSON数组 Returns: - Tuple[bool, Dict[str, Any]]: (是否成功, 提取的字段字典) + Tuple[bool, Union[Dict[str, Any], List[Dict[str, Any]]]]: (是否成功, 提取的字段字典或字典列表) """ content = content.strip() result = {} - + # 设置默认值 if default_values: result.update(default_values) - # 尝试解析JSON + # 首先尝试解析为JSON数组 + if allow_array: + try: + # 尝试找到文本中的JSON数组 + array_pattern = r"\[[\s\S]*\]" + array_match = re.search(array_pattern, content) + if array_match: + array_content = array_match.group() + json_array = json.loads(array_content) + + # 确认是数组类型 + if isinstance(json_array, list): + # 验证数组中的每个项目是否包含所有必需字段 + valid_items = [] + for item in json_array: + if not isinstance(item, dict): + continue + + # 检查是否有所有必需字段 + if all(field in item for field in items): + # 验证字段类型 + if required_types: + type_valid = True + for field, expected_type in required_types.items(): + if field in item and not isinstance(item[field], expected_type): + type_valid = False + break + + if not type_valid: + continue + + # 验证字符串字段不为空 + string_valid = True + for field in items: + if isinstance(item[field], str) and not item[field].strip(): + string_valid = False + break + + if not string_valid: + continue + + valid_items.append(item) + + if valid_items: + return True, valid_items + except json.JSONDecodeError: + logger.debug("JSON数组解析失败,尝试解析单个JSON对象") + except Exception as e: + logger.debug(f"尝试解析JSON数组时出错: {str(e)}") + + # 尝试解析JSON对象 try: json_data = json.loads(content) except json.JSONDecodeError: diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index f422b8c99..9541eed17 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -26,11 +26,11 @@ logger = get_module_logger("llm_generator", config=llm_config) class ResponseGenerator: def __init__(self): self.model_normal = LLM_request( - model=global_config.llm_normal, temperature=0.3, max_tokens=256, request_type="response_heartflow" + model=global_config.llm_normal, temperature=0.15, max_tokens=256, request_type="response_heartflow" ) self.model_sum = LLM_request( - model=global_config.llm_summary_by_topic, temperature=0.7, max_tokens=2000, request_type="relation" + model=global_config.llm_summary_by_topic, temperature=0.6, max_tokens=2000, request_type="relation" ) self.current_model_type = "r1" # 默认使用 R1 self.current_model_name = "unknown model" From 157f2bc0447ce956417f69699f58ecc324e24c95 Mon Sep 17 00:00:00 2001 From: HexatomicRing <54496918+HexatomicRing@users.noreply.github.com> Date: Fri, 11 Apr 2025 10:35:34 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D=E5=92=8Cregex=E9=87=8D=E5=A4=8D=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reasoning_prompt_builder.py | 23 ++++++++++--------- .../think_flow_prompt_builder.py | 23 ++++++++++--------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py index 045045bae..75a876a9c 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py @@ -115,17 +115,18 @@ class PromptBuilder: f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}" ) keywords_reaction_prompt += rule.get("reaction", "") + "," - for pattern in rule.get("regex", []): - result = pattern.search(message_txt) - if result: - reaction = rule.get('reaction', '') - for name, content in result.groupdict().items(): - reaction = reaction.replace(f'[{name}]', content) - logger.info( - f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" - ) - keywords_reaction_prompt += reaction + "," - break + else: + for pattern in rule.get("regex", []): + result = pattern.search(message_txt) + if result: + reaction = rule.get('reaction', '') + for name, content in result.groupdict().items(): + reaction = reaction.replace(f'[{name}]', content) + logger.info( + f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" + ) + keywords_reaction_prompt += reaction + "," + break # 中文高手(新加的好玩功能) prompt_ger = "" diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index 8938e2e78..7ae7940bb 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -61,17 +61,18 @@ class PromptBuilder: f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}" ) keywords_reaction_prompt += rule.get("reaction", "") + "," - for pattern in rule.get("regex", []): - result = pattern.search(message_txt) - if result: - reaction = rule.get('reaction', '') - for name, content in result.groupdict().items(): - reaction = reaction.replace(f'[{name}]', content) - logger.info( - f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" - ) - keywords_reaction_prompt += reaction + "," - break + else: + for pattern in rule.get("regex", []): + result = pattern.search(message_txt) + if result: + reaction = rule.get('reaction', '') + for name, content in result.groupdict().items(): + reaction = reaction.replace(f'[{name}]', content) + logger.info( + f"匹配到以下正则表达式:{pattern},触发反应:{reaction}" + ) + keywords_reaction_prompt += reaction + "," + break # 中文高手(新加的好玩功能) prompt_ger = "" From 138fc11752d0bc5d4d6a7159e34c4677e5bb45a9 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 11 Apr 2025 10:48:14 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix:=E5=AD=A9=E5=AD=90=E4=BB=AC=EF=BC=8CPFC?= =?UTF-8?q?=E7=BB=88=E4=BA=8E=E5=A4=8D=E6=B4=BB=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 4 ++-- src/plugins/PFC/chat_observer.py | 28 +++++++++++----------------- src/plugins/PFC/message_storage.py | 15 ++++++--------- src/plugins/PFC/observation_info.py | 12 ++---------- 4 files changed, 21 insertions(+), 38 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 6eb67b7d4..372474ac0 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -66,14 +66,14 @@ class ActionPlanner: chat_history_list = observation_info.chat_history chat_history_text = "" for msg in chat_history_list: - chat_history_text += f"{msg}\n" + chat_history_text += f"{msg.get('detailed_plain_text', '')}\n" if observation_info.new_messages_count > 0: new_messages_list = observation_info.unprocessed_messages chat_history_text += f"有{observation_info.new_messages_count}条新消息:\n" for msg in new_messages_list: - chat_history_text += f"{msg}\n" + chat_history_text += f"{msg.get('detailed_plain_text', '')}\n" observation_info.clear_unprocessed_messages() diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index b9f704917..a766e3b4a 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -18,38 +18,36 @@ class ChatObserver: _instances: Dict[str, "ChatObserver"] = {} @classmethod - def get_instance(cls, stream_id: str, message_storage: Optional[MessageStorage] = None) -> "ChatObserver": + def get_instance(cls, stream_id: str) -> "ChatObserver": """获取或创建观察器实例 Args: stream_id: 聊天流ID - message_storage: 消息存储实现,如果为None则使用MongoDB实现 Returns: ChatObserver: 观察器实例 """ if stream_id not in cls._instances: - cls._instances[stream_id] = cls(stream_id, message_storage) + cls._instances[stream_id] = cls(stream_id) return cls._instances[stream_id] - def __init__(self, stream_id: str, message_storage: Optional[MessageStorage] = None): + def __init__(self, stream_id: str): """初始化观察器 Args: stream_id: 聊天流ID - message_storage: 消息存储实现,如果为None则使用MongoDB实现 """ if stream_id in self._instances: raise RuntimeError(f"ChatObserver for {stream_id} already exists. Use get_instance() instead.") self.stream_id = stream_id - self.message_storage = message_storage or MongoDBMessageStorage() + self.message_storage = MongoDBMessageStorage() # self.last_user_speak_time: Optional[float] = None # 对方上次发言时间 # self.last_bot_speak_time: Optional[float] = None # 机器人上次发言时间 # self.last_check_time: float = time.time() # 上次查看聊天记录时间 - self.last_message_read: Optional[str] = None # 最后读取的消息ID - self.last_message_time: Optional[float] = None # 最后一条消息的时间戳 + self.last_message_read: Optional[Dict[str, Any]] = None # 最后读取的消息ID + self.last_message_time: float = time.time() self.waiting_start_time: float = time.time() # 等待开始时间,初始化为当前时间 @@ -133,12 +131,6 @@ class ChatObserver: notification = create_cold_chat_notification(sender="chat_observer", target="pfc", is_cold=is_cold) await self.notification_manager.send_notification(notification) - async def get_new_message(self) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: - """获取上一次观察的时间点后的新消息,插入到历史记录中,并返回新消息和历史记录两个对象""" - messages = await self.message_storage.get_messages_after(self.stream_id, self.last_message_read) - for message in messages: - await self._add_message_to_history(message) - return messages, self.message_history def new_message_after(self, time_point: float) -> bool: """判断是否在指定时间点后有新消息 @@ -200,12 +192,13 @@ class ChatObserver: Returns: List[Dict[str, Any]]: 新消息列表 """ - new_messages = await self.message_storage.get_messages_after(self.stream_id, self.last_message_read) + new_messages = await self.message_storage.get_messages_after(self.stream_id, self.last_message_time) if new_messages: - self.last_message_read = new_messages[-1]["message_id"] + self.last_message_read = new_messages[-1] + self.last_message_time = new_messages[-1]["time"] - print(f"获取111111111122222222新消息: {new_messages}") + print(f"获取数据库中找到的新消息: {new_messages}") return new_messages @@ -267,6 +260,7 @@ class ChatObserver: except Exception as e: logger.error(f"更新循环出错: {e}") + logger.error(traceback.format_exc()) self._update_complete.set() # 即使出错也要设置完成事件 def trigger_update(self): diff --git a/src/plugins/PFC/message_storage.py b/src/plugins/PFC/message_storage.py index 88f409641..75bab6edd 100644 --- a/src/plugins/PFC/message_storage.py +++ b/src/plugins/PFC/message_storage.py @@ -1,18 +1,18 @@ from abc import ABC, abstractmethod from typing import List, Dict, Any, Optional from src.common.database import db - +import time class MessageStorage(ABC): """消息存储接口""" @abstractmethod - async def get_messages_after(self, chat_id: str, message_id: Optional[str] = None) -> List[Dict[str, Any]]: + async def get_messages_after(self, chat_id: str, message: Dict[str, Any]) -> List[Dict[str, Any]]: """获取指定消息ID之后的所有消息 Args: chat_id: 聊天ID - message_id: 消息ID,如果为None则获取所有消息 + message: 消息 Returns: List[Dict[str, Any]]: 消息列表 @@ -53,14 +53,11 @@ class MongoDBMessageStorage(MessageStorage): def __init__(self): self.db = db - async def get_messages_after(self, chat_id: str, message_id: Optional[str] = None) -> List[Dict[str, Any]]: + async def get_messages_after(self, chat_id: str, message_time: float) -> List[Dict[str, Any]]: query = {"chat_id": chat_id} + print(f"storage_check_message: {message_time}") - if message_id: - # 获取ID大于message_id的消息 - last_message = self.db.messages.find_one({"message_id": message_id}) - if last_message: - query["time"] = {"$gt": last_message["time"]} + query["time"] = {"$gt": message_time} return list(self.db.messages.find(query).sort("time", 1)) diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index 947c3205d..01f619dc3 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -132,11 +132,6 @@ class ObservationInfo: stream_id: 聊天流ID """ self.chat_observer = chat_observer - print(f"1919810----------------------绑定-----------------------------") - print(self.chat_observer) - print(f"1919810--------------------绑定-----------------------------") - print(self.chat_observer.notification_manager) - print(f"1919810-------------------绑定-----------------------------") self.chat_observer.notification_manager.register_handler( target="observation_info", notification_type=NotificationType.NEW_MESSAGE, handler=self.handler ) @@ -144,9 +139,6 @@ class ObservationInfo: target="observation_info", notification_type=NotificationType.COLD_CHAT, handler=self.handler ) print("1919810------------------------绑定-----------------------------") - print(f"1919810--------------------绑定-----------------------------") - print(self.chat_observer.notification_manager) - print(f"1919810-------------------绑定-----------------------------") def unbind_from_chat_observer(self): """解除与chat_observer的绑定""" @@ -235,10 +227,10 @@ class ObservationInfo: """清空未处理消息列表""" # 将未处理消息添加到历史记录中 for message in self.unprocessed_messages: - if "processed_plain_text" in message: - self.chat_history.append(message["processed_plain_text"]) + self.chat_history.append(message) # 清空未处理消息列表 self.has_unread_messages = False self.unprocessed_messages.clear() + self.chat_history_count = len(self.chat_history) self.new_messages_count = 0 From 27c10ff29d91a518226c3538febd13453fa744be Mon Sep 17 00:00:00 2001 From: DrSmoothl <1787882683@qq.com> Date: Fri, 11 Apr 2025 10:55:45 +0800 Subject: [PATCH 5/9] fix: Ruff --- src/do_tool/tool_can_use/__init__.py | 11 ++++++++++- src/do_tool/tool_can_use/base_tool.py | 12 ++++-------- src/do_tool/tool_can_use/get_knowledge.py | 2 +- src/do_tool/tool_use.py | 1 - src/heart_flow/observation.py | 2 -- src/heart_flow/sub_heartflow.py | 11 +++++------ .../think_flow_chat/think_flow_prompt_builder.py | 4 ++-- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/do_tool/tool_can_use/__init__.py b/src/do_tool/tool_can_use/__init__.py index cc196d07a..3189d2897 100644 --- a/src/do_tool/tool_can_use/__init__.py +++ b/src/do_tool/tool_can_use/__init__.py @@ -7,5 +7,14 @@ from src.do_tool.tool_can_use.base_tool import ( TOOL_REGISTRY ) +__all__ = [ + 'BaseTool', + 'register_tool', + 'discover_tools', + 'get_all_tool_definitions', + 'get_tool_instance', + 'TOOL_REGISTRY' +] + # 自动发现并注册工具 -discover_tools() \ No newline at end of file +discover_tools() \ No newline at end of file diff --git a/src/do_tool/tool_can_use/base_tool.py b/src/do_tool/tool_can_use/base_tool.py index 03aac5e4c..c8c80ebe8 100644 --- a/src/do_tool/tool_can_use/base_tool.py +++ b/src/do_tool/tool_can_use/base_tool.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Any, Optional, Union, Type +from typing import Dict, List, Any, Optional, Type import inspect import importlib import pkgutil @@ -73,13 +73,9 @@ def discover_tools(): # 获取当前目录路径 current_dir = os.path.dirname(os.path.abspath(__file__)) package_name = os.path.basename(current_dir) - parent_dir = os.path.dirname(current_dir) - - # 导入当前包 - package = importlib.import_module(f"src.do_tool.{package_name}") # 遍历包中的所有模块 - for _, module_name, is_pkg in pkgutil.iter_modules([current_dir]): + for _, module_name, _ in pkgutil.iter_modules([current_dir]): # 跳过当前模块和__pycache__ if module_name == "base_tool" or module_name.startswith("__"): continue @@ -88,7 +84,7 @@ def discover_tools(): module = importlib.import_module(f"src.do_tool.{package_name}.{module_name}") # 查找模块中的工具类 - for name, obj in inspect.getmembers(module): + for _, obj in inspect.getmembers(module): if inspect.isclass(obj) and issubclass(obj, BaseTool) and obj != BaseTool: register_tool(obj) @@ -116,4 +112,4 @@ def get_tool_instance(tool_name: str) -> Optional[BaseTool]: tool_class = TOOL_REGISTRY.get(tool_name) if not tool_class: return None - return tool_class() \ No newline at end of file + return tool_class() \ No newline at end of file diff --git a/src/do_tool/tool_can_use/get_knowledge.py b/src/do_tool/tool_can_use/get_knowledge.py index 06ea7a91b..fa17dfbf6 100644 --- a/src/do_tool/tool_can_use/get_knowledge.py +++ b/src/do_tool/tool_can_use/get_knowledge.py @@ -2,7 +2,7 @@ from src.do_tool.tool_can_use.base_tool import BaseTool, register_tool from src.plugins.chat.utils import get_embedding from src.common.database import db from src.common.logger import get_module_logger -from typing import Dict, Any, Union, List +from typing import Dict, Any, Union logger = get_module_logger("get_knowledge_tool") diff --git a/src/do_tool/tool_use.py b/src/do_tool/tool_use.py index a2e23ab21..95118f79f 100644 --- a/src/do_tool/tool_use.py +++ b/src/do_tool/tool_use.py @@ -5,7 +5,6 @@ from src.common.database import db import time import json from src.common.logger import get_module_logger -from typing import Union from src.do_tool.tool_can_use import get_all_tool_definitions, get_tool_instance logger = get_module_logger("tool_use") diff --git a/src/heart_flow/observation.py b/src/heart_flow/observation.py index f507374cb..55ab9db11 100644 --- a/src/heart_flow/observation.py +++ b/src/heart_flow/observation.py @@ -4,8 +4,6 @@ from datetime import datetime from src.plugins.models.utils_model import LLM_request from src.plugins.config.config import global_config from src.common.database import db -from src.individuality.individuality import Individuality -import random # 所有观察的基类 diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py index baa20c64f..9cf2e2ea2 100644 --- a/src/heart_flow/sub_heartflow.py +++ b/src/heart_flow/sub_heartflow.py @@ -5,18 +5,17 @@ from src.plugins.models.utils_model import LLM_request from src.plugins.config.config import global_config import re import time -from src.plugins.schedule.schedule_generator import bot_schedule -from src.plugins.memory_system.Hippocampus import HippocampusManager +# from src.plugins.schedule.schedule_generator import bot_schedule +# from src.plugins.memory_system.Hippocampus import HippocampusManager from src.common.logger import get_module_logger, LogConfig, SUB_HEARTFLOW_STYLE_CONFIG # noqa: E402 -from src.plugins.chat.utils import get_embedding -from src.common.database import db -from typing import Union +# from src.plugins.chat.utils import get_embedding +# from src.common.database import db +# from typing import Union from src.individuality.individuality import Individuality import random from src.plugins.chat.chat_stream import ChatStream from src.plugins.person_info.relationship_manager import relationship_manager from src.plugins.chat.utils import get_recent_group_speaker -import json from src.do_tool.tool_use import ToolUser subheartflow_config = LogConfig( diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index 0e00eea95..43b0db219 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -108,7 +108,7 @@ class PromptBuilder: 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) + # prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1) # 日程构建 @@ -166,7 +166,7 @@ class PromptBuilder: ) -> tuple[str, str]: individuality = Individuality.get_instance() - prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1) + # prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1) prompt_identity = individuality.get_prompt(type="identity", x_person=2, level=1) From b11ebbc8323ea2155376aff74f9a9afc2ad3705f Mon Sep 17 00:00:00 2001 From: DrSmoothl <1787882683@qq.com> Date: Fri, 11 Apr 2025 10:57:49 +0800 Subject: [PATCH 6/9] fix: Ruff x2 --- src/plugins/PFC/chat_observer.py | 4 ++-- src/plugins/PFC/message_storage.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index a766e3b4a..0af11e135 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -1,12 +1,12 @@ import time import asyncio import traceback -from typing import Optional, Dict, Any, List, Tuple +from typing import Optional, Dict, Any, List from src.common.logger import get_module_logger from ..message.message_base import UserInfo from ..config.config import global_config from .chat_states import NotificationManager, create_new_message_notification, create_cold_chat_notification -from .message_storage import MessageStorage, MongoDBMessageStorage +from .message_storage import MongoDBMessageStorage logger = get_module_logger("chat_observer") diff --git a/src/plugins/PFC/message_storage.py b/src/plugins/PFC/message_storage.py index 75bab6edd..afd233347 100644 --- a/src/plugins/PFC/message_storage.py +++ b/src/plugins/PFC/message_storage.py @@ -1,7 +1,6 @@ from abc import ABC, abstractmethod -from typing import List, Dict, Any, Optional +from typing import List, Dict, Any from src.common.database import db -import time class MessageStorage(ABC): """消息存储接口""" From 1ac9a66cee430d86c0a7e67c97e1d20c35f1a5dc Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Fri, 11 Apr 2025 13:10:15 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E4=B8=8D=E5=B0=8F=E5=BF=83=E7=82=B8?= =?UTF-8?q?=E4=BA=86logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index c9568ecba..653efd45d 100644 --- a/bot.py +++ b/bot.py @@ -16,7 +16,7 @@ confirm_logger_config = LogConfig( console_format=CONFIRM_STYLE_CONFIG["console_format"], file_format=CONFIRM_STYLE_CONFIG["file_format"], ) -confirm_logger = get_module_logger("main_bot", config=confirm_logger_config) +confirm_logger = get_module_logger("confirm", config=confirm_logger_config) # 获取没有加载env时的环境变量 env_mask = {key: os.getenv(key) for key in os.environ} From 70f3dcba1fd96c7c8d025dd2ae2e6817d75c0c3c Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 11 Apr 2025 16:01:57 +0800 Subject: [PATCH 8/9] =?UTF-8?q?better=EF=BC=9A=E6=9B=B4=E6=96=B0PFC?= =?UTF-8?q?=EF=BC=8C=E7=8E=B0=E5=8F=AF=E6=8B=A5=E6=9C=89=E5=A4=9A=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E4=BB=A5=E5=8F=8A=E5=85=B6=E4=BB=96=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/heart_flow/heartflow.py | 31 ++++++--- src/plugins/PFC/action_planner.py | 61 ++++++++++------ src/plugins/PFC/chat_observer.py | 2 +- src/plugins/PFC/conversation.py | 37 +++++++--- src/plugins/PFC/message_storage.py | 2 +- src/plugins/PFC/observation_info.py | 4 +- src/plugins/PFC/pfc.py | 62 +++++++---------- src/plugins/PFC/pfc_manager.py | 2 +- src/plugins/PFC/reply_generator.py | 75 +++++++++++++++----- src/plugins/PFC/waiter.py | 85 ++++++++++++++++------- src/plugins/config/config.py | 2 +- src/plugins/memory_system/debug_memory.py | 32 +-------- 12 files changed, 238 insertions(+), 157 deletions(-) diff --git a/src/heart_flow/heartflow.py b/src/heart_flow/heartflow.py index 3ea51917c..c4afaa0b0 100644 --- a/src/heart_flow/heartflow.py +++ b/src/heart_flow/heartflow.py @@ -21,11 +21,14 @@ logger = get_module_logger("heartflow", config=heartflow_config) class CurrentState: def __init__(self): - self.willing = 0 + self.current_state_info = "" self.mood_manager = MoodManager() self.mood = self.mood_manager.get_prompt() + + self.attendance_factor = 0 + self.engagement_factor = 0 def update_current_state_info(self): self.current_state_info = self.mood_manager.get_current_mood() @@ -41,7 +44,9 @@ class Heartflow: ) self._subheartflows: Dict[Any, SubHeartflow] = {} - self.active_subheartflows_nums = 0 + + + async def _cleanup_inactive_subheartflows(self): """定期清理不活跃的子心流""" @@ -63,11 +68,8 @@ class Heartflow: logger.info(f"已清理不活跃的子心流: {subheartflow_id}") await asyncio.sleep(30) # 每分钟检查一次 - - async def heartflow_start_working(self): - # 启动清理任务 - asyncio.create_task(self._cleanup_inactive_subheartflows()) - + + async def _sub_heartflow_update(self): while True: # 检查是否存在子心流 if not self._subheartflows: @@ -78,6 +80,17 @@ class Heartflow: await self.do_a_thinking() await asyncio.sleep(global_config.heart_flow_update_interval) # 5分钟思考一次 + async def heartflow_start_working(self): + + # 启动清理任务 + asyncio.create_task(self._cleanup_inactive_subheartflows()) + + # 启动子心流更新任务 + asyncio.create_task(self._sub_heartflow_update()) + + async def _update_current_state(self): + print("TODO") + async def do_a_thinking(self): logger.debug("麦麦大脑袋转起来了") self.current_state.update_current_state_info() @@ -188,17 +201,13 @@ class Heartflow: try: if subheartflow_id not in self._subheartflows: - logger.debug(f"创建 subheartflow: {subheartflow_id}") subheartflow = SubHeartflow(subheartflow_id) # 创建一个观察对象,目前只可以用chat_id创建观察对象 logger.debug(f"创建 observation: {subheartflow_id}") observation = ChattingObservation(subheartflow_id) - - logger.debug("添加 observation ") subheartflow.add_observation(observation) logger.debug("添加 observation 成功") # 创建异步任务 - logger.debug("创建异步任务") asyncio.create_task(subheartflow.subheartflow_start_working()) logger.debug("创建异步任务 成功") self._subheartflows[subheartflow_id] = subheartflow diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 372474ac0..7afd16693 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -44,26 +44,31 @@ class ActionPlanner: logger.debug(f"开始规划行动:当前目标: {conversation_info.goal_list}") # 构建对话目标 + goals_str = "" if conversation_info.goal_list: - last_goal = conversation_info.goal_list[-1] - print(last_goal) - # 处理字典或元组格式 - if isinstance(last_goal, tuple) and len(last_goal) == 2: - goal, reasoning = last_goal - elif isinstance(last_goal, dict) and 'goal' in last_goal and 'reasoning' in last_goal: - # 处理字典格式 - goal = last_goal.get('goal', "目前没有明确对话目标") - reasoning = last_goal.get('reasoning', "目前没有明确对话目标,最好思考一个对话目标") - else: - # 处理未知格式 - goal = "目前没有明确对话目标" - reasoning = "目前没有明确对话目标,最好思考一个对话目标" + for goal_reason in conversation_info.goal_list: + # 处理字典或元组格式 + if isinstance(goal_reason, tuple): + # 假设元组的第一个元素是目标,第二个元素是原因 + goal = goal_reason[0] + reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因" + elif isinstance(goal_reason, dict): + goal = goal_reason.get('goal') + reasoning = goal_reason.get('reasoning', "没有明确原因") + else: + # 如果是其他类型,尝试转为字符串 + goal = str(goal_reason) + reasoning = "没有明确原因" + + goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" + goals_str += goal_str else: goal = "目前没有明确对话目标" reasoning = "目前没有明确对话目标,最好思考一个对话目标" + goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" # 获取聊天历史记录 - chat_history_list = observation_info.chat_history + chat_history_list = observation_info.chat_history[-20:] if len(observation_info.chat_history) >= 20 else observation_info.chat_history chat_history_text = "" for msg in chat_history_list: chat_history_text += f"{msg.get('detailed_plain_text', '')}\n" @@ -80,15 +85,30 @@ class ActionPlanner: personality_text = f"你的名字是{self.name},{self.personality_info}" # 构建action历史文本 - action_history_list = conversation_info.done_action + action_history_list = conversation_info.done_action[-10:] if len(conversation_info.done_action) >= 10 else conversation_info.done_action action_history_text = "你之前做的事情是:" for action in action_history_list: - action_history_text += f"{action}\n" + if isinstance(action, dict): + action_type = action.get('action') + action_reason = action.get('reason') + action_status = action.get('status') + if action_status == "recall": + action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n" + elif action_status == "done": + action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n" + elif isinstance(action, tuple): + # 假设元组的格式是(action_type, action_reason, action_status) + action_type = action[0] if len(action) > 0 else "未知行动" + action_reason = action[1] if len(action) > 1 else "未知原因" + action_status = action[2] if len(action) > 2 else "done" + if action_status == "recall": + action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n" + elif action_status == "done": + action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n" prompt = f"""{personality_text}。现在你在参与一场QQ聊天,请分析以下内容,根据信息决定下一步行动: -当前对话目标:{goal} -产生该对话目标的原因:{reasoning} +当前对话目标:{goals_str} {action_history_text} @@ -98,10 +118,11 @@ class ActionPlanner: 请你接下去想想要你要做什么,可以发言,可以等待,可以倾听,可以调取知识。注意不同行动类型的要求,不要重复发言: 行动类型: fetch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择 -wait: 当你做出了发言,对方尚未回复时等待对方的回复 +wait: 当你做出了发言,对方尚未回复时暂时等待对方的回复 listening: 倾听对方发言,当你认为对方发言尚未结束时采用 direct_reply: 不符合上述情况,回复对方,注意不要过多或者重复发言 rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择,会重新思考对话目标 +end_conversation: 结束对话,当你觉得谈话暂时结束时选择,停止该场对话 请以JSON格式输出,包含以下字段: 1. action: 行动类型,注意你之前的行为 @@ -126,7 +147,7 @@ rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择 reason = result["reason"] # 验证action类型 - if action not in ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal"]: + if action not in ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal", "end_conversation"]: logger.warning(f"未知的行动类型: {action},默认使用listening") action = "listening" diff --git a/src/plugins/PFC/chat_observer.py b/src/plugins/PFC/chat_observer.py index 0af11e135..844f346f3 100644 --- a/src/plugins/PFC/chat_observer.py +++ b/src/plugins/PFC/chat_observer.py @@ -198,7 +198,7 @@ class ChatObserver: self.last_message_read = new_messages[-1] self.last_message_time = new_messages[-1]["time"] - print(f"获取数据库中找到的新消息: {new_messages}") + # print(f"获取数据库中找到的新消息: {new_messages}") return new_messages diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index a5da3e48d..4ce30ac9d 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -3,7 +3,7 @@ import datetime from typing import Dict, Any from ..chat.message import Message from .pfc_types import ConversationState -from .pfc import ChatObserver, GoalAnalyzer, Waiter, DirectMessageSender +from .pfc import ChatObserver, GoalAnalyzer, DirectMessageSender from src.common.logger import get_module_logger from .action_planner import ActionPlanner from .observation_info import ObservationInfo @@ -13,6 +13,8 @@ from ..chat.chat_stream import ChatStream from ..message.message_base import UserInfo from src.plugins.chat.chat_stream import chat_manager from .pfc_KnowledgeFetcher import KnowledgeFetcher +from .waiter import Waiter + import traceback logger = get_module_logger("pfc_conversation") @@ -94,6 +96,15 @@ class Conversation: # 执行行动 await self._handle_action(action, reason, self.observation_info, self.conversation_info) + + for goal in self.conversation_info.goal_list: + # 检查goal是否为元组类型,如果是元组则使用索引访问,如果是字典则使用get方法 + if isinstance(goal, tuple): + # 假设元组的第一个元素是目标内容 + print(f"goal: {goal}") + if goal[0] == "结束对话": + self.should_continue = False + break def _check_new_messages_after_planning(self): """检查在规划后是否有新消息""" @@ -151,14 +162,19 @@ class Conversation: if self._check_new_messages_after_planning(): logger.info("333333发现新消息,重新考虑行动") + conversation_info.done_action[-1].update( + { + "status": "recall", + "time": datetime.datetime.now().strftime("%H:%M:%S"), + } + ) return None await self._send_reply() - conversation_info.done_action.append( + + conversation_info.done_action[-1].update( { - "action": action, - "reason": reason, "status": "done", "time": datetime.datetime.now().strftime("%H:%M:%S"), } @@ -184,16 +200,17 @@ class Conversation: elif action == "listening": self.state = ConversationState.LISTENING logger.info("倾听对方发言...") - if await self.waiter.wait(): # 如果返回True表示超时 - await self._send_timeout_message() - await self._stop_conversation() + await self.waiter.wait_listening(conversation_info) + + + elif action == "end_conversation": + self.should_continue = False + logger.info("决定结束对话...") else: # wait self.state = ConversationState.WAITING logger.info("等待更多信息...") - if await self.waiter.wait(): # 如果返回True表示超时 - await self._send_timeout_message() - await self._stop_conversation() + await self.waiter.wait(self.conversation_info) async def _send_timeout_message(self): """发送超时结束消息""" diff --git a/src/plugins/PFC/message_storage.py b/src/plugins/PFC/message_storage.py index afd233347..fbab0b2b6 100644 --- a/src/plugins/PFC/message_storage.py +++ b/src/plugins/PFC/message_storage.py @@ -54,7 +54,7 @@ class MongoDBMessageStorage(MessageStorage): async def get_messages_after(self, chat_id: str, message_time: float) -> List[Dict[str, Any]]: query = {"chat_id": chat_id} - print(f"storage_check_message: {message_time}") + # print(f"storage_check_message: {message_time}") query["time"] = {"$gt": message_time} diff --git a/src/plugins/PFC/observation_info.py b/src/plugins/PFC/observation_info.py index 01f619dc3..a8b804449 100644 --- a/src/plugins/PFC/observation_info.py +++ b/src/plugins/PFC/observation_info.py @@ -157,8 +157,8 @@ class ObservationInfo: Args: message: 消息数据 """ - print("1919810-----------------------------------------------------") - logger.debug(f"更新信息from_message: {message}") + # print("1919810-----------------------------------------------------") + # logger.debug(f"更新信息from_message: {message}") self.last_message_time = message["time"] self.last_message_id = message["message_id"] diff --git a/src/plugins/PFC/pfc.py b/src/plugins/PFC/pfc.py index 0a20812b9..8a660d674 100644 --- a/src/plugins/PFC/pfc.py +++ b/src/plugins/PFC/pfc.py @@ -54,11 +54,28 @@ class GoalAnalyzer: Tuple[str, str, str]: (目标, 方法, 原因) """ # 构建对话目标 - goal_list = conversation_info.goal_list - goal_text = "" - for goal, reason in goal_list: - goal_text += f"目标:{goal};" - goal_text += f"原因:{reason}\n" + goals_str = "" + if conversation_info.goal_list: + for goal_reason in conversation_info.goal_list: + # 处理字典或元组格式 + if isinstance(goal_reason, tuple): + # 假设元组的第一个元素是目标,第二个元素是原因 + goal = goal_reason[0] + reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因" + elif isinstance(goal_reason, dict): + goal = goal_reason.get('goal') + reasoning = goal_reason.get('reasoning', "没有明确原因") + else: + # 如果是其他类型,尝试转为字符串 + goal = str(goal_reason) + reasoning = "没有明确原因" + + goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" + goals_str += goal_str + else: + goal = "目前没有明确对话目标" + reasoning = "目前没有明确对话目标,最好思考一个对话目标" + goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" # 获取聊天历史记录 chat_history_list = observation_info.chat_history @@ -88,7 +105,7 @@ class GoalAnalyzer: {action_history_text} 当前对话目标: -{goal_text} +{goals_str} 聊天记录: {chat_history_text} @@ -98,6 +115,7 @@ class GoalAnalyzer: 2. 修改现有目标 3. 添加新目标 4. 删除不再相关的目标 +5. 如果你想结束对话,请设置一个目标,目标goal为"结束对话",原因reasoning为你希望结束对话 请以JSON数组格式输出当前的所有对话目标,每个目标包含以下字段: 1. goal: 对话目标(简短的一句话) @@ -275,38 +293,6 @@ class GoalAnalyzer: return False, False, f"分析出错: {str(e)}" -class Waiter: - """快 速 等 待""" - - def __init__(self, stream_id: str): - self.chat_observer = ChatObserver.get_instance(stream_id) - self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2) - self.name = global_config.BOT_NICKNAME - - async def wait(self) -> bool: - """等待 - - Returns: - bool: 是否超时(True表示超时) - """ - # 使用当前时间作为等待开始时间 - wait_start_time = time.time() - self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间 - - while True: - # 检查是否有新消息 - if self.chat_observer.new_message_after(wait_start_time): - logger.info("等待结束,收到新消息") - return False - - # 检查是否超时 - if time.time() - wait_start_time > 300: - logger.info("等待超过300秒,结束对话") - return True - - await asyncio.sleep(1) - logger.info("等待中...") - class DirectMessageSender: """直接发送消息到平台的发送器""" diff --git a/src/plugins/PFC/pfc_manager.py b/src/plugins/PFC/pfc_manager.py index 5be15a100..6b2adff13 100644 --- a/src/plugins/PFC/pfc_manager.py +++ b/src/plugins/PFC/pfc_manager.py @@ -41,7 +41,7 @@ class PFCManager: logger.debug(f"会话实例正在初始化中: {stream_id}") return None - if stream_id in self._instances: + if stream_id in self._instances and self._instances[stream_id].should_continue: logger.debug(f"使用现有会话实例: {stream_id}") return self._instances[stream_id] diff --git a/src/plugins/PFC/reply_generator.py b/src/plugins/PFC/reply_generator.py index 00ac7c413..40083e041 100644 --- a/src/plugins/PFC/reply_generator.py +++ b/src/plugins/PFC/reply_generator.py @@ -16,7 +16,7 @@ class ReplyGenerator: def __init__(self, stream_id: str): self.llm = LLM_request( - model=global_config.llm_normal, temperature=0.7, max_tokens=300, request_type="reply_generation" + model=global_config.llm_normal, temperature=0.3, max_tokens=300, request_type="reply_generation" ) self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2) self.name = global_config.BOT_NICKNAME @@ -39,33 +39,76 @@ class ReplyGenerator: # 构建提示词 logger.debug(f"开始生成回复:当前目标: {conversation_info.goal_list}") - goal_list = conversation_info.goal_list - goal_text = "" - for goal, reason in goal_list: - goal_text += f"目标:{goal};" - goal_text += f"原因:{reason}\n" - + # 构建对话目标 + goals_str = "" + if conversation_info.goal_list: + for goal_reason in conversation_info.goal_list: + # 处理字典或元组格式 + if isinstance(goal_reason, tuple): + # 假设元组的第一个元素是目标,第二个元素是原因 + goal = goal_reason[0] + reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因" + elif isinstance(goal_reason, dict): + goal = goal_reason.get('goal') + reasoning = goal_reason.get('reasoning', "没有明确原因") + else: + # 如果是其他类型,尝试转为字符串 + goal = str(goal_reason) + reasoning = "没有明确原因" + + goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" + goals_str += goal_str + else: + goal = "目前没有明确对话目标" + reasoning = "目前没有明确对话目标,最好思考一个对话目标" + goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n" + # 获取聊天历史记录 - chat_history_list = observation_info.chat_history + chat_history_list = observation_info.chat_history[-20:] if len(observation_info.chat_history) >= 20 else observation_info.chat_history chat_history_text = "" for msg in chat_history_list: - chat_history_text += f"{msg}\n" + chat_history_text += f"{msg.get('detailed_plain_text', '')}\n" - # 整理知识缓存 - knowledge_text = "" - knowledge_list = conversation_info.knowledge_list - for knowledge in knowledge_list: - knowledge_text += f"知识:{knowledge}\n" + if observation_info.new_messages_count > 0: + new_messages_list = observation_info.unprocessed_messages + + chat_history_text += f"有{observation_info.new_messages_count}条新消息:\n" + for msg in new_messages_list: + chat_history_text += f"{msg.get('detailed_plain_text', '')}\n" + + observation_info.clear_unprocessed_messages() personality_text = f"你的名字是{self.name},{self.personality_info}" + # 构建action历史文本 + action_history_list = conversation_info.done_action[-10:] if len(conversation_info.done_action) >= 10 else conversation_info.done_action + action_history_text = "你之前做的事情是:" + for action in action_history_list: + if isinstance(action, dict): + action_type = action.get('action') + action_reason = action.get('reason') + action_status = action.get('status') + if action_status == "recall": + action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n" + elif action_status == "done": + action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n" + elif isinstance(action, tuple): + # 假设元组的格式是(action_type, action_reason, action_status) + action_type = action[0] if len(action) > 0 else "未知行动" + action_reason = action[1] if len(action) > 1 else "未知原因" + action_status = action[2] if len(action) > 2 else "done" + if action_status == "recall": + action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n" + elif action_status == "done": + action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n" + prompt = f"""{personality_text}。现在你在参与一场QQ聊天,请根据以下信息生成回复: -当前对话目标:{goal_text} -{knowledge_text} +当前对话目标:{goals_str} 最近的聊天记录: {chat_history_text} + 请根据上述信息,以你的性格特征生成一个自然、得体的回复。回复应该: 1. 符合对话目标,以"你"的角度发言 2. 体现你的性格特征 diff --git a/src/plugins/PFC/waiter.py b/src/plugins/PFC/waiter.py index 66f98e9c3..0af5047fa 100644 --- a/src/plugins/PFC/waiter.py +++ b/src/plugins/PFC/waiter.py @@ -1,46 +1,81 @@ from src.common.logger import get_module_logger from .chat_observer import ChatObserver +from .conversation_info import ConversationInfo +from src.individuality.individuality import Individuality +from ..config.config import global_config +import time +import asyncio logger = get_module_logger("waiter") class Waiter: - """等待器,用于等待对话流中的事件""" + """快 速 等 待""" def __init__(self, stream_id: str): - self.stream_id = stream_id self.chat_observer = ChatObserver.get_instance(stream_id) + self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2) + self.name = global_config.BOT_NICKNAME - async def wait(self, timeout: float = 20.0) -> bool: - """等待用户回复或超时 - - Args: - timeout: 超时时间(秒) + async def wait(self, conversation_info: ConversationInfo) -> bool: + """等待 Returns: - bool: 如果因为超时返回则为True,否则为False + bool: 是否超时(True表示超时) """ - try: - message_before = self.chat_observer.get_last_message() + # 使用当前时间作为等待开始时间 + wait_start_time = time.time() + self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间 - # 等待新消息 - logger.debug(f"等待新消息,超时时间: {timeout}秒") + while True: + # 检查是否有新消息 + if self.chat_observer.new_message_after(wait_start_time): + logger.info("等待结束,收到新消息") + return False - is_timeout = await self.chat_observer.wait_for_update(timeout=timeout) - if is_timeout: - logger.debug("等待超时,没有收到新消息") + # 检查是否超时 + if time.time() - wait_start_time > 15: + logger.info("等待超过300秒,结束对话") + wait_goal = { + "goal": "你等待了5分钟,思考接下来要做什么", + "reason": "对方很久没有回复你的消息了" + } + conversation_info.goal_list.append(wait_goal) + print(f"添加目标: {wait_goal}") + return True - # 检查是否是新消息 - message_after = self.chat_observer.get_last_message() - if message_before and message_after and message_before.get("message_id") == message_after.get("message_id"): - # 如果消息ID相同,说明没有新消息 - logger.debug("没有收到新消息") + await asyncio.sleep(1) + logger.info("等待中...") + + async def wait_listening(self, conversation_info: ConversationInfo) -> bool: + """等待倾听 + + Returns: + bool: 是否超时(True表示超时) + """ + # 使用当前时间作为等待开始时间 + wait_start_time = time.time() + self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间 + + while True: + # 检查是否有新消息 + if self.chat_observer.new_message_after(wait_start_time): + logger.info("等待结束,收到新消息") + return False + + # 检查是否超时 + if time.time() - wait_start_time > 30: + logger.info("等待超过300秒,结束对话") + wait_goal = { + "goal": "你等待了5分钟,思考接下来要做什么", + "reason": "对方话说一半消失了,很久没有回复" + } + conversation_info.goal_list.append(wait_goal) + print(f"添加目标: {wait_goal}") + return True - logger.debug("收到新消息") - return False + await asyncio.sleep(1) + logger.info("等待中...") - except Exception as e: - logger.error(f"等待时出错: {str(e)}") - return True diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index be3343292..582c68c5e 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -28,7 +28,7 @@ logger = get_module_logger("config", config=config_config) # 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 is_test = True mai_version_main = "0.6.2" -mai_version_fix = "snapshot-1" +mai_version_fix = "snapshot-2" if mai_version_fix: if is_test: diff --git a/src/plugins/memory_system/debug_memory.py b/src/plugins/memory_system/debug_memory.py index 657811ac6..3b98d0d42 100644 --- a/src/plugins/memory_system/debug_memory.py +++ b/src/plugins/memory_system/debug_memory.py @@ -26,22 +26,7 @@ async def test_memory_system(): # 测试记忆检索 test_text = "千石可乐在群里聊天" - test_text = """[03-24 10:39:37] 麦麦(ta的id:2814567326): 早说散步结果下雨改成室内运动啊 -[03-24 10:39:37] 麦麦(ta的id:2814567326): [回复:变量] 变量就像今天计划总变 -[03-24 10:39:44] 状态异常(ta的id:535554838): 要把本地文件改成弹出来的路径吗 -[03-24 10:40:35] 状态异常(ta的id:535554838): [图片:这张图片显示的是Windows系统的环境变量设置界面。界面左侧列出了多个环境变量的值,包括Intel Dev Redist、Windows、Windows PowerShell、OpenSSH、NVIDIA Corporation的目录等。右侧有新建、编辑、浏览、删除、上移、下移和编辑文本等操作按钮。图片下方有一个错误提示框,显示"Windows找不到文件'mongodb\\bin\\mongod.exe'。请确定文件名是否正确后,再试一次。"这意味着用户试图运行MongoDB的mongod.exe程序时,系统找不到该文件。这可能是因为MongoDB的安装路径未正确添加到系统环境变量中,或者文件路径有误。 -图片的含义可能是用户正在尝试设置MongoDB的环境变量,以便在命令行或其他程序中使用MongoDB。如果用户正确设置了环境变量,那么他们应该能够通过命令行或其他方式启动MongoDB服务。] -[03-24 10:41:08] 一根猫(ta的id:108886006): [回复 麦麦 的消息: [回复某人消息] 改系统变量或者删库重配 ] [@麦麦] 我中途修改人格,需要重配吗 -[03-24 10:41:54] 麦麦(ta的id:2814567326): [回复:[回复 麦麦 的消息: [回复某人消息] 改系统变量或者删库重配 ] [@麦麦] 我中途修改人格,需要重配吗] 看情况 -[03-24 10:41:54] 麦麦(ta的id:2814567326): 难 -[03-24 10:41:54] 麦麦(ta的id:2814567326): 小改变量就行,大动骨安排重配像游戏副本南度改太大会崩 -[03-24 10:45:33] 霖泷(ta的id:1967075066): 话说现在思考高达一分钟 -[03-24 10:45:38] 霖泷(ta的id:1967075066): 是不是哪里出问题了 -[03-24 10:45:39] 艾卡(ta的id:1786525298): [表情包:这张表情包展示了一个动漫角色,她有着紫色的头发和大大的眼睛,表情显得有些困惑或不解。她的头上有一个问号,进一步强调了她的疑惑。整体情感表达的是困惑或不解。] -[03-24 10:46:12] (ta的id:3229291803): [表情包:这张表情包显示了一只手正在做"点赞"的动作,通常表示赞同、喜欢或支持。这个表情包所表达的情感是积极的、赞同的或支持的。] -[03-24 10:46:37] 星野風禾(ta的id:2890165435): 还能思考高达 -[03-24 10:46:39] 星野風禾(ta的id:2890165435): 什么知识库 -[03-24 10:46:49] ❦幻凌慌てない(ta的id:2459587037): 为什么改了回复系数麦麦还是不怎么回复?大佬们""" # noqa: E501 + # test_text = '''千石可乐:分不清AI的陪伴和人类的陪伴,是这样吗?''' print(f"开始测试记忆检索,测试文本: {test_text}\n") @@ -56,21 +41,6 @@ async def test_memory_system(): print(f"主题: {topic}") print(f"- {memory_items}") - # 测试记忆遗忘 - # forget_start_time = time.time() - # # print("开始测试记忆遗忘...") - # await hippocampus_manager.forget_memory(percentage=0.005) - # # print("记忆遗忘完成") - # forget_end_time = time.time() - # print(f"记忆遗忘耗时: {forget_end_time - forget_start_time:.2f} 秒") - - # 获取所有节点 - # nodes = hippocampus_manager.get_all_node_names() - # print(f"当前记忆系统中的节点数量: {len(nodes)}") - # print("节点列表:") - # for node in nodes: - # print(f"- {node}") - except Exception as e: print(f"测试过程中出现错误: {e}") raise From 97c38beb4417043ceb947e3ac1cc89f27b22ae8d Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Fri, 11 Apr 2025 16:11:12 +0800 Subject: [PATCH 9/9] =?UTF-8?q?better:PFC=20=E6=9B=B4=E5=A5=BD=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E7=BB=93=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 2 +- src/plugins/PFC/conversation.py | 6 ++++++ src/plugins/PFC/waiter.py | 13 +++++++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 7afd16693..b6b47f8b9 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -122,7 +122,7 @@ wait: 当你做出了发言,对方尚未回复时暂时等待对方的回复 listening: 倾听对方发言,当你认为对方发言尚未结束时采用 direct_reply: 不符合上述情况,回复对方,注意不要过多或者重复发言 rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择,会重新思考对话目标 -end_conversation: 结束对话,当你觉得谈话暂时结束时选择,停止该场对话 +end_conversation: 结束对话,长时间没回复或者当你觉得谈话暂时结束时选择,停止该场对话 请以JSON格式输出,包含以下字段: 1. action: 行动类型,注意你之前的行为 diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index 4ce30ac9d..599b1c453 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -150,6 +150,8 @@ class Conversation: ) if action == "direct_reply": + self.waiter.wait_accumulated_time = 0 + self.state = ConversationState.GENERATING self.generated_reply = await self.reply_generator.generate(observation_info, conversation_info) print(f"生成回复: {self.generated_reply}") @@ -181,6 +183,8 @@ class Conversation: ) elif action == "fetch_knowledge": + self.waiter.wait_accumulated_time = 0 + self.state = ConversationState.FETCHING knowledge = "TODO:知识" topic = "TODO:关键词" @@ -194,6 +198,8 @@ class Conversation: self.conversation_info.knowledge_list[topic] += knowledge elif action == "rethink_goal": + self.waiter.wait_accumulated_time = 0 + self.state = ConversationState.RETHINKING await self.goal_analyzer.analyze_goal(conversation_info, observation_info) diff --git a/src/plugins/PFC/waiter.py b/src/plugins/PFC/waiter.py index 0af5047fa..6c55c243e 100644 --- a/src/plugins/PFC/waiter.py +++ b/src/plugins/PFC/waiter.py @@ -16,6 +16,8 @@ class Waiter: self.chat_observer = ChatObserver.get_instance(stream_id) self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2) self.name = global_config.BOT_NICKNAME + + self.wait_accumulated_time = 0 async def wait(self, conversation_info: ConversationInfo) -> bool: """等待 @@ -34,10 +36,12 @@ class Waiter: return False # 检查是否超时 - if time.time() - wait_start_time > 15: + if time.time() - wait_start_time > 300: + self.wait_accumulated_time += 300 + logger.info("等待超过300秒,结束对话") wait_goal = { - "goal": "你等待了5分钟,思考接下来要做什么", + "goal": f"你等待了{self.wait_accumulated_time/60}分钟,思考接下来要做什么", "reason": "对方很久没有回复你的消息了" } conversation_info.goal_list.append(wait_goal) @@ -65,10 +69,11 @@ class Waiter: return False # 检查是否超时 - if time.time() - wait_start_time > 30: + if time.time() - wait_start_time > 300: + self.wait_accumulated_time += 300 logger.info("等待超过300秒,结束对话") wait_goal = { - "goal": "你等待了5分钟,思考接下来要做什么", + "goal": f"你等待了{self.wait_accumulated_time/60}分钟,思考接下来要做什么", "reason": "对方话说一半消失了,很久没有回复" } conversation_info.goal_list.append(wait_goal)