refactor(at_user): 优化艾特用户插件逻辑并移除模糊匹配

重构了艾特用户插件,主要改动如下:
- 移除 `fuzzywuzzy` 依赖和相关的模糊匹配逻辑,改为直接通过 `person_info_manager` 精确查找用户。
- 优化了 `AtAction` 的执行流程,现在通过调用 `DefaultReplyer` 生成更智能、更符合上下文的回复内容,而不是发送固定文本。
- 新增了 `/at` 命令,允许用户通过指令直接艾特指定用户并发送消息。
- 删除了 `proactive_thinker.py` 中不再使用的 `_get_reminder_context` 方法,以清理与旧提醒功能相关的代码。
This commit is contained in:
minecraft1024a
2025-09-13 12:54:23 +08:00
committed by Windpicker-owo
parent 5eaa5f74e8
commit 31bdaa3747
2 changed files with 88 additions and 79 deletions

View File

@@ -138,20 +138,6 @@ class ProactiveThinker:
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):

View File

@@ -8,6 +8,7 @@ from src.plugin_system import (
ActionInfo,
ActionActivationType,
)
from src.person_info.person_info import get_person_info_manager
from src.common.logger import get_logger
from src.plugin_system.base.component_types import ChatType
@@ -20,16 +21,16 @@ class AtAction(BaseAction):
# === 基本信息(必须填写)===
action_name = "at_user"
action_description = "发送艾特消息"
activation_type = ActionActivationType.LLM_JUDGE
activation_type = ActionActivationType.LLM_JUDGE # 消息接收时激活(?)
parallel_action = False
chat_type_allow = ChatType.GROUP
# === 功能描述(必须填写)===
action_parameters = {"user_name": "需要艾特用户的名字", "at_message": "艾特用户时要发送的消"}
action_parameters = {"user_name": "需要艾特用户的名字", "at_message": "艾特用户时要发送的消,注意消息里不要有@"}
action_require = [
"当用户明确要求你去'''''提醒''艾特'某人时使用",
"当你判断,为了让特定的人看到消息,需要代表用户去呼叫他/她时使用",
"例如:'你去叫一下张三''提醒一下李四开会'",
"需要艾特某个用户时使用",
"当你需要提醒特定用户查看消息时使用",
"在回复中需要明确指向某个用户时使用",
]
llm_judge_prompt = """
判定是否需要使用艾特用户动作的条件:
@@ -47,43 +48,24 @@ class AtAction(BaseAction):
if not user_name or not at_message:
logger.warning("艾特用户的动作缺少必要参数。")
await self.store_action_info(
action_build_into_prompt=True,
action_prompt_display=f"执行了艾特用户动作:艾特用户 {user_name} 并发送消息: {at_message},失败了,因为没有提供必要参数",
action_done=False,
)
return False, "缺少必要参数"
from src.plugin_system.apis import send_api
from fuzzywuzzy import process
group_id = self.chat_stream.group_info.group_id
if not group_id:
return False, "无法获取群组ID"
response = await send_api.adapter_command_to_stream(
action="get_group_member_list",
params={"group_id": group_id},
stream_id=self.chat_id,
)
if response.get("status") != "ok":
return False, f"获取群成员列表失败: {response.get('message')}"
member_list = response.get("data", [])
if not member_list:
return False, "群成员列表为空"
# 使用模糊匹配找到最接近的用户名
choices = {member["card"] or member["nickname"]: member["user_id"] for member in member_list}
best_match, score = process.extractOne(user_name, choices.keys())
if score < 30: # 设置一个匹配度阈值
logger.info(f"找不到与 '{user_name}' 高度匹配的用户 (最佳匹配: {best_match}, 分数: {score})")
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, "用户不存在"
user_id = choices[best_match]
user_info = {"user_id": user_id, "user_nickname": best_match}
try:
# 使用回复器生成艾特回复,而不是直接发送命令
from src.chat.replyer.default_generator import DefaultReplyer
from src.chat.message_receive.chat_stream import get_chat_manager
# 获取当前聊天流
chat_manager = get_chat_manager()
chat_stream = chat_manager.get_stream(self.chat_id)
@@ -91,56 +73,97 @@ class AtAction(BaseAction):
logger.error(f"找不到聊天流: {self.stream_id}")
return False, "聊天流不存在"
# 创建回复器实例
replyer = DefaultReplyer(chat_stream)
# 构建回复对象,将艾特消息作为回复目标
reply_to = f"{user_name}:{at_message}"
extra_info = f"你需要艾特用户 {user_name} 并回复他们说: {at_message}"
success, llm_response, _ = await replyer.generate_reply_with_context(
reply_to=f"{user_name}:{at_message}",
# 使用回复器生成回复
success, llm_response, prompt = await replyer.generate_reply_with_context(
reply_to=reply_to,
extra_info=extra_info,
enable_tool=False,
enable_tool=False, # 艾特回复通常不需要工具调用
from_plugin=False
)
if not success or not llm_response:
if success and llm_response:
# 获取生成的回复内容
reply_content = llm_response.get("content", "")
if reply_content:
# 获取用户QQ号发送真正的艾特消息
user_id = user_info.get("user_id")
# 发送真正的艾特命令,使用回复器生成的智能内容
await self.send_command(
"SEND_AT_MESSAGE",
args={"qq_id": user_id, "text": reply_content},
display_message=f"艾特用户 {user_name} 并发送智能回复: {reply_content}",
)
await self.store_action_info(
action_build_into_prompt=True,
action_prompt_display=f"执行了艾特用户动作:艾特用户 {user_name} 并发送智能回复: {reply_content}",
action_done=True,
)
logger.info(f"成功通过回复器生成智能内容并发送真正的艾特消息给 {user_name}: {reply_content}")
return True, "智能艾特消息发送成功"
else:
logger.warning("回复器生成了空内容")
return False, "回复内容为空"
else:
logger.error("回复器生成回复失败")
return False, "回复生成失败"
final_message_raw = llm_response.get("content", "")
if not final_message_raw:
logger.warning("回复器生成了空内容")
return False, "回复内容为空"
# 对LLM生成的内容进行后处理解析[SPLIT]标记并将分段消息合并
from src.chat.utils.utils import process_llm_response
final_message_segments = process_llm_response(final_message_raw, enable_splitter=True, enable_chinese_typo=False)
final_message = " ".join(final_message_segments)
await self.send_command(
"SEND_AT_MESSAGE",
args={"group_id": self.chat_stream.group_info.group_id, "qq_id": user_id, "text": final_message},
display_message=f"艾特用户 {user_name} 并发送消息: {final_message}",
)
await self.store_action_info(
action_build_into_prompt=True,
action_prompt_display=f"执行了艾特用户动作:艾特用户 {user_name} 并发送消息: {final_message}",
action_done=True,
)
logger.info(f"成功发送艾特消息给 {user_name}: {final_message}")
return True, "艾特消息发送成功"
except Exception as e:
logger.error(f"执行艾特用户动作时发生异常: {e}", exc_info=True)
await self.store_action_info(
action_build_into_prompt=True,
action_prompt_display=f"执行艾特用户动作失败:{str(e)}",
action_done=False,
)
return False, f"执行失败: {str(e)}"
class AtCommand(BaseCommand):
command_name: str = "at_user"
description: str = "通过名字艾特用户"
command_pattern: str = r"/at\s+@?(?P<name>[\S]+)(?:\s+(?P<text>.*))?"
async def execute(self) -> Tuple[bool, str, bool]:
name = self.matched_groups.get("name")
text = self.matched_groups.get("text", "")
if not name:
await self.send_text("请指定要艾特的用户名称。")
return False, "缺少用户名称", True
person_info_manager = get_person_info_manager()
user_info = await person_info_manager.get_person_info_by_name(name)
if not user_info or not user_info.get("user_id"):
await self.send_text(f"找不到名为 '{name}' 的用户。")
return False, "用户不存在", True
user_id = user_info.get("user_id")
await self.send_command(
"SEND_AT_MESSAGE",
args={"qq_id": user_id, "text": text},
display_message=f"艾特用户 {name} 并发送消息: {text}",
)
return True, "艾特消息已发送", True
@register_plugin
class AtUserPlugin(BasePlugin):
plugin_name: str = "at_user_plugin"
enable_plugin: bool = True
dependencies: list[str] = []
python_dependencies: list[str] = ["fuzzywuzzy", "python-Levenshtein"]
python_dependencies: list[str] = []
config_file_name: str = "config.toml"
config_schema: dict = {}