refactor(planner): 重构动作规划器为模块化结构
将原有的 `ActionPlanner` 类拆分为三个独立的模块:`PlanGenerator`、`PlanFilter` 和 `PlanExecutor`。`ActionPlanner` 现在作为协调器,按顺序调用这三个模块,使规划流程更加清晰和模块化。 - **PlanGenerator**: 负责根据聊天模式和上下文生成初始规划。 - **PlanFilter**: 负责审查和筛选由生成器产生的动作。 - **PlanExecutor**: 负责执行最终确定的动作。 此重构简化了 `cycle_processor` 中的调用逻辑,并为未来的功能扩展(如更复杂的过滤规则)提供了更好的基础。同时,引入了新的 `Plan` 数据模型来统一规划过程中的数据传递。
This commit is contained in:
@@ -180,7 +180,7 @@ class CycleProcessor:
|
||||
cycle_timers, thinking_id = self.cycle_tracker.start_cycle()
|
||||
logger.info(f"{self.log_prefix} 开始第{self.context.cycle_counter}次思考")
|
||||
|
||||
if ENABLE_S4U:
|
||||
if ENABLE_S4U and self.context.chat_stream and self.context.chat_stream.user_info:
|
||||
await send_typing(self.context.chat_stream.user_info.user_id)
|
||||
|
||||
loop_start_time = time.time()
|
||||
@@ -194,30 +194,17 @@ class CycleProcessor:
|
||||
logger.error(f"{self.context.log_prefix} 动作修改失败: {e}")
|
||||
available_actions = {}
|
||||
|
||||
# 执行planner
|
||||
planner_info = self.action_planner.get_necessary_info()
|
||||
prompt_info = await self.action_planner.build_planner_prompt(
|
||||
is_group_chat=planner_info[0],
|
||||
chat_target_info=planner_info[1],
|
||||
current_available_actions=planner_info[2],
|
||||
)
|
||||
from src.plugin_system.core.event_manager import event_manager
|
||||
from src.plugin_system import EventType
|
||||
|
||||
# 触发规划前事件
|
||||
result = await event_manager.trigger_event(
|
||||
EventType.ON_PLAN, permission_group="SYSTEM", stream_id=self.context.chat_stream
|
||||
)
|
||||
if not result.all_continue_process():
|
||||
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于规划前中断了内容生成")
|
||||
|
||||
# 规划动作
|
||||
with Timer("规划器", cycle_timers):
|
||||
actions, _ = await self.action_planner.plan(
|
||||
mode=mode,
|
||||
loop_start_time=loop_start_time,
|
||||
available_actions=available_actions,
|
||||
)
|
||||
from src.plugin_system.core.event_manager import event_manager
|
||||
from src.plugin_system import EventType
|
||||
|
||||
result = await event_manager.trigger_event(
|
||||
EventType.ON_PLAN, permission_group="SYSTEM", stream_id=self.context.chat_stream
|
||||
)
|
||||
if not result.all_continue_process():
|
||||
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于规划前中断了内容生成")
|
||||
with Timer("规划器", cycle_timers):
|
||||
actions, _ = await self.action_planner.plan(mode=mode)
|
||||
|
||||
async def execute_action(action_info):
|
||||
"""执行单个动作的通用函数"""
|
||||
@@ -373,7 +360,7 @@ class CycleProcessor:
|
||||
self.context.chat_instance.cycle_tracker.end_cycle(loop_info, cycle_timers)
|
||||
self.context.chat_instance.cycle_tracker.print_cycle_info(cycle_timers)
|
||||
|
||||
action_type = actions[0]["action_type"] if actions else "no_action"
|
||||
action_type = actions["action_type"] if actions else "no_action"
|
||||
return action_type
|
||||
|
||||
async def _handle_action(
|
||||
@@ -424,7 +411,7 @@ class CycleProcessor:
|
||||
if "reply" in available_actions:
|
||||
fallback_action = "reply"
|
||||
elif available_actions:
|
||||
fallback_action = list(available_actions.keys())[0]
|
||||
fallback_action = list(available_actions.keys())
|
||||
|
||||
if fallback_action and fallback_action != action:
|
||||
logger.info(f"{self.context.log_prefix} 使用回退动作: {fallback_action}")
|
||||
|
||||
@@ -117,64 +117,6 @@ class ProactiveThinker:
|
||||
trigger_event (ProactiveTriggerEvent): 触发事件。
|
||||
"""
|
||||
try:
|
||||
# 如果是提醒事件,直接使用当前上下文执行at_user动作
|
||||
if trigger_event.source == "reminder_system":
|
||||
# 1. 获取上下文信息
|
||||
metadata = trigger_event.metadata or {}
|
||||
reminder_content = trigger_event.reason.replace("定时提醒:", "").strip()
|
||||
|
||||
# 2. 使用LLM智能解析目标用户名
|
||||
target_user_name = None
|
||||
|
||||
# 首先尝试从完整的原始信息中解析(如果有的话)
|
||||
full_content = trigger_event.reason
|
||||
logger.info(f"{self.context.log_prefix} 解析提醒内容: '{full_content}'")
|
||||
|
||||
sender_name = metadata.get("sender_name")
|
||||
target_user_name = await self._extract_target_user_with_llm(full_content, sender_name)
|
||||
|
||||
if not target_user_name:
|
||||
logger.warning(f"无法从提醒 '{reminder_content}' 中确定目标用户,回退")
|
||||
# 回退到生成普通提醒消息
|
||||
fallback_action = {
|
||||
"action_type": "proactive_reply",
|
||||
"action_data": {"topic": f"定时提醒:{reminder_content}"},
|
||||
"action_message": metadata
|
||||
}
|
||||
await self._generate_reminder_proactive_reply(fallback_action, trigger_event, reminder_content)
|
||||
return
|
||||
|
||||
# 3. 直接使用当前上下文的cycle_processor执行at_user动作
|
||||
try:
|
||||
success, _, _ = await self.cycle_processor._handle_action(
|
||||
action="at_user",
|
||||
reasoning="执行定时提醒",
|
||||
action_data={
|
||||
"user_name": target_user_name,
|
||||
"at_message": reminder_content
|
||||
},
|
||||
cycle_timers={},
|
||||
thinking_id="",
|
||||
action_message=metadata,
|
||||
)
|
||||
if success:
|
||||
logger.info(f"{self.context.log_prefix} 成功执行定时提醒艾特用户 {target_user_name}")
|
||||
return
|
||||
else:
|
||||
raise Exception("at_user action failed")
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.context.log_prefix} at_user动作执行失败: {e},回退到专用提醒回复")
|
||||
# 回退到专用的定时提醒回复
|
||||
fallback_action = {
|
||||
"action_type": "proactive_reply",
|
||||
"action_data": {"topic": f"定时提醒:{reminder_content}"},
|
||||
"action_message": metadata
|
||||
}
|
||||
await self._generate_reminder_proactive_reply(fallback_action, trigger_event, reminder_content)
|
||||
return
|
||||
|
||||
else:
|
||||
# 对于其他来源的主动思考,正常调用规划器
|
||||
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")
|
||||
@@ -196,164 +138,7 @@ class ProactiveThinker:
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 主动思考执行异常: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
async def _extract_target_user_with_llm(self, reminder_content: str, sender_name: str) -> str:
|
||||
"""
|
||||
使用LLM从提醒内容中提取目标用户名
|
||||
|
||||
Args:
|
||||
reminder_content: 完整的提醒内容
|
||||
sender_name: 消息发送者的昵称
|
||||
|
||||
Returns:
|
||||
提取出的用户名,如果找不到则返回None
|
||||
"""
|
||||
try:
|
||||
from src.llm_models.utils_model import LLMRequest
|
||||
from src.config.config import model_config
|
||||
|
||||
bot_name = global_config.bot.nickname
|
||||
user_extraction_prompt = f'''
|
||||
从以下提醒消息中提取需要被提醒的目标用户名。
|
||||
|
||||
**重要认知**:
|
||||
- 你的名字是"{bot_name}"。当消息中提到"{bot_name}"时,通常是在称呼你。
|
||||
- 消息的发送者是"{sender_name}"。当消息中出现"我"、"咱"等第一人称代词时,指代的就是"{sender_name}"。
|
||||
|
||||
提醒消息: "{reminder_content}"
|
||||
|
||||
规则:
|
||||
1. 分析消息,找出真正需要被提醒的人。
|
||||
2. 如果提醒目标是第一人称(如"我"),那么目标就是发送者"{sender_name}"。
|
||||
3. **绝对不能**提取你自己的名字("{bot_name}")作为目标。
|
||||
4. 只提取最关键的人名,不要包含多余的词语。
|
||||
5. 如果没有明确的提醒目标(既不是其他人,也不是发送者自己),请回答"无"。
|
||||
|
||||
示例:
|
||||
- 消息: "定时提醒:{bot_name},10分钟后提醒我去打深渊" -> "{sender_name}"
|
||||
- 消息: "定时提醒:{bot_name},提醒阿范一分钟后去写模组" -> "阿范"
|
||||
- 消息: "定时提醒:一分钟后提醒一闪喝水" -> "一闪"
|
||||
- 消息: "定时提醒:喝水" -> "无"
|
||||
- 消息: "定时提醒:{bot_name},记得休息" -> "无"
|
||||
|
||||
请直接输出提取到的用户名,如果不存在则输出"无"。
|
||||
'''
|
||||
|
||||
llm_request = LLMRequest(
|
||||
model_set=model_config.model_task_config.utils_small,
|
||||
request_type="reminder_user_extraction"
|
||||
)
|
||||
|
||||
response, _ = await llm_request.generate_response_async(prompt=user_extraction_prompt)
|
||||
|
||||
if response and response.strip() != "无":
|
||||
logger.info(f"LLM成功提取目标用户: '{response.strip()}'")
|
||||
return response.strip()
|
||||
else:
|
||||
logger.warning(f"LLM未能从 '{reminder_content}' 中提取目标用户")
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"使用LLM提取用户名时出错: {e}")
|
||||
return None
|
||||
|
||||
async def _generate_reminder_proactive_reply(self, action_result: Dict[str, Any], trigger_event: ProactiveTriggerEvent, reminder_content: str):
|
||||
"""
|
||||
为定时提醒事件生成专用的主动回复
|
||||
|
||||
Args:
|
||||
action_result: 动作结果
|
||||
trigger_event: 触发事件
|
||||
reminder_content: 提醒内容
|
||||
"""
|
||||
try:
|
||||
logger.info(f"{self.context.log_prefix} 生成定时提醒专用回复: '{reminder_content}'")
|
||||
|
||||
# 获取基本信息
|
||||
bot_name = global_config.bot.nickname
|
||||
personality = global_config.personality
|
||||
identity_block = (
|
||||
f"你的名字是{bot_name}。\n"
|
||||
f"关于你:{personality.personality_core},并且{personality.personality_side}。\n"
|
||||
f"你的身份是{personality.identity},平时说话风格是{personality.reply_style}。"
|
||||
)
|
||||
mood_block = f"你现在的心情是:{mood_manager.get_mood_by_chat_id(self.context.stream_id).mood_state}"
|
||||
|
||||
# 获取日程信息
|
||||
schedule_block = "你今天没有日程安排。"
|
||||
if global_config.planning_system.schedule_enable:
|
||||
if current_activity := schedule_manager.get_current_activity():
|
||||
schedule_block = f"你当前正在:{current_activity}。"
|
||||
|
||||
# 为定时提醒定制的专用提示词
|
||||
reminder_prompt = f"""
|
||||
## 你的角色
|
||||
{identity_block}
|
||||
|
||||
## 你的心情
|
||||
{mood_block}
|
||||
|
||||
## 你今天的日程安排
|
||||
{schedule_block}
|
||||
|
||||
## 定时提醒任务
|
||||
你收到了一个定时提醒:"{reminder_content}"
|
||||
这是一个自动触发的提醒事件,你需要根据提醒内容发送一条友好的提醒消息。
|
||||
|
||||
## 任务要求
|
||||
- 这是一个定时提醒,要体现出你的贴心和关怀
|
||||
- 根据提醒内容的具体情况(如"喝水"、"休息"等)给出相应的提醒
|
||||
- 保持你一贯的温暖、俏皮风格
|
||||
- 可以加上一些鼓励或关心的话语
|
||||
- 直接输出提醒消息,不要解释为什么要提醒
|
||||
|
||||
请生成一条温暖贴心的提醒消息。
|
||||
"""
|
||||
|
||||
response_text = await generator_api.generate_response_custom(
|
||||
chat_stream=self.context.chat_stream,
|
||||
prompt=reminder_prompt,
|
||||
request_type="chat.replyer.reminder",
|
||||
)
|
||||
|
||||
if response_text:
|
||||
response_set = process_human_text(
|
||||
content=response_text,
|
||||
enable_splitter=global_config.response_splitter.enable,
|
||||
enable_chinese_typo=global_config.chinese_typo.enable,
|
||||
)
|
||||
await self.cycle_processor.response_handler.send_response(
|
||||
response_set, time.time(), action_result.get("action_message")
|
||||
)
|
||||
await store_action_info(
|
||||
chat_stream=self.context.chat_stream,
|
||||
action_name="reminder_reply",
|
||||
action_data={"reminder_content": reminder_content, "response": response_text},
|
||||
action_prompt_display=f"定时提醒回复: {reminder_content}",
|
||||
action_done=True,
|
||||
)
|
||||
logger.info(f"{self.context.log_prefix} 成功发送定时提醒回复: {response_text}")
|
||||
else:
|
||||
logger.error(f"{self.context.log_prefix} 定时提醒回复生成失败。")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 生成定时提醒回复时异常: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
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):
|
||||
"""
|
||||
获取实时信息,构建最终的生成提示词,并生成和发送主动回复。
|
||||
@@ -394,10 +179,6 @@ class ProactiveThinker:
|
||||
logger.warning(f"{self.context.log_prefix} 未找到 web_search 工具实例。")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.context.log_prefix} 主动思考时网络搜索失败: {e}")
|
||||
|
||||
if trigger_event.source == "reminder_system" and trigger_event.related_message_id:
|
||||
chat_context_block = await self._get_reminder_context(trigger_event.related_message_id)
|
||||
else:
|
||||
message_list = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=self.context.stream_id,
|
||||
timestamp=time.time(),
|
||||
|
||||
Reference in New Issue
Block a user