feat:成功融合reasoning和HFC,由主心流统一调控
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
@@ -79,4 +79,16 @@ await heartflow.heartflow_start_working()
|
||||
|
||||
1. 子心流会在长时间不活跃后自动清理
|
||||
2. 需要合理配置更新间隔以平衡性能和响应速度
|
||||
3. 观察系统会限制消息处理数量以避免过载
|
||||
3. 观察系统会限制消息处理数量以避免过载
|
||||
|
||||
|
||||
更新:
|
||||
把聊天控制移动到心流下吧
|
||||
首先心流要根据日程以及当前状况判定总体状态MaiStateInfo
|
||||
|
||||
然后根据每个子心流的运行情况,给子心流分配聊天资源(ChatStateInfo:ABSENT 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
11
src/heart_flow/Update.md
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
更新:
|
||||
把聊天控制移动到心流下吧
|
||||
首先心流要根据日程以及当前状况判定总体状态MaiStateInfo
|
||||
|
||||
然后根据每个子心流的运行情况,给子心流分配聊天资源(ChatStateInfo:ABSENT 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 |
@@ -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]:
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user