feat(schedule): 优化日程提示并增加时间感知能力
日程系统现在可以更详细地描述当前活动,包括计划的起止时间、已进行时间和剩余时间,为AI角色提供更强的时间感知和情境感。 主要变更: - `schedule_manager`的`get_current_activity`现在返回包含活动和时间范围的字典,而不仅仅是活动名称。 - 在`default_generator`中,重构了日程提示的生成逻辑,使其能够计算并展示活动的详细时间信息。 - 修复了多处可能因变量为空(如`msg_content`、`user_nickname`)或事件处理结果为`None`而引发的潜在错误。 - 统一了各处对日程信息的调用方式。
This commit is contained in:
committed by
Windpicker-owo
parent
b7392ffea3
commit
28b56fc0b4
@@ -8,7 +8,7 @@ import random
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from src.chat.express.expression_selector import expression_selector
|
from src.chat.express.expression_selector import expression_selector
|
||||||
@@ -316,7 +316,7 @@ class DefaultReplyer:
|
|||||||
result = await event_manager.trigger_event(
|
result = await event_manager.trigger_event(
|
||||||
EventType.POST_LLM, permission_group="SYSTEM", prompt=prompt, stream_id=stream_id
|
EventType.POST_LLM, permission_group="SYSTEM", prompt=prompt, stream_id=stream_id
|
||||||
)
|
)
|
||||||
if not result.all_continue_process():
|
if result and not result.all_continue_process():
|
||||||
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于请求前中断了内容生成")
|
raise UserWarning(f"插件{result.get_summary().get('stopped_handlers', '')}于请求前中断了内容生成")
|
||||||
|
|
||||||
# 4. 调用 LLM 生成回复
|
# 4. 调用 LLM 生成回复
|
||||||
@@ -343,7 +343,7 @@ class DefaultReplyer:
|
|||||||
llm_response=llm_response,
|
llm_response=llm_response,
|
||||||
stream_id=stream_id,
|
stream_id=stream_id,
|
||||||
)
|
)
|
||||||
if not result.all_continue_process():
|
if result and not result.all_continue_process():
|
||||||
raise UserWarning(
|
raise UserWarning(
|
||||||
f"插件{result.get_summary().get('stopped_handlers', '')}于请求后取消了内容生成"
|
f"插件{result.get_summary().get('stopped_handlers', '')}于请求后取消了内容生成"
|
||||||
)
|
)
|
||||||
@@ -910,11 +910,12 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
# 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示
|
# 处理消息内容中的用户引用,确保bot回复在消息内容中也正确显示
|
||||||
from src.chat.utils.chat_message_builder import replace_user_references_sync
|
from src.chat.utils.chat_message_builder import replace_user_references_sync
|
||||||
msg_content = replace_user_references_sync(
|
if msg_content:
|
||||||
msg_content,
|
msg_content = replace_user_references_sync(
|
||||||
platform,
|
msg_content,
|
||||||
replace_bot_name=True
|
platform,
|
||||||
)
|
replace_bot_name=True
|
||||||
|
)
|
||||||
|
|
||||||
# 添加兴趣度信息
|
# 添加兴趣度信息
|
||||||
interest_score = interest_scores.get(msg_id, 0.0)
|
interest_score = interest_scores.get(msg_id, 0.0)
|
||||||
@@ -1205,8 +1206,8 @@ class DefaultReplyer:
|
|||||||
await person_info_manager.first_knowing_some_one(
|
await person_info_manager.first_knowing_some_one(
|
||||||
platform, # type: ignore
|
platform, # type: ignore
|
||||||
reply_message.get("user_id"), # type: ignore
|
reply_message.get("user_id"), # type: ignore
|
||||||
reply_message.get("user_nickname"),
|
reply_message.get("user_nickname") or "",
|
||||||
reply_message.get("user_cardname"),
|
reply_message.get("user_cardname") or "",
|
||||||
)
|
)
|
||||||
|
|
||||||
# 检查是否是bot自己的名字,如果是则替换为"(你)"
|
# 检查是否是bot自己的名字,如果是则替换为"(你)"
|
||||||
@@ -1403,9 +1404,44 @@ class DefaultReplyer:
|
|||||||
if global_config.planning_system.schedule_enable:
|
if global_config.planning_system.schedule_enable:
|
||||||
from src.schedule.schedule_manager import schedule_manager
|
from src.schedule.schedule_manager import schedule_manager
|
||||||
|
|
||||||
current_activity = schedule_manager.get_current_activity()
|
activity_info = schedule_manager.get_current_activity()
|
||||||
if current_activity:
|
if activity_info:
|
||||||
schedule_block = f"你当前正在:{current_activity}。"
|
activity = activity_info.get("activity")
|
||||||
|
time_range = activity_info.get("time_range")
|
||||||
|
now = datetime.now()
|
||||||
|
|
||||||
|
if time_range:
|
||||||
|
try:
|
||||||
|
start_str, end_str = time_range.split("-")
|
||||||
|
start_time = datetime.strptime(start_str.strip(), "%H:%M").replace(
|
||||||
|
year=now.year, month=now.month, day=now.day
|
||||||
|
)
|
||||||
|
end_time = datetime.strptime(end_str.strip(), "%H:%M").replace(
|
||||||
|
year=now.year, month=now.month, day=now.day
|
||||||
|
)
|
||||||
|
|
||||||
|
if end_time < start_time:
|
||||||
|
end_time += timedelta(days=1)
|
||||||
|
if now < start_time:
|
||||||
|
now += timedelta(days=1)
|
||||||
|
|
||||||
|
if start_time <= now < end_time:
|
||||||
|
duration_minutes = (now - start_time).total_seconds() / 60
|
||||||
|
remaining_minutes = (end_time - now).total_seconds() / 60
|
||||||
|
schedule_block = (
|
||||||
|
f"你当前正在进行“{activity}”,"
|
||||||
|
f"计划时间从{start_time.strftime('%H:%M')}到{end_time.strftime('%H:%M')}。"
|
||||||
|
f"这项活动已经开始了{duration_minutes:.0f}分钟,"
|
||||||
|
f"预计还有{remaining_minutes:.0f}分钟结束。"
|
||||||
|
"(日程只是提醒,你可以根据聊天内容灵活安排时间)"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
schedule_block = f"你当前正在:{activity}。"
|
||||||
|
|
||||||
|
except (ValueError, AttributeError):
|
||||||
|
schedule_block = f"你当前正在:{activity}。"
|
||||||
|
else:
|
||||||
|
schedule_block = f"你当前正在:{activity}。"
|
||||||
|
|
||||||
moderation_prompt_block = (
|
moderation_prompt_block = (
|
||||||
"请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
"请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||||
@@ -1511,7 +1547,7 @@ class DefaultReplyer:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 使用新的统一Prompt系统 - 使用正确的模板名称
|
# 使用新的统一Prompt系统 - 使用正确的模板名称
|
||||||
template_name = None
|
template_name = ""
|
||||||
if current_prompt_mode == "s4u":
|
if current_prompt_mode == "s4u":
|
||||||
template_name = "s4u_style_prompt"
|
template_name = "s4u_style_prompt"
|
||||||
elif current_prompt_mode == "normal":
|
elif current_prompt_mode == "normal":
|
||||||
|
|||||||
@@ -135,8 +135,9 @@ class ChatterPlanFilter:
|
|||||||
plan.decided_actions = [ActionPlannerInfo(action_type="no_action", reasoning=f"筛选时出错: {e}")]
|
plan.decided_actions = [ActionPlannerInfo(action_type="no_action", reasoning=f"筛选时出错: {e}")]
|
||||||
|
|
||||||
# 在返回最终计划前,打印将要执行的动作
|
# 在返回最终计划前,打印将要执行的动作
|
||||||
action_types = [action.action_type for action in plan.decided_actions]
|
if plan.decided_actions:
|
||||||
logger.info(f"选择动作: [{SKY_BLUE}{', '.join(action_types) if action_types else '无'}{RESET_COLOR}]")
|
action_types = [action.action_type for action in plan.decided_actions]
|
||||||
|
logger.info(f"选择动作: [{SKY_BLUE}{', '.join(action_types) if action_types else '无'}{RESET_COLOR}]")
|
||||||
|
|
||||||
return plan
|
return plan
|
||||||
|
|
||||||
@@ -174,8 +175,9 @@ class ChatterPlanFilter:
|
|||||||
if angry_prompt_addition:
|
if angry_prompt_addition:
|
||||||
schedule_block = angry_prompt_addition
|
schedule_block = angry_prompt_addition
|
||||||
elif global_config.planning_system.schedule_enable:
|
elif global_config.planning_system.schedule_enable:
|
||||||
if current_activity := schedule_manager.get_current_activity():
|
if activity_info := schedule_manager.get_current_activity():
|
||||||
schedule_block = f"你当前正在:{current_activity},但注意它与群聊的聊天无关。"
|
activity = activity_info.get("activity", "未知活动")
|
||||||
|
schedule_block = f"你当前正在:{activity},但注意它与群聊的聊天无关。"
|
||||||
|
|
||||||
mood_block = ""
|
mood_block = ""
|
||||||
# 如果被吵醒,则心情也是愤怒的,不需要另外的情绪模块
|
# 如果被吵醒,则心情也是愤怒的,不需要另外的情绪模块
|
||||||
@@ -277,9 +279,7 @@ class ChatterPlanFilter:
|
|||||||
is_group_chat = plan.chat_type == ChatType.GROUP
|
is_group_chat = plan.chat_type == ChatType.GROUP
|
||||||
chat_context_description = "你现在正在一个群聊中"
|
chat_context_description = "你现在正在一个群聊中"
|
||||||
if not is_group_chat and plan.target_info:
|
if not is_group_chat and plan.target_info:
|
||||||
chat_target_name = (
|
chat_target_name = plan.target_info.person_name or plan.target_info.user_nickname or "对方"
|
||||||
plan.target_info.get("person_name") or plan.target_info.get("user_nickname") or "对方"
|
|
||||||
)
|
|
||||||
chat_context_description = f"你正在和 {chat_target_name} 私聊"
|
chat_context_description = f"你正在和 {chat_target_name} 私聊"
|
||||||
|
|
||||||
action_options_block = await self._build_action_options(plan.available_actions)
|
action_options_block = await self._build_action_options(plan.available_actions)
|
||||||
@@ -554,13 +554,21 @@ class ChatterPlanFilter:
|
|||||||
):
|
):
|
||||||
reasoning = f"LLM 返回了当前不可用的动作 '{action}'。原始理由: {reasoning}"
|
reasoning = f"LLM 返回了当前不可用的动作 '{action}'。原始理由: {reasoning}"
|
||||||
action = "no_action"
|
action = "no_action"
|
||||||
|
from src.common.data_models.database_data_model import DatabaseMessages
|
||||||
|
|
||||||
|
action_message_obj = None
|
||||||
|
if target_message_obj:
|
||||||
|
try:
|
||||||
|
action_message_obj = DatabaseMessages(**target_message_obj)
|
||||||
|
except Exception:
|
||||||
|
logger.warning("无法将目标消息转换为DatabaseMessages对象")
|
||||||
|
|
||||||
parsed_actions.append(
|
parsed_actions.append(
|
||||||
ActionPlannerInfo(
|
ActionPlannerInfo(
|
||||||
action_type=action,
|
action_type=action,
|
||||||
reasoning=reasoning,
|
reasoning=reasoning,
|
||||||
action_data=action_data,
|
action_data=action_data,
|
||||||
action_message=target_message_obj,
|
action_message=action_message_obj,
|
||||||
available_actions=plan.available_actions,
|
available_actions=plan.available_actions,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -640,11 +648,11 @@ class ChatterPlanFilter:
|
|||||||
# 特殊处理set_emoji_like的emoji参数
|
# 特殊处理set_emoji_like的emoji参数
|
||||||
from src.plugins.built_in.social_toolkit_plugin.qq_emoji_list import qq_face
|
from src.plugins.built_in.social_toolkit_plugin.qq_emoji_list import qq_face
|
||||||
|
|
||||||
emoji_options = [
|
emoji_options = []
|
||||||
re.search(r"\[表情:(.+?)\]", name).group(1)
|
for name in qq_face.values():
|
||||||
for name in qq_face.values()
|
match = re.search(r"\[表情:(.+?)\]", name)
|
||||||
if re.search(r"\[表情:(.+?)\]", name)
|
if match:
|
||||||
]
|
emoji_options.append(match.group(1))
|
||||||
example_value = f"<从'{', '.join(emoji_options[:10])}...'中选择一个>"
|
example_value = f"<从'{', '.join(emoji_options[:10])}...'中选择一个>"
|
||||||
else:
|
else:
|
||||||
example_value = f"<{p_desc}>"
|
example_value = f"<{p_desc}>"
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ class ScheduleManager:
|
|||||||
schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n"
|
schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n"
|
||||||
logger.info(schedule_str)
|
logger.info(schedule_str)
|
||||||
|
|
||||||
def get_current_activity(self) -> str | None:
|
def get_current_activity(self) -> dict[str, Any] | None:
|
||||||
if not global_config.planning_system.schedule_enable or not self.today_schedule:
|
if not global_config.planning_system.schedule_enable or not self.today_schedule:
|
||||||
return None
|
return None
|
||||||
now = datetime.now().time()
|
now = datetime.now().time()
|
||||||
@@ -161,7 +161,7 @@ class ScheduleManager:
|
|||||||
start_time = datetime.strptime(start_str.strip(), "%H:%M").time()
|
start_time = datetime.strptime(start_str.strip(), "%H:%M").time()
|
||||||
end_time = datetime.strptime(end_str.strip(), "%H:%M").time()
|
end_time = datetime.strptime(end_str.strip(), "%H:%M").time()
|
||||||
if (start_time <= now < end_time) or (end_time < start_time and (now >= start_time or now < end_time)):
|
if (start_time <= now < end_time) or (end_time < start_time and (now >= start_time or now < end_time)):
|
||||||
return activity
|
return {"activity": activity, "time_range": time_range}
|
||||||
except (ValueError, KeyError, AttributeError) as e:
|
except (ValueError, KeyError, AttributeError) as e:
|
||||||
logger.warning(f"解析日程事件失败: {event}, 错误: {e}")
|
logger.warning(f"解析日程事件失败: {event}, 错误: {e}")
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user