diff --git a/src/do_tool/tool_can_use/get_current_task.py b/src/do_tool/tool_can_use/get_current_task.py index dcfd059dd..eafcf1d51 100644 --- a/src/do_tool/tool_can_use/get_current_task.py +++ b/src/do_tool/tool_can_use/get_current_task.py @@ -1,7 +1,8 @@ -from src.do_tool.tool_can_use.base_tool import BaseTool, register_tool +from src.do_tool.tool_can_use.base_tool import BaseTool from src.plugins.schedule.schedule_generator import bot_schedule from src.common.logger import get_module_logger from typing import Dict, Any +from datetime import datetime logger = get_module_logger("get_current_task_tool") @@ -9,19 +10,19 @@ logger = get_module_logger("get_current_task_tool") class GetCurrentTaskTool(BaseTool): """获取当前正在做的事情/最近的任务工具""" - name = "get_current_task" - description = "获取当前正在做的事情/最近的任务" + name = "get_schedule" + description = "获取当前正在做的事情,或者某个时间点/时间段的日程信息" parameters = { "type": "object", "properties": { - "num": {"type": "integer", "description": "要获取的任务数量"}, - "time_info": {"type": "boolean", "description": "是否包含时间信息"}, + "start_time": {"type": "string", "description": "开始时间,格式为'HH:MM',填写current则获取当前任务"}, + "end_time": {"type": "string", "description": "结束时间,格式为'HH:MM',填写current则获取当前任务"}, }, - "required": [], + "required": ["start_time", "end_time"], } async def execute(self, function_args: Dict[str, Any], message_txt: str = "") -> Dict[str, Any]: - """执行获取当前任务 + """执行获取当前任务或指定时间段的日程信息 Args: function_args: 工具参数 @@ -30,25 +31,31 @@ class GetCurrentTaskTool(BaseTool): Returns: Dict: 工具执行结果 """ - try: - # 获取参数,如果没有提供则使用默认值 - num = function_args.get("num", 1) - time_info = function_args.get("time_info", False) + start_time = function_args.get("start_time") + end_time = function_args.get("end_time") - # 调用日程系统获取当前任务 - current_task = bot_schedule.get_current_num_task(num=num, time_info=time_info) - - # 格式化返回结果 + # 如果 start_time 或 end_time 为 "current",则获取当前任务 + if start_time == "current" or end_time == "current": + current_task = bot_schedule.get_current_num_task(num=1, time_info=True) + current_time = datetime.now().strftime("%H:%M:%S") + current_date = datetime.now().strftime("%Y-%m-%d") if current_task: - task_info = current_task + task_info = f"{current_date} {current_time},你在{current_task}" else: - task_info = "当前没有正在进行的任务" + task_info = f"{current_time} {current_date},没在做任何事情" + # 如果提供了时间范围,则获取该时间段的日程信息 + elif start_time and end_time: + tasks = await bot_schedule.get_task_from_time_to_time(start_time, end_time) + if tasks: + task_list = [] + for task in tasks: + task_time = task[0].strftime("%H:%M") + task_content = task[1] + task_list.append(f"{task_time}时,{task_content}") + task_info = "\n".join(task_list) + else: + task_info = f"在 {start_time} 到 {end_time} 之间没有找到日程信息" - return {"name": "get_current_task", "content": f"当前任务信息: {task_info}"} - except Exception as e: - logger.error(f"获取当前任务工具执行失败: {str(e)}") - return {"name": "get_current_task", "content": f"获取当前任务失败: {str(e)}"} + return {"name": "get_current_task", "content": f"日程信息: {task_info}"} -# 注册工具 -# register_tool(GetCurrentTaskTool) diff --git a/src/do_tool/tool_can_use/get_memory.py b/src/do_tool/tool_can_use/get_memory.py index 146e18e53..059c53b12 100644 --- a/src/do_tool/tool_can_use/get_memory.py +++ b/src/do_tool/tool_can_use/get_memory.py @@ -3,13 +3,13 @@ from src.plugins.memory_system.Hippocampus import HippocampusManager from src.common.logger import get_module_logger from typing import Dict, Any -logger = get_module_logger("get_memory_tool") +logger = get_module_logger("mid_chat_mem_tool") class GetMemoryTool(BaseTool): """从记忆系统中获取相关记忆的工具""" - name = "get_memory" + name = "mid_chat_mem" description = "从记忆系统中获取相关记忆" parameters = { "type": "object", @@ -49,10 +49,10 @@ class GetMemoryTool(BaseTool): else: content = f"你不太记得有关{text}的记忆,你对此不太了解" - return {"name": "get_memory", "content": content} + return {"name": "mid_chat_mem", "content": content} except Exception as e: logger.error(f"记忆获取工具执行失败: {str(e)}") - return {"name": "get_memory", "content": f"记忆获取失败: {str(e)}"} + return {"name": "mid_chat_mem", "content": f"记忆获取失败: {str(e)}"} # 注册工具 diff --git a/src/do_tool/tool_can_use/get_time_date.py b/src/do_tool/tool_can_use/get_time_date.py new file mode 100644 index 000000000..358b67584 --- /dev/null +++ b/src/do_tool/tool_can_use/get_time_date.py @@ -0,0 +1,39 @@ +from src.do_tool.tool_can_use.base_tool import BaseTool +from src.common.logger import get_module_logger +from typing import Dict, Any +from datetime import datetime + +logger = get_module_logger("get_time_date") + +class GetCurrentDateTimeTool(BaseTool): + """获取当前时间、日期、年份和星期的工具""" + + name = "get_current_date_time" + description = "当有人询问或者涉及到具体时间或者日期的时候,必须使用这个工具" + parameters = { + "type": "object", + "properties": {}, + "required": [], + } + + async def execute(self, function_args: Dict[str, Any], message_txt: str = "") -> Dict[str, Any]: + """执行获取当前时间、日期、年份和星期 + + Args: + function_args: 工具参数(此工具不使用) + message_txt: 原始消息文本(此工具不使用) + + Returns: + Dict: 工具执行结果 + """ + current_time = datetime.now().strftime("%H:%M:%S") + current_date = datetime.now().strftime("%Y-%m-%d") + current_year = datetime.now().strftime("%Y") + current_weekday = datetime.now().strftime("%A") + + return { + "name": "get_current_date_time", + "content": f"当前时间: {current_time}, 日期: {current_date}, 年份: {current_year}, 星期: {current_weekday}" + } + + diff --git a/src/do_tool/tool_can_use/mid_chat_mem.py b/src/do_tool/tool_can_use/mid_chat_mem.py new file mode 100644 index 000000000..26d26704a --- /dev/null +++ b/src/do_tool/tool_can_use/mid_chat_mem.py @@ -0,0 +1,40 @@ +from src.do_tool.tool_can_use.base_tool import BaseTool +from src.common.logger import get_module_logger +from typing import Dict, Any + +logger = get_module_logger("get_mid_memory_tool") + + +class GetMidMemoryTool(BaseTool): + """从记忆系统中获取相关记忆的工具""" + + name = "mid_chat_mem" + description = "之前的聊天内容中获取具体信息,当最新消息提到,或者你需要回复的消息中提到,你可以使用这个工具" + parameters = { + "type": "object", + "properties": { + "id": {"type": "integer", "description": "要查询的聊天记录id"}, + }, + "required": ["id"], + } + + async def execute(self, function_args: Dict[str, Any], message_txt: str = "") -> Dict[str, Any]: + """执行记忆获取 + + Args: + function_args: 工具参数 + message_txt: 原始消息文本 + + Returns: + Dict: 工具执行结果 + """ + try: + id = function_args.get("id") + return {"name": "mid_chat_mem", "content": str(id)} + except Exception as e: + logger.error(f"聊天记录获取工具执行失败: {str(e)}") + return {"name": "mid_chat_mem", "content": f"聊天记录获取失败: {str(e)}"} + + +# 注册工具 +# register_tool(GetMemoryTool) diff --git a/src/do_tool/tool_use.py b/src/do_tool/tool_use.py index 81339ef32..87b46c942 100644 --- a/src/do_tool/tool_use.py +++ b/src/do_tool/tool_use.py @@ -6,6 +6,7 @@ import time import json from src.common.logger import get_module_logger, TOOL_USE_STYLE_CONFIG, LogConfig from src.do_tool.tool_can_use import get_all_tool_definitions, get_tool_instance +from src.heart_flow.sub_heartflow import SubHeartflow tool_use_config = LogConfig( # 使用消息发送专用样式 @@ -21,7 +22,7 @@ class ToolUser: model=global_config.llm_heartflow, temperature=0.2, max_tokens=1000, request_type="tool_use" ) - async def _build_tool_prompt(self, message_txt: str, sender_name: str, chat_stream: ChatStream, reply_message:str = ""): + async def _build_tool_prompt(self, message_txt: str, sender_name: str, chat_stream: ChatStream, subheartflow: SubHeartflow = None): """构建工具使用的提示词 Args: @@ -32,6 +33,12 @@ class ToolUser: Returns: str: 构建好的提示词 """ + if subheartflow: + mid_memory_info = subheartflow.observations[0].mid_memory_info + # print(f"intol111111111111111111111111111111111222222222222mid_memory_info:{mid_memory_info}") + else: + mid_memory_info = "" + new_messages = list( db.messages.find({"chat_id": chat_stream.stream_id, "time": {"$gt": time.time()}}).sort("time", 1).limit(15) ) @@ -43,13 +50,12 @@ class ToolUser: # 这些信息应该从调用者传入,而不是从self获取 bot_name = global_config.BOT_NICKNAME prompt = "" + prompt += mid_memory_info prompt += "你正在思考如何回复群里的消息。\n" prompt += f"你注意到{sender_name}刚刚说:{message_txt}\n" - if reply_message: - prompt += f"你刚刚回复的内容是:{reply_message}\n" prompt += f"注意你就是{bot_name},{bot_name}指的就是你。" - prompt += "你现在需要对群里的聊天内容进行回复,现在选择工具来对消息和你的回复进行处理,你是否需要额外的信息,或者进行一些动作,比如回忆或者搜寻已有的知识,改变关系和情感,或者了解你现在正在做什么,请输出你需要的工具,或者你需要的额外信息。" + prompt += "你现在需要对群里的聊天内容进行回复,现在选择工具来对消息和你的回复进行处理,你是否需要额外的信息,比如回忆或者搜寻已有的知识,改变关系和情感,或者了解你现在正在做什么。" return prompt def _define_tools(self): @@ -83,20 +89,8 @@ class ToolUser: # 执行工具 result = await tool_instance.execute(function_args, message_txt) if result: - # 根据工具名称确定类型标签 - tool_type = "" - if "memory" in function_name.lower(): - tool_type = "memory" - elif "schedule" in function_name.lower() or "task" in function_name.lower(): - tool_type = "schedule" - elif "knowledge" in function_name.lower(): - tool_type = "knowledge" - elif "change_relationship" in function_name.lower(): - tool_type = "change_relationship" - elif "change_mood" in function_name.lower(): - tool_type = "change_mood" - else: - tool_type = "other" + # 直接使用 function_name 作为 tool_type + tool_type = function_name return { "tool_call_id": tool_call["id"], @@ -110,7 +104,7 @@ class ToolUser: logger.error(f"执行工具调用时发生错误: {str(e)}") return None - async def use_tool(self, message_txt: str, sender_name: str, chat_stream: ChatStream): + async def use_tool(self, message_txt: str, sender_name: str, chat_stream: ChatStream, subheartflow: SubHeartflow = None): """使用工具辅助思考,判断是否需要额外信息 Args: @@ -123,7 +117,12 @@ class ToolUser: """ try: # 构建提示词 - prompt = await self._build_tool_prompt(message_txt, sender_name, chat_stream) + prompt = await self._build_tool_prompt( + message_txt, + sender_name, + chat_stream, + subheartflow + ) # 定义可用工具 tools = self._define_tools() @@ -158,30 +157,26 @@ class ToolUser: tool_calls_str = "" for tool_call in tool_calls: tool_calls_str += f"{tool_call['function']['name']}\n" - logger.info(f"模型请求调用{len(tool_calls)}个工具: {tool_calls_str}") + logger.info(f"根据:\n{prompt}\n模型请求调用{len(tool_calls)}个工具: {tool_calls_str}") tool_results = [] - structured_info = { - "memory": [], - "schedule": [], - "knowledge": [], - "change_relationship": [], - "change_mood": [], - "other": [] - } + structured_info = {} # 动态生成键 # 执行所有工具调用 for tool_call in tool_calls: result = await self._execute_tool_call(tool_call, message_txt) if result: tool_results.append(result) - # 将工具结果添加到对应类型的列表中 - structured_info[result["type"]].append({ + # 使用工具名称作为键 + tool_name = result["name"] + if tool_name not in structured_info: + structured_info[tool_name] = [] + structured_info[tool_name].append({ "name": result["name"], "content": result["content"] }) # 如果有工具结果,返回结构化的信息 - if any(structured_info.values()): + if structured_info: logger.info(f"工具调用收集到结构化信息: {json.dumps(structured_info, ensure_ascii=False)}") return { "used_tools": True, diff --git a/src/heart_flow/observation.py b/src/heart_flow/observation.py index aef23f964..7da08ecc5 100644 --- a/src/heart_flow/observation.py +++ b/src/heart_flow/observation.py @@ -4,7 +4,9 @@ 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.common.logger import get_module_logger +import traceback +logger = get_module_logger("observation") # 所有观察的基类 class Observation: @@ -34,23 +36,59 @@ class ChattingObservation(Observation): self.last_summary_time = 0 # 上次更新summary的时间 self.sub_observe = None + self.max_now_obs_len = 20 + self.overlap_len = 5 + self.mid_memorys = [] + self.max_mid_memory_len = 5 + self.mid_memory_info = "" + self.now_message_info = "" + + self.updating_old = False self.llm_summary = LLM_request( model=global_config.llm_observation, temperature=0.7, max_tokens=300, request_type="chat_observation" ) # 进行一次观察 返回观察结果observe_info + def get_observe_info(self, ids = None): + if ids: + mid_memory_str = "" + for id in ids: + print(f"id:{id}") + try: + for mid_memory in self.mid_memorys: + if mid_memory['id'] == id: + mid_memory_by_id = mid_memory + msg_str = "" + for msg in mid_memory_by_id['messages']: + msg_str += f"{msg['detailed_plain_text']}" + time_diff = int((datetime.now().timestamp() - mid_memory_by_id['created_at']) / 60) + mid_memory_str += f"距离现在{time_diff}分钟前:\n{msg_str}\n" + except Exception as e: + logger.error(f"获取mid_memory_id失败: {e}") + traceback.print_exc() + # print(f"获取mid_memory_id失败: {e}") + return self.now_message_info + + return mid_memory_str + "现在群里正在聊:\n" + self.now_message_info + + else: + return self.now_message_info + async def observe(self): - # 查找新消息,限制最多30条 + # 查找新消息 new_messages = list( db.messages.find({"chat_id": self.chat_id, "time": {"$gt": self.last_observe_time}}) .sort("time", 1) - .limit(15) - ) # 按时间正序排列,最多15条 - + ) # 按时间正序排列 + if not new_messages: return self.observe_info # 没有新消息,返回上次观察结果 + self.last_observe_time = new_messages[-1]["time"] + + self.talking_message.extend(new_messages) + # 将新消息转换为字符串格式 new_messages_str = "" for msg in new_messages: @@ -60,82 +98,61 @@ class ChattingObservation(Observation): # print(f"new_messages_str:{new_messages_str}") # 将新消息添加到talking_message,同时保持列表长度不超过20条 - self.talking_message.extend(new_messages) - if len(self.talking_message) > 15: - self.talking_message = self.talking_message[-15:] # 只保留最新的15条 - self.translate_message_list_to_str() + + if len(self.talking_message) > self.max_now_obs_len and not self.updating_old: + self.updating_old = True + # 计算需要保留的消息数量 + keep_messages_count = self.max_now_obs_len - self.overlap_len + # 提取所有超出保留数量的最老消息 + oldest_messages = self.talking_message[:-keep_messages_count] + self.talking_message = self.talking_message[-keep_messages_count:] + oldest_messages_str = "\n".join([msg["detailed_plain_text"] for msg in oldest_messages]) + oldest_timestamps = [msg["time"] for msg in oldest_messages] - # 更新观察次数 - # self.observe_times += 1 - self.last_observe_time = new_messages[-1]["time"] + # 调用 LLM 总结主题 + prompt = f"请总结以下聊天记录的主题:\n{oldest_messages_str}\n主题,用一句话概括包括人物事件和主要信息,不要分点:" + try: + summary, _ = await self.llm_summary.generate_response_async(prompt) + except Exception as e: + print(f"总结主题失败: {e}") + summary = "无法总结主题" - # 检查是否需要更新summary - # current_time = int(datetime.now().timestamp()) - # if current_time - self.last_summary_time >= 30: # 如果超过30秒,重置计数 - # self.summary_count = 0 - # self.last_summary_time = current_time + mid_memory = { + "id": str(int(datetime.now().timestamp())), + "theme": summary, + "messages": oldest_messages, + "timestamps": oldest_timestamps, + "chat_id": self.chat_id, + "created_at": datetime.now().timestamp() + } + # print(f"mid_memory:{mid_memory}") + # 存入内存中的 mid_memorys + self.mid_memorys.append(mid_memory) + if len(self.mid_memorys) > self.max_mid_memory_len: + self.mid_memorys.pop(0) + + mid_memory_str = "之前聊天的内容概括是:\n" + for mid_memory in self.mid_memorys: + time_diff = int((datetime.now().timestamp() - mid_memory['created_at']) / 60) + mid_memory_str += f"距离现在{time_diff}分钟前(聊天记录id:{mid_memory['id']}):{mid_memory['theme']}\n" + self.mid_memory_info = mid_memory_str + + + self.updating_old = False + + # print(f"处理后self.talking_message:{self.talking_message}") - # if self.summary_count < self.max_update_in_30s: # 如果30秒内更新次数小于2次 - # await self.update_talking_summary(new_messages_str) - # print(f"更新聊天总结:{self.observe_info}11111111111111") - # self.summary_count += 1 - updated_observe_info = await self.update_talking_summary(new_messages_str) - print(f"更新聊天总结:{updated_observe_info}11111111111111") - self.observe_info = updated_observe_info + now_message_str = "" + now_message_str += self.translate_message_list_to_str(talking_message=self.talking_message) + self.now_message_info = now_message_str + + + + logger.debug(f"压缩早期记忆:{self.mid_memory_info}\n现在聊天内容:{self.now_message_info}") - return updated_observe_info - - async def carefully_observe(self): - # 查找新消息,限制最多40条 - new_messages = list( - db.messages.find({"chat_id": self.chat_id, "time": {"$gt": self.last_observe_time}}) - .sort("time", 1) - .limit(30) - ) # 按时间正序排列,最多30条 - - if not new_messages: - return self.observe_info # 没有新消息,返回上次观察结果 - - # 将新消息转换为字符串格式 - new_messages_str = "" - for msg in new_messages: - if "detailed_plain_text" in msg: - new_messages_str += f"{msg['detailed_plain_text']}\n" - - # 将新消息添加到talking_message,同时保持列表长度不超过30条 - self.talking_message.extend(new_messages) - if len(self.talking_message) > 30: - self.talking_message = self.talking_message[-30:] # 只保留最新的30条 - self.translate_message_list_to_str() - - # 更新观察次数 - self.observe_times += 1 - self.last_observe_time = new_messages[-1]["time"] - - updated_observe_info = await self.update_talking_summary(new_messages_str) - self.observe_info = updated_observe_info - return updated_observe_info async def update_talking_summary(self, new_messages_str): - # 基于已经有的talking_summary,和新的talking_message,生成一个summary - # print(f"更新聊天总结:{self.talking_summary}") - # 开始构建prompt - # prompt_personality = "你" - # # person - # individuality = Individuality.get_instance() - # personality_core = individuality.personality.personality_core - # prompt_personality += personality_core - - # personality_sides = individuality.personality.personality_sides - # random.shuffle(personality_sides) - # prompt_personality += f",{personality_sides[0]}" - - # identity_detail = individuality.identity.identity_detail - # random.shuffle(identity_detail) - # prompt_personality += f",{identity_detail[0]}" - - # personality_info = prompt_personality prompt = "" # prompt += f"{personality_info}" @@ -155,7 +172,9 @@ class ChattingObservation(Observation): # print(f"prompt:{prompt}") # print(f"self.observe_info:{self.observe_info}") - def translate_message_list_to_str(self): - self.talking_message_str = "" - for message in self.talking_message: - self.talking_message_str += message["detailed_plain_text"] + def translate_message_list_to_str(self, talking_message): + talking_message_str = "" + for message in talking_message: + talking_message_str += message["detailed_plain_text"] + + return talking_message_str diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py index 81fa89445..226572acc 100644 --- a/src/heart_flow/sub_heartflow.py +++ b/src/heart_flow/sub_heartflow.py @@ -1,4 +1,4 @@ -from .observation import Observation +from .observation import Observation, ChattingObservation import asyncio from src.plugins.moods.moods import MoodManager from src.plugins.models.utils_model import LLM_request @@ -32,9 +32,10 @@ def init_prompt(): prompt = "" # prompt += f"麦麦的总体想法是:{self.main_heartflow_info}\n\n" prompt += "{extra_info}\n" + # prompt += "{prompt_schedule}\n" prompt += "{relation_prompt_all}\n" prompt += "{prompt_personality}\n" - prompt += "刚刚你的想法是{current_thinking_info}。如果有新的内容,记得转换话题\n" + prompt += "刚刚你的想法是{current_thinking_info}。可以适当转换话题\n" prompt += "-----------------------------------\n" prompt += "现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{chat_observe_info}\n" prompt += "你现在{mood_info}\n" @@ -91,7 +92,7 @@ class SubHeartflow: self.is_active = False - self.observations: list[Observation] = [] + self.observations: list[ChattingObservation] = [] self.running_knowledges = [] @@ -151,14 +152,22 @@ class SubHeartflow: observation = self.observations[0] await observation.observe() - async def do_thinking_before_reply(self, message_txt: str, sender_name: str, chat_stream: ChatStream, extra_info: str): + async def do_thinking_before_reply(self, message_txt: str, sender_name: str, chat_stream: ChatStream, extra_info: str, obs_id: int = None): current_thinking_info = self.current_mind mood_info = self.current_state.mood # mood_info = "你很生气,很愤怒" observation = self.observations[0] - chat_observe_info = observation.observe_info - + if obs_id: + print(f"11111111111有id,开始获取观察信息{obs_id}") + chat_observe_info = observation.get_observe_info(obs_id) + else: + chat_observe_info = observation.get_observe_info() + extra_info_prompt = "" + for tool_name, tool_data in extra_info.items(): + extra_info_prompt += f"{tool_name} 相关信息:\n" + for item in tool_data: + extra_info_prompt += f"- {item['name']}: {item['content']}\n" # 开始构建prompt prompt_personality = f"你的名字是{self.bot_name},你" @@ -215,7 +224,8 @@ class SubHeartflow: # prompt += f"记得结合上述的消息,生成内心想法,文字不要浮夸,注意你就是{self.bot_name},{self.bot_name}指的就是你。" prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_before")).format( - extra_info, + extra_info_prompt, + # prompt_schedule, relation_prompt_all, prompt_personality, current_thinking_info, @@ -249,6 +259,12 @@ class SubHeartflow: personality_core = individuality.personality.personality_core prompt_personality += personality_core + + extra_info_prompt = "" + for tool_name, tool_data in extra_info.items(): + extra_info_prompt += f"{tool_name} 相关信息:\n" + for item in tool_data: + extra_info_prompt += f"- {item['name']}: {item['content']}\n" personality_sides = individuality.personality.personality_sides random.shuffle(personality_sides) @@ -268,7 +284,7 @@ class SubHeartflow: reply_info = reply_content prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_after")).format( - extra_info, + extra_info_prompt, prompt_personality, chat_observe_info, current_thinking_info, diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index 6d2455202..8d9aa1f8e 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -3,7 +3,6 @@ from ..person_info.relationship_manager import relationship_manager from .chat_stream import chat_manager from .message_sender import message_manager from ..storage.storage import MessageStorage -from .auto_speak import auto_speak_manager __all__ = [ @@ -12,5 +11,4 @@ __all__ = [ "chat_manager", "message_manager", "MessageStorage", - "auto_speak_manager", ] diff --git a/src/plugins/chat/auto_speak.py b/src/plugins/chat/auto_speak.py deleted file mode 100644 index ac76a2714..000000000 --- a/src/plugins/chat/auto_speak.py +++ /dev/null @@ -1,184 +0,0 @@ -import time -import asyncio -import random -from random import random as random_float -from typing import Dict -from ..config.config import global_config -from .message import MessageSending, MessageThinking, MessageSet, MessageRecv -from ..message.message_base import UserInfo, Seg -from .message_sender import message_manager -from ..moods.moods import MoodManager -from ..chat_module.reasoning_chat.reasoning_generator import ResponseGenerator -from src.common.logger import get_module_logger -from src.heart_flow.heartflow import heartflow -from ...common.database import db - -logger = get_module_logger("auto_speak") - - -class AutoSpeakManager: - def __init__(self): - self._last_auto_speak_time: Dict[str, float] = {} # 记录每个聊天流上次自主发言的时间 - self.mood_manager = MoodManager.get_instance() - self.gpt = ResponseGenerator() # 添加gpt实例 - self._started = False - self._check_task = None - self.db = db - - async def get_chat_info(self, chat_id: str) -> dict: - """从数据库获取聊天流信息""" - chat_info = await self.db.chat_streams.find_one({"stream_id": chat_id}) - return chat_info - - async def start_auto_speak_check(self): - """启动自动发言检查任务""" - if not self._started: - self._check_task = asyncio.create_task(self._periodic_check()) - self._started = True - logger.success("自动发言检查任务已启动") - - async def _periodic_check(self): - """定期检查是否需要自主发言""" - while True and global_config.enable_think_flow: - # 获取所有活跃的子心流 - active_subheartflows = [] - for chat_id, subheartflow in heartflow._subheartflows.items(): - if ( - subheartflow.is_active and subheartflow.current_state.willing > 0 - ): # 只考虑活跃且意愿值大于0.5的子心流 - active_subheartflows.append((chat_id, subheartflow)) - logger.debug( - f"发现活跃子心流 - 聊天ID: {chat_id}, 意愿值: {subheartflow.current_state.willing:.2f}" - ) - - if not active_subheartflows: - logger.debug("当前没有活跃的子心流") - await asyncio.sleep(20) # 添加异步等待 - continue - - # 随机选择一个活跃的子心流 - chat_id, subheartflow = random.choice(active_subheartflows) - logger.info(f"随机选择子心流 - 聊天ID: {chat_id}, 意愿值: {subheartflow.current_state.willing:.2f}") - - # 检查是否应该自主发言 - if await self.check_auto_speak(subheartflow): - logger.info(f"准备自主发言 - 聊天ID: {chat_id}") - # 生成自主发言 - bot_user_info = UserInfo( - user_id=global_config.BOT_QQ, - user_nickname=global_config.BOT_NICKNAME, - platform="qq", # 默认使用qq平台 - ) - - # 创建一个空的MessageRecv对象作为上下文 - message = MessageRecv( - { - "message_info": { - "user_info": {"user_id": chat_id, "user_nickname": "", "platform": "qq"}, - "group_info": None, - "platform": "qq", - "time": time.time(), - }, - "processed_plain_text": "", - "raw_message": "", - "is_emoji": False, - } - ) - - await self.generate_auto_speak( - subheartflow, message, bot_user_info, message.message_info["user_info"], message.message_info - ) - else: - logger.debug(f"不满足自主发言条件 - 聊天ID: {chat_id}") - - # 每分钟检查一次 - await asyncio.sleep(20) - - # await asyncio.sleep(5) # 发生错误时等待5秒再继续 - - async def check_auto_speak(self, subheartflow) -> bool: - """检查是否应该自主发言""" - if not subheartflow: - return False - - current_time = time.time() - chat_id = subheartflow.observe_chat_id - - # 获取上次自主发言时间 - if chat_id not in self._last_auto_speak_time: - self._last_auto_speak_time[chat_id] = 0 - last_speak_time = self._last_auto_speak_time.get(chat_id, 0) - - # 如果距离上次自主发言不到5分钟,不发言 - if current_time - last_speak_time < 30: - logger.debug( - f"距离上次发言时间太短 - 聊天ID: {chat_id}, 剩余时间: {30 - (current_time - last_speak_time):.1f}秒" - ) - return False - - # 获取当前意愿值 - current_willing = subheartflow.current_state.willing - - if current_willing > 0.1 and random_float() < 0.5: - self._last_auto_speak_time[chat_id] = current_time - logger.info(f"满足自主发言条件 - 聊天ID: {chat_id}, 意愿值: {current_willing:.2f}") - return True - - logger.debug(f"不满足自主发言条件 - 聊天ID: {chat_id}, 意愿值: {current_willing:.2f}") - return False - - async def generate_auto_speak(self, subheartflow, message, bot_user_info: UserInfo, userinfo, messageinfo): - """生成自主发言内容""" - thinking_time_point = round(time.time(), 2) - think_id = "mt" + str(thinking_time_point) - thinking_message = MessageThinking( - message_id=think_id, - chat_stream=None, # 不需要chat_stream - bot_user_info=bot_user_info, - reply=message, - thinking_start_time=thinking_time_point, - ) - - message_manager.add_message(thinking_message) - - # 生成自主发言内容 - try: - response, raw_content = await self.gpt.generate_response(message) - except Exception as e: - logger.error(f"生成自主发言内容时发生错误: {e}") - return False - - if response: - message_set = MessageSet(None, think_id) # 不需要chat_stream - mark_head = False - - for msg in response: - message_segment = Seg(type="text", data=msg) - bot_message = MessageSending( - message_id=think_id, - chat_stream=None, # 不需要chat_stream - bot_user_info=bot_user_info, - sender_info=userinfo, - message_segment=message_segment, - reply=message, - is_head=not mark_head, - is_emoji=False, - thinking_start_time=thinking_time_point, - ) - if not mark_head: - mark_head = True - message_set.add_message(bot_message) - - message_manager.add_message(message_set) - - # 更新情绪和关系 - stance, emotion = await self.gpt._get_emotion_tags(raw_content, message.processed_plain_text) - self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor) - - return True - - return False - - -# 创建全局AutoSpeakManager实例 -auto_speak_manager = AutoSpeakManager() 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 3f0869aa0..88d041d5b 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 @@ -236,7 +236,7 @@ class ThinkFlowChat: if random() < reply_probability: try: do_reply = True - + # 回复前处理 await willing_manager.before_generate_reply_handle(message.message_info.message_id) @@ -252,80 +252,77 @@ class ThinkFlowChat: info_catcher = info_catcher_manager.get_info_catcher(thinking_id) info_catcher.catch_decide_to_response(message) + # 观察 try: - # 观察 with Timer("观察", timing_results): await heartflow.get_subheartflow(chat.stream_id).do_observe() except Exception as e: logger.error(f"心流观察失败: {e}") + traceback.print_exc() info_catcher.catch_after_observe(timing_results["观察"]) + # 思考前使用工具 update_relationship = "" + get_mid_memory_id = [] + tool_result_info = {} try: with Timer("思考前使用工具", timing_results): - tool_result = await self.tool_user.use_tool(message.processed_plain_text, message.message_info.user_info.user_nickname, chat) + tool_result = await self.tool_user.use_tool( + message.processed_plain_text, + message.message_info.user_info.user_nickname, + chat, + heartflow.get_subheartflow(chat.stream_id)) # 如果工具被使用且获得了结果,将收集到的信息合并到思考中 - collected_info = "" + # collected_info = "" if tool_result.get("used_tools", False): - - # 如果有收集到的结构化信息,将其格式化后添加到当前思考中 if "structured_info" in tool_result: - info = tool_result["structured_info"] - # 处理记忆信息 - if info["memory"]: - collected_info += "\n记忆相关信息:\n" - for mem in info["memory"]: - collected_info += f"- {mem['name']}: {mem['content']}\n" + tool_result_info = tool_result["structured_info"] + # collected_info = "" + get_mid_memory_id = [] + update_relationship = "" - # 处理日程信息 - if info["schedule"]: - collected_info += "\n日程相关信息:\n" - for sch in info["schedule"]: - collected_info += f"- {sch['name']}: {sch['content']}\n" - - # 处理知识信息 - if info["knowledge"]: - collected_info += "\n知识相关信息:\n" - for know in info["knowledge"]: - collected_info += f"- {know['name']}: {know['content']}\n" - - # 处理关系信息 - if info["change_relationship"]: - collected_info += "\n关系相关信息:\n" - for rel in info["change_relationship"]: - collected_info += f"- {rel['name']}: {rel['content']}\n" - # print("11111111111111111111111111111") - update_relationship += rel["content"] - # print(f"11111111111111111111111111111{update_relationship}") + # 动态解析工具结果 + for tool_name, tool_data in tool_result_info.items(): + # tool_result_info += f"\n{tool_name} 相关信息:\n" + # for item in tool_data: + # tool_result_info += f"- {item['name']}: {item['content']}\n" + + # 特殊判定:mid_chat_mem + if tool_name == "mid_chat_mem": + for mid_memory in tool_data: + get_mid_memory_id.append(mid_memory['content']) + + # 特殊判定:change_mood + if tool_name == "change_mood": + for mood in tool_data: + self.mood_manager.update_mood_from_emotion( + mood['content'], + global_config.mood_intensity_factor + ) + + # 特殊判定:change_relationship + if tool_name == "change_relationship": + update_relationship = tool_data[0]["content"] - # 处理心情信息 - if info["change_mood"]: - collected_info += "\n心情相关信息:\n" - for mood in info["change_mood"]: - collected_info += f"- {mood['name']}: {mood['content']}\n" - - # 处理其他信息 - if info["other"]: - collected_info += "\n其他相关信息:\n" - for other in info["other"]: - collected_info += f"- {other['name']}: {other['content']}\n" except Exception as e: logger.error(f"思考前工具调用失败: {e}") logger.error(traceback.format_exc()) - + # 处理关系更新 if update_relationship: - # ori_response = ",".join(response_set) - # print("22222222222222222222222222222") - stance, emotion = await self.gpt._get_emotion_tags_with_reason("你还没有回复", message.processed_plain_text,update_relationship) + stance, emotion = await self.gpt._get_emotion_tags_with_reason( + "你还没有回复", + message.processed_plain_text, + update_relationship + ) await relationship_manager.calculate_update_relationship_value( - chat_stream=message.chat_stream, label=emotion, stance=stance - ) - print("33333333333333333333333333333") + chat_stream=message.chat_stream, + label=emotion, + stance=stance + ) - # 思考前脑内状态 try: with Timer("思考前脑内状态", timing_results): @@ -335,7 +332,8 @@ class ThinkFlowChat: message_txt=message.processed_plain_text, sender_name=message.message_info.user_info.user_nickname, chat_stream=chat, - extra_info=collected_info + obs_id = get_mid_memory_id, + extra_info=tool_result_info ) except Exception as e: logger.error(f"心流思考前脑内状态失败: {e}") @@ -371,66 +369,6 @@ class ThinkFlowChat: logger.error(f"心流处理表情包失败: {e}") - # 思考后使用工具 - try: - with Timer("思考后使用工具", timing_results): - tool_result = await self.tool_user.use_tool(message.processed_plain_text, message.message_info.user_info.user_nickname, chat) - # 如果工具被使用且获得了结果,将收集到的信息合并到思考中 - collected_info = "" - if tool_result.get("used_tools", False): - - # 如果有收集到的结构化信息,将其格式化后添加到当前思考中 - if "structured_info" in tool_result: - info = tool_result["structured_info"] - # 处理记忆信息 - if info["memory"]: - collected_info += "\n记忆相关信息:\n" - for mem in info["memory"]: - collected_info += f"- {mem['name']}: {mem['content']}\n" - - # 处理日程信息 - if info["schedule"]: - collected_info += "\n日程相关信息:\n" - for sch in info["schedule"]: - collected_info += f"- {sch['name']}: {sch['content']}\n" - - # 处理知识信息 - if info["knowledge"]: - collected_info += "\n知识相关信息:\n" - for know in info["knowledge"]: - collected_info += f"- {know['name']}: {know['content']}\n" - - # 处理关系信息 - if info["change_relationship"]: - collected_info += "\n关系相关信息:\n" - for rel in info["change_relationship"]: - collected_info += f"- {rel['name']}: {rel['content']}\n" - - # 处理心情信息 - if info["change_mood"]: - collected_info += "\n心情相关信息:\n" - for mood in info["change_mood"]: - collected_info += f"- {mood['name']}: {mood['content']}\n" - - # 处理其他信息 - if info["other"]: - collected_info += "\n其他相关信息:\n" - for other in info["other"]: - collected_info += f"- {other['name']}: {other['content']}\n" - except Exception as e: - logger.error(f"思考后工具调用失败: {e}") - logger.error(traceback.format_exc()) - - # 更新关系 - if info["change_relationship"]: - ori_response = ",".join(response_set) - stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text,info["change_relationship"]["content"]) - await relationship_manager.calculate_update_relationship_value( - chat_stream=message.chat_stream, label=emotion, stance=stance - ) - - - try: with Timer("思考后脑内状态更新", timing_results): stream_id = message.chat_stream.stream_id @@ -440,7 +378,7 @@ class ThinkFlowChat: stream_id, limit=global_config.MAX_CONTEXT_SIZE, combine=True ) - await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt,collected_info) + await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt,tool_result_info) except Exception as e: logger.error(f"心流思考后脑内状态更新失败: {e}") 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 a6e516739..f02172625 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 @@ -43,7 +43,7 @@ def init_prompt(): {chat_talking_prompt} 现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n 你刚刚脑子里在想:{current_mind_info} -现在请你读读之前的聊天记录,然后给出日常,口语化且简短的回复内容,只给出文字的回复内容,不要有内心独白: +现在请你读读之前的聊天记录,然后给出日常,口语化且简短的回复内容,请只对一个话题进行回复,只给出文字的回复内容,不要有内心独白: """, "heart_flow_prompt_simple", ) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 28dc39074..c8cfcea16 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -60,7 +60,7 @@ appearance = "用几句话描述外貌特征" # 外貌特征 enable_schedule_gen = true # 是否启用日程表(尚未完成) prompt_schedule_gen = "用几句话描述描述性格特点或行动规律,这个特征会用来生成日程表" schedule_doing_update_interval = 900 # 日程表更新间隔 单位秒 -schedule_temperature = 0.2 # 日程表温度,建议0.2-0.5 +schedule_temperature = 0.1 # 日程表温度,建议0.1-0.5 time_zone = "Asia/Shanghai" # 给你的机器人设置时区,可以解决运行电脑时区和国内时区不同的情况,或者模拟国外留学生日程 [platforms] # 必填项目,填写每个平台适配器提供的链接