From cbaed95938158da26960d9275fa98dcf4004d7d2 Mon Sep 17 00:00:00 2001 From: 114514 <2514624910@qq.com> Date: Sun, 27 Apr 2025 10:10:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?PFC=E4=BC=98=E5=8C=96=EF=BC=9A=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E6=96=B0=E7=9A=84=E5=86=B3=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加了一个可先的新的屏蔽决策,防止机器人受到骚扰信息消耗token,以前的结束对话结束以后,如果收到新的骚扰信息依然会再次进入决策,这次的屏蔽则是直接屏蔽10分钟(无任何决策),在之后实例自我销毁结束。 --- src/plugins/PFC/action_planner.py | 9 +++++---- src/plugins/PFC/conversation.py | 30 +++++++++++++++++++++++++++++- src/plugins/PFC/pfc_manager.py | 15 +++++++++++++++ src/plugins/PFC/pfc_types.py | 1 + 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 4e39483bf..55bf067ef 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -244,7 +244,7 @@ class ActionPlanner: last_action_context += f"- 该行动当前状态: {status}\n" # --- 构建最终的 Prompt --- - prompt = f"""{persona_text}。现在你在参与一场QQ私聊,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以发言,可以等待,可以倾听,可以调取知识: + prompt = f"""{persona_text}。现在你在参与一场QQ私聊,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以发言,可以等待,可以倾听,可以调取知识,甚至可以屏蔽对方: 【当前对话目标】 {goals_str if goals_str.strip() else "- 目前没有明确对话目标,请考虑设定一个。"} @@ -261,12 +261,13 @@ class ActionPlanner: ------ 可选行动类型以及解释: -etch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择,对方若提到你太认识的人名或实体也可以尝试 +etch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择,对方若提到你太认识的人名或实体也可以尝试选择 wait: 暂时不说话,等待对方回复(尤其是在你刚发言后、或上次发言因重复、发言过多被拒时、或不确定做什么时,这是较安全的选择) -listening: 倾听对方发言,当你认为对方话才说到一半,发言明显未结束时采用 +listening: 倾听对方发言,当你认为对方话才说到一半,发言明显未结束时选择 direct_reply: 直接回复或发送新消息,允许适当的追问和深入话题,**但是避免在因重复被拒后立即使用,也不要在对方没有回复的情况下过多的“消息轰炸”或重复发言** rethink_goal: 重新思考对话目标,当发现对话目标不再适用或对话卡住时选择,注意私聊的环境是灵活的,有可能需要经常选择 end_conversation: 结束对话,对方长时间没回复或者当你觉得对话告一段落时可以选择 +block_and_ignore: 更加极端的结束对话方式,直接结束对话并在一段时间内无视对方所有发言(屏蔽),当对话让你感到十分不适,或你遭到各类骚扰时选择 请以JSON格式输出你的决策: {{ @@ -292,7 +293,7 @@ end_conversation: 结束对话,对方长时间没回复或者当你觉得对 reason = result.get("reason", "LLM未提供原因,默认等待") # 验证action类型 - valid_actions = ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal", "end_conversation"] + valid_actions = ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal", "end_conversation", "block_and_ignore"] if action not in valid_actions: logger.warning(f"LLM返回了未知的行动类型: '{action}',强制改为 wait") reason = f"(原始行动'{action}'无效,已强制改为wait) {reason}" diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index c290008b8..9205a7c65 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -4,7 +4,7 @@ import datetime # from .message_storage import MongoDBMessageStorage from src.plugins.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat from ...config.config import global_config -from typing import Dict, Any +from typing import Dict, Any, Optional from ..chat.message import Message from .pfc_types import ConversationState from .pfc import ChatObserver, GoalAnalyzer, DirectMessageSender @@ -36,6 +36,7 @@ class Conversation: self.stream_id = stream_id self.state = ConversationState.INIT self.should_continue = False + self.ignore_until_timestamp: Optional[float] = None # 回复相关 self.generated_reply = "" @@ -125,6 +126,18 @@ class Conversation: async def _plan_and_action_loop(self): """思考步,PFC核心循环模块""" while self.should_continue: + if self.ignore_until_timestamp and time.time() < self.ignore_until_timestamp: + # 仍在忽略期间,等待下次检查 + await asyncio.sleep(30) # 每 30 秒检查一次 + continue # 跳过本轮循环的剩余部分 + elif self.ignore_until_timestamp and time.time() >= self.ignore_until_timestamp: + # 忽略期结束,现在正常地结束对话 + logger.info(f"忽略时间已到 {self.stream_id},准备结束对话。") + self.ignore_until_timestamp = None # 清除时间戳 + self.should_continue = False # 现在停止循环 + # (可选)在这里记录一个 'end_conversation' 动作 + # 或者确保管理器会基于 should_continue 为 False 来清理它 + continue # 跳过本轮循环的剩余部分,让它终止 try: # --- 在规划前记录当前新消息数量 --- initial_new_message_count = 0 @@ -399,6 +412,21 @@ class Conversation: ) # 这里不需要 return,主循环会在下一轮检查 should_continue + elif action == "block_and_ignore": + logger.info("不想再理你了...") + # 1. 标记对话为暂时忽略 + ignore_duration_seconds = 10 * 60 # 10 分钟 + self.ignore_until_timestamp = time.time() + ignore_duration_seconds + logger.info(f"将忽略此对话直到: {datetime.datetime.fromtimestamp(self.ignore_until_timestamp)}") + conversation_info.done_action[action_index].update( + { + "status": "done", # 或者一个自定义状态,比如 "ignored" + "final_reason": "Detected potential harassment, ignoring temporarily.", # 检测到潜在骚扰,暂时忽略 + "time": datetime.datetime.now().strftime("%H:%M:%S"), + } + ) + self.state = ConversationState.IGNORED + else: # 对应 'wait' 动作 self.state = ConversationState.WAITING logger.info("等待更多信息...") diff --git a/src/plugins/PFC/pfc_manager.py b/src/plugins/PFC/pfc_manager.py index 6b2adff13..2b91c0a9c 100644 --- a/src/plugins/PFC/pfc_manager.py +++ b/src/plugins/PFC/pfc_manager.py @@ -1,3 +1,4 @@ +import time from typing import Dict, Optional from src.common.logger import get_module_logger from .conversation import Conversation @@ -44,7 +45,21 @@ class PFCManager: if stream_id in self._instances and self._instances[stream_id].should_continue: logger.debug(f"使用现有会话实例: {stream_id}") return self._instances[stream_id] + if stream_id in self._instances: + instance = self._instances[stream_id] + if hasattr(instance, 'ignore_until_timestamp') and \ + instance.ignore_until_timestamp and \ + time.time() < instance.ignore_until_timestamp: + logger.debug(f"会话实例当前处于忽略状态: {stream_id}") + # 返回 None 阻止交互。或者可以返回实例但标记它被忽略了喵? + # 还是返回 None 吧喵。 + return None + # 检查 should_continue 状态 + if instance.should_continue: + logger.debug(f"使用现有会话实例: {stream_id}") + return instance + # else: 实例存在但不应继续 try: # 创建新实例 logger.info(f"创建新的对话实例: {stream_id}") diff --git a/src/plugins/PFC/pfc_types.py b/src/plugins/PFC/pfc_types.py index 7391c448d..0ea5eda64 100644 --- a/src/plugins/PFC/pfc_types.py +++ b/src/plugins/PFC/pfc_types.py @@ -17,6 +17,7 @@ class ConversationState(Enum): LISTENING = "倾听" ENDED = "结束" JUDGING = "判断" + IGNORED = "屏蔽" ActionType = Literal["direct_reply", "fetch_knowledge", "wait"] From 78cbc6beb75cb21792acf22e23b31a5281115473 Mon Sep 17 00:00:00 2001 From: 114514 <2514624910@qq.com> Date: Sun, 27 Apr 2025 10:54:24 +0800 Subject: [PATCH 2/2] =?UTF-8?q?PFC=E4=BC=98=E5=8C=96=EF=BC=9A=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E6=96=B0=E7=9A=84=E5=86=B3=E7=AD=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加了一个可先的新的屏蔽决策,防止机器人受到骚扰信息消耗token,以前的结束对话结束以后,如果收到新的骚扰信息依然会再次进入决策,这次的屏蔽则是直接屏蔽10分钟(无任何决策),在之后实例自我销毁结束。顺便修复之前的typo --- src/plugins/PFC/action_planner.py | 2 +- src/plugins/PFC/conversation.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 6b8ee914b..082741263 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -261,7 +261,7 @@ class ActionPlanner: ------ 可选行动类型以及解释: -etch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择,对方若提到你太认识的人名或实体也可以尝试选择 +fetch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择,对方若提到你不太认识的人名或实体也可以尝试选择 wait: 暂时不说话,等待对方回复(尤其是在你刚发言后、或上次发言因重复、发言过多被拒时、或不确定做什么时,这是较安全的选择) listening: 倾听对方发言,当你认为对方话才说到一半,发言明显未结束时选择 direct_reply: 直接回复或发送新消息,允许适当的追问和深入话题,**但是避免在因重复被拒后立即使用,也不要在对方没有回复的情况下过多的“消息轰炸”或重复发言** diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index 1b25da30c..f68098f13 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -2,8 +2,8 @@ import time import asyncio import datetime # from .message_storage import MongoDBMessageStorage -from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_ch -from ...config.config import global_config +from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat +# from ...config.config import global_config from typing import Dict, Any, Optional from ..chat.message import Message from .pfc_types import ConversationState