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

@@ -33,11 +33,11 @@ async def get_subheartflow_cycle_info(subheartflow_id: str, history_len: int) ->
async def get_normal_chat_replies(subheartflow_id: str, limit: int = 10) -> list: async def get_normal_chat_replies(subheartflow_id: str, limit: int = 10) -> list:
"""获取子心流的NormalChat回复记录 """获取子心流的NormalChat回复记录
Args: Args:
subheartflow_id: 子心流ID subheartflow_id: 子心流ID
limit: 最大返回数量默认10条 limit: 最大返回数量默认10条
Returns: Returns:
list: 回复记录列表,如果未找到则返回空列表 list: 回复记录列表,如果未找到则返回空列表
""" """

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
@@ -150,22 +149,22 @@ class DefaultExpressor:
action_data=action_data, action_data=action_data,
) )
with Timer("选择表情", cycle_timers): with Timer("选择表情", cycle_timers):
emoji_keyword = action_data.get("emojis", []) emoji_keyword = action_data.get("emojis", [])
emoji_base64 = await self._choose_emoji(emoji_keyword) emoji_base64 = await self._choose_emoji(emoji_keyword)
if emoji_base64: if emoji_base64:
reply.append(("emoji", emoji_base64)) reply.append(("emoji", emoji_base64))
if reply: if reply:
with Timer("发送消息", cycle_timers): with Timer("发送消息", cycle_timers):
sent_msg_list = await self.send_response_messages( sent_msg_list = await self.send_response_messages(
anchor_message=anchor_message, anchor_message=anchor_message,
thinking_id=thinking_id, thinking_id=thinking_id,
response_set=reply, response_set=reply,
) )
has_sent_something = True has_sent_something = True
else: else:
logger.warning(f"{self.log_prefix} 文本回复生成失败") logger.warning(f"{self.log_prefix} 文本回复生成失败")
if not has_sent_something: if not has_sent_something:
logger.warning(f"{self.log_prefix} 回复动作未包含任何有效内容") logger.warning(f"{self.log_prefix} 回复动作未包含任何有效内容")
@@ -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:
""" """
执行懒初始化操作 执行懒初始化操作
@@ -287,6 +292,19 @@ class HeartFChatting:
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()): async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
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)
@@ -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,36 +532,46 @@ class HeartFChatting:
thinking_id: 思考ID thinking_id: 思考ID
返回: 返回:
tuple[bool, str]: (是否执行了动作, 思考消息ID) tuple[bool, str, str]: (是否执行了动作, 思考消息ID, 命令)
""" """
try: try:
# 使用工厂创建动作处理器实例 # 使用工厂创建动作处理器实例
action_handler = self.action_manager.create_action( try:
action_name=action, action_handler = self.action_manager.create_action(
action_data=action_data, action_name=action,
reasoning=reasoning, action_data=action_data,
cycle_timers=cycle_timers, reasoning=reasoning,
thinking_id=thinking_id, cycle_timers=cycle_timers,
observations=self.all_observations, thinking_id=thinking_id,
expressor=self.expressor, observations=self.all_observations,
chat_stream=self.chat_stream, expressor=self.expressor,
log_prefix=self.log_prefix, chat_stream=self.chat_stream,
shutting_down=self._shutting_down, log_prefix=self.log_prefix,
) 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

@@ -77,9 +77,8 @@ class StructuredInfo:
info_str = "" info_str = ""
# 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

@@ -76,7 +76,7 @@ class ToolProcessor(BaseProcessor):
# 更新WorkingObservation中的结构化信息 # 更新WorkingObservation中的结构化信息
logger.debug(f"工具调用结果: {result}") logger.debug(f"工具调用结果: {result}")
for observation in observations: for observation in observations:
if isinstance(observation, StructureObservation): if isinstance(observation, StructureObservation):
for structured_info in result: for structured_info in result:
@@ -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

@@ -19,24 +19,24 @@ logger = get_logger("memory_activator")
def get_keywords_from_json(json_str): def get_keywords_from_json(json_str):
""" """
从JSON字符串中提取关键词列表 从JSON字符串中提取关键词列表
Args: Args:
json_str: JSON格式的字符串 json_str: JSON格式的字符串
Returns: Returns:
List[str]: 关键词列表 List[str]: 关键词列表
""" """
try: try:
# 使用repair_json修复JSON格式 # 使用repair_json修复JSON格式
fixed_json = repair_json(json_str) fixed_json = repair_json(json_str)
# 如果repair_json返回的是字符串需要解析为Python对象 # 如果repair_json返回的是字符串需要解析为Python对象
if isinstance(fixed_json, str): if isinstance(fixed_json, str):
result = json.loads(fixed_json) result = json.loads(fixed_json)
else: else:
# 如果repair_json直接返回了字典对象直接使用 # 如果repair_json直接返回了字典对象直接使用
result = fixed_json result = fixed_json
# 提取关键词 # 提取关键词
keywords = result.get("keywords", []) keywords = result.get("keywords", [])
return keywords return keywords
@@ -100,7 +100,7 @@ class MemoryActivator:
# 将缓存的关键词转换为字符串用于prompt # 将缓存的关键词转换为字符串用于prompt
cached_keywords_str = ", ".join(self.cached_keywords) if self.cached_keywords else "暂无历史关键词" cached_keywords_str = ", ".join(self.cached_keywords) if self.cached_keywords else "暂无历史关键词"
prompt = await global_prompt_manager.format_prompt( prompt = await global_prompt_manager.format_prompt(
"memory_activator_prompt", "memory_activator_prompt",
obs_info_text=obs_info_text, obs_info_text=obs_info_text,
@@ -116,7 +116,7 @@ class MemoryActivator:
# 只取response的第一个元素字符串 # 只取response的第一个元素字符串
response_str = response[0] response_str = response[0]
keywords = list(get_keywords_from_json(response_str)) keywords = list(get_keywords_from_json(response_str))
# 更新关键词缓存 # 更新关键词缓存
if keywords: if keywords:
# 限制缓存大小最多保留10个关键词 # 限制缓存大小最多保留10个关键词
@@ -124,12 +124,12 @@ class MemoryActivator:
# 转换为列表,移除最早的关键词 # 转换为列表,移除最早的关键词
cached_list = list(self.cached_keywords) cached_list = list(self.cached_keywords)
self.cached_keywords = set(cached_list[-8:]) self.cached_keywords = set(cached_list[-8:])
# 添加新的关键词到缓存 # 添加新的关键词到缓存
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,28 +92,30 @@ 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, (
"info", self._run_cleanup_cycle,
f"清理任务已启动 间隔:{CLEANUP_INTERVAL_SECONDS}s", "info",
"_cleanup_task", f"清理任务已启动 间隔:{CLEANUP_INTERVAL_SECONDS}s",
), "_cleanup_task",
# 新增私聊激活任务配置 ),
( # 新增私聊激活任务配置
# Use lambda to pass the interval to the runner function (
lambda: self._run_private_chat_activation_cycle(PRIVATE_CHAT_ACTIVATION_CHECK_INTERVAL_SECONDS), # Use lambda to pass the interval to the runner function
"debug", lambda: self._run_private_chat_activation_cycle(PRIVATE_CHAT_ACTIVATION_CHECK_INTERVAL_SECONDS),
f"私聊激活检查任务已启动 间隔:{PRIVATE_CHAT_ACTIVATION_CHECK_INTERVAL_SECONDS}s", "debug",
"_private_chat_activation_task", f"私聊激活检查任务已启动 间隔:{PRIVATE_CHAT_ACTIVATION_CHECK_INTERVAL_SECONDS}s",
), "_private_chat_activation_task",
# ( ),
# self._run_into_focus_cycle, # (
# "debug", # 设为debug避免过多日志 # self._run_into_focus_cycle,
# f"专注评估任务已启动 间隔:{INTEREST_EVAL_INTERVAL_SECONDS}s", # "debug", # 设为debug避免过多日志
# "_into_focus_task", # f"专注评估任务已启动 间隔:{INTEREST_EVAL_INTERVAL_SECONDS}s",
# ) # "_into_focus_task",
]) # )
]
)
else: else:
logger.info("聊天模式为 normal跳过启动清理任务、私聊激活任务和专注评估任务") logger.info("聊天模式为 normal跳过启动清理任务、私聊激活任务和专注评估任务")

View File

@@ -59,11 +59,11 @@ class Heartflow:
async def api_get_normal_chat_replies(self, subheartflow_id: str, limit: int = 10) -> Optional[List[dict]]: async def api_get_normal_chat_replies(self, subheartflow_id: str, limit: int = 10) -> Optional[List[dict]]:
"""获取子心流的NormalChat回复记录 """获取子心流的NormalChat回复记录
Args: Args:
subheartflow_id: 子心流ID subheartflow_id: 子心流ID
limit: 最大返回数量默认10条 limit: 最大返回数量默认10条
Returns: Returns:
Optional[List[dict]]: 回复记录列表如果子心流不存在则返回None Optional[List[dict]]: 回复记录列表如果子心流不存在则返回None
""" """
@@ -71,7 +71,7 @@ class Heartflow:
if not subheartflow: if not subheartflow:
logger.warning(f"尝试获取不存在的子心流 {subheartflow_id} 的NormalChat回复记录") logger.warning(f"尝试获取不存在的子心流 {subheartflow_id} 的NormalChat回复记录")
return None return None
return subheartflow.get_normal_chat_recent_replies(limit) return subheartflow.get_normal_chat_recent_replies(limit)
async def heartflow_start_working(self): async def heartflow_start_working(self):

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":
@@ -131,9 +130,9 @@ class SubHeartflow:
if rewind or not self.normal_chat_instance: if rewind or not self.normal_chat_instance:
# 提供回调函数用于接收需要切换到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,
) )
# 进行异步初始化 # 进行异步初始化
@@ -152,12 +151,12 @@ class SubHeartflow:
async def _handle_switch_to_focus_request(self) -> None: async def _handle_switch_to_focus_request(self) -> None:
""" """
处理来自NormalChat的切换到focus模式的请求 处理来自NormalChat的切换到focus模式的请求
Args: Args:
stream_id: 请求切换的stream_id stream_id: 请求切换的stream_id
""" """
logger.info(f"{self.log_prefix} 收到NormalChat请求切换到focus模式") logger.info(f"{self.log_prefix} 收到NormalChat请求切换到focus模式")
# 切换到focus模式 # 切换到focus模式
current_state = self.chat_state.chat_status current_state = self.chat_state.chat_status
if current_state == ChatState.NORMAL: if current_state == ChatState.NORMAL:
@@ -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,38 +315,35 @@ 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实例的最近回复记录
Args: Args:
limit: 最大返回数量默认10条 limit: 最大返回数量默认10条
Returns: Returns:
List[dict]: 最近的回复记录列表如果没有NormalChat实例则返回空列表 List[dict]: 最近的回复记录列表如果没有NormalChat实例则返回空列表
""" """
if self.normal_chat_instance: if self.normal_chat_instance:
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
@@ -187,40 +186,40 @@ class SubHeartflowManager:
) )
# async def sbhf_normal_into_focus(self): # async def sbhf_normal_into_focus(self):
# """评估子心流兴趣度满足条件则提升到FOCUSED状态基于start_hfc_probability""" # """评估子心流兴趣度满足条件则提升到FOCUSED状态基于start_hfc_probability"""
# try: # try:
# for sub_hf in list(self.subheartflows.values()): # for sub_hf in list(self.subheartflows.values()):
# flow_id = sub_hf.subheartflow_id # flow_id = sub_hf.subheartflow_id
# stream_name = chat_manager.get_stream_name(flow_id) or flow_id # stream_name = chat_manager.get_stream_name(flow_id) or flow_id
# # 跳过已经是FOCUSED状态的子心流 # # 跳过已经是FOCUSED状态的子心流
# if sub_hf.chat_state.chat_status == ChatState.FOCUSED: # if sub_hf.chat_state.chat_status == ChatState.FOCUSED:
# continue # continue
# if sub_hf.interest_chatting.start_hfc_probability == 0: # if sub_hf.interest_chatting.start_hfc_probability == 0:
# continue # continue
# else: # else:
# logger.debug( # logger.debug(
# f"{stream_name},现在状态: {sub_hf.chat_state.chat_status.value},进入专注概率: {sub_hf.interest_chatting.start_hfc_probability}" # f"{stream_name},现在状态: {sub_hf.chat_state.chat_status.value},进入专注概率: {sub_hf.interest_chatting.start_hfc_probability}"
# ) # )
# if random.random() >= sub_hf.interest_chatting.start_hfc_probability: # if random.random() >= sub_hf.interest_chatting.start_hfc_probability:
# continue # continue
# # 获取最新状态并执行提升 # # 获取最新状态并执行提升
# current_subflow = self.subheartflows.get(flow_id) # current_subflow = self.subheartflows.get(flow_id)
# if not current_subflow: # if not current_subflow:
# continue # continue
# logger.info( # logger.info(
# f"{stream_name} 触发 认真水群 (概率={current_subflow.interest_chatting.start_hfc_probability:.2f})" # f"{stream_name} 触发 认真水群 (概率={current_subflow.interest_chatting.start_hfc_probability:.2f})"
# ) # )
# # 执行状态提升 # # 执行状态提升
# await current_subflow.change_chat_state(ChatState.FOCUSED) # await current_subflow.change_chat_state(ChatState.FOCUSED)
# except Exception as e: # except Exception as e:
# logger.error(f"启动HFC 兴趣评估失败: {e}", exc_info=True) # logger.error(f"启动HFC 兴趣评估失败: {e}", exc_info=True)
async def sbhf_focus_into_normal(self, subflow_id: Any): async def sbhf_focus_into_normal(self, subflow_id: Any):
""" """
@@ -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

@@ -49,14 +49,14 @@ class NormalChat:
self.last_speak_time = 0 self.last_speak_time = 0
self._chat_task: Optional[asyncio.Task] = None self._chat_task: Optional[asyncio.Task] = None
self._initialized = False # Track initialization status self._initialized = False # Track initialization status
# 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply} # 记录最近的回复内容,每项包含: {time, user_message, response, is_mentioned, is_reference_reply}
self.recent_replies = [] self.recent_replies = []
self.max_replies_history = 20 # 最多保存最近20条回复记录 self.max_replies_history = 20 # 最多保存最近20条回复记录
# 添加回调函数用于在满足条件时通知切换到focus_chat模式 # 添加回调函数用于在满足条件时通知切换到focus_chat模式
self.on_switch_to_focus_callback = on_switch_to_focus_callback self.on_switch_to_focus_callback = on_switch_to_focus_callback
# 最近回复检查相关 # 最近回复检查相关
self._last_check_time = time.time() self._last_check_time = time.time()
self._check_interval = 10 # 每10秒检查一次是否需要切换到focus模式 self._check_interval = 10 # 每10秒检查一次是否需要切换到focus模式
@@ -207,12 +207,12 @@ class NormalChat:
if self._chat_task is None or self._chat_task.cancelled(): if self._chat_task is None or self._chat_task.cancelled():
logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出") logger.info(f"[{self.stream_name}] 兴趣监控任务被取消或置空,退出")
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:
@@ -329,28 +329,28 @@ class NormalChat:
# 检查 first_bot_msg 是否为 None (例如思考消息已被移除的情况) # 检查 first_bot_msg 是否为 None (例如思考消息已被移除的情况)
if first_bot_msg: if first_bot_msg:
info_catcher.catch_after_response(timing_results["消息发送"], response_set, first_bot_msg) info_catcher.catch_after_response(timing_results["消息发送"], response_set, first_bot_msg)
# 记录回复信息到最近回复列表中 # 记录回复信息到最近回复列表中
reply_info = { reply_info = {
"time": time.time(), "time": time.time(),
"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()
else: else:
logger.warning(f"[{self.stream_name}] 思考消息 {thinking_id} 在发送前丢失,无法记录 info_catcher") logger.warning(f"[{self.stream_name}] 思考消息 {thinking_id} 在发送前丢失,无法记录 info_catcher")
@@ -563,10 +563,10 @@ class NormalChat:
# 获取最近回复记录的方法 # 获取最近回复记录的方法
def get_recent_replies(self, limit: int = 10) -> List[dict]: def get_recent_replies(self, limit: int = 10) -> List[dict]:
"""获取最近的回复记录 """获取最近的回复记录
Args: Args:
limit: 最大返回数量默认10条 limit: 最大返回数量默认10条
Returns: Returns:
List[dict]: 最近的回复记录列表,每项包含: List[dict]: 最近的回复记录列表,每项包含:
time: 回复时间戳 time: 回复时间戳
@@ -583,21 +583,23 @@ class NormalChat:
async def _check_switch_to_focus(self) -> None: async def _check_switch_to_focus(self) -> None:
"""检查是否满足切换到focus模式的条件""" """检查是否满足切换到focus模式的条件"""
if not self.on_switch_to_focus_callback: if not self.on_switch_to_focus_callback:
return # 如果没有设置回调函数,直接返回 return # 如果没有设置回调函数,直接返回
current_time = time.time() current_time = time.time()
time_threshold = 120 / global_config.focus_chat.auto_focus_threshold time_threshold = 120 / global_config.focus_chat.auto_focus_threshold
reply_threshold = 6 * global_config.focus_chat.auto_focus_threshold reply_threshold = 6 * global_config.focus_chat.auto_focus_threshold
one_minute_ago = current_time - time_threshold one_minute_ago = current_time - time_threshold
# 统计1分钟内的回复数量 # 统计1分钟内的回复数量
recent_reply_count = sum(1 for reply in self.recent_replies if reply["time"] > one_minute_ago) recent_reply_count = sum(1 for reply in self.recent_replies if reply["time"] > one_minute_ago)
# print(111111111111111333333333333333333333333331111111111111111111111111111111111) # print(111111111111111333333333333333333333333331111111111111111111111111111111111)
# 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,12 +44,13 @@ 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):
"""关系配置类""" """关系配置类"""
give_name: bool = False give_name: bool = False
"""是否给其他人取名""" """是否给其他人取名"""
@dataclass @dataclass
@@ -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
"""可观察到的最长上下文大小,超过这个值的上下文会被压缩""" """可观察到的最长上下文大小,超过这个值的上下文会被压缩"""
@@ -381,17 +385,16 @@ 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: {})
"""组件模型配置""" """组件模型配置"""
utils_small: dict[str, Any] = field(default_factory=lambda: {}) utils_small: dict[str, Any] = field(default_factory=lambda: {})
"""组件小模型配置""" """组件小模型配置"""
normal_chat_1: dict[str, Any] = field(default_factory=lambda: {}) normal_chat_1: dict[str, Any] = field(default_factory=lambda: {})
"""normal_chat首要回复模型模型配置""" """normal_chat首要回复模型模型配置"""
normal_chat_2: dict[str, Any] = field(default_factory=lambda: {}) normal_chat_2: dict[str, Any] = field(default_factory=lambda: {})
"""normal_chat次要回复模型配置""" """normal_chat次要回复模型配置"""
@@ -403,22 +406,22 @@ class ModelConfig(ConfigBase):
focus_working_memory: dict[str, Any] = field(default_factory=lambda: {}) focus_working_memory: dict[str, Any] = field(default_factory=lambda: {})
"""专注工作记忆模型配置""" """专注工作记忆模型配置"""
focus_chat_mind: dict[str, Any] = field(default_factory=lambda: {}) focus_chat_mind: dict[str, Any] = field(default_factory=lambda: {})
"""专注聊天规划模型配置""" """专注聊天规划模型配置"""
focus_self_recognize: dict[str, Any] = field(default_factory=lambda: {}) focus_self_recognize: dict[str, Any] = field(default_factory=lambda: {})
"""专注自我识别模型配置""" """专注自我识别模型配置"""
focus_tool_use: dict[str, Any] = field(default_factory=lambda: {}) focus_tool_use: dict[str, Any] = field(default_factory=lambda: {})
"""专注工具使用模型配置""" """专注工具使用模型配置"""
focus_planner: dict[str, Any] = field(default_factory=lambda: {}) focus_planner: dict[str, Any] = field(default_factory=lambda: {})
"""专注规划模型配置""" """专注规划模型配置"""
focus_expressor: dict[str, Any] = field(default_factory=lambda: {}) focus_expressor: dict[str, Any] = field(default_factory=lambda: {})
"""专注表达器模型配置""" """专注表达器模型配置"""
embedding: dict[str, Any] = field(default_factory=lambda: {}) embedding: 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

@@ -636,18 +636,18 @@ class LLMRequest:
"messages": messages, "messages": messages,
**params_copy, **params_copy,
} }
# 添加temp参数如果不是默认值0.7 # 添加temp参数如果不是默认值0.7
if self.temp != 0.7: if self.temp != 0.7:
payload["temperature"] = self.temp payload["temperature"] = self.temp
# 添加enable_thinking参数如果不是默认值False # 添加enable_thinking参数如果不是默认值False
if not self.enable_thinking: if not self.enable_thinking:
payload["enable_thinking"] = False payload["enable_thinking"] = False
if self.thinking_budget != 4096: if self.thinking_budget != 4096:
payload["thinking_budget"] = self.thinking_budget payload["thinking_budget"] = self.thinking_budget
if "max_tokens" not in payload and "max_completion_tokens" not in payload: if "max_tokens" not in payload and "max_completion_tokens" not in payload:
payload["max_tokens"] = global_config.model.model_max_output_length payload["max_tokens"] = global_config.model.model_max_output_length
# 如果 payload 中依然存在 max_tokens 且需要转换,在这里进行再次检查 # 如果 payload 中依然存在 max_tokens 且需要转换,在这里进行再次检查

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,15 +10,13 @@ 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关闭必填",
} }
action_require = [ action_require = [
"当群聊过于混乱需要安静时使用", "当群聊过于混乱需要安静时使用",
"当需要临时暂停群聊讨论时使用", "当需要临时暂停群聊讨论时使用",
"当有人要求开启全体禁言时使用", "当有人要求开启全体禁言时使用",
"当管理员需要发布重要公告时使用", "当管理员需要发布重要公告时使用",
] ]
@@ -31,7 +29,7 @@ class GroupWholeBanAction(PluginAction):
# 获取参数 # 获取参数
enable = self.action_data.get("enable") enable = self.action_data.get("enable")
if enable is None: if enable is None:
error_msg = "全体禁言参数不完整需要enable参数" error_msg = "全体禁言参数不完整需要enable参数"
logger.error(f"{self.log_prefix} {error_msg}") logger.error(f"{self.log_prefix} {error_msg}")
@@ -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,20 +52,12 @@ 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}全体禁言"
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 执行全体禁言动作时出错: {e}") logger.error(f"{self.log_prefix} 执行全体禁言动作时出错: {e}")
await self.send_message_by_expressor(f"执行全体禁言动作时出错: {e}") await self.send_message_by_expressor(f"执行全体禁言动作时出错: {e}")
return False, f"执行全体禁言动作时出错: {e}" return False, f"执行全体禁言动作时出错: {e}"

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": "禁言时长,输入你要禁言的时长,单位为秒,必填,必须为数字",
@@ -38,7 +36,7 @@ class MuteAction(PluginAction):
target = self.action_data.get("target") target = self.action_data.get("target")
duration = self.action_data.get("duration") duration = self.action_data.get("duration")
reason = self.action_data.get("reason", "违反群规") reason = self.action_data.get("reason", "违反群规")
if not target or not duration: if not target or not duration:
error_msg = "禁言参数不完整需要target和duration" error_msg = "禁言参数不完整需要target和duration"
logger.error(f"{self.log_prefix} {error_msg}") logger.error(f"{self.log_prefix} {error_msg}")
@@ -46,7 +44,7 @@ class MuteAction(PluginAction):
# 获取用户ID # 获取用户ID
platform, user_id = await self.get_user_id_by_person_name(target) platform, user_id = await self.get_user_id_by_person_name(target)
if not user_id: if not user_id:
error_msg = f"未找到用户 {target} 的ID" error_msg = f"未找到用户 {target} 的ID"
logger.error(f"{self.log_prefix} {error_msg}") logger.error(f"{self.log_prefix} {error_msg}")
@@ -58,19 +56,12 @@ class MuteAction(PluginAction):
try: try:
# 确保duration是字符串类型 # 确保duration是字符串类型
duration_str = str(duration) duration_str = str(duration)
# 发送群聊禁言命令,按照新格式 # 发送群聊禁言命令,按照新格式
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}")
return True, f"成功禁言 {target},时长 {duration}" return True, f"成功禁言 {target},时长 {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 # 思考间隔 单位秒,可以有效减少消耗