feat:成功融合reasoning和HFC,由主心流统一调控

This commit is contained in:
SengokuCola
2025-04-22 02:01:52 +08:00
parent 5b894f7f59
commit 55254549be
19 changed files with 708 additions and 785 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -79,4 +79,16 @@ await heartflow.heartflow_start_working()
1. 子心流会在长时间不活跃后自动清理
2. 需要合理配置更新间隔以平衡性能和响应速度
3. 观察系统会限制消息处理数量以避免过载
3. 观察系统会限制消息处理数量以避免过载
更新:
把聊天控制移动到心流下吧
首先心流要根据日程以及当前状况判定总体状态MaiStateInfo
然后根据每个子心流的运行情况给子心流分配聊天资源ChatStateInfoABSENT CHAT 或者 FOCUS
子心流负责根据状态进行执行
1.将interest.py进行拆分class InterestChatting 将会在 sub_heartflow中声明每个sub_heartflow都会所属一个InterestChatting
class InterestManager 将会在heartflow中声明成为heartflow的一个组件伴随heartflow产生

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

11
src/heart_flow/Update.md Normal file
View File

@@ -0,0 +1,11 @@
更新:
把聊天控制移动到心流下吧
首先心流要根据日程以及当前状况判定总体状态MaiStateInfo
然后根据每个子心流的运行情况给子心流分配聊天资源ChatStateInfoABSENT CHAT 或者 FOCUS
子心流负责根据状态进行执行
1.将interest.py进行拆分class InterestChatting 将会在 sub_heartflow中声明每个sub_heartflow都会所属一个InterestChatting
class InterestManager 将会在heartflow中声明成为heartflow的一个组件伴随heartflow产生

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -5,12 +5,16 @@ from src.config.config import global_config
from src.plugins.schedule.schedule_generator import bot_schedule
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
import asyncio
from src.common.logger import get_module_logger, LogConfig, HEARTFLOW_STYLE_CONFIG # noqa: E402
from src.common.logger import get_module_logger, LogConfig, HEARTFLOW_STYLE_CONFIG # 修改
from src.individuality.individuality import Individuality
import time
import random
from typing import Dict, Any, Optional
import traceback
import enum
import os # 新增
import json # 新增
from src.plugins.chat.chat_stream import chat_manager # 新增
heartflow_config = LogConfig(
# 使用海马体专用样式
@@ -41,80 +45,262 @@ def init_prompt():
Prompt(prompt, "mind_summary_prompt")
class CurrentState:
# --- 新增:从 interest.py 移动过来的常量 ---
LOG_DIRECTORY = "logs/interest"
HISTORY_LOG_FILENAME = "interest_history.log"
CLEANUP_INTERVAL_SECONDS = 1200 # 清理任务运行间隔 (例如20分钟) - 保持与 interest.py 一致
INACTIVE_THRESHOLD_SECONDS = 1200 # 不活跃时间阈值 (例如20分钟) - 保持与 interest.py 一致
LOG_INTERVAL_SECONDS = 3 # 日志记录间隔 (例如3秒) - 保持与 interest.py 一致
# --- 结束新增常量 ---
# 新增 ChatStatus 枚举
class MaiState(enum.Enum):
"""
聊天状态:
OFFLINE: 不在线:回复概率极低,不会进行任何聊天
PEEKING: 看一眼手机:回复概率较低,会进行一些普通聊天
NORMAL_CHAT: 正常聊天:回复概率较高,会进行一些普通聊天和少量的专注聊天
FOCUSED_CHAT: 专注聊天:回复概率极高,会进行专注聊天和少量的普通聊天
"""
OFFLINE = "不在线"
PEEKING = "看一眼手机"
NORMAL_CHAT = "正常聊天"
FOCUSED_CHAT = "专注聊天"
def get_normal_chat_max_num(self):
if self == MaiState.OFFLINE:
return 0
elif self == MaiState.PEEKING:
return 1
elif self == MaiState.NORMAL_CHAT:
return 3
elif self == MaiState.FOCUSED_CHAT:
return 2
def get_focused_chat_max_num(self):
if self == MaiState.OFFLINE:
return 0
elif self == MaiState.PEEKING:
return 0
elif self == MaiState.NORMAL_CHAT:
return 1
elif self == MaiState.FOCUSED_CHAT:
return 2
class MaiStateInfo:
def __init__(self):
self.current_state_info = ""
self.chat_status = "IDLE"
# 使用枚举类型初始化状态,默认为不在线
self.mai_status: MaiState = MaiState.OFFLINE
self.normal_chatting = []
self.focused_chatting = []
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()
# 新增更新聊天状态的方法
def update_mai_status(self, new_status: MaiState):
"""更新聊天状态"""
if isinstance(new_status, MaiState):
self.mai_status = new_status
logger.info(f"麦麦状态更新为: {self.mai_status.value}")
else:
logger.warning(f"尝试设置无效的麦麦状态: {new_status}")
class Heartflow:
def __init__(self):
self.current_mind = "你什么也没想"
self.past_mind = []
self.current_state: CurrentState = CurrentState()
self.current_state: MaiStateInfo = MaiStateInfo()
self.llm_model = LLMRequest(
model=global_config.llm_heartflow, temperature=0.6, max_tokens=1000, request_type="heart_flow"
)
self._subheartflows: Dict[Any, SubHeartflow] = {}
async def _cleanup_inactive_subheartflows(self):
"""定期清理不活跃的子心流"""
# --- 新增:日志和清理相关属性 (从 InterestManager 移动) ---
self._history_log_file_path = os.path.join(LOG_DIRECTORY, HISTORY_LOG_FILENAME)
self._ensure_log_directory() # 初始化时确保目录存在
self._cleanup_task: Optional[asyncio.Task] = None
self._logging_task: Optional[asyncio.Task] = None
# 注意:衰减任务 (_decay_task) 不再需要,衰减在 SubHeartflow 的 InterestChatting 内部处理
# --- 结束新增属性 ---
def _ensure_log_directory(self): # 新增方法 (从 InterestManager 移动)
"""确保日志目录存在"""
# 移除 try-except 块,根据用户要求
os.makedirs(LOG_DIRECTORY, exist_ok=True)
logger.info(f"Log directory '{LOG_DIRECTORY}' ensured.")
# except OSError as e:
# logger.error(f"Error creating log directory '{LOG_DIRECTORY}': {e}")
async def _periodic_cleanup_task(
self, interval_seconds: int, max_age_seconds: int
): # 新增方法 (从 InterestManager 移动和修改)
"""后台清理任务的异步函数"""
while True:
current_time = time.time()
inactive_subheartflows_ids = [] # 修改变量名以清晰表示存储的是ID
await asyncio.sleep(interval_seconds)
logger.info(f"[Heartflow] 运行定期清理 (间隔: {interval_seconds}秒)...")
self.cleanup_inactive_subheartflows(max_age_seconds=max_age_seconds) # 调用 Heartflow 自己的清理方法
# 检查所有子心流
# 使用 list(self._subheartflows.items()) 避免在迭代时修改字典
for subheartflow_id, subheartflow in list(self._subheartflows.items()):
if (
current_time - subheartflow.last_active_time > global_config.sub_heart_flow_stop_time
): # 10分钟 = 600秒
logger.info(f"发现不活跃的子心流: {subheartflow_id}, 准备清理。")
# 1. 标记子心流让其后台任务停止
subheartflow.should_stop = True
# 2. 将ID添加到待清理列表
inactive_subheartflows_ids.append(subheartflow_id)
async def _periodic_log_task(self, interval_seconds: int): # 新增方法 (从 InterestManager 移动和修改)
"""后台日志记录任务的异步函数 (记录所有子心流的兴趣历史数据)"""
while True:
await asyncio.sleep(interval_seconds)
try:
current_timestamp = time.time()
all_interest_states = self.get_all_interest_states() # 获取所有子心流的兴趣状态
# 清理不活跃的子心流 (从字典中移除)
for subheartflow_id in inactive_subheartflows_ids:
if subheartflow_id in self._subheartflows:
del self._subheartflows[subheartflow_id]
logger.info(f"已从主心流移除子心流: {subheartflow_id}")
else:
logger.warning(f"尝试移除子心流 {subheartflow_id} 时发现其已被移除。")
# 以追加模式打开历史日志文件
# 移除 try-except IO 块,根据用户要求
with open(self._history_log_file_path, "a", encoding="utf-8") as f:
count = 0
# 创建 items 快照以安全迭代
items_snapshot = list(all_interest_states.items())
for stream_id, state in items_snapshot:
# 从 chat_manager 获取 group_name
group_name = stream_id # 默认值
try:
chat_stream = chat_manager.get_stream(stream_id)
if chat_stream and chat_stream.group_info:
group_name = chat_stream.group_info.group_name
elif chat_stream and not chat_stream.group_info: # 处理私聊
group_name = (
f"私聊_{chat_stream.user_info.user_nickname}"
if chat_stream.user_info
else stream_id
)
except Exception:
# 不记录警告,避免刷屏,使用默认 stream_id 即可
# logger.warning(f"Could not get group name for stream_id {stream_id}: {e}")
pass # 静默处理
await asyncio.sleep(30) # 每分钟检查一次
log_entry = {
"timestamp": round(current_timestamp, 2),
"stream_id": stream_id,
"interest_level": state.get("interest_level", 0.0), # 使用 get 获取,提供默认值
"group_name": group_name,
"reply_probability": state.get("current_reply_probability", 0.0), # 使用 get 获取
"is_above_threshold": state.get("is_above_threshold", False), # 使用 get 获取
}
# 将每个条目作为单独的 JSON 行写入
f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
count += 1
# logger.debug(f"[Heartflow] Successfully appended {count} interest history entries to {self._history_log_file_path}")
async def _sub_heartflow_update(self):
# except IOError as e:
# logger.error(f"[Heartflow] Error writing interest history log to {self._history_log_file_path}: {e}")
except Exception as e: # 保留对其他异常的捕获
logger.error(f"[Heartflow] Unexpected error during periodic history logging: {e}")
logger.error(traceback.format_exc()) # 记录 traceback
def get_all_interest_states(self) -> Dict[str, Dict]: # 新增方法
"""获取所有活跃子心流的当前兴趣状态"""
states = {}
# 创建副本以避免在迭代时修改字典
items_snapshot = list(self._subheartflows.items())
for stream_id, subheartflow in items_snapshot:
try:
# 从 SubHeartflow 获取其 InterestChatting 的状态
states[stream_id] = subheartflow.get_interest_state()
except Exception as e:
logger.warning(f"[Heartflow] Error getting interest state for subheartflow {stream_id}: {e}")
return states
def cleanup_inactive_subheartflows(self, max_age_seconds=INACTIVE_THRESHOLD_SECONDS): # 修改此方法以使用兴趣时间
"""
清理长时间不活跃的子心流记录 (基于兴趣交互时间)
max_age_seconds: 超过此时间未通过兴趣系统交互的将被清理
"""
current_time = time.time()
keys_to_remove = []
_initial_count = len(self._subheartflows)
# 创建副本以避免在迭代时修改字典
items_snapshot = list(self._subheartflows.items())
for subheartflow_id, subheartflow in items_snapshot:
should_remove = False
reason = ""
# 检查 InterestChatting 的最后交互时间
last_interaction = subheartflow.interest_chatting.last_interaction_time
if max_age_seconds is not None and (current_time - last_interaction) > max_age_seconds:
should_remove = True
reason = (
f"interest inactive time ({current_time - last_interaction:.0f}s) > max age ({max_age_seconds}s)"
)
if should_remove:
keys_to_remove.append(subheartflow_id)
stream_name = chat_manager.get_stream_name(subheartflow_id) or subheartflow_id # 获取流名称
logger.debug(f"[Heartflow] Marking stream {stream_name} for removal. Reason: {reason}")
# 标记子心流让其后台任务停止 (如果其后台任务还在运行)
subheartflow.should_stop = True
if keys_to_remove:
logger.info(f"[Heartflow] 清理识别到 {len(keys_to_remove)} 个不活跃的流。")
for key in keys_to_remove:
if key in self._subheartflows:
# 尝试取消子心流的后台任务
task_to_cancel = self._subheartflows[key].task
if task_to_cancel and not task_to_cancel.done():
task_to_cancel.cancel()
logger.debug(f"[Heartflow] Cancelled background task for subheartflow {key}")
# 从字典中删除
del self._subheartflows[key]
stream_name = chat_manager.get_stream_name(key) or key # 获取流名称
logger.debug(f"[Heartflow] 移除了流: {stream_name}")
final_count = len(self._subheartflows) # 直接获取当前长度
logger.info(f"[Heartflow] 清理完成。移除了 {len(keys_to_remove)} 个流。当前数量: {final_count}")
else:
# logger.info(f"[Heartflow] 清理完成。没有流符合移除条件。当前数量: {initial_count}") # 减少日志噪音
pass
async def _sub_heartflow_update(self): # 这个任务目前作用不大,可以考虑移除或赋予新职责
while True:
# 检查是否存在子心流
if not self._subheartflows:
# logger.info("当前没有子心流,等待新的子心流创建...")
await asyncio.sleep(30) # 每分钟检查一次是否有新的子心流
await asyncio.sleep(30) # 短暂休眠
continue
# await self.do_a_thinking()
# await asyncio.sleep(global_config.heart_flow_update_interval * 3) # 5分钟思考一次
# 当前无实际操作,只是等待
await asyncio.sleep(300)
async def heartflow_start_working(self):
# 启动清理任务
asyncio.create_task(self._cleanup_inactive_subheartflows())
# 启动清理任务 (使用新的 periodic_cleanup_task)
if self._cleanup_task is None or self._cleanup_task.done():
self._cleanup_task = asyncio.create_task(
self._periodic_cleanup_task(
interval_seconds=CLEANUP_INTERVAL_SECONDS,
max_age_seconds=INACTIVE_THRESHOLD_SECONDS,
)
)
logger.info(
f"[Heartflow] 已创建定期清理任务。间隔: {CLEANUP_INTERVAL_SECONDS}s, 不活跃阈值: {INACTIVE_THRESHOLD_SECONDS}s"
)
else:
logger.warning("[Heartflow] 跳过创建清理任务: 任务已在运行或存在。")
# 启动子心流更新任务
asyncio.create_task(self._sub_heartflow_update())
# 启动日志任务 (使用新的 periodic_log_task)
if self._logging_task is None or self._logging_task.done():
self._logging_task = asyncio.create_task(self._periodic_log_task(interval_seconds=LOG_INTERVAL_SECONDS))
logger.info(f"[Heartflow] 已创建定期日志任务。间隔: {LOG_INTERVAL_SECONDS}s")
else:
logger.warning("[Heartflow] 跳过创建日志任务: 任务已在运行或存在。")
# (可选) 启动旧的子心流更新任务,如果它还有用的话
# asyncio.create_task(self._sub_heartflow_update())
@staticmethod
async def _update_current_state():
@@ -133,146 +319,149 @@ class Heartflow:
prompt_personality += personality_core
personality_sides = individuality.personality.personality_sides
random.shuffle(personality_sides)
prompt_personality += f",{personality_sides[0]}"
# 检查列表是否为空
if 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]}"
# 检查列表是否为空
if identity_detail:
random.shuffle(identity_detail)
prompt_personality += f",{identity_detail[0]}"
personality_info = prompt_personality
current_thinking_info = self.current_mind
mood_info = self.current_state.mood
related_memory_info = "memory"
related_memory_info = "memory" # TODO: 替换为实际的记忆获取逻辑
try:
sub_flows_info = await self.get_all_subheartflows_minds()
sub_flows_info = await self.get_all_subheartflows_minds_summary() # 修改为调用汇总方法
except Exception as e:
logger.error(f"获取子心流想法失败: {e}")
return
logger.error(f"[Heartflow] 获取子心流想法汇总失败: {e}")
logger.error(traceback.format_exc())
sub_flows_info = "(获取子心流想法时出错)" # 提供默认值
schedule_info = bot_schedule.get_current_num_task(num=4, time_info=True)
# prompt = ""
# prompt += f"你刚刚在做的事情是:{schedule_info}\n"
# prompt += f"{personality_info}\n"
# prompt += f"你想起来{related_memory_info}。"
# prompt += f"刚刚你的主要想法是{current_thinking_info}。"
# prompt += f"你还有一些小想法,因为你在参加不同的群聊天,这是你正在做的事情:{sub_flows_info}\n"
# prompt += f"你现在{mood_info}。"
# prompt += "现在你接下去继续思考,产生新的想法,但是要基于原有的主要想法,不要分点输出,"
# prompt += "输出连贯的内心独白,不要太长,但是记得结合上述的消息,关注新内容:"
prompt = (await global_prompt_manager.get_prompt_async("thinking_prompt")).format(
schedule_info, personality_info, related_memory_info, current_thinking_info, sub_flows_info, mood_info
schedule_info=schedule_info, # 使用关键字参数确保正确格式化
personality_info=personality_info,
related_memory_info=related_memory_info,
current_thinking_info=current_thinking_info,
sub_flows_info=sub_flows_info,
mood_info=mood_info,
)
try:
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
if not response:
logger.warning("[Heartflow] 内心独白 LLM 返回空结果。")
response = "(暂时没什么想法...)" # 提供默认想法
self.update_current_mind(response) # 更新主心流想法
logger.info(f"麦麦的总体脑内状态:{self.current_mind}")
# 更新所有子心流的主心流信息
items_snapshot = list(self._subheartflows.items()) # 创建快照
for _, subheartflow in items_snapshot:
subheartflow.main_heartflow_info = response
except Exception as e:
logger.error(f"内心独白获取失败: {e}")
return
self.update_current_mind(response)
self.current_mind = response
logger.info(f"麦麦的总体脑内状态:{self.current_mind}")
# logger.info("麦麦想了想,当前活动:")
# await bot_schedule.move_doing(self.current_mind)
for _, subheartflow in self._subheartflows.items():
subheartflow.main_heartflow_info = response
logger.error(f"[Heartflow] 内心独白获取失败: {e}")
logger.error(traceback.format_exc())
# 此处不返回,允许程序继续执行,但主心流想法未更新
def update_current_mind(self, response):
self.past_mind.append(self.current_mind)
self.current_mind = response
async def get_all_subheartflows_minds(self):
sub_minds = ""
for _, subheartflow in self._subheartflows.items():
sub_minds += subheartflow.current_mind
async def get_all_subheartflows_minds_summary(self): # 重命名并修改
"""获取所有子心流的当前想法,并进行汇总"""
sub_minds_list = []
# 创建快照
items_snapshot = list(self._subheartflows.items())
for _, subheartflow in items_snapshot:
sub_minds_list.append(subheartflow.current_mind)
return await self.minds_summary(sub_minds)
if not sub_minds_list:
return "(当前没有活跃的子心流想法)"
minds_str = "\n".join([f"- {mind}" for mind in sub_minds_list]) # 格式化为列表
# 调用 LLM 进行汇总
return await self.minds_summary(minds_str)
async def minds_summary(self, minds_str):
"""使用 LLM 汇总子心流的想法字符串"""
# 开始构建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]}"
prompt_personality += individuality.personality.personality_core
if individuality.personality.personality_sides:
prompt_personality += f",{random.choice(individuality.personality.personality_sides)}" # 随机选一个
if individuality.identity.identity_detail:
prompt_personality += f",{random.choice(individuality.identity.identity_detail)}" # 随机选一个
personality_info = prompt_personality
mood_info = self.current_state.mood
bot_name = global_config.BOT_NICKNAME # 使用全局配置中的机器人昵称
# prompt = ""
# prompt += f"{personality_info}\n"
# prompt += f"现在{global_config.BOT_NICKNAME}的想法是:{self.current_mind}\n"
# prompt += f"现在{global_config.BOT_NICKNAME}在qq群里进行聊天聊天的话题如下{minds_str}\n"
# prompt += f"你现在{mood_info}\n"
# prompt += """现在请你总结这些聊天内容,注意关注聊天内容对原有的想法的影响,输出连贯的内心独白
# 不要太长,但是记得结合上述的消息,要记得你的人设,关注新内容:"""
prompt = (await global_prompt_manager.get_prompt_async("mind_summary_prompt")).format(
personality_info, global_config.BOT_NICKNAME, self.current_mind, minds_str, mood_info
personality_info=personality_info, # 使用关键字参数
bot_name=bot_name,
current_mind=self.current_mind,
minds_str=minds_str,
mood_info=mood_info,
)
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
return response
try:
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
if not response:
logger.warning("[Heartflow] 想法汇总 LLM 返回空结果。")
return "(想法汇总失败...)"
return response
except Exception as e:
logger.error(f"[Heartflow] 想法汇总失败: {e}")
logger.error(traceback.format_exc())
return "(想法汇总时发生错误...)"
async def create_subheartflow(self, subheartflow_id: Any) -> Optional[SubHeartflow]:
"""
获取或创建一个新的SubHeartflow实例。
如果实例已存在,则直接返回。
如果不存在,则创建实例、观察对象、启动后台任务,并返回新实例。
创建过程中发生任何错误将返回 None。
Args:
subheartflow_id: 用于标识子心流的ID (例如群聊ID)。
Returns:
对应的 SubHeartflow 实例,如果创建失败则返回 None。
(主要逻辑不变InterestChatting 现在在 SubHeartflow 内部创建)
"""
# 检查是否已存在
existing_subheartflow = self._subheartflows.get(subheartflow_id)
if existing_subheartflow:
logger.debug(f"返回已存在的 subheartflow: {subheartflow_id}")
# 如果已存在,确保其 last_active_time 更新 (如果需要的话)
# existing_subheartflow.last_active_time = time.time() # 移除,活跃时间由实际操作更新
# logger.debug(f"[Heartflow] 返回已存在的 subheartflow: {subheartflow_id}")
return existing_subheartflow
# 如果不存在,则创建新的
logger.info(f"尝试创建新的 subheartflow: {subheartflow_id}")
logger.info(f"[Heartflow] 尝试创建新的 subheartflow: {subheartflow_id}")
try:
# 创建 SubHeartflow它内部会创建 InterestChatting
subheartflow = SubHeartflow(subheartflow_id)
# 创建并初始化观察对象
logger.debug(f"{subheartflow_id} 创建 observation")
logger.debug(f"[Heartflow] {subheartflow_id} 创建 observation")
observation = ChattingObservation(subheartflow_id)
await observation.initialize() # 等待初始化完成
await observation.initialize()
subheartflow.add_observation(observation)
logger.debug(f"{subheartflow_id} 添加 observation 成功")
logger.debug(f"[Heartflow] {subheartflow_id} 添加 observation 成功")
# 创建并存储后台任务
# 创建并存储后台任务 (SubHeartflow 自己的后台任务)
subheartflow.task = asyncio.create_task(subheartflow.subheartflow_start_working())
logger.debug(f"{subheartflow_id} 创建后台任务成功")
logger.debug(f"[Heartflow] {subheartflow_id} 创建后台任务成功")
# 添加到管理字典
self._subheartflows[subheartflow_id] = subheartflow
logger.info(f"添加 subheartflow {subheartflow_id} 成功")
logger.info(f"[Heartflow] 添加 subheartflow {subheartflow_id} 成功")
return subheartflow
except Exception as e:
# 记录详细错误信息
logger.error(f"创建 subheartflow {subheartflow_id} 失败: {e}")
logger.error(traceback.format_exc()) # 记录完整的 traceback
# 考虑是否需要更具体的错误处理或资源清理逻辑
logger.error(f"[Heartflow] 创建 subheartflow {subheartflow_id} 失败: {e}")
logger.error(traceback.format_exc())
return None
def get_subheartflow(self, observe_chat_id: Any) -> Optional[SubHeartflow]:

View File

@@ -4,21 +4,20 @@ from src.plugins.moods.moods import MoodManager
from src.plugins.models.utils_model import LLMRequest
from src.config.config import global_config
import time
from typing import Optional, List
from typing import Optional, List, Dict
import traceback
from src.plugins.chat.utils import parse_text_timestamps
# from src.plugins.schedule.schedule_generator import bot_schedule
# from src.plugins.memory_system.Hippocampus import HippocampusManager
import enum
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.individuality.individuality import Individuality
import random
from src.plugins.person_info.relationship_manager import relationship_manager
from ..plugins.utils.prompt_builder import Prompt, global_prompt_manager
from src.plugins.chat.message import MessageRecv
import math
# 定义常量 (从 interest.py 移动过来)
MAX_INTEREST = 15.0
subheartflow_config = LogConfig(
# 使用海马体专用样式
@@ -27,6 +26,12 @@ subheartflow_config = LogConfig(
)
logger = get_module_logger("subheartflow", config=subheartflow_config)
interest_log_config = LogConfig(
console_format=SUB_HEARTFLOW_STYLE_CONFIG["console_format"],
file_format=SUB_HEARTFLOW_STYLE_CONFIG["file_format"],
)
interest_logger = get_module_logger("InterestChatting", config=interest_log_config)
def init_prompt():
prompt = ""
@@ -48,16 +53,166 @@ def init_prompt():
Prompt(prompt, "sub_heartflow_prompt_before")
class CurrentState:
class ChatState(enum.Enum):
ABSENT = "不参与"
CHAT = "闲聊"
FOCUSED = "专注"
class ChatStateInfo:
def __init__(self):
self.willing = 0
self.current_state_info = ""
self.chat_status: ChatState = ChatState.ABSENT
self.mood_manager = MoodManager()
self.mood = self.mood_manager.get_prompt()
def update_current_state_info(self):
self.current_state_info = self.mood_manager.get_current_mood()
def update_chat_state_info(self):
self.chat_state_info = self.mood_manager.get_current_mood()
base_reply_probability = 0.05
probability_increase_rate_per_second = 0.08
max_reply_probability = 1
class InterestChatting:
def __init__(
self,
decay_rate=global_config.default_decay_rate_per_second,
max_interest=MAX_INTEREST,
trigger_threshold=global_config.reply_trigger_threshold,
base_reply_probability=base_reply_probability,
increase_rate=probability_increase_rate_per_second,
decay_factor=global_config.probability_decay_factor_per_second,
max_probability=max_reply_probability,
):
self.interest_level: float = 0.0
self.last_update_time: float = time.time()
self.decay_rate_per_second: float = decay_rate
self.max_interest: float = max_interest
self.last_interaction_time: float = self.last_update_time
self.trigger_threshold: float = trigger_threshold
self.base_reply_probability: float = base_reply_probability
self.probability_increase_rate: float = increase_rate
self.probability_decay_factor: float = decay_factor
self.max_reply_probability: float = max_probability
self.current_reply_probability: float = 0.0
self.is_above_threshold: bool = False
self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
def add_interest_dict(self, message: MessageRecv, interest_value: float, is_mentioned: bool):
self.interest_dict[message.message_info.message_id] = (message, interest_value, is_mentioned)
self.last_interaction_time = time.time()
def _calculate_decay(self, current_time: float):
time_delta = current_time - self.last_update_time
if time_delta > 0:
old_interest = self.interest_level
if self.interest_level < 1e-9:
self.interest_level = 0.0
else:
if self.decay_rate_per_second <= 0:
interest_logger.warning(
f"InterestChatting encountered non-positive decay rate: {self.decay_rate_per_second}. Setting interest to 0."
)
self.interest_level = 0.0
elif self.interest_level < 0:
interest_logger.warning(
f"InterestChatting encountered negative interest level: {self.interest_level}. Setting interest to 0."
)
self.interest_level = 0.0
else:
try:
decay_factor = math.pow(self.decay_rate_per_second, time_delta)
self.interest_level *= decay_factor
except ValueError as e:
interest_logger.error(
f"Math error during decay calculation: {e}. Rate: {self.decay_rate_per_second}, Delta: {time_delta}, Level: {self.interest_level}. Setting interest to 0."
)
self.interest_level = 0.0
if old_interest != self.interest_level:
self.last_update_time = current_time
def _update_reply_probability(self, current_time: float):
time_delta = current_time - self.last_update_time
if time_delta <= 0:
return
currently_above = self.interest_level >= self.trigger_threshold
if currently_above:
if not self.is_above_threshold:
self.current_reply_probability = self.base_reply_probability
interest_logger.debug(
f"兴趣跨过阈值 ({self.trigger_threshold}). 概率重置为基础值: {self.base_reply_probability:.4f}"
)
else:
increase_amount = self.probability_increase_rate * time_delta
self.current_reply_probability += increase_amount
self.current_reply_probability = min(self.current_reply_probability, self.max_reply_probability)
else:
if 0 < self.probability_decay_factor < 1:
decay_multiplier = math.pow(self.probability_decay_factor, time_delta)
self.current_reply_probability *= decay_multiplier
if self.current_reply_probability < 1e-6:
self.current_reply_probability = 0.0
elif self.probability_decay_factor <= 0:
if self.current_reply_probability > 0:
interest_logger.warning(f"无效的衰减因子 ({self.probability_decay_factor}). 设置概率为0.")
self.current_reply_probability = 0.0
self.current_reply_probability = max(self.current_reply_probability, 0.0)
self.is_above_threshold = currently_above
def increase_interest(self, current_time: float, value: float):
self._update_reply_probability(current_time)
self._calculate_decay(current_time)
self.interest_level += value
self.interest_level = min(self.interest_level, self.max_interest)
self.last_update_time = current_time
self.last_interaction_time = current_time
def decrease_interest(self, current_time: float, value: float):
self._update_reply_probability(current_time)
self.interest_level -= value
self.interest_level = max(self.interest_level, 0.0)
self.last_update_time = current_time
self.last_interaction_time = current_time
def get_interest(self) -> float:
current_time = time.time()
self._update_reply_probability(current_time)
self._calculate_decay(current_time)
self.last_update_time = current_time
return self.interest_level
def get_state(self) -> dict:
interest = self.get_interest()
return {
"interest_level": round(interest, 2),
"last_update_time": self.last_update_time,
"current_reply_probability": round(self.current_reply_probability, 4),
"is_above_threshold": self.is_above_threshold,
"last_interaction_time": self.last_interaction_time,
}
def should_evaluate_reply(self) -> bool:
current_time = time.time()
self._update_reply_probability(current_time)
if self.current_reply_probability > 0:
trigger = random.random() < self.current_reply_probability
return trigger
else:
return False
class SubHeartflow:
@@ -66,7 +221,10 @@ class SubHeartflow:
self.current_mind = "你什么也没想"
self.past_mind = []
self.current_state: CurrentState = CurrentState()
self.chat_state: ChatStateInfo = ChatStateInfo()
self.interest_chatting = InterestChatting()
self.llm_model = LLMRequest(
model=global_config.llm_sub_heartflow,
temperature=global_config.llm_sub_heartflow["temp"],
@@ -123,7 +281,7 @@ class SubHeartflow:
self.last_active_time = time.time() # 更新最后激活时间戳
current_thinking_info = self.current_mind
mood_info = self.current_state.mood
mood_info = self.chat_state.mood
observation = self._get_primary_observation()
# --- 获取观察信息 --- #
@@ -255,6 +413,26 @@ class SubHeartflow:
logger.warning(f"SubHeartflow {self.subheartflow_id} 没有找到有效的 ChattingObservation")
return None
def get_interest_state(self) -> dict:
"""获取当前兴趣状态"""
return self.interest_chatting.get_state()
def get_interest_level(self) -> float:
"""获取当前兴趣等级"""
return self.interest_chatting.get_interest()
def should_evaluate_reply(self) -> bool:
"""判断是否应该评估回复"""
return self.interest_chatting.should_evaluate_reply()
def add_interest_dict_entry(self, message: MessageRecv, interest_value: float, is_mentioned: bool):
"""添加兴趣字典条目"""
self.interest_chatting.add_interest_dict(message, interest_value, is_mentioned)
def get_interest_dict(self) -> Dict[str, tuple[MessageRecv, float, bool]]:
"""获取兴趣字典"""
return self.interest_chatting.interest_dict
init_prompt()
# subheartflow = SubHeartflow()