feat:新增记忆唤醒流程
This commit is contained in:
@@ -129,7 +129,8 @@ class CycleAnalyzer:
|
||||
try:
|
||||
duration = float(line[3:].strip().split("秒")[0])
|
||||
total_duration += duration
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.error(f"解析耗时时出错: {e}")
|
||||
pass
|
||||
|
||||
# 解析工具使用
|
||||
@@ -174,7 +175,8 @@ class CycleAnalyzer:
|
||||
timestamp_str = filename.split("_", 2)[2].split(".")[0]
|
||||
timestamp = time.mktime(time.strptime(timestamp_str, "%Y%m%d_%H%M%S"))
|
||||
all_cycles.append((timestamp, stream_id, filepath))
|
||||
except:
|
||||
except Exception as e:
|
||||
logger.error(f"从文件名中提取时间戳时出错: {e}")
|
||||
continue
|
||||
|
||||
# 按时间戳排序,取最新的count个
|
||||
|
||||
@@ -130,9 +130,6 @@ def main():
|
||||
parser = argparse.ArgumentParser(description="HeartFC循环信息查看工具")
|
||||
subparsers = parser.add_subparsers(dest="command", help="子命令")
|
||||
|
||||
# 列出所有聊天流
|
||||
list_streams_parser = subparsers.add_parser("list-streams", help="列出所有聊天流")
|
||||
|
||||
# 分析聊天流
|
||||
analyze_parser = subparsers.add_parser("analyze", help="分析指定聊天流的循环信息")
|
||||
analyze_parser.add_argument("stream_id", help="聊天流ID")
|
||||
|
||||
@@ -168,9 +168,6 @@ class CycleDetail:
|
||||
filename = f"cycle_{cycle_info.cycle_id}_{timestamp}.txt"
|
||||
filepath = os.path.join(stream_dir, filename)
|
||||
|
||||
# 将CycleInfo转换为JSON格式
|
||||
cycle_data = cycle_info.to_dict()
|
||||
|
||||
# 格式化输出成易读的格式
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
# 写入基本信息
|
||||
|
||||
@@ -31,6 +31,7 @@ from src.heart_flow.observation.working_observation import WorkingObservation
|
||||
from src.plugins.heartFC_chat.info_processors.tool_processor import ToolProcessor
|
||||
from src.plugins.heartFC_chat.expressors.default_expressor import DefaultExpressor
|
||||
from src.plugins.heartFC_chat.hfc_utils import _create_empty_anchor_message
|
||||
from src.plugins.heartFC_chat.memory_activator import MemoryActivator
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
@@ -115,39 +116,6 @@ class ActionManager:
|
||||
self._original_actions_backup = None
|
||||
# logger.debug("恢复了原始动作集") # 可选日志
|
||||
|
||||
def clear_actions(self):
|
||||
"""清空所有动作"""
|
||||
self._available_actions.clear()
|
||||
|
||||
def reset_to_default(self):
|
||||
"""重置为默认动作集"""
|
||||
self._available_actions = DEFAULT_ACTIONS.copy()
|
||||
|
||||
|
||||
# 在文件开头添加自定义异常类
|
||||
class HeartFCError(Exception):
|
||||
"""麦麦聊天系统基础异常类"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PlannerError(HeartFCError):
|
||||
"""规划器异常"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ReplierError(HeartFCError):
|
||||
"""回复器异常"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SenderError(HeartFCError):
|
||||
"""发送器异常"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
async def _handle_cycle_delay(action_taken_this_cycle: bool, cycle_start_time: float, log_prefix: str):
|
||||
"""处理循环延迟"""
|
||||
@@ -202,6 +170,7 @@ class HeartFChatting:
|
||||
self.hfcloop_observation = HFCloopObservation(observe_id=self.stream_id)
|
||||
self.tool_processor = ToolProcessor(subheartflow_id=self.stream_id)
|
||||
self.working_observation = WorkingObservation(observe_id=self.stream_id)
|
||||
self.memory_activator = MemoryActivator()
|
||||
|
||||
# 日志前缀
|
||||
self.log_prefix: str = str(chat_id) # Initial default, will be updated
|
||||
@@ -439,6 +408,8 @@ class HeartFChatting:
|
||||
for observation in observations:
|
||||
logger.debug(f"{self.log_prefix} 观察信息: {observation}")
|
||||
|
||||
running_memorys = await self.memory_activator.activate_memory(observations)
|
||||
|
||||
# 记录并行任务开始时间
|
||||
parallel_start_time = time.time()
|
||||
logger.debug(f"{self.log_prefix} 开始信息处理器并行任务")
|
||||
@@ -446,16 +417,16 @@ class HeartFChatting:
|
||||
# 并行执行两个任务:思考和工具执行
|
||||
with Timer("执行 信息处理器", cycle_timers):
|
||||
# 1. 子思维思考 - 不执行工具调用
|
||||
think_task = asyncio.create_task(self.mind_processor.process_info(observations=observations))
|
||||
think_task = asyncio.create_task(self.mind_processor.process_info(observations=observations,running_memorys=running_memorys))
|
||||
logger.debug(f"{self.log_prefix} 启动子思维思考任务")
|
||||
|
||||
# 2. 工具执行器 - 专门处理工具调用
|
||||
tool_task = asyncio.create_task(self.tool_processor.process_info(observations=observations))
|
||||
tool_task = asyncio.create_task(self.tool_processor.process_info(observations=observations,running_memorys=running_memorys))
|
||||
logger.debug(f"{self.log_prefix} 启动工具执行任务")
|
||||
|
||||
# 3. 聊天信息处理器
|
||||
chatting_info_task = asyncio.create_task(
|
||||
self.chatting_info_processor.process_info(observations=observations)
|
||||
self.chatting_info_processor.process_info(observations=observations,running_memorys=running_memorys)
|
||||
)
|
||||
logger.debug(f"{self.log_prefix} 启动聊天信息处理器任务")
|
||||
|
||||
@@ -578,7 +549,7 @@ class HeartFChatting:
|
||||
return await handler(reasoning, action_data, cycle_timers)
|
||||
else: # no_reply
|
||||
return await handler(reasoning, planner_start_db_time, cycle_timers), ""
|
||||
except HeartFCError as e:
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 处理{action}时出错: {e}")
|
||||
# 出错时也重置计数器
|
||||
self._lian_xu_bu_hui_fu_ci_shu = 0
|
||||
@@ -738,12 +709,6 @@ class HeartFChatting:
|
||||
history = history[-last_n:]
|
||||
return [cycle.to_dict() for cycle in history]
|
||||
|
||||
def get_last_cycle_info(self) -> Optional[Dict[str, Any]]:
|
||||
"""获取最近一个循环的信息"""
|
||||
if self._cycle_history:
|
||||
return self._cycle_history[-1].to_dict()
|
||||
return None
|
||||
|
||||
async def _planner(self, all_plan_info: List[InfoBase], cycle_timers: dict) -> Dict[str, Any]:
|
||||
"""
|
||||
规划器 (Planner): 使用LLM根据上下文决定是否和如何回复。
|
||||
@@ -989,16 +954,13 @@ class HeartFChatting:
|
||||
success, reply_set = await self.expressor.deal_reply(
|
||||
cycle_timers=cycle_timers, action_data=reply_data, anchor_message=anchor_message, reasoning=reasoning
|
||||
)
|
||||
|
||||
|
||||
reply_text = ""
|
||||
for reply in reply_set:
|
||||
reply_text += reply
|
||||
|
||||
self._current_cycle.set_response_info(
|
||||
success=success,
|
||||
reply_text=reply_text,
|
||||
anchor_message=anchor_message,
|
||||
)
|
||||
|
||||
return success, reply_text
|
||||
|
||||
self._current_cycle.set_response_info(
|
||||
response_text=reply_text,
|
||||
)
|
||||
|
||||
return success, reply_text
|
||||
|
||||
@@ -848,7 +848,6 @@ class PromptBuilder:
|
||||
for name in action_keys:
|
||||
desc = current_available_actions[name]
|
||||
action_options_text += f"- '{name}': {desc}\n"
|
||||
example_action_key = action_keys[0] if action_keys else "no_reply"
|
||||
|
||||
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from src.plugins.chat.message import MessageRecv, BaseMessageInfo
|
||||
from src.plugins.chat.chat_stream import ChatStream
|
||||
from src.plugins.chat.message import UserInfo
|
||||
from src.common.logger_manager import get_logger
|
||||
import json
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -42,3 +43,22 @@ async def _create_empty_anchor_message(
|
||||
logger.error(f"Error getting/creating anchor message: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
return None
|
||||
|
||||
|
||||
def get_keywords_from_json(json_str: str) -> list[str]:
|
||||
# 提取JSON内容
|
||||
start = json_str.find("{")
|
||||
end = json_str.rfind("}") + 1
|
||||
if start == -1 or end == 0:
|
||||
logger.error("未找到有效的JSON内容")
|
||||
return []
|
||||
|
||||
json_content = json_str[start:end]
|
||||
|
||||
# 解析JSON
|
||||
try:
|
||||
json_data = json.loads(json_content)
|
||||
return json_data.get("keywords", [])
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"JSON解析失败: {e}")
|
||||
return []
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Any, Optional
|
||||
from typing import List, Any, Optional, Dict
|
||||
from src.heart_flow.info.info_base import InfoBase
|
||||
from src.heart_flow.chatting_observation import Observation
|
||||
from src.heart_flow.observation.observation import Observation
|
||||
from src.common.logger_manager import get_logger
|
||||
|
||||
logger = get_logger("base_processor")
|
||||
@@ -21,7 +21,7 @@ class BaseProcessor(ABC):
|
||||
|
||||
@abstractmethod
|
||||
async def process_info(
|
||||
self, infos: List[InfoBase], observations: Optional[List[Observation]] = None, **kwargs: Any
|
||||
self, infos: List[InfoBase], observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None, **kwargs: Any
|
||||
) -> List[InfoBase]:
|
||||
"""处理信息对象的抽象方法
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from src.common.logger_manager import get_logger
|
||||
from src.heart_flow.observation.chatting_observation import ChattingObservation
|
||||
from src.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||
from src.heart_flow.info.cycle_info import CycleInfo
|
||||
|
||||
from typing import Dict
|
||||
logger = get_logger("observation")
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class ChattingInfoProcessor(BaseProcessor):
|
||||
"""初始化观察处理器"""
|
||||
super().__init__()
|
||||
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, **kwargs: Any) -> List[InfoBase]:
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None, **kwargs: Any) -> List[InfoBase]:
|
||||
"""处理Observation对象
|
||||
|
||||
Args:
|
||||
|
||||
@@ -22,6 +22,7 @@ from src.plugins.heartFC_chat.info_processors.processor_utils import (
|
||||
calculate_replacement_probability,
|
||||
get_spark,
|
||||
)
|
||||
from typing import Dict
|
||||
|
||||
logger = get_logger("sub_heartflow")
|
||||
|
||||
@@ -29,6 +30,7 @@ logger = get_logger("sub_heartflow")
|
||||
def init_prompt():
|
||||
# --- Group Chat Prompt ---
|
||||
group_prompt = """
|
||||
{memory_str}
|
||||
{extra_info}
|
||||
{relation_prompt}
|
||||
你的名字是{bot_name},{prompt_personality},你现在{mood_info}
|
||||
@@ -50,6 +52,7 @@ def init_prompt():
|
||||
|
||||
# --- Private Chat Prompt ---
|
||||
private_prompt = """
|
||||
{memory_str}
|
||||
{extra_info}
|
||||
{relation_prompt}
|
||||
你的名字是{bot_name},{prompt_personality},你现在{mood_info}
|
||||
@@ -121,7 +124,7 @@ class MindProcessor(BaseProcessor):
|
||||
self.structured_info_str = "\n".join(lines)
|
||||
logger.debug(f"{self.log_prefix} 更新 structured_info_str: \n{self.structured_info_str}")
|
||||
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, *infos) -> List[dict]:
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None, *infos) -> List[dict]:
|
||||
"""处理信息对象
|
||||
|
||||
Args:
|
||||
@@ -130,14 +133,14 @@ class MindProcessor(BaseProcessor):
|
||||
Returns:
|
||||
List[dict]: 处理后的结构化信息列表
|
||||
"""
|
||||
current_mind = await self.do_thinking_before_reply(observations)
|
||||
current_mind = await self.do_thinking_before_reply(observations,running_memorys)
|
||||
|
||||
mind_info = MindInfo()
|
||||
mind_info.set_current_mind(current_mind)
|
||||
|
||||
return [mind_info]
|
||||
|
||||
async def do_thinking_before_reply(self, observations: Optional[List[Observation]] = None):
|
||||
async def do_thinking_before_reply(self, observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None):
|
||||
"""
|
||||
在回复前进行思考,生成内心想法并收集工具调用结果
|
||||
|
||||
@@ -166,6 +169,12 @@ class MindProcessor(BaseProcessor):
|
||||
f"{self.log_prefix} 当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}"
|
||||
)
|
||||
|
||||
memory_str = ""
|
||||
if running_memorys:
|
||||
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
|
||||
for running_memory in running_memorys:
|
||||
memory_str += f"{running_memory['topic']}: {running_memory['content']}\n"
|
||||
|
||||
# ---------- 1. 准备基础数据 ----------
|
||||
# 获取现有想法和情绪状态
|
||||
previous_mind = self.current_mind if self.current_mind else ""
|
||||
@@ -210,6 +219,7 @@ class MindProcessor(BaseProcessor):
|
||||
logger.debug(f"{self.log_prefix} 使用{'群聊' if is_group_chat else '私聊'}思考模板")
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async(template_name)).format(
|
||||
memory_str=memory_str,
|
||||
extra_info=self.structured_info_str,
|
||||
prompt_personality=prompt_personality,
|
||||
relation_prompt=relation_prompt,
|
||||
|
||||
@@ -5,11 +5,11 @@ import time
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.individuality.individuality import Individuality
|
||||
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
from src.do_tool.tool_use import ToolUser
|
||||
from src.tools.tool_use import ToolUser
|
||||
from src.plugins.utils.json_utils import process_llm_tool_calls
|
||||
from src.plugins.person_info.relationship_manager import relationship_manager
|
||||
from .base_processor import BaseProcessor
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Dict
|
||||
from src.heart_flow.observation.observation import Observation
|
||||
from src.heart_flow.observation.working_observation import WorkingObservation
|
||||
from src.heart_flow.info.structured_info import StructuredInfo
|
||||
@@ -30,6 +30,8 @@ def init_prompt():
|
||||
你当前的额外信息:
|
||||
{extra_info}
|
||||
|
||||
{memory_str}
|
||||
|
||||
你的心情是:{mood_info}
|
||||
|
||||
{relation_prompt}
|
||||
@@ -61,7 +63,7 @@ class ToolProcessor(BaseProcessor):
|
||||
)
|
||||
self.structured_info = []
|
||||
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, *infos) -> List[dict]:
|
||||
async def process_info(self, observations: Optional[List[Observation]] = None, running_memorys: Optional[List[Dict]] = None, *infos) -> List[dict]:
|
||||
"""处理信息对象
|
||||
|
||||
Args:
|
||||
@@ -74,7 +76,7 @@ class ToolProcessor(BaseProcessor):
|
||||
if observations:
|
||||
for observation in observations:
|
||||
if isinstance(observation, ChattingObservation):
|
||||
result, used_tools, prompt = await self.execute_tools(observation)
|
||||
result, used_tools, prompt = await self.execute_tools(observation, running_memorys)
|
||||
|
||||
# 更新WorkingObservation中的结构化信息
|
||||
for observation in observations:
|
||||
@@ -92,7 +94,7 @@ class ToolProcessor(BaseProcessor):
|
||||
|
||||
return [structured_info]
|
||||
|
||||
async def execute_tools(self, observation: ChattingObservation):
|
||||
async def execute_tools(self, observation: ChattingObservation, running_memorys: Optional[List[Dict]] = None):
|
||||
"""
|
||||
并行执行工具,返回结构化信息
|
||||
|
||||
@@ -112,23 +114,21 @@ class ToolProcessor(BaseProcessor):
|
||||
tool_instance = ToolUser()
|
||||
tools = tool_instance._define_tools()
|
||||
|
||||
logger.debug(f"observation: {observation}")
|
||||
logger.debug(f"observation.chat_target_info: {observation.chat_target_info}")
|
||||
logger.debug(f"observation.is_group_chat: {observation.is_group_chat}")
|
||||
logger.debug(f"observation.person_list: {observation.person_list}")
|
||||
# logger.debug(f"observation: {observation}")
|
||||
# logger.debug(f"observation.chat_target_info: {observation.chat_target_info}")
|
||||
# logger.debug(f"observation.is_group_chat: {observation.is_group_chat}")
|
||||
# logger.debug(f"observation.person_list: {observation.person_list}")
|
||||
|
||||
is_group_chat = observation.is_group_chat
|
||||
if not is_group_chat:
|
||||
chat_target_name = (
|
||||
observation.chat_target_info.get("person_name")
|
||||
or observation.chat_target_info.get("user_nickname")
|
||||
or "对方"
|
||||
)
|
||||
else:
|
||||
chat_target_name = "群聊"
|
||||
|
||||
chat_observe_info = observation.get_observe_info()
|
||||
person_list = observation.person_list
|
||||
|
||||
memory_str = ""
|
||||
if running_memorys:
|
||||
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
|
||||
for running_memory in running_memorys:
|
||||
memory_str += f"{running_memory['topic']}: {running_memory['content']}\n"
|
||||
|
||||
# 构建关系信息
|
||||
relation_prompt = "【关系信息】\n"
|
||||
@@ -148,6 +148,7 @@ class ToolProcessor(BaseProcessor):
|
||||
# 构建专用于工具调用的提示词
|
||||
prompt = await global_prompt_manager.format_prompt(
|
||||
"tool_executor_prompt",
|
||||
memory_str=memory_str,
|
||||
extra_info="extra_structured_info",
|
||||
chat_observe_info=chat_observe_info,
|
||||
# chat_target_name=chat_target_name,
|
||||
|
||||
70
src/plugins/heartFC_chat/memory_activator.py
Normal file
70
src/plugins/heartFC_chat/memory_activator.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from src.heart_flow.observation.chatting_observation import ChattingObservation
|
||||
from src.heart_flow.observation.working_observation import WorkingObservation
|
||||
from src.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||
from src.plugins.models.utils_model import LLMRequest
|
||||
from src.config.config import global_config
|
||||
from src.common.logger_manager import get_logger
|
||||
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
from src.plugins.heartFC_chat.hfc_utils import get_keywords_from_json
|
||||
from datetime import datetime
|
||||
from src.plugins.memory_system.Hippocampus import HippocampusManager
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
logger = get_logger("memory_activator")
|
||||
|
||||
Prompt(
|
||||
"""
|
||||
你是一个记忆分析器,你需要根据以下信息来进行会议
|
||||
以下是一场聊天中的信息,请根据这些信息,总结出几个关键词作为记忆回忆的触发词
|
||||
|
||||
{obs_info_text}
|
||||
|
||||
请输出一个json格式,包含以下字段:
|
||||
{
|
||||
"keywords": ["关键词1", "关键词2", "关键词3",......]
|
||||
}
|
||||
不要输出其他多余内容,只输出json格式就好
|
||||
""",
|
||||
"memory_activator_prompt",
|
||||
)
|
||||
|
||||
|
||||
class MemoryActivator:
|
||||
def __init__(self):
|
||||
self.summart_model = LLMRequest(
|
||||
model=global_config.llm_observation, temperature=0.7, max_tokens=300, request_type="chat_observation"
|
||||
)
|
||||
self.running_memory = []
|
||||
|
||||
async def activate_memory(self, observations) -> List[Dict]:
|
||||
obs_info_text = ""
|
||||
for observation in observations:
|
||||
if isinstance(observation, ChattingObservation):
|
||||
obs_info_text += observation.get_observe_info()
|
||||
elif isinstance(observation, WorkingObservation):
|
||||
working_info = observation.get_observe_info()
|
||||
for working_info_item in working_info:
|
||||
obs_info_text += f"{working_info_item['type']}: {working_info_item['content']}\n"
|
||||
elif isinstance(observation, HFCloopObservation):
|
||||
obs_info_text += observation.get_observe_info()
|
||||
|
||||
prompt = global_prompt_manager.format_prompt("memory_activator_prompt", obs_info_text=obs_info_text)
|
||||
|
||||
response = self.summart_model.generate_response(prompt)
|
||||
|
||||
keywords = get_keywords_from_json(response)
|
||||
|
||||
# 调用记忆系统获取相关记忆
|
||||
related_memory = await HippocampusManager.get_instance().get_memory_from_topic(
|
||||
valid_keywords=keywords, max_memory_num=3, max_memory_length=2, max_depth=3
|
||||
)
|
||||
|
||||
logger.debug(f"获取到的记忆: {related_memory}")
|
||||
|
||||
if related_memory:
|
||||
for topic, memory in related_memory:
|
||||
self.running_memory.append({"topic": topic, "content": memory, "timestamp": datetime.now().isoformat()})
|
||||
logger.debug(f"添加新记忆: {topic} - {memory}")
|
||||
|
||||
return self.running_memory
|
||||
Reference in New Issue
Block a user