feat(emoji): 新增表情选择模式并重构选择逻辑

引入了两种表情选择模式,允许用户通过配置决定表情选择的行为:
- **emotion**: 基于情感标签进行选择,利用LLM根据对话上下文挑选最合适的情感。
- **description**: 基于表情的详细描述进行选择,让LLM从描述列表中挑选最匹配的表情。

此项重构将原有的单一选择逻辑拆分为两种独立的模式,并为每种模式优化了相应的提示词(Prompt)和处理流程,提高了表情选择的灵活性和准确性。同时,在配置文件中添加了`emoji_selection_mode`选项以支持此新功能。
This commit is contained in:
minecraft1024a
2025-09-12 21:41:38 +08:00
committed by Windpicker-owo
parent b18a13b091
commit 6ecb7a7d6a
3 changed files with 96 additions and 27 deletions

View File

@@ -387,6 +387,7 @@ class EmojiConfig(ValidatedConfigBase):
content_filtration: bool = Field(default=False, description="内容过滤") content_filtration: bool = Field(default=False, description="内容过滤")
filtration_prompt: str = Field(default="符合公序良俗", description="过滤提示") filtration_prompt: str = Field(default="符合公序良俗", description="过滤提示")
enable_emotion_analysis: bool = Field(default=True, description="启用情感分析") enable_emotion_analysis: bool = Field(default=True, description="启用情感分析")
emoji_selection_mode: Literal["emotion", "description"] = Field(default="emotion", description="表情选择模式")
max_context_emojis: int = Field(default=30, description="每次随机传递给LLM的表情包最大数量0为全部") max_context_emojis: int = Field(default=30, description="每次随机传递给LLM的表情包最大数量0为全部")

View File

@@ -99,6 +99,9 @@ class EmojiAction(BaseAction):
available_emotions = list(emotion_map.keys()) available_emotions = list(emotion_map.keys())
emoji_base64, emoji_description = "", "" emoji_base64, emoji_description = "", ""
# 4. 根据配置选择不同的表情选择模式
if global_config.emoji.emoji_selection_mode == "emotion":
# --- 情感标签选择模式 ---
if not available_emotions: if not available_emotions:
logger.warning(f"{self.log_prefix} 获取到的表情包均无情感标签, 将随机发送") logger.warning(f"{self.log_prefix} 获取到的表情包均无情感标签, 将随机发送")
emoji_base64, emoji_description = random.choice(all_emojis_data) emoji_base64, emoji_description = random.choice(all_emojis_data)
@@ -114,7 +117,7 @@ class EmojiAction(BaseAction):
show_actions=False, show_actions=False,
) )
# 4. 构建prompt让LLM选择情感 # 构建prompt让LLM选择情感
prompt = f""" prompt = f"""
你是一个正在进行聊天的网友,你需要根据一个理由和最近的聊天记录,从一个情感标签列表中选择最匹配的一个。 你是一个正在进行聊天的网友,你需要根据一个理由和最近的聊天记录,从一个情感标签列表中选择最匹配的一个。
这是最近的聊天记录: 这是最近的聊天记录:
@@ -130,12 +133,12 @@ class EmojiAction(BaseAction):
else: else:
logger.debug(f"{self.log_prefix} 生成的LLM Prompt: {prompt}") logger.debug(f"{self.log_prefix} 生成的LLM Prompt: {prompt}")
# 5. 调用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("planner")
if not chat_model_config: if not chat_model_config:
logger.error(f"{self.log_prefix} 未找到'utils_small'模型配置无法调用LLM") logger.error(f"{self.log_prefix} 未找到'planner'模型配置无法调用LLM")
return False, "未找到'utils_small'模型配置" return False, "未找到'planner'模型配置"
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"
@@ -160,6 +163,67 @@ class EmojiAction(BaseAction):
) )
emoji_base64, emoji_description = random.choice(all_emojis_data) emoji_base64, emoji_description = random.choice(all_emojis_data)
elif global_config.emoji.emoji_selection_mode == "description":
# --- 详细描述选择模式 ---
# 获取最近的5条消息内容用于判断
recent_messages = message_api.get_recent_messages(chat_id=self.chat_id, limit=5)
messages_text = ""
if recent_messages:
messages_text = message_api.build_readable_messages(
messages=recent_messages,
timestamp_mode="normal_no_YMD",
truncate=False,
show_actions=False,
)
# 准备表情描述列表
emoji_descriptions = [desc for _, desc in all_emojis_data]
# 构建prompt让LLM选择描述
prompt = f"""
你是一个正在进行聊天的网友,你需要根据一个理由和最近的聊天记录,从一个表情包描述列表中选择最匹配的一个。
这是最近的聊天记录:
{messages_text}
这是理由:“{reason}
这里是可用的表情包描述:{emoji_descriptions}
请直接返回最匹配的那个表情包描述,不要进行任何解释或添加其他多余的文字。
"""
logger.debug(f"{self.log_prefix} 生成的LLM Prompt: {prompt}")
# 调用LLM
models = llm_api.get_available_models()
chat_model_config = models.get("planner")
if not chat_model_config:
logger.error(f"{self.log_prefix} 未找到'planner'模型配置无法调用LLM")
return False, "未找到'planner'模型配置"
success, chosen_description, _, _ = await llm_api.generate_with_model(
prompt, model_config=chat_model_config, request_type="emoji"
)
if not success:
logger.warning(f"{self.log_prefix} LLM调用失败: {chosen_description}, 将随机选择一个表情包")
emoji_base64, emoji_description = random.choice(all_emojis_data)
else:
chosen_description = chosen_description.strip().replace('"', "").replace("'", "")
logger.info(f"{self.log_prefix} LLM选择的描述: {chosen_description}")
# 查找与选择的描述匹配的表情包
matched_emoji = next((item for item in all_emojis_data if item == chosen_description), None)
if matched_emoji:
emoji_base64, emoji_description = matched_emoji
logger.info(f"{self.log_prefix} 找到匹配描述 '{chosen_description}' 的表情包")
else:
logger.warning(
f"{self.log_prefix} LLM选择的描述 '{chosen_description}' 不在可用列表中, 将随机选择一个表情包"
)
emoji_base64, emoji_description = random.choice(all_emojis_data)
else:
logger.error(f"{self.log_prefix} 无效的表情选择模式: {global_config.emoji.emoji_selection_mode}")
return False, "无效的表情选择模式"
# 7. 发送表情包 # 7. 发送表情包
success = await self.send_emoji(emoji_base64) success = await self.send_emoji(emoji_base64)

View File

@@ -263,6 +263,10 @@ steal_emoji = true # 是否偷取表情包让MoFox-Bot可以将一些表情
content_filtration = false # 是否启用表情包过滤,只有符合该要求的表情包才会被保存 content_filtration = false # 是否启用表情包过滤,只有符合该要求的表情包才会被保存
filtration_prompt = "符合公序良俗" # 表情包过滤要求,只有符合该要求的表情包才会被保存 filtration_prompt = "符合公序良俗" # 表情包过滤要求,只有符合该要求的表情包才会被保存
enable_emotion_analysis = false # 是否启用表情包感情关键词二次识别,启用后表情包在第一次识别完毕后将送入第二次大模型识别来总结感情关键词,并构建进回复和决策器的上下文消息中 enable_emotion_analysis = false # 是否启用表情包感情关键词二次识别,启用后表情包在第一次识别完毕后将送入第二次大模型识别来总结感情关键词,并构建进回复和决策器的上下文消息中
# 表情选择模式, 可选值为 "emotion" 或 "description"
# emotion: 让大模型从情感标签中选择
# description: 让大模型从详细描述中选择
emoji_selection_mode = "emotion"
max_context_emojis = 30 # 每次随机传递给LLM的表情包详细描述的最大数量0为全部 max_context_emojis = 30 # 每次随机传递给LLM的表情包详细描述的最大数量0为全部
[memory] [memory]