Merge branch 'afc' of https://github.com/MoFox-Studio/MoFox_Bot into afc
This commit is contained in:
@@ -4,6 +4,7 @@ PlanExecutor: 接收 Plan 对象并执行其中的所有动作。
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
@@ -83,11 +84,11 @@ class PlanExecutor:
|
|||||||
execution_results.extend(reply_result["results"])
|
execution_results.extend(reply_result["results"])
|
||||||
self.execution_stats["reply_executions"] += len(reply_actions)
|
self.execution_stats["reply_executions"] += len(reply_actions)
|
||||||
|
|
||||||
# 并行执行其他动作
|
# 将其他动作放入后台任务执行,避免阻塞主流程
|
||||||
if other_actions:
|
if other_actions:
|
||||||
other_result = await self._execute_other_actions(other_actions, plan)
|
asyncio.create_task(self._execute_other_actions(other_actions, plan))
|
||||||
execution_results.extend(other_result["results"])
|
logger.info(f"已将 {len(other_actions)} 个其他动作放入后台任务执行。")
|
||||||
self.execution_stats["other_action_executions"] += len(other_actions)
|
# 注意:后台任务的结果不会立即计入本次返回的统计数据
|
||||||
|
|
||||||
# 更新总体统计
|
# 更新总体统计
|
||||||
self.execution_stats["total_executed"] += len(plan.decided_actions)
|
self.execution_stats["total_executed"] += len(plan.decided_actions)
|
||||||
@@ -216,12 +217,35 @@ class PlanExecutor:
|
|||||||
try:
|
try:
|
||||||
logger.info(f"执行其他动作: {action_info.action_type} (原因: {action_info.reasoning})")
|
logger.info(f"执行其他动作: {action_info.action_type} (原因: {action_info.reasoning})")
|
||||||
|
|
||||||
|
action_data = action_info.action_data or {}
|
||||||
|
|
||||||
|
# 针对 poke_user 动作,特殊处理
|
||||||
|
if action_info.action_type == "poke_user":
|
||||||
|
target_message = action_info.action_message
|
||||||
|
if target_message:
|
||||||
|
# 优先直接获取 user_id,这才是最可靠的信息
|
||||||
|
user_id = target_message.get("user_id")
|
||||||
|
if user_id:
|
||||||
|
action_data["user_id"] = user_id
|
||||||
|
logger.info(f"检测到戳一戳动作,目标用户ID: {user_id}")
|
||||||
|
else:
|
||||||
|
# 如果没有 user_id,再尝试用 user_nickname 作为备用方案
|
||||||
|
user_name = target_message.get("user_nickname")
|
||||||
|
if user_name:
|
||||||
|
action_data["user_name"] = user_name
|
||||||
|
logger.info(f"检测到戳一戳动作,目标用户: {user_name}")
|
||||||
|
else:
|
||||||
|
logger.warning("无法从戳一戳消息中获取用户ID或昵称。")
|
||||||
|
|
||||||
|
# 传递原始消息ID以支持引用
|
||||||
|
action_data["target_message_id"] = target_message.get("message_id")
|
||||||
|
|
||||||
# 构建动作参数
|
# 构建动作参数
|
||||||
action_params = {
|
action_params = {
|
||||||
"chat_id": plan.chat_id,
|
"chat_id": plan.chat_id,
|
||||||
"target_message": action_info.action_message,
|
"target_message": action_info.action_message,
|
||||||
"reasoning": action_info.reasoning,
|
"reasoning": action_info.reasoning,
|
||||||
"action_data": action_info.action_data or {},
|
"action_data": action_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 通过动作管理器执行动作
|
# 通过动作管理器执行动作
|
||||||
|
|||||||
@@ -699,7 +699,32 @@ class DefaultReplyer:
|
|||||||
)
|
)
|
||||||
read_history_prompt = f"这是已读历史消息,仅作为当前聊天情景的参考:\n{read_content}"
|
read_history_prompt = f"这是已读历史消息,仅作为当前聊天情景的参考:\n{read_content}"
|
||||||
else:
|
else:
|
||||||
read_history_prompt = "暂无已读历史消息"
|
# 如果没有已读消息,则从数据库加载最近的上下文
|
||||||
|
logger.info("暂无已读历史消息,正在从数据库加载上下文...")
|
||||||
|
fallback_messages = get_raw_msg_before_timestamp_with_chat(
|
||||||
|
chat_id=chat_id,
|
||||||
|
timestamp=time.time(),
|
||||||
|
limit=global_config.chat.max_context_size,
|
||||||
|
)
|
||||||
|
if fallback_messages:
|
||||||
|
# 从 unread_messages 获取 message_id 列表,用于去重
|
||||||
|
unread_message_ids = {msg.message_id for msg in unread_messages}
|
||||||
|
filtered_fallback_messages = [
|
||||||
|
msg for msg in fallback_messages if msg.get("message_id") not in unread_message_ids
|
||||||
|
]
|
||||||
|
|
||||||
|
if filtered_fallback_messages:
|
||||||
|
read_content = build_readable_messages(
|
||||||
|
filtered_fallback_messages,
|
||||||
|
replace_bot_name=True,
|
||||||
|
timestamp_mode="normal_no_YMD",
|
||||||
|
truncate=True,
|
||||||
|
)
|
||||||
|
read_history_prompt = f"这是已读历史消息,仅作为当前聊天情景的参考:\n{read_content}"
|
||||||
|
else:
|
||||||
|
read_history_prompt = "暂无已读历史消息"
|
||||||
|
else:
|
||||||
|
read_history_prompt = "暂无已读历史消息"
|
||||||
|
|
||||||
# 构建未读历史消息 prompt(包含兴趣度)
|
# 构建未读历史消息 prompt(包含兴趣度)
|
||||||
unread_history_prompt = ""
|
unread_history_prompt = ""
|
||||||
|
|||||||
@@ -747,7 +747,7 @@ class VideoAnalyzer:
|
|||||||
os.unlink(temp_path)
|
os.unlink(temp_path)
|
||||||
|
|
||||||
# 保存分析结果到数据库(仅保存成功的结果)
|
# 保存分析结果到数据库(仅保存成功的结果)
|
||||||
if success:
|
if success and not result.startswith("❌"):
|
||||||
metadata = {"filename": filename, "file_size": len(video_bytes), "analysis_timestamp": time.time()}
|
metadata = {"filename": filename, "file_size": len(video_bytes), "analysis_timestamp": time.time()}
|
||||||
self._store_video_result(video_hash=video_hash, description=result, metadata=metadata)
|
self._store_video_result(video_hash=video_hash, description=result, metadata=metadata)
|
||||||
logger.info("✅ 分析结果已保存到数据库")
|
logger.info("✅ 分析结果已保存到数据库")
|
||||||
|
|||||||
@@ -152,10 +152,10 @@ class EmojiAction(BaseAction):
|
|||||||
|
|
||||||
# 调用LLM
|
# 调用LLM
|
||||||
models = llm_api.get_available_models()
|
models = llm_api.get_available_models()
|
||||||
chat_model_config = models.get("planner")
|
chat_model_config = models.get("utils")
|
||||||
if not chat_model_config:
|
if not chat_model_config:
|
||||||
logger.error(f"{self.log_prefix} 未找到'planner'模型配置,无法调用LLM")
|
logger.error(f"{self.log_prefix} 未找到'utils'模型配置,无法调用LLM")
|
||||||
return False, "未找到'planner'模型配置"
|
return False, "未找到'utils'模型配置"
|
||||||
|
|
||||||
success, chosen_emotion, _, _ = await llm_api.generate_with_model(
|
success, chosen_emotion, _, _ = await llm_api.generate_with_model(
|
||||||
prompt, model_config=chat_model_config, request_type="emoji"
|
prompt, model_config=chat_model_config, request_type="emoji"
|
||||||
@@ -212,10 +212,10 @@ class EmojiAction(BaseAction):
|
|||||||
|
|
||||||
# 调用LLM
|
# 调用LLM
|
||||||
models = llm_api.get_available_models()
|
models = llm_api.get_available_models()
|
||||||
chat_model_config = models.get("planner")
|
chat_model_config = models.get("utils")
|
||||||
if not chat_model_config:
|
if not chat_model_config:
|
||||||
logger.error(f"{self.log_prefix} 未找到'planner'模型配置,无法调用LLM")
|
logger.error(f"{self.log_prefix} 未找到'utils'模型配置,无法调用LLM")
|
||||||
return False, "未找到'planner'模型配置"
|
return False, "未找到'utils'模型配置"
|
||||||
|
|
||||||
success, chosen_description, _, _ = await llm_api.generate_with_model(
|
success, chosen_description, _, _ = await llm_api.generate_with_model(
|
||||||
prompt, model_config=chat_model_config, request_type="emoji"
|
prompt, model_config=chat_model_config, request_type="emoji"
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ class PokeAction(BaseAction):
|
|||||||
|
|
||||||
# === 功能描述(必须填写)===
|
# === 功能描述(必须填写)===
|
||||||
action_parameters = {
|
action_parameters = {
|
||||||
"user_name": "需要戳一戳的用户的名字",
|
"user_name": "需要戳一戳的用户的名字 (可选)",
|
||||||
|
"user_id": "需要戳一戳的用户的ID (可选,优先级更高)",
|
||||||
"times": "需要戳一戳的次数 (默认为 1)",
|
"times": "需要戳一戳的次数 (默认为 1)",
|
||||||
}
|
}
|
||||||
action_require = ["当需要戳某个用户时使用", "当你想提醒特定用户时使用"]
|
action_require = ["当需要戳某个用户时使用", "当你想提醒特定用户时使用"]
|
||||||
@@ -46,32 +47,38 @@ class PokeAction(BaseAction):
|
|||||||
|
|
||||||
async def execute(self) -> Tuple[bool, str]:
|
async def execute(self) -> Tuple[bool, str]:
|
||||||
"""执行戳一戳的动作"""
|
"""执行戳一戳的动作"""
|
||||||
|
user_id = self.action_data.get("user_id")
|
||||||
user_name = self.action_data.get("user_name")
|
user_name = self.action_data.get("user_name")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
times = int(self.action_data.get("times", 1))
|
times = int(self.action_data.get("times", 1))
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
times = 1
|
times = 1
|
||||||
|
|
||||||
if not user_name:
|
# 优先使用 user_id
|
||||||
logger.warning("戳一戳动作缺少 'user_name' 参数。")
|
if not user_id:
|
||||||
return False, "缺少 'user_name' 参数"
|
if not user_name:
|
||||||
|
logger.warning("戳一戳动作缺少 'user_id' 或 'user_name' 参数。")
|
||||||
|
return False, "缺少用户标识参数"
|
||||||
|
|
||||||
user_info = await get_person_info_manager().get_person_info_by_name(user_name)
|
# 备用方案:通过 user_name 查找
|
||||||
if not user_info or not user_info.get("user_id"):
|
user_info = await get_person_info_manager().get_person_info_by_name(user_name)
|
||||||
logger.info(f"找不到名为 '{user_name}' 的用户。")
|
if not user_info or not user_info.get("user_id"):
|
||||||
return False, f"找不到名为 '{user_name}' 的用户"
|
logger.info(f"找不到名为 '{user_name}' 的用户。")
|
||||||
|
return False, f"找不到名为 '{user_name}' 的用户"
|
||||||
|
user_id = user_info.get("user_id")
|
||||||
|
|
||||||
user_id = user_info.get("user_id")
|
display_name = user_name or user_id
|
||||||
|
|
||||||
for i in range(times):
|
for i in range(times):
|
||||||
logger.info(f"正在向 {user_name} ({user_id}) 发送第 {i + 1}/{times} 次戳一戳...")
|
logger.info(f"正在向 {display_name} ({user_id}) 发送第 {i + 1}/{times} 次戳一戳...")
|
||||||
await self.send_command(
|
await self.send_command(
|
||||||
"SEND_POKE", args={"qq_id": user_id}, display_message=f"戳了戳 {user_name} ({i + 1}/{times})"
|
"SEND_POKE", args={"qq_id": user_id}, display_message=f"戳了戳 {display_name} ({i + 1}/{times})"
|
||||||
)
|
)
|
||||||
# 添加一个小的延迟,以避免发送过快
|
# 添加一个小的延迟,以避免发送过快
|
||||||
await asyncio.sleep(0.5)
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
success_message = f"已向 {user_name} 发送 {times} 次戳一戳。"
|
success_message = f"已向 {display_name} 发送 {times} 次戳一戳。"
|
||||||
await self.store_action_info(
|
await self.store_action_info(
|
||||||
action_build_into_prompt=True, action_prompt_display=success_message, action_done=True
|
action_build_into_prompt=True, action_prompt_display=success_message, action_done=True
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user