This commit is contained in:
tcmofashi
2025-04-11 20:00:00 +08:00
29 changed files with 531 additions and 427 deletions

2
bot.py
View File

@@ -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}

View File

@@ -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()

View File

@@ -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)

View File

@@ -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")

View File

@@ -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")

View File

@@ -43,12 +43,15 @@ def init_prompt():
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()
@@ -63,7 +66,9 @@ class Heartflow:
)
self._subheartflows: Dict[Any, SubHeartflow] = {}
self.active_subheartflows_nums = 0
async def _cleanup_inactive_subheartflows(self):
"""定期清理不活跃的子心流"""
@@ -86,10 +91,7 @@ class Heartflow:
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:
@@ -100,6 +102,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()
@@ -216,17 +229,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

View File

@@ -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
# 所有观察的基类

View File

@@ -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(

View File

@@ -44,39 +44,71 @@ class ActionPlanner:
logger.debug(f"开始规划行动:当前目标: {conversation_info.goal_list}")
# 构建对话目标
goals_str = ""
if conversation_info.goal_list:
goal, reasoning = conversation_info.goal_list[-1]
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"
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()
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}
@@ -86,10 +118,11 @@ class ActionPlanner:
请你接下去想想要你要做什么,可以发言,可以等待,可以倾听,可以调取知识。注意不同行动类型的要求,不要重复发言:
行动类型:
fetch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择
wait: 当你做出了发言,对方尚未回复时等待对方的回复
wait: 当你做出了发言,对方尚未回复时暂时等待对方的回复
listening: 倾听对方发言,当你认为对方发言尚未结束时采用
direct_reply: 不符合上述情况,回复对方,注意不要过多或者重复发言
rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择,会重新思考对话目标
end_conversation: 结束对话,长时间没回复或者当你觉得谈话暂时结束时选择,停止该场对话
请以JSON格式输出包含以下字段
1. action: 行动类型,注意你之前的行为
@@ -114,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"

View File

@@ -1,11 +1,12 @@
import time
import asyncio
from typing import Optional, Dict, Any, List, Tuple
import traceback
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")
@@ -17,45 +18,39 @@ 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_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[Dict[str, Any]] = None # 最后读取的消息ID
self.last_message_time: float = time.time()
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 +67,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 +93,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
# 更新说话时间
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"]
try:
# 发送新消息通知
notification = create_new_message_notification(sender="chat_observer", target="pfc", message=message)
# 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()
@@ -140,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:
"""判断是否在指定时间点后有新消息
@@ -156,9 +141,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")
@@ -210,10 +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"获取数据库中找到的新消息: {new_messages}")
return new_messages
@@ -231,26 +216,32 @@ class ChatObserver:
if new_messages:
self.last_message_read = new_messages[-1]["message_id"]
logger.debug(f"获取指定时间点111之前的消息: {new_messages}")
return new_messages
"""主要观察循环"""
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() # 重置触发事件
@@ -269,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):
@@ -355,51 +347,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 +368,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}"

View File

@@ -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):
@@ -134,11 +141,15 @@ class NotificationManager:
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]:
@@ -171,6 +182,13 @@ class NotificationManager:
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
# 一些常用的通知创建函数
def create_new_message_notification(sender: str, target: str, message: Dict[str, Any]) -> Notification:
@@ -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

View File

@@ -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")
@@ -60,9 +62,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}")
@@ -94,6 +97,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):
"""检查在规划后是否有新消息"""
if self.observation_info.new_messages_count > 0:
@@ -138,8 +150,11 @@ 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}")
# # 检查回复是否合适
# is_suitable, reason, need_replan = await self.reply_generator.check_reply(
@@ -148,20 +163,28 @@ 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"),
}
)
elif action == "fetch_knowledge":
self.waiter.wait_accumulated_time = 0
self.state = ConversationState.FETCHING
knowledge = "TODO:知识"
topic = "TODO:关键词"
@@ -175,22 +198,25 @@ 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)
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):
"""发送超时结束消息"""
@@ -212,15 +238,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():

View File

@@ -1,18 +1,17 @@
from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional
from typing import List, Dict, Any
from src.common.database import db
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 +52,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))

View File

@@ -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}")

View File

@@ -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]):
"""处理通知
async def handle_notification(self, notification):
# 获取通知类型和数据
notification_type = notification.type
data = notification.data
Args:
notification: 通知数据
"""
notification_type = notification.get("type")
data = notification.get("data", {})
if notification_type == "NEW_MESSAGE":
if notification_type == NotificationType.NEW_MESSAGE:
# 处理新消息通知
logger.debug(f"收到新消息通知data: {data}")
message = data.get("message", {})
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", ""))
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")
elif notification_type == "COLD_CHAT":
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)
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,29 @@ 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
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------------------------绑定-----------------------------")
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 +157,11 @@ class ObservationInfo:
Args:
message: 消息数据
"""
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"]
self.last_message_content = message.get("processed_plain_text", "")
user_info = UserInfo.from_dict(message.get("user_info", {}))
@@ -169,7 +181,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):
"""更新冷场状态
@@ -216,24 +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
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)

View File

@@ -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,12 +115,14 @@ class GoalAnalyzer:
2. 修改现有目标
3. 添加新目标
4. 删除不再相关的目标
5. 如果你想结束对话请设置一个目标目标goal为"结束对话"原因reasoning为你希望结束对话
请以JSON格式输出当前的所有对话目标包含以下字段
请以JSON数组格式输出当前的所有对话目标,每个目标包含以下字段:
1. goal: 对话目标(简短的一句话)
2. reasoning: 对话原因,为什么设定这个目标(简要解释)
输出格式示例:
[
{{
"goal": "回答用户关于Python编程的具体问题",
"reasoning": "用户提出了关于Python的技术问题需要专业且准确的解答"
@@ -111,7 +130,8 @@ class GoalAnalyzer:
{{
"goal": "回答用户关于python安装的具体问题",
"reasoning": "用户提出了关于Python的技术问题需要专业且准确的解答"
}}"""
}}
]"""
logger.debug(f"发送到LLM的提示词: {prompt}")
try:
@@ -120,13 +140,37 @@ class GoalAnalyzer:
except Exception as e:
logger.error(f"分析对话目标时出错: {str(e)}")
content = ""
# 使用简化函数提取JSON内容
success, result = get_items_from_json(
content, "goal", "reasoning", required_types={"goal": str, "reasoning": str}
)
# TODO
conversation_info.goal_list.append(result)
# 使用改进后的get_items_from_json函数处理JSON数组
success, result = get_items_from_json(
content, "goal", "reasoning",
required_types={"goal": str, "reasoning": str},
allow_array=True
)
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):
"""更新目标列表
@@ -249,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:
"""直接发送消息到平台的发送器"""

View File

@@ -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]

View File

@@ -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,9 +20,10 @@ 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 = {}
@@ -30,7 +32,57 @@ def get_items_from_json(
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:

View File

@@ -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. 体现你的性格特征

View File

@@ -1,46 +1,86 @@
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:
"""等待用户回复或超时
self.wait_accumulated_time = 0
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}")
is_timeout = await self.chat_observer.wait_for_update(timeout=timeout)
if is_timeout:
logger.debug("等待超时,没有收到新消息")
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("没有收到新消息")
return True
logger.debug("收到新消息")
while True:
# 检查是否有新消息
if self.chat_observer.new_message_after(wait_start_time):
logger.info("等待结束,收到新消息")
return False
except Exception as e:
logger.error(f"等待时出错: {str(e)}")
# 检查是否超时
if time.time() - wait_start_time > 300:
self.wait_accumulated_time += 300
logger.info("等待超过300秒结束对话")
wait_goal = {
"goal": f"你等待了{self.wait_accumulated_time/60}分钟,思考接下来要做什么",
"reason": "对方很久没有回复你的消息了"
}
conversation_info.goal_list.append(wait_goal)
print(f"添加目标: {wait_goal}")
return True
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 > 300:
self.wait_accumulated_time += 300
logger.info("等待超过300秒结束对话")
wait_goal = {
"goal": f"你等待了{self.wait_accumulated_time/60}分钟,思考接下来要做什么",
"reason": "对方话说一半消失了,很久没有回复"
}
conversation_info.goal_list.append(wait_goal)
print(f"添加目标: {wait_goal}")
return True
await asyncio.sleep(1)
logger.info("等待中...")

View File

@@ -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}"
)

View File

@@ -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
@@ -301,7 +301,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}"
)

View File

@@ -140,6 +140,18 @@ class PromptBuilder:
f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}"
)
keywords_reaction_prompt += rule.get("reaction", "") + ""
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 = ""

View File

@@ -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}"
)

View File

@@ -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"

View File

@@ -106,6 +106,18 @@ class PromptBuilder:
f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}"
)
keywords_reaction_prompt += rule.get("reaction", "") + ""
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 = ""
@@ -160,7 +172,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)
# 日程构建
# schedule_prompt = f'''你现在正在做的事情是:{bot_schedule.get_current_num_task(num = 1,time_info = False)}'''
@@ -231,7 +243,7 @@ class PromptBuilder:
content: str = "",
) -> 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)
# chat_target = "你正在qq群里聊天"

View File

@@ -1,4 +1,5 @@
import os
import re
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from dateutil import tz
@@ -27,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:
@@ -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"]

View File

@@ -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

View File

@@ -149,6 +149,11 @@ enable = false # 仅作示例,不会触发
keywords = ["测试关键词回复","test",""]
reaction = "回答“测试成功”"
[[keywords_reaction.rules]] # 使用正则表达式匹配句式
enable = false # 仅作示例,不会触发
regex = ["^(?P<n>\\S{1,20})是这样的$"] # 将匹配到的词汇命名为n反应中对应的[n]会被替换为匹配到的内容,若不了解正则表达式请勿编写
reaction = "请按照以下模板造句:[n]是这样的xx只要xx就可以可是[n]要考虑的事情就很多了比如什么时候xx什么时候xx什么时候xx。请自由发挥替换xx部分只需保持句式结构同时表达一种将[n]过度重视的反讽意味)"
[chinese_typo]
enable = true # 是否启用中文错别字生成器
error_rate=0.001 # 单字替换概率