feat(reminder): 增强定时提醒系统,实现智能用户识别与上下文感知回复

对定时提醒功能进行了重大重构和增强,使其更加智能和人性化。

主要更新包括:
- **智能用户识别**: 引入LLM从提醒内容中提取需要@的目标用户,取代了原有的简单正则表达式匹配,能够更准确地理解自然语言指令,如“提醒阿范喝水”。
- **专用提醒回复**: 当提醒任务不包含特定目标用户时(如“提醒我喝水”),系统不再简单回退,而是会调用一个专用的LLM流程,生成一条符合Bot性格的、温暖贴心的提醒消息。
- **上下文传递**: 创建提醒时会保存完整的原始消息,并在触发提醒时将其传递给处理流程。这使得LLM在执行@操作或生成回复时能拥有完整上下文,避免了记忆割裂感。
- **@用户匹配优化**: 增强了`at_user`动作的底层用户匹配逻辑,采用“精确匹配 -> 包含匹配 -> 模糊匹配”的多层策略,显著提高了在群聊中查找目标用户的准确率和鲁棒性。
- **提示词优化**: 全面优化了提醒流程中涉及的LLM提示词,无论是用户提取还是最终的@消息生成,都更具情景感,使Bot的回复更加自然流畅。
This commit is contained in:
tt-P607
2025-09-09 22:14:27 +08:00
committed by Windpicker-owo
parent 47ee2ba693
commit eb80bd91a1
4 changed files with 244 additions and 73 deletions

View File

@@ -70,15 +70,49 @@ class AtAction(BaseAction):
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())
# 优化用户匹配逻辑
best_match = None
user_id = None
# 1. 完全精确匹配
for member in member_list:
card = member.get("card", "")
nickname = member.get("nickname", "")
if user_name == card or user_name == nickname:
best_match = card if user_name == card else nickname
user_id = member["user_id"]
logger.info(f"找到完全精确匹配: '{user_name}' -> '{best_match}' (ID: {user_id})")
break
if score < 30: # 设置一个匹配度阈值
logger.info(f"找不到与 '{user_name}' 高度匹配的用户 (最佳匹配: {best_match}, 分数: {score})")
return False, "用户不存在"
# 2. 包含关系匹配
if not best_match:
containing_matches = []
for member in member_list:
card = member.get("card", "")
nickname = member.get("nickname", "")
if user_name in card:
containing_matches.append((card, member["user_id"]))
elif user_name in nickname:
containing_matches.append((nickname, member["user_id"]))
user_id = choices[best_match]
if containing_matches:
# 选择最短的匹配项,因为通常更精确
best_match, user_id = min(containing_matches, key=lambda x: len(x[0]))
logger.info(f"找到包含关系匹配: '{user_name}' -> '{best_match}' (ID: {user_id})")
# 3. 模糊匹配作为兜底
if not best_match:
choices = {member["card"] or member["nickname"]: member["user_id"] for member in member_list}
fuzzy_match, score = process.extractOne(user_name, choices.keys())
if score >= 60: # 维持较高的阈值
best_match = fuzzy_match
user_id = choices[best_match]
logger.info(f"找到模糊匹配: '{user_name}' -> '{best_match}' (ID: {user_id}, Score: {score})")
if not best_match:
logger.warning(f"所有匹配策略都未能找到用户: '{user_name}'")
return False, "用户不存在"
user_info = {"user_id": user_id, "user_nickname": best_match}
try:
@@ -93,13 +127,17 @@ class AtAction(BaseAction):
return False, "聊天流不存在"
replyer = DefaultReplyer(chat_stream)
extra_info = f"你需要艾特用户 {user_name} 并回复他们说: {at_message}"
# 优化提示词,消除记忆割裂感
reminder_task = at_message.replace("定时提醒:", "").strip()
extra_info = f"""你之前记下了一个提醒任务:'{reminder_task}'
现在时间到了,你需要去提醒用户 '{user_name}'
请像一个朋友一样,自然地完成这个提醒,而不是生硬地复述任务。"""
success, llm_response, _ = await replyer.generate_reply_with_context(
reply_to=f"{user_name}:{at_message}",
reply_to=f"是时候提醒'{user_name}'", # 内部上下文,更符合执行任务的语境
extra_info=extra_info,
enable_tool=False,
from_plugin=False
from_plugin=True # 标记为插件调用以便LLM更好地理解上下文
)
if not success or not llm_response: