feat(core): 实现消息异步处理并引入LLM驱动的智能表情回应

本次更新对系统核心处理流程和插件功能进行了重要升级,主要包含以下两方面:

1.  **消息处理异步化**:
    - 在 `main.py` 中引入了 `asyncio.create_task` 机制,将每条消息的处理过程包装成一个独立的后台任务。
    - 这解决了长时间运行的AI或插件操作可能阻塞主事件循环的问题,显著提升了机器人的响应速度和系统稳定性。
    - 为后台任务添加了完成回调,现在可以详细地记录每个消息处理任务的成功、失败或取消状态及其耗时,便于监控和调试。

2.  **`set_emoji_like` 插件智能化**:
    - 为 `set_emoji_like` 插件增加了LLM驱动的表情选择功能。当动作指令未指定具体表情时,插件会自动构建包含聊天上下文、情绪和人设的提示,请求LLM选择一个最合适的表情进行回应。
    - 为支持此功能,对AFC规划器的提示词进行了优化,为LLM提供了更清晰的参数示例和规则,提高了动作生成的准确性。

此外,为了统一日志规范,将 `[所见]` 消息接收日志集中到 `bot.py` 中,确保在任何过滤逻辑执行前记录所有收到的消息,并移除了插件中重复的日志。
This commit is contained in:
tt-P607
2025-09-24 15:43:12 +08:00
parent 37f9b46bcf
commit 1a24233b86
6 changed files with 130 additions and 12 deletions

View File

@@ -13,11 +13,14 @@ from src.common.logger import get_logger
from src.plugin_system.apis import send_api
from .qq_emoji_list import qq_face
from src.plugin_system.base.component_types import ChatType
from src.plugin_system.apis import llm_api
from src.config.config import model_config, global_config
from src.chat.utils.chat_message_builder import build_readable_messages
logger = get_logger("set_emoji_like_plugin")
def get_emoji_id(emoji_input: str) -> str | None:
async def get_emoji_id(emoji_input: str) -> str | None:
"""根据输入获取表情ID"""
# 如果输入本身就是数字ID直接返回
if emoji_input.isdigit() or (isinstance(emoji_input, str) and emoji_input.startswith("😊")):
@@ -99,11 +102,19 @@ class SetEmojiLikeAction(BaseAction):
set_like = self.action_data.get("set", True)
if not emoji_input:
logger.error("未提供表情")
return False, "未提供表情"
logger.info("未提供表情将由LLM决定")
try:
emoji_input = await self.ask_llm_for_emoji()
if not emoji_input:
logger.error("LLM未能选择表情")
return False, "LLM未能选择表情"
except Exception as e:
logger.error(f"请求LLM选择表情时出错: {e}")
return False, f"请求LLM选择表情时出错: {e}"
logger.info(f"设置表情回应: {emoji_input}, 是否设置: {set_like}")
emoji_id = get_emoji_id(emoji_input)
emoji_id = await get_emoji_id(emoji_input)
if not emoji_id:
logger.error(f"找不到表情: '{emoji_input}'。请从可用列表中选择。")
await self.store_action_info(
@@ -160,6 +171,71 @@ class SetEmojiLikeAction(BaseAction):
)
return False, f"设置表情回应失败: {e}"
async def ask_llm_for_emoji(self) -> str | None:
"""构建Prompt并请求LLM选择一个表情"""
from src.mood.mood_manager import mood_manager
from src.individuality.individuality import get_individuality
from src.chat.message_manager.message_manager import message_manager
# 1. 获取上下文信息
stream_context = message_manager.stream_contexts.get(self.chat_stream.stream_id)
if not stream_context:
logger.error(f"无法为 stream_id '{self.chat_stream.stream_id}' 找到 StreamContext")
return None
history_messages = stream_context.get_latest_messages(20)
chat_context = build_readable_messages(
[msg.flatten() for msg in history_messages],
replace_bot_name=True,
timestamp_mode="normal_no_YMD",
truncate=True,
)
target_message_content = self.action_message.get("processed_plain_text", "")
mood = mood_manager.get_mood_by_chat_id(self.chat_stream.stream_id).mood_state
identity = await get_individuality().get_personality_block()
# 2. 构建Prompt
emoji_options_str = ", ".join(self.emoji_options)
bot_name = global_config.bot.nickname or "爱莉希雅"
prompt = f"""
# 指令:选择一个最合适的表情来回应消息
## 场景描述
你的名字是“{bot_name}”。
{identity}
你现在的心情是:{mood}
## 聊天上下文
下面是最近的聊天记录:
{chat_context}
## 你的任务
你需要针对下面的这条消息,选择一个最合适的表情来“贴”在上面,以表达你的心情和回应。
目标消息:"{target_message_content}"
## 表情选项
请从以下表情中,选择一个最能代表你此刻心情的表情。你只能选择一个,并直接返回它的【名称】。
{emoji_options_str}
## 输出要求
直接输出你选择的表情【名称】,不要添加任何多余的文字、解释或标点符号。
你选择的表情名称是:
"""
# 3. 调用LLM
success, response, _, _ = await llm_api.generate_with_model(
prompt, model_config.model_task_config.tool_executor
)
if success and response:
# 清理LLM返回的可能存在的额外字符
cleaned_response = re.sub(r"[\[\]\'\"]", "", response).strip()
logger.info(f"LLM选择了表情: '{cleaned_response}'")
return cleaned_response
return None
# ===== 插件注册 =====
@register_plugin