fix:优化离开focus模式的机制,完全移除Interest机制,

This commit is contained in:
SengokuCola
2025-05-27 23:08:44 +08:00
parent 369de9d137
commit 43e465860f
27 changed files with 263 additions and 250 deletions

View File

@@ -13,7 +13,6 @@ from src.chat.emoji_system.emoji_manager import emoji_manager
from src.chat.focus_chat.heartFC_sender import HeartFCSender from src.chat.focus_chat.heartFC_sender import HeartFCSender
from src.chat.utils.utils import process_llm_response from src.chat.utils.utils import process_llm_response
from src.chat.utils.info_catcher import info_catcher_manager from src.chat.utils.info_catcher import info_catcher_manager
from src.manager.mood_manager import mood_manager
from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.chat.focus_chat.hfc_utils import parse_thinking_id_to_timestamp from src.chat.focus_chat.hfc_utils import parse_thinking_id_to_timestamp
@@ -174,6 +173,7 @@ class DefaultExpressor:
except Exception as e: except Exception as e:
logger.error(f"回复失败: {e}") logger.error(f"回复失败: {e}")
traceback.print_exc()
return False, None return False, None
# --- 回复器 (Replier) 的定义 --- # # --- 回复器 (Replier) 的定义 --- #
@@ -443,7 +443,9 @@ class DefaultExpressor:
set_reply = True set_reply = True
else: else:
set_reply = False set_reply = False
sent_msg = await self.heart_fc_sender.send_message(bot_message, has_thinking=True, typing=typing, set_reply=set_reply) sent_msg = await self.heart_fc_sender.send_message(
bot_message, has_thinking=True, typing=typing, set_reply=set_reply
)
reply_message_ids.append(part_message_id) # 记录我们生成的ID reply_message_ids.append(part_message_id) # 记录我们生成的ID

View File

@@ -3,7 +3,7 @@ import contextlib
import time import time
import traceback import traceback
from collections import deque from collections import deque
from typing import List, Optional, Dict, Any, Deque from typing import List, Optional, Dict, Any, Deque, Callable, Awaitable
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.chat.message_receive.chat_stream import chat_manager from src.chat.message_receive.chat_stream import chat_manager
from rich.traceback import install from rich.traceback import install
@@ -84,6 +84,7 @@ class HeartFChatting:
self, self,
chat_id: str, chat_id: str,
observations: list[Observation], observations: list[Observation],
on_stop_focus_chat: Optional[Callable[[], Awaitable[None]]] = None,
): ):
""" """
HeartFChatting 初始化函数 HeartFChatting 初始化函数
@@ -91,6 +92,7 @@ class HeartFChatting:
参数: 参数:
chat_id: 聊天流唯一标识符(如stream_id) chat_id: 聊天流唯一标识符(如stream_id)
observations: 关联的观察列表 observations: 关联的观察列表
on_stop_focus_chat: 当收到stop_focus_chat命令时调用的回调函数
""" """
# 基础属性 # 基础属性
self.stream_id: str = chat_id # 聊天流ID self.stream_id: str = chat_id # 聊天流ID
@@ -143,6 +145,9 @@ class HeartFChatting:
self._current_cycle: Optional[CycleDetail] = None self._current_cycle: Optional[CycleDetail] = None
self._shutting_down: bool = False # 关闭标志位 self._shutting_down: bool = False # 关闭标志位
# 存储回调函数
self.on_stop_focus_chat = on_stop_focus_chat
async def _initialize(self) -> bool: async def _initialize(self) -> bool:
""" """
执行懒初始化操作 执行懒初始化操作
@@ -288,6 +293,19 @@ class HeartFChatting:
logger.debug(f"模板 {self.chat_stream.context.get_template_name()}") logger.debug(f"模板 {self.chat_stream.context.get_template_name()}")
loop_info = await self._observe_process_plan_action_loop(cycle_timers, thinking_id) loop_info = await self._observe_process_plan_action_loop(cycle_timers, thinking_id)
print(loop_info["loop_action_info"]["command"])
if loop_info["loop_action_info"]["command"] == "stop_focus_chat":
logger.info(f"{self.log_prefix} 麦麦决定停止专注聊天")
# 如果设置了回调函数,则调用它
if self.on_stop_focus_chat:
try:
await self.on_stop_focus_chat()
logger.info(f"{self.log_prefix} 成功调用回调函数处理停止专注聊天")
except Exception as e:
logger.error(f"{self.log_prefix} 调用停止专注聊天回调函数时出错: {e}")
logger.error(traceback.format_exc())
break
self._current_cycle.set_loop_info(loop_info) self._current_cycle.set_loop_info(loop_info)
self.hfcloop_observation.add_loop_info(self._current_cycle) self.hfcloop_observation.add_loop_info(self._current_cycle)
@@ -410,7 +428,7 @@ class HeartFChatting:
return all_plan_info return all_plan_info
async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> tuple[bool, str]: async def _observe_process_plan_action_loop(self, cycle_timers: dict, thinking_id: str) -> dict:
try: try:
with Timer("观察", cycle_timers): with Timer("观察", cycle_timers):
# await self.observations[0].observe() # await self.observations[0].observe()
@@ -466,13 +484,14 @@ class HeartFChatting:
logger.info(f"{self.log_prefix} 麦麦决定'{action_str}', 原因'{reasoning}'") logger.info(f"{self.log_prefix} 麦麦决定'{action_str}', 原因'{reasoning}'")
success, reply_text = await self._handle_action( success, reply_text, command = await self._handle_action(
action_type, reasoning, action_data, cycle_timers, thinking_id action_type, reasoning, action_data, cycle_timers, thinking_id
) )
loop_action_info = { loop_action_info = {
"action_taken": success, "action_taken": success,
"reply_text": reply_text, "reply_text": reply_text,
"command": command,
} }
loop_info = { loop_info = {
@@ -487,7 +506,12 @@ class HeartFChatting:
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} FOCUS聊天处理失败: {e}") logger.error(f"{self.log_prefix} FOCUS聊天处理失败: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return {} return {
"loop_observation_info": {},
"loop_processor_info": {},
"loop_plan_info": {},
"loop_action_info": {"action_taken": False, "reply_text": "", "command": ""},
}
async def _handle_action( async def _handle_action(
self, self,
@@ -496,7 +520,7 @@ class HeartFChatting:
action_data: dict, action_data: dict,
cycle_timers: dict, cycle_timers: dict,
thinking_id: str, thinking_id: str,
) -> tuple[bool, str]: ) -> tuple[bool, str, str]:
""" """
处理规划动作,使用动作工厂创建相应的动作处理器 处理规划动作,使用动作工厂创建相应的动作处理器
@@ -508,10 +532,11 @@ class HeartFChatting:
thinking_id: 思考ID thinking_id: 思考ID
返回: 返回:
tuple[bool, str]: (是否执行了动作, 思考消息ID) tuple[bool, str, str]: (是否执行了动作, 思考消息ID, 命令)
""" """
try: try:
# 使用工厂创建动作处理器实例 # 使用工厂创建动作处理器实例
try:
action_handler = self.action_manager.create_action( action_handler = self.action_manager.create_action(
action_name=action, action_name=action,
action_data=action_data, action_data=action_data,
@@ -524,20 +549,29 @@ class HeartFChatting:
log_prefix=self.log_prefix, log_prefix=self.log_prefix,
shutting_down=self._shutting_down, shutting_down=self._shutting_down,
) )
except Exception as e:
logger.error(f"{self.log_prefix} 创建动作处理器时出错: {e}")
traceback.print_exc()
return False, "", ""
if not action_handler: if not action_handler:
logger.warning(f"{self.log_prefix} 未能创建动作处理器: {action}, 原因: {reasoning}") logger.warning(f"{self.log_prefix} 未能创建动作处理器: {action}, 原因: {reasoning}")
return False, "" return False, "", ""
# 处理动作并获取结果 # 处理动作并获取结果
success, reply_text = await action_handler.handle_action() result = await action_handler.handle_action()
if len(result) == 3:
return success, reply_text success, reply_text, command = result
else:
success, reply_text = result
command = ""
logger.info(f"{self.log_prefix} 麦麦决定'{action}', 原因'{reasoning}',返回结果'{success}', '{reply_text}', '{command}'")
return success, reply_text, command
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 处理{action}时出错: {e}") logger.error(f"{self.log_prefix} 处理{action}时出错: {e}")
traceback.print_exc() traceback.print_exc()
return False, "" return False, "", ""
async def shutdown(self): async def shutdown(self):
"""优雅关闭HeartFChatting实例取消活动循环任务""" """优雅关闭HeartFChatting实例取消活动循环任务"""

View File

@@ -205,8 +205,8 @@ class HeartFCMessageReceiver:
# 6. 兴趣度计算与更新 # 6. 兴趣度计算与更新
interested_rate, is_mentioned = await _calculate_interest(message) interested_rate, is_mentioned = await _calculate_interest(message)
await subheartflow.interest_chatting.increase_interest(value=interested_rate) # await subheartflow.interest_chatting.increase_interest(value=interested_rate)
subheartflow.interest_chatting.add_interest_dict(message, interested_rate, is_mentioned) subheartflow.add_interest_message(message, interested_rate, is_mentioned)
# 7. 日志记录 # 7. 日志记录
mes_name = chat.group_info.group_name if chat.group_info else "私聊" mes_name = chat.group_info.group_name if chat.group_info else "私聊"

View File

@@ -79,7 +79,6 @@ class StructuredInfo:
# print(f"self.data: {self.data}") # print(f"self.data: {self.data}")
for key, value in self.data.items(): for key, value in self.data.items():
# print(f"key: {key}, value: {value}") # print(f"key: {key}, value: {value}")
info_str += f"信息类型:{key},信息内容:{value}\n" info_str += f"信息类型:{key},信息内容:{value}\n"

View File

@@ -8,7 +8,6 @@ from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservati
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
from src.chat.message_receive.chat_stream import chat_manager from src.chat.message_receive.chat_stream import chat_manager
from typing import Dict from typing import Dict
from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config from src.config.config import global_config
import random import random
@@ -137,9 +136,11 @@ class ActionProcessor(BaseProcessor):
reply_sequence.append(action_type == "reply") reply_sequence.append(action_type == "reply")
# 检查no_reply比例 # 检查no_reply比例
if len(recent_cycles) >= 5 and (no_reply_count / len(recent_cycles)) >= 0.8: if len(recent_cycles) >= (5 * global_config.focus_chat.exit_focus_threshold) and (no_reply_count / len(recent_cycles)) >= (0.75 * global_config.focus_chat.exit_focus_threshold):
if global_config.chat.chat_mode == "auto": if global_config.chat.chat_mode == "auto":
result["add"].append("exit_focus_chat") result["add"].append("exit_focus_chat")
result["remove"].append("no_reply")
result["remove"].append("reply")
# 获取最近三次的reply状态 # 获取最近三次的reply状态
last_three = reply_sequence[-3:] if len(reply_sequence) >= 3 else reply_sequence last_three = reply_sequence[-3:] if len(reply_sequence) >= 3 else reply_sequence

View File

@@ -92,7 +92,7 @@ class ToolProcessor(BaseProcessor):
# print(f"working_info: {working_info}") # print(f"working_info: {working_info}")
# print(f"working_info.get('type'): {working_info.get('type')}") # print(f"working_info.get('type'): {working_info.get('type')}")
# print(f"working_info.get('content'): {working_info.get('content')}") # print(f"working_info.get('content'): {working_info.get('content')}")
structured_info.set_info(key=working_info.get('type'), value=working_info.get('content')) structured_info.set_info(key=working_info.get("type"), value=working_info.get("content"))
# info = structured_info.get_processed_info() # info = structured_info.get_processed_info()
# print(f"info: {info}") # print(f"info: {info}")

View File

@@ -129,7 +129,7 @@ class MemoryActivator:
self.cached_keywords.update(keywords) self.cached_keywords.update(keywords)
logger.debug(f"更新关键词缓存: {self.cached_keywords}") logger.debug(f"更新关键词缓存: {self.cached_keywords}")
#调用记忆系统获取相关记忆 # 调用记忆系统获取相关记忆
related_memory = await HippocampusManager.get_instance().get_memory_from_topic( related_memory = await HippocampusManager.get_instance().get_memory_from_topic(
valid_keywords=keywords, max_memory_num=3, max_memory_length=2, max_depth=3 valid_keywords=keywords, max_memory_num=3, max_memory_length=2, max_depth=3
) )

View File

@@ -29,7 +29,6 @@ class ActionManager:
# 当前正在使用的动作集合,默认加载默认动作 # 当前正在使用的动作集合,默认加载默认动作
self._using_actions: Dict[str, ActionInfo] = {} self._using_actions: Dict[str, ActionInfo] = {}
# 默认动作集,仅作为快照,用于恢复默认 # 默认动作集,仅作为快照,用于恢复默认
self._default_actions: Dict[str, ActionInfo] = {} self._default_actions: Dict[str, ActionInfo] = {}
@@ -159,9 +158,9 @@ class ActionManager:
Optional[BaseAction]: 创建的动作处理器实例如果动作名称未注册则返回None Optional[BaseAction]: 创建的动作处理器实例如果动作名称未注册则返回None
""" """
# 检查动作是否在当前使用的动作集中 # 检查动作是否在当前使用的动作集中
if action_name not in self._using_actions: # if action_name not in self._using_actions:
logger.warning(f"当前不可用的动作类型: {action_name}") # logger.warning(f"当前不可用的动作类型: {action_name}")
return None # return None
handler_class = _ACTION_REGISTRY.get(action_name) handler_class = _ACTION_REGISTRY.get(action_name)
if not handler_class: if not handler_class:
@@ -283,7 +282,9 @@ class ActionManager:
def restore_actions(self) -> None: def restore_actions(self) -> None:
"""恢复到默认动作集""" """恢复到默认动作集"""
logger.debug(f"恢复动作集: 从 {list(self._using_actions.keys())} 恢复到默认动作集 {list(self._default_actions.keys())}") logger.debug(
f"恢复动作集: 从 {list(self._using_actions.keys())} 恢复到默认动作集 {list(self._default_actions.keys())}"
)
self._using_actions = self._default_actions.copy() self._using_actions = self._default_actions.copy()
def restore_default_actions(self) -> None: def restore_default_actions(self) -> None:

View File

@@ -1,5 +1,6 @@
# 导入所有动作模块以确保装饰器被执行 # 导入所有动作模块以确保装饰器被执行
from . import reply_action # noqa from . import reply_action # noqa
from . import no_reply_action # noqa from . import no_reply_action # noqa
from . import exit_focus_chat_action # noqa
# 在此处添加更多动作模块导入 # 在此处添加更多动作模块导入

View File

@@ -5,8 +5,6 @@ from src.chat.focus_chat.planners.actions.base_action import BaseAction, registe
from typing import Tuple, List from typing import Tuple, List
from src.chat.heart_flow.observation.observation import Observation from src.chat.heart_flow.observation.observation import Observation
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.chat.heart_flow.heartflow import heartflow
from src.chat.heart_flow.sub_heartflow import ChatState
logger = get_logger("action_taken") logger = get_logger("action_taken")
@@ -27,7 +25,7 @@ class ExitFocusChatAction(BaseAction):
"当前内容不需要持续专注关注,你决定退出专注聊天", "当前内容不需要持续专注关注,你决定退出专注聊天",
"聊天内容已经完成,你决定退出专注聊天", "聊天内容已经完成,你决定退出专注聊天",
] ]
default = True default = False
def __init__( def __init__(
self, self,
@@ -56,7 +54,6 @@ class ExitFocusChatAction(BaseAction):
self.observations = observations self.observations = observations
self.log_prefix = log_prefix self.log_prefix = log_prefix
self._shutting_down = shutting_down self._shutting_down = shutting_down
self.chat_id = chat_stream.stream_id
async def handle_action(self) -> Tuple[bool, str]: async def handle_action(self) -> Tuple[bool, str]:
""" """
@@ -74,23 +71,8 @@ class ExitFocusChatAction(BaseAction):
try: try:
# 转换状态 # 转换状态
status_message = "" status_message = ""
self.sub_heartflow = await heartflow.get_or_create_subheartflow(self.chat_id) command = "stop_focus_chat"
if self.sub_heartflow: return True, status_message, command
try:
# 转换为normal_chat状态
await self.sub_heartflow.change_chat_state(ChatState.CHAT)
status_message = "已成功切换到普通聊天模式"
logger.info(f"{self.log_prefix} {status_message}")
except Exception as e:
error_msg = f"切换到普通聊天模式失败: {str(e)}"
logger.error(f"{self.log_prefix} {error_msg}")
return False, error_msg
else:
warning_msg = "未找到有效的sub heartflow实例无法切换状态"
logger.warning(f"{self.log_prefix} {warning_msg}")
return False, warning_msg
return True, status_message
except asyncio.CancelledError: except asyncio.CancelledError:
logger.info(f"{self.log_prefix} 处理 'exit_focus_chat' 时等待被中断 (CancelledError)") logger.info(f"{self.log_prefix} 处理 'exit_focus_chat' 时等待被中断 (CancelledError)")
@@ -99,4 +81,4 @@ class ExitFocusChatAction(BaseAction):
error_msg = f"处理 'exit_focus_chat' 时发生错误: {str(e)}" error_msg = f"处理 'exit_focus_chat' 时发生错误: {str(e)}"
logger.error(f"{self.log_prefix} {error_msg}") logger.error(f"{self.log_prefix} {error_msg}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
return False, error_msg return False, "", ""

View File

@@ -156,7 +156,7 @@ class ActionPlanner:
logger.info(f"{self.log_prefix}{reasoning}") logger.info(f"{self.log_prefix}{reasoning}")
self.action_manager.restore_actions() self.action_manager.restore_actions()
logger.debug( logger.debug(
f"{self.log_prefix}恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}" f"{self.log_prefix}沉默后恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}"
) )
return { return {
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning}, "action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning},
@@ -241,7 +241,7 @@ class ActionPlanner:
# 恢复到默认动作集 # 恢复到默认动作集
self.action_manager.restore_actions() self.action_manager.restore_actions()
logger.debug( logger.debug(
f"{self.log_prefix}恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}" f"{self.log_prefix}规划后恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}"
) )
action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning} action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning}

View File

@@ -33,7 +33,10 @@ class MemoryManager:
self._id_map: Dict[str, MemoryItem] = {} self._id_map: Dict[str, MemoryItem] = {}
self.llm_summarizer = LLMRequest( self.llm_summarizer = LLMRequest(
model=global_config.model.focus_working_memory, temperature=0.3, max_tokens=512, request_type="memory_summarization" model=global_config.model.focus_working_memory,
temperature=0.3,
max_tokens=512,
request_type="memory_summarization",
) )
@property @property

View File

@@ -92,7 +92,8 @@ class BackgroundTaskManager:
# 根据 chat_mode 条件添加其他任务 # 根据 chat_mode 条件添加其他任务
if not (global_config.chat.chat_mode == "normal"): if not (global_config.chat.chat_mode == "normal"):
task_configs.extend([ task_configs.extend(
[
( (
self._run_cleanup_cycle, self._run_cleanup_cycle,
"info", "info",
@@ -113,7 +114,8 @@ class BackgroundTaskManager:
# f"专注评估任务已启动 间隔:{INTEREST_EVAL_INTERVAL_SECONDS}s", # f"专注评估任务已启动 间隔:{INTEREST_EVAL_INTERVAL_SECONDS}s",
# "_into_focus_task", # "_into_focus_task",
# ) # )
]) ]
)
else: else:
logger.info("聊天模式为 normal跳过启动清理任务、私聊激活任务和专注评估任务") logger.info("聊天模式为 normal跳过启动清理任务、私聊激活任务和专注评估任务")

View File

@@ -20,9 +20,9 @@ MAX_REPLY_PROBABILITY = 1
class InterestChatting: class InterestChatting:
def __init__( def __init__(
self, self,
decay_rate=global_config.focus_chat.default_decay_rate_per_second, decay_rate=0.95,
max_interest=MAX_INTEREST, max_interest=MAX_INTEREST,
trigger_threshold=global_config.focus_chat.reply_trigger_threshold, trigger_threshold=4,
max_probability=MAX_REPLY_PROBABILITY, max_probability=MAX_REPLY_PROBABILITY,
): ):
# 基础属性初始化 # 基础属性初始化

View File

@@ -1,5 +1,4 @@
from datetime import datetime from datetime import datetime
from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config from src.config.config import global_config
import traceback import traceback
from src.chat.utils.chat_message_builder import ( from src.chat.utils.chat_message_builder import (

View File

@@ -12,7 +12,6 @@ from src.chat.normal_chat.normal_chat import NormalChat
from src.chat.heart_flow.mai_state_manager import MaiStateInfo from src.chat.heart_flow.mai_state_manager import MaiStateInfo
from src.chat.heart_flow.chat_state_info import ChatState, ChatStateInfo from src.chat.heart_flow.chat_state_info import ChatState, ChatStateInfo
from .utils_chat import get_chat_type_and_target_info from .utils_chat import get_chat_type_and_target_info
from .interest_chatting import InterestChatting
from src.config.config import global_config from src.config.config import global_config
@@ -51,7 +50,7 @@ class SubHeartflow:
# --- End Initialization --- # --- End Initialization ---
# 兴趣检测器 # 兴趣检测器
self.interest_chatting: InterestChatting = InterestChatting() self.interest_dict: Dict[str, tuple[MessageRecv, float, bool]] = {}
# 活动状态管理 # 活动状态管理
self.should_stop = False # 停止标志 self.should_stop = False # 停止标志
@@ -85,8 +84,8 @@ class SubHeartflow:
# --- End using utility function --- # --- End using utility function ---
# Initialize interest system (existing logic) # Initialize interest system (existing logic)
await self.interest_chatting.initialize() # await self.interest_chatting.initialize()
logger.debug(f"{self.log_prefix} InterestChatting 实例已初始化。") # logger.debug(f"{self.log_prefix} InterestChatting 实例已初始化。")
# 根据配置决定初始状态 # 根据配置决定初始状态
if global_config.chat.chat_mode == "focus": if global_config.chat.chat_mode == "focus":
@@ -132,8 +131,8 @@ class SubHeartflow:
# 提供回调函数用于接收需要切换到focus模式的通知 # 提供回调函数用于接收需要切换到focus模式的通知
self.normal_chat_instance = NormalChat( self.normal_chat_instance = NormalChat(
chat_stream=chat_stream, chat_stream=chat_stream,
interest_dict=self.get_interest_dict(), interest_dict=self.interest_dict,
on_switch_to_focus_callback=self._handle_switch_to_focus_request on_switch_to_focus_callback=self._handle_switch_to_focus_request,
) )
# 进行异步初始化 # 进行异步初始化
@@ -166,6 +165,21 @@ class SubHeartflow:
else: else:
logger.warning(f"{self.log_prefix} 当前状态为{current_state.value}无法切换到FOCUSED状态") logger.warning(f"{self.log_prefix} 当前状态为{current_state.value}无法切换到FOCUSED状态")
async def _handle_stop_focus_chat_request(self) -> None:
"""
处理来自HeartFChatting的停止focus模式的请求
当收到stop_focus_chat命令时被调用
"""
logger.info(f"{self.log_prefix} 收到HeartFChatting请求停止focus模式")
# 切换到normal模式
current_state = self.chat_state.chat_status
if current_state == ChatState.FOCUSED:
await self.change_chat_state(ChatState.NORMAL)
logger.info(f"{self.log_prefix} 已根据HeartFChatting请求从FOCUSED切换到NORMAL状态")
else:
logger.warning(f"{self.log_prefix} 当前状态为{current_state.value}无法切换到NORMAL状态")
async def _stop_heart_fc_chat(self): async def _stop_heart_fc_chat(self):
"""停止并清理 HeartFChatting 实例""" """停止并清理 HeartFChatting 实例"""
if self.heart_fc_instance: if self.heart_fc_instance:
@@ -182,7 +196,7 @@ class SubHeartflow:
async def _start_heart_fc_chat(self) -> bool: async def _start_heart_fc_chat(self) -> bool:
"""启动 HeartFChatting 实例,确保 NormalChat 已停止""" """启动 HeartFChatting 实例,确保 NormalChat 已停止"""
await self._stop_normal_chat() # 确保普通聊天监控已停止 await self._stop_normal_chat() # 确保普通聊天监控已停止
self.clear_interest_dict() # 清理兴趣字典,准备专注聊天 self.interest_dict.clear()
log_prefix = self.log_prefix log_prefix = self.log_prefix
# 如果实例已存在,检查其循环任务状态 # 如果实例已存在,检查其循环任务状态
@@ -211,6 +225,7 @@ class SubHeartflow:
self.heart_fc_instance = HeartFChatting( self.heart_fc_instance = HeartFChatting(
chat_id=self.subheartflow_id, chat_id=self.subheartflow_id,
observations=self.observations, observations=self.observations,
on_stop_focus_chat=self._handle_stop_focus_chat_request,
) )
# 初始化并启动 HeartFChatting # 初始化并启动 HeartFChatting
@@ -259,7 +274,7 @@ class SubHeartflow:
elif new_state == ChatState.ABSENT: elif new_state == ChatState.ABSENT:
logger.info(f"{log_prefix} 进入 ABSENT 状态,停止所有聊天活动...") logger.info(f"{log_prefix} 进入 ABSENT 状态,停止所有聊天活动...")
self.clear_interest_dict() self.interest_dict.clear()
await self._stop_normal_chat() await self._stop_normal_chat()
await self._stop_heart_fc_chat() await self._stop_heart_fc_chat()
state_changed = True state_changed = True
@@ -300,17 +315,11 @@ class SubHeartflow:
logger.warning(f"SubHeartflow {self.subheartflow_id} 没有找到有效的 ChattingObservation") logger.warning(f"SubHeartflow {self.subheartflow_id} 没有找到有效的 ChattingObservation")
return None return None
async def get_interest_state(self) -> dict:
return await self.interest_chatting.get_state()
def get_normal_chat_last_speak_time(self) -> float: def get_normal_chat_last_speak_time(self) -> float:
if self.normal_chat_instance: if self.normal_chat_instance:
return self.normal_chat_instance.last_speak_time return self.normal_chat_instance.last_speak_time
return 0 return 0
def get_interest_dict(self) -> Dict[str, tuple[MessageRecv, float, bool]]:
return self.interest_chatting.interest_dict
def get_normal_chat_recent_replies(self, limit: int = 10) -> List[dict]: def get_normal_chat_recent_replies(self, limit: int = 10) -> List[dict]:
"""获取NormalChat实例的最近回复记录 """获取NormalChat实例的最近回复记录
@@ -324,14 +333,17 @@ class SubHeartflow:
return self.normal_chat_instance.get_recent_replies(limit) return self.normal_chat_instance.get_recent_replies(limit)
return [] return []
def clear_interest_dict(self): def add_interest_message(self, message: MessageRecv, interest_value: float, is_mentioned: bool):
self.interest_chatting.interest_dict.clear() self.interest_dict[message.message_info.message_id] = (message, interest_value, is_mentioned)
# 如果字典长度超过10删除最旧的消息
if len(self.interest_dict) > 10:
oldest_key = next(iter(self.interest_dict))
self.interest_dict.pop(oldest_key)
async def get_full_state(self) -> dict: async def get_full_state(self) -> dict:
"""获取子心流的完整状态,包括兴趣、思维和聊天状态。""" """获取子心流的完整状态,包括兴趣、思维和聊天状态。"""
interest_state = await self.get_interest_state()
return { return {
"interest_state": interest_state, "interest_state": "interest_state",
"chat_state": self.chat_state.chat_status.value, "chat_state": self.chat_state.chat_status.value,
"chat_state_changed_time": self.chat_state_changed_time, "chat_state_changed_time": self.chat_state_changed_time,
} }
@@ -349,10 +361,6 @@ class SubHeartflow:
await self._stop_normal_chat() await self._stop_normal_chat()
await self._stop_heart_fc_chat() await self._stop_heart_fc_chat()
# 停止兴趣更新任务
if self.interest_chatting:
logger.info(f"{self.log_prefix} 停止兴趣系统后台任务...")
await self.interest_chatting.stop_updates()
# 取消可能存在的旧后台任务 (self.task) # 取消可能存在的旧后台任务 (self.task)
if self.task and not self.task.done(): if self.task and not self.task.done():

View File

@@ -1,6 +1,5 @@
import asyncio import asyncio
import time import time
import random
from typing import Dict, Any, Optional, List from typing import Dict, Any, Optional, List
from src.common.logger_manager import get_logger from src.common.logger_manager import get_logger
from src.chat.message_receive.chat_stream import chat_manager from src.chat.message_receive.chat_stream import chat_manager
@@ -249,7 +248,7 @@ class SubHeartflowManager:
) )
try: try:
# 从HFC到CHAT时清空兴趣字典 # 从HFC到CHAT时清空兴趣字典
subflow.clear_interest_dict() subflow.interest_dict.clear()
await subflow.change_chat_state(target_state) await subflow.change_chat_state(target_state)
final_state = subflow.chat_state.chat_status final_state = subflow.chat_state.chat_status
if final_state == target_state: if final_state == target_state:

View File

@@ -209,10 +209,10 @@ class NormalChat:
break break
# 定期检查是否需要切换到focus模式 # 定期检查是否需要切换到focus模式
current_time = time.time() # current_time = time.time()
if current_time - self._last_check_time > self._check_interval: # if current_time - self._last_check_time > self._check_interval:
await self._check_switch_to_focus() # await self._check_switch_to_focus()
self._last_check_time = current_time # self._last_check_time = current_time
items_to_process = list(self.interest_dict.items()) items_to_process = list(self.interest_dict.items())
if not items_to_process: if not items_to_process:
@@ -336,17 +336,17 @@ class NormalChat:
"user_message": message.processed_plain_text, "user_message": message.processed_plain_text,
"user_info": { "user_info": {
"user_id": message.message_info.user_info.user_id, "user_id": message.message_info.user_info.user_id,
"user_nickname": message.message_info.user_info.user_nickname "user_nickname": message.message_info.user_info.user_nickname,
}, },
"response": response_set, "response": response_set,
"is_mentioned": is_mentioned, "is_mentioned": is_mentioned,
"is_reference_reply": message.reply is not None, # 判断是否为引用回复 "is_reference_reply": message.reply is not None, # 判断是否为引用回复
"timing": {k: round(v, 2) for k, v in timing_results.items()} "timing": {k: round(v, 2) for k, v in timing_results.items()},
} }
self.recent_replies.append(reply_info) self.recent_replies.append(reply_info)
# 保持最近回复历史在限定数量内 # 保持最近回复历史在限定数量内
if len(self.recent_replies) > self.max_replies_history: if len(self.recent_replies) > self.max_replies_history:
self.recent_replies = self.recent_replies[-self.max_replies_history:] self.recent_replies = self.recent_replies[-self.max_replies_history :]
# 检查是否需要切换到focus模式 # 检查是否需要切换到focus模式
await self._check_switch_to_focus() await self._check_switch_to_focus()
@@ -597,7 +597,9 @@ class NormalChat:
# print(recent_reply_count) # print(recent_reply_count)
# 如果1分钟内回复数量大于8触发切换到focus模式 # 如果1分钟内回复数量大于8触发切换到focus模式
if recent_reply_count > reply_threshold: if recent_reply_count > reply_threshold:
logger.info(f"[{self.stream_name}] 检测到1分钟内回复数量({recent_reply_count})大于{reply_threshold}触发切换到focus模式") logger.info(
f"[{self.stream_name}] 检测到1分钟内回复数量({recent_reply_count})大于{reply_threshold}触发切换到focus模式"
)
try: try:
# 调用回调函数通知上层切换到focus模式 # 调用回调函数通知上层切换到focus模式
await self.on_switch_to_focus_callback() await self.on_switch_to_focus_callback()

View File

@@ -44,6 +44,7 @@ class IdentityConfig(ConfigBase):
identity_detail: list[str] = field(default_factory=lambda: []) identity_detail: list[str] = field(default_factory=lambda: [])
"""身份特征""" """身份特征"""
@dataclass @dataclass
class RelationshipConfig(ConfigBase): class RelationshipConfig(ConfigBase):
"""关系配置类""" """关系配置类"""
@@ -125,6 +126,9 @@ class FocusChatConfig(ConfigBase):
auto_focus_threshold: float = 1.0 auto_focus_threshold: float = 1.0
"""自动切换到专注聊天的阈值,越低越容易进入专注聊天""" """自动切换到专注聊天的阈值,越低越容易进入专注聊天"""
exit_focus_threshold: float = 1.0
"""自动退出专注聊天的阈值,越低越容易退出专注聊天"""
observation_context_size: int = 12 observation_context_size: int = 12
"""可观察到的最长上下文大小,超过这个值的上下文会被压缩""" """可观察到的最长上下文大小,超过这个值的上下文会被压缩"""
@@ -382,7 +386,6 @@ class ModelConfig(ConfigBase):
model_max_output_length: int = 800 # 最大回复长度 model_max_output_length: int = 800 # 最大回复长度
utils: dict[str, Any] = field(default_factory=lambda: {}) utils: dict[str, Any] = field(default_factory=lambda: {})
"""组件模型配置""" """组件模型配置"""
@@ -430,7 +433,3 @@ class ModelConfig(ConfigBase):
pfc_reply_checker: dict[str, Any] = field(default_factory=lambda: {}) pfc_reply_checker: dict[str, Any] = field(default_factory=lambda: {})
"""PFC回复检查模型配置""" """PFC回复检查模型配置"""

View File

@@ -261,7 +261,7 @@ class PersonInfoManager:
current_name_set = set(self.person_name_list.values()) current_name_set = set(self.person_name_list.values())
while current_try < max_retries: while current_try < max_retries:
prompt_personality = individuality.get_prompt(x_person=2, level=1) # prompt_personality = individuality.get_prompt(x_person=2, level=1)
bot_name = individuality.personality.bot_nickname bot_name = individuality.personality.bot_nickname
qv_name_prompt = f"你是{bot_name}一个伪装成人类的AI你不能让别人发现这一点" qv_name_prompt = f"你是{bot_name}一个伪装成人类的AI你不能让别人发现这一点"
@@ -273,9 +273,7 @@ class PersonInfoManager:
qv_name_prompt += f"你之前叫他{old_name},是因为{old_reason}" qv_name_prompt += f"你之前叫他{old_name},是因为{old_reason}"
qv_name_prompt += f"\n其他取名的要求是:{request},不要太浮夸,简短," qv_name_prompt += f"\n其他取名的要求是:{request},不要太浮夸,简短,"
qv_name_prompt += ( qv_name_prompt += "\n请根据以上用户信息想想你叫他什么比较好不要太浮夸请最好使用用户的qq昵称可以稍作修改优先使用原文。优先使用用户的qq昵称或者群昵称原文。"
"\n请根据以上用户信息想想你叫他什么比较好不要太浮夸请最好使用用户的qq昵称可以稍作修改优先使用原文。优先使用用户的qq昵称或者群昵称原文。"
)
if existing_names_str: if existing_names_str:
qv_name_prompt += f"\n请注意,以下名称已被你尝试过或已知存在,请避免:{existing_names_str}\n" qv_name_prompt += f"\n请注意,以下名称已被你尝试过或已知存在,请避免:{existing_names_str}\n"

View File

@@ -10,9 +10,7 @@ class GroupWholeBanAction(PluginAction):
"""群聊全体禁言动作处理类""" """群聊全体禁言动作处理类"""
action_name = "group_whole_ban_action" action_name = "group_whole_ban_action"
action_description = ( action_description = "开启或关闭群聊全体禁言,当群聊过于混乱或需要安静时使用"
"开启或关闭群聊全体禁言,当群聊过于混乱或需要安静时使用"
)
action_parameters = { action_parameters = {
"enable": "是否开启全体禁言输入True开启False关闭必填", "enable": "是否开启全体禁言输入True开启False关闭必填",
} }
@@ -39,9 +37,9 @@ class GroupWholeBanAction(PluginAction):
# 确保enable是布尔类型 # 确保enable是布尔类型
if isinstance(enable, str): if isinstance(enable, str):
if enable.lower() in ['true', '1', 'yes', '开启', '']: if enable.lower() in ["true", "1", "yes", "开启", ""]:
enable = True enable = True
elif enable.lower() in ['false', '0', 'no', '关闭', '']: elif enable.lower() in ["false", "0", "no", "关闭", ""]:
enable = False enable = False
else: else:
error_msg = f"无效的enable参数: {enable}应该是True或False" error_msg = f"无效的enable参数: {enable}应该是True或False"
@@ -54,15 +52,7 @@ class GroupWholeBanAction(PluginAction):
try: try:
# 发送群聊全体禁言命令,按照新格式 # 发送群聊全体禁言命令,按照新格式
await self.send_message( await self.send_message(type="command", data={"name": "GROUP_WHOLE_BAN", "args": {"enable": enable}})
type="command",
data={
"name": "GROUP_WHOLE_BAN",
"args": {
"enable": enable
}
}
)
logger.info(f"{self.log_prefix} 成功{action_text}全体禁言") logger.info(f"{self.log_prefix} 成功{action_text}全体禁言")
return True, f"成功{action_text}全体禁言" return True, f"成功{action_text}全体禁言"

View File

@@ -10,9 +10,7 @@ class MuteAction(PluginAction):
"""群聊禁言动作处理类""" """群聊禁言动作处理类"""
action_name = "mute_action" action_name = "mute_action"
action_description = ( action_description = "如果某人违反了公序良俗,或者别人戳你太多,或者某人刷屏,一定要禁言某人,如果你很生气,可以禁言某人,可以自选禁言时长,视严重程度而定。"
"如果某人违反了公序良俗,或者别人戳你太多,或者某人刷屏,一定要禁言某人,如果你很生气,可以禁言某人,可以自选禁言时长,视严重程度而定。"
)
action_parameters = { action_parameters = {
"target": "禁言对象,输入你要禁言的对象的名字,必填", "target": "禁言对象,输入你要禁言的对象的名字,必填",
"duration": "禁言时长,输入你要禁言的时长,单位为秒,必填,必须为数字", "duration": "禁言时长,输入你要禁言的时长,单位为秒,必填,必须为数字",
@@ -61,14 +59,7 @@ class MuteAction(PluginAction):
# 发送群聊禁言命令,按照新格式 # 发送群聊禁言命令,按照新格式
await self.send_message( await self.send_message(
type="command", type="command", data={"name": "GROUP_BAN", "args": {"qq_id": str(user_id), "duration": duration_str}}
data={
"name": "GROUP_BAN",
"args": {
"qq_id": str(user_id),
"duration": duration_str
}
}
) )
logger.info(f"{self.log_prefix} 成功禁言用户 {target}({user_id}),时长 {duration}") logger.info(f"{self.log_prefix} 成功禁言用户 {target}({user_id}),时长 {duration}")

View File

@@ -3,6 +3,8 @@ from src.common.logger_manager import get_logger
from src.tools.tool_can_use import get_all_tool_definitions, get_tool_instance from src.tools.tool_can_use import get_all_tool_definitions, get_tool_instance
logger = get_logger("tool_use") logger = get_logger("tool_use")
class ToolUser: class ToolUser:
@staticmethod @staticmethod
def _define_tools(): def _define_tools():

View File

@@ -80,7 +80,7 @@ talk_frequency_down_groups = [] #降低回复频率的群号码
[focus_chat] #专注聊天 [focus_chat] #专注聊天
auto_focus_threshold = 1 # 自动切换到专注聊天的阈值,越低越容易进入专注聊天 auto_focus_threshold = 1 # 自动切换到专注聊天的阈值,越低越容易进入专注聊天
exit_focus_threshold = 1 # 自动退出专注聊天的阈值,越低越容易退出专注聊天
consecutive_no_reply_threshold = 3 # 连续不回复的阈值,越低越容易结束专注聊天 consecutive_no_reply_threshold = 3 # 连续不回复的阈值,越低越容易结束专注聊天
think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗 think_interval = 3 # 思考间隔 单位秒,可以有效减少消耗