refactor(social_toolkit): 简化提醒动作的参数提取逻辑
将 `RemindAction` 中的参数提取逻辑从内部LLM调用改为依赖外部的 `action_data`。这使得动作的职责更单一,专注于执行提醒任务,而将参数解析的责任交给了上游的动作调度器或LLM规划器。 同时,进行了以下代码风格和依赖项的清理: - 统一使用 `datetime.datetime.now()` - 移除了不再需要的本地LLM调用和相关提示词 - 调整了 `activation_type` 的定义以符合规范 - 修正了 `llm_api` 调用,使用更合适的 `utils_small` 模型进行时间解析 - 整理了代码格式和导入语句,提高了可读性
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import re
|
||||
from typing import List, Tuple, Type
|
||||
from typing import List, Tuple, Type, Optional
|
||||
|
||||
from src.plugin_system import (
|
||||
BasePlugin,
|
||||
@@ -17,6 +17,8 @@ from dateutil.parser import parse as parse_datetime
|
||||
from src.manager.async_task_manager import AsyncTask, async_task_manager
|
||||
from src.plugin_system.apis import send_api, llm_api, generator_api
|
||||
from src.plugin_system.base.component_types import ComponentType
|
||||
from typing import Optional
|
||||
from src.chat.message_receive.chat_stream import ChatStream
|
||||
import asyncio
|
||||
import datetime
|
||||
|
||||
@@ -24,9 +26,21 @@ logger = get_logger("set_emoji_like_plugin")
|
||||
|
||||
# ============================ AsyncTask ============================
|
||||
|
||||
|
||||
class ReminderTask(AsyncTask):
|
||||
def __init__(self, delay: float, stream_id: str, group_id: Optional[str], is_group: bool, target_user_id: str, target_user_name: str, event_details: str, creator_name: str, chat_stream: "ChatStream"):
|
||||
super().__init__(task_name=f"ReminderTask_{target_user_id}_{datetime.now().timestamp()}")
|
||||
def __init__(
|
||||
self,
|
||||
delay: float,
|
||||
stream_id: str,
|
||||
group_id: Optional[str],
|
||||
is_group: bool,
|
||||
target_user_id: str,
|
||||
target_user_name: str,
|
||||
event_details: str,
|
||||
creator_name: str,
|
||||
chat_stream: ChatStream,
|
||||
):
|
||||
super().__init__(task_name=f"ReminderTask_{target_user_id}_{datetime.datetime.now().timestamp()}")
|
||||
self.delay = delay
|
||||
self.stream_id = stream_id
|
||||
self.group_id = group_id
|
||||
@@ -42,7 +56,7 @@ class ReminderTask(AsyncTask):
|
||||
if self.delay > 0:
|
||||
logger.info(f"等待 {self.delay:.2f} 秒后执行提醒...")
|
||||
await asyncio.sleep(self.delay)
|
||||
|
||||
|
||||
logger.info(f"执行提醒任务: 给 {self.target_user_name} 发送关于 '{self.event_details}' 的提醒")
|
||||
|
||||
extra_info = f"现在是提醒时间,请你以一种符合你人设的、俏皮的方式提醒 {self.target_user_name}。\n提醒内容: {self.event_details}\n设置提醒的人: {self.creator_name}"
|
||||
@@ -50,7 +64,7 @@ class ReminderTask(AsyncTask):
|
||||
chat_stream=self.chat_stream,
|
||||
extra_info=extra_info,
|
||||
reply_message=self.chat_stream.context.get_last_message().to_dict(),
|
||||
request_type="plugin.reminder.remind_message"
|
||||
request_type="plugin.reminder.remind_message",
|
||||
)
|
||||
|
||||
if success and reply_set:
|
||||
@@ -63,7 +77,7 @@ class ReminderTask(AsyncTask):
|
||||
await send_api.adapter_command_to_stream(
|
||||
action="send_group_msg",
|
||||
params={"group_id": self.group_id, "message": message_payload},
|
||||
stream_id=self.stream_id
|
||||
stream_id=self.stream_id,
|
||||
)
|
||||
else:
|
||||
await send_api.text_to_stream(text=text, stream_id=self.stream_id)
|
||||
@@ -73,12 +87,12 @@ class ReminderTask(AsyncTask):
|
||||
if self.is_group:
|
||||
message_payload = [
|
||||
{"type": "at", "data": {"qq": self.target_user_id}},
|
||||
{"type": "text", "data": {"text": f" {reminder_text}"}}
|
||||
{"type": "text", "data": {"text": f" {reminder_text}"}},
|
||||
]
|
||||
await send_api.adapter_command_to_stream(
|
||||
action="send_group_msg",
|
||||
params={"group_id": self.group_id, "message": message_payload},
|
||||
stream_id=self.stream_id
|
||||
stream_id=self.stream_id,
|
||||
)
|
||||
else:
|
||||
await send_api.text_to_stream(text=reminder_text, stream_id=self.stream_id)
|
||||
@@ -91,6 +105,7 @@ class ReminderTask(AsyncTask):
|
||||
|
||||
# =============================== Actions ===============================
|
||||
|
||||
|
||||
def get_emoji_id(emoji_input: str) -> str | None:
|
||||
"""根据输入获取表情ID"""
|
||||
# 如果输入本身就是数字ID,直接返回
|
||||
@@ -116,6 +131,7 @@ def get_emoji_id(emoji_input: str) -> str | None:
|
||||
|
||||
# ===== Action组件 =====
|
||||
|
||||
|
||||
class PokeAction(BaseAction):
|
||||
"""发送戳一戳动作"""
|
||||
|
||||
@@ -146,7 +162,7 @@ class PokeAction(BaseAction):
|
||||
"""执行戳一戳的动作"""
|
||||
user_id = self.action_data.get("user_id")
|
||||
user_name = self.action_data.get("user_name")
|
||||
|
||||
|
||||
try:
|
||||
times = int(self.action_data.get("times", 1))
|
||||
except (ValueError, TypeError):
|
||||
@@ -157,14 +173,14 @@ class PokeAction(BaseAction):
|
||||
if not user_name:
|
||||
logger.warning("戳一戳动作缺少 'user_id' 或 'user_name' 参数。")
|
||||
return False, "缺少用户标识参数"
|
||||
|
||||
|
||||
# 备用方案:通过 user_name 查找
|
||||
user_info = await get_person_info_manager().get_person_info_by_name(user_name)
|
||||
if not user_info or not user_info.get("user_id"):
|
||||
logger.info(f"找不到名为 '{user_name}' 的用户。")
|
||||
return False, f"找不到名为 '{user_name}' 的用户"
|
||||
user_id = user_info.get("user_id")
|
||||
|
||||
|
||||
display_name = user_name or user_id
|
||||
|
||||
for i in range(times):
|
||||
@@ -181,6 +197,7 @@ class PokeAction(BaseAction):
|
||||
)
|
||||
return True, success_message
|
||||
|
||||
|
||||
class SetEmojiLikeAction(BaseAction):
|
||||
"""设置消息表情回应"""
|
||||
|
||||
@@ -266,7 +283,9 @@ class SetEmojiLikeAction(BaseAction):
|
||||
try:
|
||||
# 使用适配器API发送贴表情命令
|
||||
success = await self.send_command(
|
||||
command_name="set_emoji_like", args={"message_id": message_id, "emoji_id": emoji_id, "set": set_like}, storage_message=False
|
||||
command_name="set_emoji_like",
|
||||
args={"message_id": message_id, "emoji_id": emoji_id, "set": set_like},
|
||||
storage_message=False,
|
||||
)
|
||||
if success:
|
||||
logger.info("设置表情回应成功")
|
||||
@@ -294,14 +313,17 @@ class SetEmojiLikeAction(BaseAction):
|
||||
)
|
||||
return False, f"设置表情回应失败: {e}"
|
||||
|
||||
|
||||
class RemindAction(BaseAction):
|
||||
"""一个能从对话中智能识别并设置定时提醒的动作。"""
|
||||
|
||||
# === 基本信息 ===
|
||||
action_name = "set_reminder"
|
||||
action_description = "根据用户的对话内容,智能地设置一个未来的提醒事项。"
|
||||
activation_type=ActionActivationType.KEYWORD,
|
||||
activation_keywords=["提醒", "叫我", "记得", "别忘了"]
|
||||
activation_type = (ActionActivationType.KEYWORD,)
|
||||
activation_keywords = ["提醒", "叫我", "记得", "别忘了"]
|
||||
chat_type_allow = ChatType.ALL
|
||||
parallel_action = True
|
||||
|
||||
# === LLM 判断与参数提取 ===
|
||||
llm_judge_prompt = ""
|
||||
@@ -309,68 +331,25 @@ class RemindAction(BaseAction):
|
||||
action_require = [
|
||||
"当用户请求在未来的某个时间点提醒他/她或别人某件事时使用",
|
||||
"适用于包含明确时间信息和事件描述的对话",
|
||||
"例如:'10分钟后提醒我收快递'、'明天早上九点喊一下李四参加晨会'"
|
||||
"例如:'10分钟后提醒我收快递'、'明天早上九点喊一下李四参加晨会'",
|
||||
]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行设置提醒的动作"""
|
||||
try:
|
||||
# 获取所有可用的模型配置
|
||||
available_models = llm_api.get_available_models()
|
||||
if "planner" not in available_models:
|
||||
raise ValueError("未找到 'planner' 决策模型配置,无法解析时间")
|
||||
model_to_use = available_models["planner"]
|
||||
|
||||
bot_name = self.chat_stream.user_info.user_nickname
|
||||
|
||||
prompt = f"""
|
||||
从以下用户输入中提取提醒事件的关键信息。
|
||||
用户输入: "{self.chat_stream.context.message.processed_plain_text}"
|
||||
Bot的名字是: "{bot_name}"
|
||||
|
||||
请仔细分析句子结构,以确定谁是提醒的真正目标。Bot自身不应被视为被提醒人。
|
||||
请以JSON格式返回提取的信息,包含以下字段:
|
||||
- "user_name": 需要被提醒的人的姓名。如果未指定,则默认为"自己"。
|
||||
- "remind_time": 描述提醒时间的自然语言字符串。
|
||||
- "event_details": 需要提醒的具体事件内容。
|
||||
|
||||
示例:
|
||||
- 用户输入: "提醒我十分钟后开会" -> {{"user_name": "自己", "remind_time": "十分钟后", "event_details": "开会"}}
|
||||
- 用户输入: "{bot_name},提醒一闪一分钟后睡觉" -> {{"user_name": "一闪", "remind_time": "一分钟后", "event_details": "睡觉"}}
|
||||
|
||||
如果无法提取完整信息,请返回一个包含空字符串的JSON对象,例如:{{"user_name": "", "remind_time": "", "event_details": ""}}
|
||||
"""
|
||||
|
||||
success, response, _, _ = await llm_api.generate_with_model(
|
||||
prompt,
|
||||
model_config=model_to_use,
|
||||
request_type="plugin.reminder.parameter_extractor"
|
||||
)
|
||||
|
||||
if not success or not response:
|
||||
raise ValueError(f"LLM未能返回有效的参数: {response}")
|
||||
|
||||
import json
|
||||
import re
|
||||
try:
|
||||
# 提取JSON部分
|
||||
json_match = re.search(r"\{.*\}", response, re.DOTALL)
|
||||
if not json_match:
|
||||
raise ValueError("LLM返回的内容中不包含JSON")
|
||||
action_data = json.loads(json_match.group(0))
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"[ReminderPlugin] LLM返回的不是有效的JSON: {response}")
|
||||
return False, "LLM返回的不是有效的JSON"
|
||||
user_name = action_data.get("user_name")
|
||||
remind_time_str = action_data.get("remind_time")
|
||||
event_details = action_data.get("event_details")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[ReminderPlugin] 解析参数时出错: {e}", exc_info=True)
|
||||
return False, "解析参数时出错"
|
||||
user_name = self.action_data.get("user_name")
|
||||
remind_time_str = self.action_data.get("remind_time")
|
||||
event_details = self.action_data.get("event_details")
|
||||
|
||||
if not all([user_name, remind_time_str, event_details]):
|
||||
missing_params = [p for p, v in {"user_name": user_name, "remind_time": remind_time_str, "event_details": event_details}.items() if not v]
|
||||
missing_params = [
|
||||
p
|
||||
for p, v in {
|
||||
"user_name": user_name,
|
||||
"remind_time": remind_time_str,
|
||||
"event_details": event_details,
|
||||
}.items()
|
||||
if not v
|
||||
]
|
||||
error_msg = f"缺少必要的提醒参数: {', '.join(missing_params)}"
|
||||
logger.warning(f"[ReminderPlugin] LLM未能提取完整参数: {error_msg}")
|
||||
return False, error_msg
|
||||
@@ -384,17 +363,17 @@ class RemindAction(BaseAction):
|
||||
except Exception:
|
||||
# 如果直接解析失败,调用 LLM 进行转换
|
||||
logger.info(f"[ReminderPlugin] 直接解析时间 '{remind_time_str}' 失败,尝试使用 LLM 进行转换...")
|
||||
|
||||
|
||||
# 获取所有可用的模型配置
|
||||
available_models = llm_api.get_available_models()
|
||||
if "planner" not in available_models:
|
||||
raise ValueError("未找到 'planner' 决策模型配置,无法解析时间")
|
||||
|
||||
if "utils_small" not in available_models:
|
||||
raise ValueError("未找到 'utils_small' 模型配置,无法解析时间")
|
||||
|
||||
# 明确使用 'planner' 模型
|
||||
model_to_use = available_models["planner"]
|
||||
model_to_use = available_models["utils_small"]
|
||||
|
||||
# 在执行时动态获取当前时间
|
||||
current_time_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
current_time_str = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
prompt = (
|
||||
f"请将以下自然语言时间短语转换为一个未来的、标准的 'YYYY-MM-DD HH:MM:SS' 格式。"
|
||||
f"请只输出转换后的时间字符串,不要包含任何其他说明或文字。\n"
|
||||
@@ -408,13 +387,11 @@ class RemindAction(BaseAction):
|
||||
f"- 当前时间: 2025-09-16 14:00:00, 用户说: '8点' -> '2025-09-16 20:00:00'\n"
|
||||
f"- 当前时间: 2025-09-16 23:00:00, 用户说: '晚上10点' -> '2025-09-17 22:00:00'"
|
||||
)
|
||||
|
||||
|
||||
success, response, _, _ = await llm_api.generate_with_model(
|
||||
prompt,
|
||||
model_config=model_to_use,
|
||||
request_type="plugin.reminder.time_parser"
|
||||
prompt, model_config=model_to_use, request_type="plugin.reminder.time_parser"
|
||||
)
|
||||
|
||||
|
||||
if not success or not response:
|
||||
raise ValueError(f"LLM未能返回有效的时间字符串: {response}")
|
||||
|
||||
@@ -427,7 +404,7 @@ class RemindAction(BaseAction):
|
||||
await self.send_text(f"抱歉,我无法理解您说的时间 '{remind_time_str}',提醒设置失败。")
|
||||
return False, f"无法解析时间 '{remind_time_str}'"
|
||||
|
||||
now = datetime.now()
|
||||
now = datetime.datetime.now()
|
||||
if target_time <= now:
|
||||
await self.send_text("提醒时间必须是一个未来的时间点哦,提醒设置失败。")
|
||||
return False, "提醒时间必须在未来"
|
||||
@@ -438,9 +415,9 @@ class RemindAction(BaseAction):
|
||||
person_manager = get_person_info_manager()
|
||||
user_id_to_remind = None
|
||||
user_name_to_remind = ""
|
||||
|
||||
|
||||
assert isinstance(user_name, str)
|
||||
|
||||
|
||||
if user_name.strip() in ["自己", "我", "me"]:
|
||||
user_id_to_remind = self.user_id
|
||||
user_name_to_remind = self.user_nickname
|
||||
@@ -454,19 +431,20 @@ class RemindAction(BaseAction):
|
||||
if user_name in name:
|
||||
user_info = await person_manager.get_values(person_id, ["user_id", "user_nickname"])
|
||||
break
|
||||
|
||||
|
||||
# 3. 模糊匹配 (此处简化为字符串相似度)
|
||||
if not user_info:
|
||||
best_match = None
|
||||
highest_similarity = 0
|
||||
for person_id, name in person_manager.person_name_list.items():
|
||||
import difflib
|
||||
|
||||
similarity = difflib.SequenceMatcher(None, user_name, name).ratio()
|
||||
if similarity > highest_similarity:
|
||||
highest_similarity = similarity
|
||||
best_match = person_id
|
||||
|
||||
if best_match and highest_similarity > 0.6: # 相似度阈值
|
||||
|
||||
if best_match and highest_similarity > 0.6: # 相似度阈值
|
||||
user_info = await person_manager.get_values(best_match, ["user_id", "user_nickname"])
|
||||
|
||||
if not user_info or not user_info.get("user_id"):
|
||||
@@ -480,20 +458,22 @@ class RemindAction(BaseAction):
|
||||
try:
|
||||
assert user_id_to_remind is not None
|
||||
assert event_details is not None
|
||||
|
||||
|
||||
reminder_task = ReminderTask(
|
||||
delay=delay_seconds,
|
||||
stream_id=self.chat_stream.stream_id,
|
||||
group_id=self.chat_stream.group_info.group_id if self.is_group and self.chat_stream.group_info else None,
|
||||
group_id=self.chat_stream.group_info.group_id
|
||||
if self.is_group and self.chat_stream.group_info
|
||||
else None,
|
||||
is_group=self.is_group,
|
||||
target_user_id=str(user_id_to_remind),
|
||||
target_user_name=str(user_name_to_remind),
|
||||
event_details=str(event_details),
|
||||
creator_name=str(self.user_nickname),
|
||||
chat_stream=self.chat_stream
|
||||
chat_stream=self.chat_stream,
|
||||
)
|
||||
await async_task_manager.add_task(reminder_task)
|
||||
|
||||
|
||||
# 4. 生成并发送确认消息
|
||||
extra_info = f"你已经成功设置了一个提醒,请以一种符合你人设的、俏皮的方式回复用户。\n提醒时间: {target_time.strftime('%Y-%m-%d %H:%M:%S')}\n提醒对象: {user_name_to_remind}\n提醒内容: {event_details}"
|
||||
last_message = self.chat_stream.context.get_last_message()
|
||||
@@ -501,7 +481,7 @@ class RemindAction(BaseAction):
|
||||
chat_stream=self.chat_stream,
|
||||
extra_info=extra_info,
|
||||
reply_message=last_message.to_dict(),
|
||||
request_type="plugin.reminder.confirm_message"
|
||||
request_type="plugin.reminder.confirm_message",
|
||||
)
|
||||
if success and reply_set:
|
||||
for _, text in reply_set:
|
||||
@@ -510,13 +490,14 @@ class RemindAction(BaseAction):
|
||||
# Fallback message
|
||||
fallback_message = f"好的,我记下了。\n将在 {target_time.strftime('%Y-%m-%d %H:%M:%S')} 提醒 {user_name_to_remind}:\n{event_details}"
|
||||
await self.send_text(fallback_message)
|
||||
|
||||
|
||||
return True, "提醒设置成功"
|
||||
except Exception as e:
|
||||
logger.error(f"[ReminderPlugin] 创建提醒任务时出错: {e}", exc_info=True)
|
||||
await self.send_text("抱歉,设置提醒时发生了一点内部错误。")
|
||||
return False, "设置提醒时发生内部错误"
|
||||
|
||||
|
||||
# ===== 插件注册 =====
|
||||
@register_plugin
|
||||
class SetEmojiLikePlugin(BasePlugin):
|
||||
|
||||
Reference in New Issue
Block a user