总之就是成了!😋😋😋主动思考终于成了
This commit is contained in:
@@ -122,53 +122,67 @@ class ProactiveThinker:
|
|||||||
try:
|
try:
|
||||||
# 如果是提醒事件,跳过规划器,直接构建默认动作
|
# 如果是提醒事件,跳过规划器,直接构建默认动作
|
||||||
if trigger_event.source == "reminder_system":
|
if trigger_event.source == "reminder_system":
|
||||||
# 1. 获取原始消息上下文
|
# 1. 获取上下文信息
|
||||||
action_message = {}
|
metadata = trigger_event.metadata or {}
|
||||||
if trigger_event.related_message_id:
|
action_message = metadata
|
||||||
# 直接将从数据库获取的完整消息记录作为 action_message
|
reminder_content = trigger_event.reason.replace("定时提醒:", "").strip()
|
||||||
action_message = await db_get(
|
|
||||||
Messages, {"message_id": trigger_event.related_message_id}, single_result=True
|
|
||||||
) or {}
|
|
||||||
|
|
||||||
# 2. 智能确定@对象
|
# 2. 确定目标用户名
|
||||||
reason_text = trigger_event.reason.replace("定时提醒:", "").strip()
|
target_user_name = None
|
||||||
user_name_match = re.search(r"艾特一下(\S+)", reason_text)
|
match = re.search(r"艾特一下([^,,\s]+)", reminder_content)
|
||||||
|
if match:
|
||||||
if user_name_match:
|
target_user_name = match.group(1)
|
||||||
user_name = user_name_match.group(1)
|
|
||||||
at_message = reason_text.replace(f"艾特一下{user_name}", "").strip()
|
|
||||||
elif action_message.get("user_nickname"):
|
|
||||||
user_name = action_message.get("user_nickname")
|
|
||||||
at_message = reason_text
|
|
||||||
else:
|
else:
|
||||||
user_name = "我"
|
from src.person_info.person_info import get_person_info_manager
|
||||||
at_message = reason_text
|
user_id = metadata.get("user_id")
|
||||||
|
platform = metadata.get("platform")
|
||||||
|
if user_id and platform:
|
||||||
|
person_id = get_person_info_manager().get_person_id(platform, user_id)
|
||||||
|
target_user_name = await get_person_info_manager().get_value(person_id, "person_name")
|
||||||
|
|
||||||
|
if not target_user_name:
|
||||||
|
logger.warning(f"无法从提醒 '{reminder_content}' 中确定目标用户,回退")
|
||||||
|
raise Exception("无法确定目标用户")
|
||||||
|
|
||||||
# 3. 构建动作
|
# 3. 构建动作
|
||||||
action_result = {
|
action_result = {
|
||||||
"action_type": "at_user",
|
"action_type": "at_user",
|
||||||
"reasoning": "执行定时提醒",
|
"reasoning": "执行定时提醒",
|
||||||
"action_data": {
|
"action_data": {
|
||||||
"user_name": user_name,
|
"user_name": target_user_name,
|
||||||
"at_message": at_message or "时间到啦!"
|
"at_message": reminder_content
|
||||||
},
|
},
|
||||||
"action_message": action_message
|
"action_message": action_message
|
||||||
}
|
}
|
||||||
|
|
||||||
# 4. 执行或回退
|
# 4. 执行或回退
|
||||||
try:
|
try:
|
||||||
success, _, _ = await self.cycle_processor._handle_action(
|
original_chat_id = metadata.get("chat_id")
|
||||||
|
if not original_chat_id:
|
||||||
|
if trigger_event.related_message_id:
|
||||||
|
db_message = await db_get(Messages, {"message_id": trigger_event.related_message_id}, single_result=True) or {}
|
||||||
|
original_chat_id = db_message.get("chat_id")
|
||||||
|
|
||||||
|
if not original_chat_id:
|
||||||
|
raise Exception("提醒事件中缺少chat_id")
|
||||||
|
|
||||||
|
from src.chat.heart_flow.heartflow import heartflow
|
||||||
|
subflow = await heartflow.get_or_create_subheartflow(original_chat_id)
|
||||||
|
if not subflow:
|
||||||
|
raise Exception(f"无法为chat_id {original_chat_id} 获取subflow")
|
||||||
|
|
||||||
|
success, _, _ = await subflow.heart_fc_instance.cycle_processor._handle_action(
|
||||||
action=action_result["action_type"],
|
action=action_result["action_type"],
|
||||||
reasoning=action_result["reasoning"],
|
reasoning=action_result["reasoning"],
|
||||||
action_data=action_result["action_data"],
|
action_data=action_result["action_data"],
|
||||||
cycle_timers={},
|
cycle_timers={},
|
||||||
thinking_id="",
|
thinking_id="",
|
||||||
action_message=action_result["action_message"]
|
action_message=action_result["action_message"],
|
||||||
)
|
)
|
||||||
if not success:
|
if not success:
|
||||||
raise Exception("at_user action failed")
|
raise Exception("at_user action failed")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
logger.warning(f"{self.context.log_prefix} at_user动作执行失败,回退到proactive_reply")
|
logger.warning(f"{self.context.log_prefix} at_user动作执行失败: {e},回退到proactive_reply")
|
||||||
fallback_action = {
|
fallback_action = {
|
||||||
"action_type": "proactive_reply",
|
"action_type": "proactive_reply",
|
||||||
"action_data": {"topic": trigger_event.reason},
|
"action_data": {"topic": trigger_event.reason},
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class SmartReminderAnalyzer:
|
|||||||
请判断用户是否想要设置提醒,如果是,请提取:
|
请判断用户是否想要设置提醒,如果是,请提取:
|
||||||
1. 是否包含提醒请求 (has_reminder: true/false)
|
1. 是否包含提醒请求 (has_reminder: true/false)
|
||||||
2. 置信度 (confidence: 0.0-1.0)
|
2. 置信度 (confidence: 0.0-1.0)
|
||||||
3. 相对时间表达 (relative_time: 如"3分钟后", "2小时后")
|
3. 相对时间表达 (relative_time: 标准化的时间表达,例如将'半小时后'转换为'30分钟后', '明天下午三点'转换为'明天15点')
|
||||||
4. 提醒内容 (content: 提醒的具体内容)
|
4. 提醒内容 (content: 提醒的具体内容)
|
||||||
5. 分析原因 (reasoning: 判断理由)
|
5. 分析原因 (reasoning: 判断理由)
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ class SmartReminderAnalyzer:
|
|||||||
{{
|
{{
|
||||||
"has_reminder": true/false,
|
"has_reminder": true/false,
|
||||||
"confidence": 0.0-1.0,
|
"confidence": 0.0-1.0,
|
||||||
"relative_time": "时间表达",
|
"relative_time": "标准化的时间表达 (例如 '30分钟后', '2小时后')",
|
||||||
"content": "提醒内容",
|
"content": "提醒内容",
|
||||||
"reasoning": "判断理由"
|
"reasoning": "判断理由"
|
||||||
}}"""
|
}}"""
|
||||||
@@ -152,7 +152,7 @@ class SmartReminderAnalyzer:
|
|||||||
logger.info(f"备用解析成功: {result}")
|
logger.info(f"备用解析成功: {result}")
|
||||||
return result
|
return result
|
||||||
except Exception as fallback_error:
|
except Exception as fallback_error:
|
||||||
logger.error(f"备用JSON解析也失败: {fallback_error}")
|
logger.error(f"备用JSON解析也失败了: {fallback_error}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"LLM分析失败: {e}")
|
logger.error(f"LLM分析失败: {e}")
|
||||||
|
|||||||
@@ -151,9 +151,9 @@ class HeartFCMessageReceiver:
|
|||||||
# 获取对应的subheartflow实例
|
# 获取对应的subheartflow实例
|
||||||
from src.chat.heart_flow.heartflow import heartflow
|
from src.chat.heart_flow.heartflow import heartflow
|
||||||
|
|
||||||
subflow = await heartflow.get_or_create_subheartflow(chat.stream_id)
|
subflow = await heartflow.get_or_create_subheartflow(metadata.get("chat_id"))
|
||||||
if not subflow:
|
if not subflow:
|
||||||
logger.error(f"无法获取subheartflow实例: {chat.stream_id}")
|
logger.error(f"无法获取subheartflow实例: {metadata.get('chat_id')}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 创建主动思考事件,触发完整的思考流程
|
# 创建主动思考事件,触发完整的思考流程
|
||||||
@@ -163,10 +163,8 @@ class HeartFCMessageReceiver:
|
|||||||
event = ProactiveTriggerEvent(
|
event = ProactiveTriggerEvent(
|
||||||
source="reminder_system",
|
source="reminder_system",
|
||||||
reason=f"定时提醒:{reminder_content}",
|
reason=f"定时提醒:{reminder_content}",
|
||||||
metadata={
|
metadata=metadata,
|
||||||
"reminder_text": reminder_content,
|
related_message_id=metadata.get("original_message_id")
|
||||||
"trigger_time": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# 通过subflow的HeartFChatting实例触发主动思考
|
# 通过subflow的HeartFChatting实例触发主动思考
|
||||||
@@ -181,10 +179,11 @@ class HeartFCMessageReceiver:
|
|||||||
|
|
||||||
# Fallback: 如果主动思考失败,直接发送提醒消息
|
# Fallback: 如果主动思考失败,直接发送提醒消息
|
||||||
try:
|
try:
|
||||||
|
from src.plugin_system.apis.send_api import text_to_stream
|
||||||
reminder_content = metadata.get('content', '提醒时间到了')
|
reminder_content = metadata.get('content', '提醒时间到了')
|
||||||
await text_to_stream(
|
await text_to_stream(
|
||||||
text=f"⏰ 提醒:{reminder_content}",
|
text=f"⏰ 提醒:{reminder_content}",
|
||||||
stream_id=chat.stream_id,
|
stream_id=metadata.get("chat_id"),
|
||||||
typing=False
|
typing=False
|
||||||
)
|
)
|
||||||
logger.info(f"Fallback提醒消息已发送: {reminder_content}")
|
logger.info(f"Fallback提醒消息已发送: {reminder_content}")
|
||||||
@@ -196,6 +195,7 @@ class HeartFCMessageReceiver:
|
|||||||
metadata = {
|
metadata = {
|
||||||
"type": "reminder",
|
"type": "reminder",
|
||||||
"user_id": reminder_event.user_id,
|
"user_id": reminder_event.user_id,
|
||||||
|
"platform": chat.platform,
|
||||||
"chat_id": chat.stream_id,
|
"chat_id": chat.stream_id,
|
||||||
"content": reminder_event.content,
|
"content": reminder_event.content,
|
||||||
"confidence": reminder_event.confidence,
|
"confidence": reminder_event.confidence,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ def init_prompt():
|
|||||||
{time_block}
|
{time_block}
|
||||||
{identity_block}
|
{identity_block}
|
||||||
|
|
||||||
|
{users_in_chat}
|
||||||
{custom_prompt_block}
|
{custom_prompt_block}
|
||||||
{chat_context_description},以下是具体的聊天内容。
|
{chat_context_description},以下是具体的聊天内容。
|
||||||
{chat_content_block}
|
{chat_content_block}
|
||||||
@@ -154,7 +155,6 @@ def init_prompt():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ActionPlanner:
|
class ActionPlanner:
|
||||||
def __init__(self, chat_id: str, action_manager: ActionManager):
|
def __init__(self, chat_id: str, action_manager: ActionManager):
|
||||||
self.chat_id = chat_id
|
self.chat_id = chat_id
|
||||||
@@ -165,7 +165,6 @@ class ActionPlanner:
|
|||||||
self.planner_llm = LLMRequest(
|
self.planner_llm = LLMRequest(
|
||||||
model_set=model_config.model_task_config.planner, request_type="planner"
|
model_set=model_config.model_task_config.planner, request_type="planner"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.last_obs_time_mark = 0.0
|
self.last_obs_time_mark = 0.0
|
||||||
|
|
||||||
async def _get_long_term_memory_context(self) -> str:
|
async def _get_long_term_memory_context(self) -> str:
|
||||||
@@ -268,14 +267,14 @@ class ActionPlanner:
|
|||||||
# 假设消息列表是按时间顺序排列的,最后一个是最新的
|
# 假设消息列表是按时间顺序排列的,最后一个是最新的
|
||||||
return message_id_list[-1].get("message")
|
return message_id_list[-1].get("message")
|
||||||
|
|
||||||
def _parse_single_action(
|
async def _parse_single_action(
|
||||||
self,
|
self,
|
||||||
action_json: dict,
|
action_json: dict,
|
||||||
message_id_list: list, # 使用 planner.py 的 list of dict
|
message_id_list: list, # 使用 planner.py 的 list of dict
|
||||||
current_available_actions: list, # 使用 planner.py 的 list of tuple
|
current_available_actions: list, # 使用 planner.py 的 list of tuple
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
[注释] 解析单个小脑LLM返回的action JSON,并将其转换为标准化的字典。
|
[注释] 解析单个LLM返回的action JSON,并将其转换为标准化的字典。
|
||||||
"""
|
"""
|
||||||
parsed_actions = []
|
parsed_actions = []
|
||||||
try:
|
try:
|
||||||
@@ -312,6 +311,16 @@ class ActionPlanner:
|
|||||||
"available_actions": available_actions_dict,
|
"available_actions": available_actions_dict,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
# 如果是at_user动作且只有user_name,尝试转换为user_id
|
||||||
|
if action == "at_user" and "user_name" in action_data and "user_id" not in action_data:
|
||||||
|
user_name = action_data["user_name"]
|
||||||
|
from src.person_info.person_info import get_person_info_manager
|
||||||
|
user_info = await get_person_info_manager().get_person_info_by_name(user_name)
|
||||||
|
if user_info and user_info.get("user_id"):
|
||||||
|
action_data["user_id"] = user_info["user_id"]
|
||||||
|
logger.info(f"成功将用户名 '{user_name}' 解析为 user_id '{user_info['user_id']}'")
|
||||||
|
else:
|
||||||
|
logger.warning(f"无法将用户名 '{user_name}' 解析为 user_id")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{self.log_prefix}解析单个action时出错: {e}")
|
logger.error(f"{self.log_prefix}解析单个action时出错: {e}")
|
||||||
parsed_actions.append(
|
parsed_actions.append(
|
||||||
@@ -349,22 +358,6 @@ class ActionPlanner:
|
|||||||
统一决策是否进行聊天回复(reply)以及执行哪些actions。
|
统一决策是否进行聊天回复(reply)以及执行哪些actions。
|
||||||
"""
|
"""
|
||||||
# --- 1. 准备上下文信息 ---
|
# --- 1. 准备上下文信息 ---
|
||||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
|
||||||
chat_id=self.chat_id,
|
|
||||||
timestamp=time.time(),
|
|
||||||
limit=int(global_config.chat.max_context_size * 0.6),
|
|
||||||
)
|
|
||||||
chat_content_block, message_id_list = build_readable_messages_with_id(
|
|
||||||
messages=message_list_before_now,
|
|
||||||
timestamp_mode="normal",
|
|
||||||
read_mark=self.last_obs_time_mark,
|
|
||||||
truncate=True,
|
|
||||||
show_actions=True,
|
|
||||||
)
|
|
||||||
if pseudo_message:
|
|
||||||
chat_content_block += f"\n[m99] 刚刚, 用户: {pseudo_message}"
|
|
||||||
self.last_obs_time_mark = time.time()
|
|
||||||
|
|
||||||
is_group_chat, chat_target_info, current_available_actions = self.get_necessary_info()
|
is_group_chat, chat_target_info, current_available_actions = self.get_necessary_info()
|
||||||
if available_actions is None:
|
if available_actions is None:
|
||||||
available_actions = current_available_actions
|
available_actions = current_available_actions
|
||||||
@@ -377,8 +370,6 @@ class ActionPlanner:
|
|||||||
chat_target_info=chat_target_info,
|
chat_target_info=chat_target_info,
|
||||||
current_available_actions=available_actions,
|
current_available_actions=available_actions,
|
||||||
mode=mode,
|
mode=mode,
|
||||||
chat_content_block_override=chat_content_block,
|
|
||||||
message_id_list_override=message_id_list,
|
|
||||||
)
|
)
|
||||||
llm_content, _ = await self.planner_llm.generate_response_async(prompt=prompt)
|
llm_content, _ = await self.planner_llm.generate_response_async(prompt=prompt)
|
||||||
|
|
||||||
@@ -392,7 +383,7 @@ class ActionPlanner:
|
|||||||
if isinstance(parsed_json, list):
|
if isinstance(parsed_json, list):
|
||||||
for item in parsed_json:
|
for item in parsed_json:
|
||||||
if isinstance(item, dict):
|
if isinstance(item, dict):
|
||||||
final_actions.extend(self._parse_single_action(item, used_message_id_list, list(available_actions.items())))
|
final_actions.extend(await self._parse_single_action(item, used_message_id_list, list(available_actions.items())))
|
||||||
|
|
||||||
# 如果是私聊且开启了强制回复,并且没有任何回复性action,则强制添加reply
|
# 如果是私聊且开启了强制回复,并且没有任何回复性action,则强制添加reply
|
||||||
if not is_group_chat and global_config.chat.force_reply_private:
|
if not is_group_chat and global_config.chat.force_reply_private:
|
||||||
@@ -402,7 +393,7 @@ class ActionPlanner:
|
|||||||
"action_type": "reply",
|
"action_type": "reply",
|
||||||
"reasoning": "私聊强制回复",
|
"reasoning": "私聊强制回复",
|
||||||
"action_data": {},
|
"action_data": {},
|
||||||
"action_message": self.get_latest_message(message_id_list),
|
"action_message": self.get_latest_message(used_message_id_list),
|
||||||
"available_actions": available_actions,
|
"available_actions": available_actions,
|
||||||
})
|
})
|
||||||
logger.info(f"{self.log_prefix}私聊强制回复已触发,添加 'reply' 动作")
|
logger.info(f"{self.log_prefix}私聊强制回复已触发,添加 'reply' 动作")
|
||||||
@@ -444,8 +435,6 @@ class ActionPlanner:
|
|||||||
chat_target_info: Optional[dict],
|
chat_target_info: Optional[dict],
|
||||||
current_available_actions: Dict[str, ActionInfo],
|
current_available_actions: Dict[str, ActionInfo],
|
||||||
mode: ChatMode = ChatMode.FOCUS,
|
mode: ChatMode = ChatMode.FOCUS,
|
||||||
chat_content_block_override: Optional[str] = None,
|
|
||||||
message_id_list_override: Optional[List] = None,
|
|
||||||
refresh_time: bool = False, # 添加缺失的参数
|
refresh_time: bool = False, # 添加缺失的参数
|
||||||
) -> tuple[str, list]:
|
) -> tuple[str, list]:
|
||||||
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
"""构建 Planner LLM 的提示词 (获取模板并填充数据)"""
|
||||||
@@ -479,7 +468,7 @@ class ActionPlanner:
|
|||||||
timestamp=time.time(),
|
timestamp=time.time(),
|
||||||
limit=int(global_config.chat.max_context_size * 0.2), # 主动思考时只看少量最近消息
|
limit=int(global_config.chat.max_context_size * 0.2), # 主动思考时只看少量最近消息
|
||||||
)
|
)
|
||||||
chat_content_block, _ = build_readable_messages_with_id(
|
chat_content_block, message_id_list = build_readable_messages_with_id(
|
||||||
messages=message_list_short,
|
messages=message_list_short,
|
||||||
timestamp_mode="normal",
|
timestamp_mode="normal",
|
||||||
truncate=False,
|
truncate=False,
|
||||||
@@ -505,7 +494,7 @@ class ActionPlanner:
|
|||||||
chat_content_block=chat_content_block or "最近没有聊天内容。",
|
chat_content_block=chat_content_block or "最近没有聊天内容。",
|
||||||
actions_before_now_block=actions_before_now_block,
|
actions_before_now_block=actions_before_now_block,
|
||||||
)
|
)
|
||||||
return prompt, []
|
return prompt, message_id_list
|
||||||
|
|
||||||
# --- FOCUS 和 NORMAL 模式的逻辑 ---
|
# --- FOCUS 和 NORMAL 模式的逻辑 ---
|
||||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
||||||
@@ -513,7 +502,6 @@ class ActionPlanner:
|
|||||||
timestamp=time.time(),
|
timestamp=time.time(),
|
||||||
limit=int(global_config.chat.max_context_size * 0.6),
|
limit=int(global_config.chat.max_context_size * 0.6),
|
||||||
)
|
)
|
||||||
|
|
||||||
chat_content_block, message_id_list = build_readable_messages_with_id(
|
chat_content_block, message_id_list = build_readable_messages_with_id(
|
||||||
messages=message_list_before_now,
|
messages=message_list_before_now,
|
||||||
timestamp_mode="normal",
|
timestamp_mode="normal",
|
||||||
@@ -581,6 +569,14 @@ class ActionPlanner:
|
|||||||
if global_config.custom_prompt.planner_custom_prompt_content:
|
if global_config.custom_prompt.planner_custom_prompt_content:
|
||||||
custom_prompt_block = global_config.custom_prompt.planner_custom_prompt_content
|
custom_prompt_block = global_config.custom_prompt.planner_custom_prompt_content
|
||||||
|
|
||||||
|
from src.person_info.person_info import get_person_info_manager
|
||||||
|
users_in_chat_str = ""
|
||||||
|
if is_group_chat and chat_target_info and chat_target_info.get("group_id"):
|
||||||
|
user_list = await get_person_info_manager().get_specific_value_list("person_name", lambda x: x is not None)
|
||||||
|
if user_list:
|
||||||
|
users_in_chat_str = "当前聊天中的用户列表(用于@):\n" + "\n".join([f"- {name} (ID: {pid})" for pid, name in user_list.items()]) + "\n"
|
||||||
|
|
||||||
|
|
||||||
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt")
|
||||||
prompt = planner_prompt_template.format(
|
prompt = planner_prompt_template.format(
|
||||||
schedule_block=schedule_block,
|
schedule_block=schedule_block,
|
||||||
@@ -596,6 +592,7 @@ class ActionPlanner:
|
|||||||
identity_block=identity_block,
|
identity_block=identity_block,
|
||||||
custom_prompt_block=custom_prompt_block,
|
custom_prompt_block=custom_prompt_block,
|
||||||
bot_name=bot_name,
|
bot_name=bot_name,
|
||||||
|
users_in_chat=users_in_chat_str
|
||||||
)
|
)
|
||||||
return prompt, message_id_list
|
return prompt, message_id_list
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -21,12 +21,12 @@ class AtAction(BaseAction):
|
|||||||
# === 基本信息(必须填写)===
|
# === 基本信息(必须填写)===
|
||||||
action_name = "at_user"
|
action_name = "at_user"
|
||||||
action_description = "发送艾特消息"
|
action_description = "发送艾特消息"
|
||||||
activation_type = ActionActivationType.LLM_JUDGE # 消息接收时激活(?)
|
activation_type = ActionActivationType.LLM_JUDGE
|
||||||
parallel_action = False
|
parallel_action = False
|
||||||
chat_type_allow = ChatType.GROUP
|
chat_type_allow = ChatType.GROUP
|
||||||
|
|
||||||
# === 功能描述(必须填写)===
|
# === 功能描述(必须填写)===
|
||||||
action_parameters = {"user_name": "需要艾特用户的名字", "at_message": "艾特用户时要发送的消,注意消息里不要有@"}
|
action_parameters = {"user_name": "需要艾特用户的名字", "at_message": "艾特用户时要发送的消息"}
|
||||||
action_require = [
|
action_require = [
|
||||||
"当需要艾特某个用户时使用",
|
"当需要艾特某个用户时使用",
|
||||||
"当你需要提醒特定用户查看消息时使用",
|
"当你需要提醒特定用户查看消息时使用",
|
||||||
@@ -48,24 +48,43 @@ class AtAction(BaseAction):
|
|||||||
|
|
||||||
if not user_name or not at_message:
|
if not user_name or not at_message:
|
||||||
logger.warning("艾特用户的动作缺少必要参数。")
|
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, "缺少必要参数"
|
return False, "缺少必要参数"
|
||||||
|
|
||||||
user_info = await get_person_info_manager().get_person_info_by_name(user_name)
|
from src.plugin_system.apis import send_api
|
||||||
if not user_info or not user_info.get("user_id"):
|
from fuzzywuzzy import process
|
||||||
logger.info(f"找不到名为 '{user_name}' 的用户。")
|
|
||||||
|
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})")
|
||||||
return False, "用户不存在"
|
return False, "用户不存在"
|
||||||
|
|
||||||
|
user_id = choices[best_match]
|
||||||
|
user_info = {"user_id": user_id, "user_nickname": best_match}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 使用回复器生成艾特回复,而不是直接发送命令
|
|
||||||
from src.chat.replyer.default_generator import DefaultReplyer
|
from src.chat.replyer.default_generator import DefaultReplyer
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
|
|
||||||
# 获取当前聊天流
|
|
||||||
chat_manager = get_chat_manager()
|
chat_manager = get_chat_manager()
|
||||||
chat_stream = chat_manager.get_stream(self.chat_id)
|
chat_stream = chat_manager.get_stream(self.chat_id)
|
||||||
|
|
||||||
@@ -73,89 +92,43 @@ class AtAction(BaseAction):
|
|||||||
logger.error(f"找不到聊天流: {self.stream_id}")
|
logger.error(f"找不到聊天流: {self.stream_id}")
|
||||||
return False, "聊天流不存在"
|
return False, "聊天流不存在"
|
||||||
|
|
||||||
# 创建回复器实例
|
|
||||||
replyer = DefaultReplyer(chat_stream)
|
replyer = DefaultReplyer(chat_stream)
|
||||||
|
|
||||||
# 构建回复对象,将艾特消息作为回复目标
|
|
||||||
reply_to = f"{user_name}:{at_message}"
|
|
||||||
extra_info = f"你需要艾特用户 {user_name} 并回复他们说: {at_message}"
|
extra_info = f"你需要艾特用户 {user_name} 并回复他们说: {at_message}"
|
||||||
|
|
||||||
# 使用回复器生成回复
|
success, llm_response, _ = await replyer.generate_reply_with_context(
|
||||||
success, llm_response, prompt = await replyer.generate_reply_with_context(
|
reply_to=f"{user_name}:{at_message}",
|
||||||
reply_to=reply_to,
|
|
||||||
extra_info=extra_info,
|
extra_info=extra_info,
|
||||||
enable_tool=False, # 艾特回复通常不需要工具调用
|
enable_tool=False,
|
||||||
from_plugin=False
|
from_plugin=False
|
||||||
)
|
)
|
||||||
|
|
||||||
if success and llm_response:
|
if not success or not 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("回复器生成回复失败")
|
logger.error("回复器生成回复失败")
|
||||||
return False, "回复生成失败"
|
return False, "回复生成失败"
|
||||||
|
|
||||||
except Exception as e:
|
final_message = llm_response.get("content", "")
|
||||||
logger.error(f"执行艾特用户动作时发生异常: {e}", exc_info=True)
|
if not final_message:
|
||||||
await self.store_action_info(
|
logger.warning("回复器生成了空内容")
|
||||||
action_build_into_prompt=True,
|
return False, "回复内容为空"
|
||||||
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(
|
await self.send_command(
|
||||||
"SEND_AT_MESSAGE",
|
"SEND_AT_MESSAGE",
|
||||||
args={"qq_id": user_id, "text": text},
|
args={"group_id": self.chat_stream.group_info.group_id, "qq_id": user_id, "text": final_message},
|
||||||
display_message=f"艾特用户 {name} 并发送消息: {text}",
|
display_message=f"艾特用户 {user_name} 并发送消息: {final_message}",
|
||||||
)
|
)
|
||||||
|
|
||||||
return True, "艾特消息已发送", True
|
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)
|
||||||
|
return False, f"执行失败: {str(e)}"
|
||||||
|
|
||||||
|
|
||||||
@register_plugin
|
@register_plugin
|
||||||
@@ -163,7 +136,7 @@ class AtUserPlugin(BasePlugin):
|
|||||||
plugin_name: str = "at_user_plugin"
|
plugin_name: str = "at_user_plugin"
|
||||||
enable_plugin: bool = True
|
enable_plugin: bool = True
|
||||||
dependencies: list[str] = []
|
dependencies: list[str] = []
|
||||||
python_dependencies: list[str] = []
|
python_dependencies: list[str] = ["fuzzywuzzy", "python-Levenshtein"]
|
||||||
config_file_name: str = "config.toml"
|
config_file_name: str = "config.toml"
|
||||||
config_schema: dict = {}
|
config_schema: dict = {}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user