主动思考定时任务优化,目前有个问题at动作会认为当建环境环是群聊,我多次尝试解决不了……唉
This commit is contained in:
@@ -11,3 +11,4 @@ class ProactiveTriggerEvent:
|
|||||||
source: str # 触发源的标识,例如 "silence_monitor", "insomnia_manager"
|
source: str # 触发源的标识,例如 "silence_monitor", "insomnia_manager"
|
||||||
reason: str # 触发的具体原因,例如 "聊天已沉默10分钟", "深夜emo"
|
reason: str # 触发的具体原因,例如 "聊天已沉默10分钟", "深夜emo"
|
||||||
metadata: Optional[Dict[str, Any]] = field(default_factory=dict) # 可选的元数据,用于传递额外信息
|
metadata: Optional[Dict[str, Any]] = field(default_factory=dict) # 可选的元数据,用于传递额外信息
|
||||||
|
related_message_id: Optional[str] = None # 关联的消息ID,用于加载上下文
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import orjson
|
import orjson
|
||||||
|
import re
|
||||||
from typing import TYPE_CHECKING, Dict, Any
|
from typing import TYPE_CHECKING, Dict, Any
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
@@ -15,7 +16,8 @@ from src.plugin_system.base.component_types import ComponentType
|
|||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages_with_id
|
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages_with_id
|
||||||
from src.mood.mood_manager import mood_manager
|
from src.mood.mood_manager import mood_manager
|
||||||
from src.common.database.sqlalchemy_database_api import store_action_info
|
from src.common.database.sqlalchemy_database_api import store_action_info, db_get
|
||||||
|
from src.common.database.sqlalchemy_models import Messages
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..cycle_processor import CycleProcessor
|
from ..cycle_processor import CycleProcessor
|
||||||
@@ -118,85 +120,156 @@ class ProactiveThinker:
|
|||||||
trigger_event (ProactiveTriggerEvent): 触发事件。
|
trigger_event (ProactiveTriggerEvent): 触发事件。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 调用规划器的 PROACTIVE 模式,让其决定下一步的行动
|
# 如果是提醒事件,跳过规划器,直接构建默认动作
|
||||||
actions, _ = await self.cycle_processor.action_planner.plan(mode=ChatMode.PROACTIVE)
|
if trigger_event.source == "reminder_system":
|
||||||
|
# 1. 获取原始消息上下文
|
||||||
|
action_message = {}
|
||||||
|
if trigger_event.related_message_id:
|
||||||
|
# 直接将从数据库获取的完整消息记录作为 action_message
|
||||||
|
action_message = await db_get(
|
||||||
|
Messages, {"message_id": trigger_event.related_message_id}, single_result=True
|
||||||
|
) or {}
|
||||||
|
|
||||||
# 通常只关心规划出的第一个动作
|
# 2. 智能确定@对象
|
||||||
action_result = actions[0] if actions else {}
|
reason_text = trigger_event.reason.replace("定时提醒:", "").strip()
|
||||||
|
user_name_match = re.search(r"艾特一下(\S+)", reason_text)
|
||||||
|
|
||||||
|
if user_name_match:
|
||||||
|
user_name = user_name_match.group(1)
|
||||||
|
at_message = reason_text.replace(f"艾特一下{user_name}", "").strip()
|
||||||
|
elif action_message.get("user_nickname"):
|
||||||
|
user_name = action_message.get("user_nickname")
|
||||||
|
at_message = reason_text
|
||||||
|
else:
|
||||||
|
user_name = "我"
|
||||||
|
at_message = reason_text
|
||||||
|
|
||||||
action_type = action_result.get("action_type")
|
# 3. 构建动作
|
||||||
|
action_result = {
|
||||||
|
"action_type": "at_user",
|
||||||
|
"reasoning": "执行定时提醒",
|
||||||
|
"action_data": {
|
||||||
|
"user_name": user_name,
|
||||||
|
"at_message": at_message or "时间到啦!"
|
||||||
|
},
|
||||||
|
"action_message": action_message
|
||||||
|
}
|
||||||
|
|
||||||
|
# 4. 执行或回退
|
||||||
|
try:
|
||||||
|
success, _, _ = await self.cycle_processor._handle_action(
|
||||||
|
action=action_result["action_type"],
|
||||||
|
reasoning=action_result["reasoning"],
|
||||||
|
action_data=action_result["action_data"],
|
||||||
|
cycle_timers={},
|
||||||
|
thinking_id="",
|
||||||
|
action_message=action_result["action_message"]
|
||||||
|
)
|
||||||
|
if not success:
|
||||||
|
raise Exception("at_user action failed")
|
||||||
|
except Exception:
|
||||||
|
logger.warning(f"{self.context.log_prefix} at_user动作执行失败,回退到proactive_reply")
|
||||||
|
fallback_action = {
|
||||||
|
"action_type": "proactive_reply",
|
||||||
|
"action_data": {"topic": trigger_event.reason},
|
||||||
|
"action_message": action_message
|
||||||
|
}
|
||||||
|
await self._generate_proactive_content_and_send(fallback_action, trigger_event)
|
||||||
|
|
||||||
if action_type == "proactive_reply":
|
|
||||||
await self._generate_proactive_content_and_send(action_result)
|
|
||||||
elif action_type != "do_nothing":
|
|
||||||
logger.warning(f"{self.context.log_prefix} 主动思考返回了未知的动作类型: {action_type}")
|
|
||||||
else:
|
else:
|
||||||
# 如果规划结果是“什么都不做”,则记录日志
|
# 对于其他来源的主动思考,正常调用规划器
|
||||||
logger.info(f"{self.context.log_prefix} 主动思考决策: 保持沉默")
|
actions, _ = await self.cycle_processor.action_planner.plan(mode=ChatMode.PROACTIVE)
|
||||||
|
action_result = actions[0] if actions else {}
|
||||||
|
action_type = action_result.get("action_type")
|
||||||
|
|
||||||
|
if action_type == "proactive_reply":
|
||||||
|
await self._generate_proactive_content_and_send(action_result, trigger_event)
|
||||||
|
elif action_type not in ["do_nothing", "no_action"]:
|
||||||
|
await self.cycle_processor._handle_action(
|
||||||
|
action=action_result["action_type"],
|
||||||
|
reasoning=action_result.get("reasoning", ""),
|
||||||
|
action_data=action_result.get("action_data", {}),
|
||||||
|
cycle_timers={},
|
||||||
|
thinking_id="",
|
||||||
|
action_message=action_result.get("action_message")
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
logger.info(f"{self.context.log_prefix} 主动思考决策: 保持沉默")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.context.log_prefix} 主动思考执行异常: {e}")
|
logger.error(f"{self.context.log_prefix} 主动思考执行异常: {e}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
async def _generate_proactive_content_and_send(self, action_result: Dict[str, Any]):
|
async def _get_reminder_context(self, message_id: str) -> str:
|
||||||
|
"""获取提醒消息的上下文"""
|
||||||
|
try:
|
||||||
|
# 只获取那一条消息
|
||||||
|
message_record = await db_get(Messages, {"message_id": message_id}, single_result=True)
|
||||||
|
if message_record:
|
||||||
|
# 使用 build_readable_messages_with_id 来格式化单条消息
|
||||||
|
chat_context_block, _ = build_readable_messages_with_id(messages=[message_record])
|
||||||
|
return chat_context_block
|
||||||
|
return "无法加载相关的聊天记录。"
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{self.context.log_prefix} 获取提醒上下文失败: {e}")
|
||||||
|
return "无法加载相关的聊天记录。"
|
||||||
|
|
||||||
|
async def _generate_proactive_content_and_send(self, action_result: Dict[str, Any], trigger_event: ProactiveTriggerEvent):
|
||||||
"""
|
"""
|
||||||
获取实时信息,构建最终的生成提示词,并生成和发送主动回复。
|
获取实时信息,构建最终的生成提示词,并生成和发送主动回复。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
action_result (Dict[str, Any]): 规划器返回的动作结果。
|
action_result (Dict[str, Any]): 规划器返回的动作结果。
|
||||||
|
trigger_event (ProactiveTriggerEvent): 触发事件。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
topic = action_result.get("action_data", {}).get("topic", "随便聊聊")
|
topic = action_result.get("action_data", {}).get("topic", "随便聊聊")
|
||||||
logger.info(f"{self.context.log_prefix} 主动思考确定主题: '{topic}'")
|
logger.info(f"{self.context.log_prefix} 主动思考确定主题: '{topic}'")
|
||||||
|
|
||||||
# 1. 获取日程信息
|
|
||||||
schedule_block = "你今天没有日程安排。"
|
schedule_block = "你今天没有日程安排。"
|
||||||
if global_config.planning_system.schedule_enable:
|
if global_config.planning_system.schedule_enable:
|
||||||
if current_activity := schedule_manager.get_current_activity():
|
if current_activity := schedule_manager.get_current_activity():
|
||||||
schedule_block = f"你当前正在:{current_activity}。"
|
schedule_block = f"你当前正在:{current_activity}。"
|
||||||
|
|
||||||
# 2. 网络搜索
|
|
||||||
news_block = "暂时没有获取到最新资讯。"
|
news_block = "暂时没有获取到最新资讯。"
|
||||||
try:
|
if trigger_event.source != "reminder_system":
|
||||||
web_search_tool = tool_api.get_tool_instance("web_search")
|
try:
|
||||||
if web_search_tool:
|
web_search_tool = tool_api.get_tool_instance("web_search")
|
||||||
# 检查工具的execute方法签名,使用正确的参数名
|
if web_search_tool:
|
||||||
try:
|
|
||||||
search_result_dict = await web_search_tool.execute(search_query=topic, max_results=10)
|
|
||||||
except TypeError:
|
|
||||||
# 如果search_query不工作,尝试其他可能的参数名
|
|
||||||
try:
|
try:
|
||||||
search_result_dict = await web_search_tool.execute(keyword=topic, max_results=10)
|
search_result_dict = await web_search_tool.execute(search_query=topic, max_results=10)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# 跳过网络搜索,避免影响主动思考
|
try:
|
||||||
logger.warning(f"{self.context.log_prefix} 网络搜索工具参数不匹配,跳过搜索")
|
search_result_dict = await web_search_tool.execute(keyword=topic, max_results=10)
|
||||||
news_block = "跳过网络搜索。"
|
except TypeError:
|
||||||
search_result_dict = None
|
logger.warning(f"{self.context.log_prefix} 网络搜索工具参数不匹配,跳过搜索")
|
||||||
|
news_block = "跳过网络搜索。"
|
||||||
if search_result_dict and not search_result_dict.get("error"):
|
search_result_dict = None
|
||||||
news_block = search_result_dict.get("content", "未能提取有效资讯。")
|
|
||||||
elif search_result_dict:
|
if search_result_dict and not search_result_dict.get("error"):
|
||||||
logger.warning(f"{self.context.log_prefix} 网络搜索返回错误: {search_result_dict.get('error')}")
|
news_block = search_result_dict.get("content", "未能提取有效资讯。")
|
||||||
else:
|
elif search_result_dict:
|
||||||
logger.warning(f"{self.context.log_prefix} 未找到 web_search 工具实例。")
|
logger.warning(f"{self.context.log_prefix} 网络搜索返回错误: {search_result_dict.get('error')}")
|
||||||
except Exception as e:
|
else:
|
||||||
logger.error(f"{self.context.log_prefix} 主动思考时网络搜索失败: {e}")
|
logger.warning(f"{self.context.log_prefix} 未找到 web_search 工具实例。")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"{self.context.log_prefix} 主动思考时网络搜索失败: {e}")
|
||||||
|
|
||||||
# 3. 获取最新的聊天上下文
|
if trigger_event.source == "reminder_system" and trigger_event.related_message_id:
|
||||||
message_list = get_raw_msg_before_timestamp_with_chat(
|
chat_context_block = await self._get_reminder_context(trigger_event.related_message_id)
|
||||||
chat_id=self.context.stream_id,
|
else:
|
||||||
timestamp=time.time(),
|
message_list = get_raw_msg_before_timestamp_with_chat(
|
||||||
limit=int(global_config.chat.max_context_size * 0.3),
|
chat_id=self.context.stream_id,
|
||||||
)
|
timestamp=time.time(),
|
||||||
chat_context_block, _ = build_readable_messages_with_id(messages=message_list)
|
limit=int(global_config.chat.max_context_size * 0.3),
|
||||||
|
)
|
||||||
|
chat_context_block, _ = build_readable_messages_with_id(messages=message_list)
|
||||||
|
|
||||||
# 4. 使用决策模型进行二次确认(节省珍贵的回复模型调用)
|
|
||||||
from src.llm_models.utils_model import LLMRequest
|
from src.llm_models.utils_model import LLMRequest
|
||||||
from src.config.config import model_config
|
from src.config.config import model_config
|
||||||
|
|
||||||
bot_name = global_config.bot.nickname
|
bot_name = global_config.bot.nickname
|
||||||
|
|
||||||
# 构建二次确认提示词
|
|
||||||
confirmation_prompt = f"""# 主动回复二次确认
|
confirmation_prompt = f"""# 主动回复二次确认
|
||||||
|
|
||||||
## 基本信息
|
## 基本信息
|
||||||
@@ -219,7 +292,6 @@ class ProactiveThinker:
|
|||||||
|
|
||||||
请严格按照上述格式输出,不要添加任何解释。"""
|
请严格按照上述格式输出,不要添加任何解释。"""
|
||||||
|
|
||||||
# 使用决策模型进行二次确认
|
|
||||||
planner_llm = LLMRequest(
|
planner_llm = LLMRequest(
|
||||||
model_set=model_config.model_task_config.planner,
|
model_set=model_config.model_task_config.planner,
|
||||||
request_type="planner"
|
request_type="planner"
|
||||||
@@ -227,12 +299,10 @@ class ProactiveThinker:
|
|||||||
|
|
||||||
confirmation_result, _ = await planner_llm.generate_response_async(prompt=confirmation_prompt)
|
confirmation_result, _ = await planner_llm.generate_response_async(prompt=confirmation_prompt)
|
||||||
|
|
||||||
# 检查二次确认结果
|
|
||||||
if not confirmation_result or "SKIP_PROACTIVE_REPLY" in confirmation_result:
|
if not confirmation_result or "SKIP_PROACTIVE_REPLY" in confirmation_result:
|
||||||
logger.info(f"{self.context.log_prefix} 决策模型二次确认决定跳过主动回复")
|
logger.info(f"{self.context.log_prefix} 决策模型二次确认决定跳过主动回复")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 5. 只有通过二次确认才调用珍贵的回复模型
|
|
||||||
bot_name = global_config.bot.nickname
|
bot_name = global_config.bot.nickname
|
||||||
personality = global_config.personality
|
personality = global_config.personality
|
||||||
identity_block = (
|
identity_block = (
|
||||||
|
|||||||
@@ -199,7 +199,8 @@ class HeartFCMessageReceiver:
|
|||||||
"chat_id": chat.stream_id,
|
"chat_id": chat.stream_id,
|
||||||
"content": reminder_event.content,
|
"content": reminder_event.content,
|
||||||
"confidence": reminder_event.confidence,
|
"confidence": reminder_event.confidence,
|
||||||
"created_at": datetime.now().isoformat()
|
"created_at": datetime.now().isoformat(),
|
||||||
|
"original_message_id": message.message_info.message_id
|
||||||
}
|
}
|
||||||
|
|
||||||
success = await event_scheduler.schedule_event(
|
success = await event_scheduler.schedule_event(
|
||||||
|
|||||||
@@ -484,6 +484,7 @@ class ActionPlanner:
|
|||||||
mode: ChatMode = ChatMode.FOCUS,
|
mode: ChatMode = ChatMode.FOCUS,
|
||||||
loop_start_time: float = 0.0,
|
loop_start_time: float = 0.0,
|
||||||
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||||
|
pseudo_message: Optional[str] = None,
|
||||||
) -> Tuple[List[Dict[str, Any]], Optional[Dict[str, Any]]]:
|
) -> Tuple[List[Dict[str, Any]], Optional[Dict[str, Any]]]:
|
||||||
"""
|
"""
|
||||||
[注释] "大脑"规划器。
|
[注释] "大脑"规划器。
|
||||||
@@ -513,6 +514,8 @@ class ActionPlanner:
|
|||||||
truncate=False,
|
truncate=False,
|
||||||
show_actions=False,
|
show_actions=False,
|
||||||
)
|
)
|
||||||
|
if pseudo_message:
|
||||||
|
chat_content_block_short += f"\n[m99] 刚刚, 用户: {pseudo_message}"
|
||||||
self.last_obs_time_mark = time.time()
|
self.last_obs_time_mark = time.time()
|
||||||
|
|
||||||
is_group_chat, chat_target_info, current_available_actions = self.get_necessary_info()
|
is_group_chat, chat_target_info, current_available_actions = self.get_necessary_info()
|
||||||
@@ -522,8 +525,8 @@ class ActionPlanner:
|
|||||||
# --- 2. 启动小脑并行思考 ---
|
# --- 2. 启动小脑并行思考 ---
|
||||||
all_sub_planner_results: List[Dict[str, Any]] = []
|
all_sub_planner_results: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
# PROACTIVE模式下禁用小脑,避免与大脑的主动思考决策冲突
|
# PROACTIVE模式下,只有在有伪消息(来自提醒)时才激活小脑
|
||||||
if mode != ChatMode.PROACTIVE:
|
if mode != ChatMode.PROACTIVE or pseudo_message:
|
||||||
try:
|
try:
|
||||||
sub_planner_actions: Dict[str, ActionInfo] = {}
|
sub_planner_actions: Dict[str, ActionInfo] = {}
|
||||||
for action_name, action_info in available_actions.items():
|
for action_name, action_info in available_actions.items():
|
||||||
@@ -607,40 +610,45 @@ class ActionPlanner:
|
|||||||
action, reasoning = "no_reply", f"大脑处理错误: {e}"
|
action, reasoning = "no_reply", f"大脑处理错误: {e}"
|
||||||
|
|
||||||
# --- 4. 整合大脑和小脑的决策 ---
|
# --- 4. 整合大脑和小脑的决策 ---
|
||||||
# 如果是私聊且开启了强制回复,则将no_reply强制改为reply
|
# 特殊规则:如果是提醒任务,且大脑决定do_nothing,则忽略大脑,采用小脑的决策
|
||||||
if not is_group_chat and global_config.chat.force_reply_private and action == "no_reply":
|
if mode == ChatMode.PROACTIVE and pseudo_message and action == "do_nothing":
|
||||||
action = "reply"
|
logger.info(f"{self.log_prefix}提醒任务触发,大脑决策为do_nothing,忽略大脑并采用小脑决策")
|
||||||
reasoning = "私聊强制回复"
|
final_actions = all_sub_planner_results
|
||||||
logger.info(f"{self.log_prefix}私聊强制回复已触发,将动作从 'no_reply' 修改为 'reply'")
|
|
||||||
|
|
||||||
is_parallel = True
|
|
||||||
for info in all_sub_planner_results:
|
|
||||||
action_type = info.get("action_type")
|
|
||||||
if action_type and action_type not in ["no_action", "no_reply"]:
|
|
||||||
action_info = available_actions.get(action_type)
|
|
||||||
if action_info and not action_info.parallel_action:
|
|
||||||
is_parallel = False
|
|
||||||
break
|
|
||||||
|
|
||||||
action_data["loop_start_time"] = loop_start_time
|
|
||||||
final_actions: List[Dict[str, Any]] = []
|
|
||||||
|
|
||||||
if is_parallel:
|
|
||||||
logger.info(f"{self.log_prefix}决策模式: 大脑与小脑并行")
|
|
||||||
if action not in ["no_action", "no_reply"]:
|
|
||||||
final_actions.append(
|
|
||||||
{
|
|
||||||
"action_type": action,
|
|
||||||
"reasoning": reasoning,
|
|
||||||
"action_data": action_data,
|
|
||||||
"action_message": target_message,
|
|
||||||
"available_actions": available_actions,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
final_actions.extend(all_sub_planner_results)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"{self.log_prefix}决策模式: 小脑优先 (检测到非并行action)")
|
# 如果是私聊且开启了强制回复,则将no_reply强制改为reply
|
||||||
final_actions.extend(all_sub_planner_results)
|
if not is_group_chat and global_config.chat.force_reply_private and action == "no_reply":
|
||||||
|
action = "reply"
|
||||||
|
reasoning = "私聊强制回复"
|
||||||
|
logger.info(f"{self.log_prefix}私聊强制回复已触发,将动作从 'no_reply' 修改为 'reply'")
|
||||||
|
|
||||||
|
is_parallel = True
|
||||||
|
for info in all_sub_planner_results:
|
||||||
|
action_type = info.get("action_type")
|
||||||
|
if action_type and action_type not in ["no_action", "no_reply"]:
|
||||||
|
action_info = available_actions.get(action_type)
|
||||||
|
if action_info and not action_info.parallel_action:
|
||||||
|
is_parallel = False
|
||||||
|
break
|
||||||
|
|
||||||
|
action_data["loop_start_time"] = loop_start_time
|
||||||
|
final_actions: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
|
if is_parallel:
|
||||||
|
logger.info(f"{self.log_prefix}决策模式: 大脑与小脑并行")
|
||||||
|
if action not in ["no_action", "no_reply"]:
|
||||||
|
final_actions.append(
|
||||||
|
{
|
||||||
|
"action_type": action,
|
||||||
|
"reasoning": reasoning,
|
||||||
|
"action_data": action_data,
|
||||||
|
"action_message": target_message,
|
||||||
|
"available_actions": available_actions,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
final_actions.extend(all_sub_planner_results)
|
||||||
|
else:
|
||||||
|
logger.info(f"{self.log_prefix}决策模式: 小脑优先 (检测到非并行action)")
|
||||||
|
final_actions.extend(all_sub_planner_results)
|
||||||
|
|
||||||
final_actions = self._filter_no_actions(final_actions)
|
final_actions = self._filter_no_actions(final_actions)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user