feat(planner): 重构动作规划器以支持组合动作和概率性表情
重写了 Planner 的主 Prompt,引导 LLM 将回复(reply)视为主动作,将表情(emoji)等视为辅助动作,从而更好地生成组合动作,使响应更生动。 - 移除了旧的“100%概率动作强制添加”逻辑,并替换为新的“概率性表情”机制。现在,当生成回复时,会根据配置的概率(emoji_chance)自动附加一个 emoji 动作。 - 改进了 emoji 动作的情感匹配逻辑,从精确匹配改为模糊匹配,提高了根据 LLM 输出找到合适表情的成功率。 - 修复了随机类型动作在激活概率计算时的一个边界条件问题。
This commit is contained in:
@@ -205,9 +205,8 @@ class ActionModifier:
|
|||||||
|
|
||||||
elif activation_type == ActionActivationType.RANDOM:
|
elif activation_type == ActionActivationType.RANDOM:
|
||||||
probability = action_info.random_activation_probability
|
probability = action_info.random_activation_probability
|
||||||
if probability >= 1.0:
|
probability = action_info.random_activation_probability
|
||||||
continue # 概率为100%或更高,直接激活
|
if random.random() >= probability:
|
||||||
if random.random() > probability:
|
|
||||||
reason = f"RANDOM类型未触发(概率{probability})"
|
reason = f"RANDOM类型未触发(概率{probability})"
|
||||||
deactivated_actions.append((action_name, reason))
|
deactivated_actions.append((action_name, reason))
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
||||||
|
|||||||
@@ -57,14 +57,25 @@ def init_prompt():
|
|||||||
|
|
||||||
{moderation_prompt}
|
{moderation_prompt}
|
||||||
|
|
||||||
现在请你根据聊天内容和用户的最新消息选择合适的action和触发action的消息:
|
**任务: 构建一个完整的响应**
|
||||||
|
你的任务是根据当前的聊天内容,构建一个完整的、人性化的响应。一个完整的响应由两部分组成:
|
||||||
|
1. **主要动作**: 这是响应的核心,通常是 `reply`(文本回复)。
|
||||||
|
2. **辅助动作 (可选)**: 这是为了增强表达效果的附加动作,例如 `emoji`(发送表情包)或 `poke_user`(戳一戳)。
|
||||||
|
|
||||||
|
**决策流程:**
|
||||||
|
1. 首先,决定是否要进行 `reply`。
|
||||||
|
2. 然后,评估当前的对话气氛和用户情绪,判断是否需要一个**辅助动作**来让你的回应更生动、更符合你的性格。
|
||||||
|
3. 如果需要,选择一个最合适的辅助动作与 `reply` 组合。
|
||||||
|
4. 如果用户明确要求了某个动作,请务必优先满足。
|
||||||
|
|
||||||
|
**可用动作:**
|
||||||
{actions_before_now_block}
|
{actions_before_now_block}
|
||||||
|
|
||||||
{no_action_block}
|
{no_action_block}
|
||||||
|
|
||||||
动作:reply
|
动作:reply
|
||||||
动作描述:参与聊天回复,发送文本进行表达
|
动作描述:参与聊天回复,发送文本进行表达
|
||||||
- 你想要闲聊或者随便附
|
- 你想要闲聊或者随便附和
|
||||||
- {mentioned_bonus}
|
- {mentioned_bonus}
|
||||||
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
- 如果你刚刚进行了回复,不要对同一个话题重复回应
|
||||||
- 不要回复自己发送的消息
|
- 不要回复自己发送的消息
|
||||||
@@ -76,11 +87,34 @@ def init_prompt():
|
|||||||
|
|
||||||
{action_options_text}
|
{action_options_text}
|
||||||
|
|
||||||
- 如果用户明确要求使用某个动作,请优先选择该动作。
|
|
||||||
- 当一个动作可以作为另一个动作的补充时,你应该同时选择它们。例如,在回复的同时可以发送表情包(emoji)。
|
|
||||||
你必须从上面列出的可用action中选择一个或多个,并说明触发action的消息id(不是消息原文)和选择该action的原因。消息id格式:m+数字
|
|
||||||
|
|
||||||
请根据动作示例,以严格的 JSON 格式输出,返回一个包含所有选定动作的JSON列表。如果只选择一个动作,也请将其包含在列表中。如果没有任何合适的动作,返回一个空列表[]。不要输出markdown格式```json等内容,直接输出且仅包含 JSON 列表内容:
|
**输出格式:**
|
||||||
|
你必须以严格的 JSON 格式输出,返回一个包含所有选定动作的JSON列表。如果没有任何合适的动作,返回一个空列表[]。
|
||||||
|
|
||||||
|
**单动作示例 (仅回复):**
|
||||||
|
[
|
||||||
|
{{
|
||||||
|
"action": "reply",
|
||||||
|
"target_message_id": "m123",
|
||||||
|
"reason": "回答用户的问题"
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
|
||||||
|
**组合动作示例 (回复 + 表情包):**
|
||||||
|
[
|
||||||
|
{{
|
||||||
|
"action": "reply",
|
||||||
|
"target_message_id": "m123",
|
||||||
|
"reason": "回答用户的问题"
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
"action": "emoji",
|
||||||
|
"target_message_id": "m123",
|
||||||
|
"reason": "用一个可爱的表情来缓和气氛"
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
|
||||||
|
不要输出markdown格式```json等内容,直接输出且仅包含 JSON 列表内容:
|
||||||
""",
|
""",
|
||||||
"planner_prompt",
|
"planner_prompt",
|
||||||
)
|
)
|
||||||
@@ -148,9 +182,9 @@ def init_prompt():
|
|||||||
动作描述:{action_description}
|
动作描述:{action_description}
|
||||||
{action_require}
|
{action_require}
|
||||||
{{
|
{{
|
||||||
"action": "{action_name}",{action_parameters},
|
"action": "{action_name}",
|
||||||
"target_message_id": "触发action的消息id",
|
"target_message_id": "触发action的消息id",
|
||||||
"reason":"触发action的原因"
|
"reason": "触发action的原因"{action_parameters}
|
||||||
}}
|
}}
|
||||||
""",
|
""",
|
||||||
"action_prompt",
|
"action_prompt",
|
||||||
@@ -409,18 +443,18 @@ class ActionPlanner:
|
|||||||
# --- 3. 后处理 ---
|
# --- 3. 后处理 ---
|
||||||
final_actions = self._filter_no_actions(final_actions)
|
final_actions = self._filter_no_actions(final_actions)
|
||||||
|
|
||||||
# === 强制后处理:确保100%概率的动作在回复时被附带 ===
|
# === 概率模式后处理:根据配置决定是否强制添加 emoji 动作 ===
|
||||||
|
if global_config.emoji.emoji_activate_type == 'random':
|
||||||
has_reply_action = any(a.get("action_type") == "reply" for a in final_actions)
|
has_reply_action = any(a.get("action_type") == "reply" for a in final_actions)
|
||||||
if has_reply_action:
|
if has_reply_action:
|
||||||
for action_name, action_info in available_actions.items():
|
|
||||||
if action_info.activation_type == ActionActivationType.RANDOM and action_info.random_activation_probability >= 1.0:
|
|
||||||
# 检查此动作是否已被选择
|
# 检查此动作是否已被选择
|
||||||
is_already_chosen = any(a.get("action_type") == action_name for a in final_actions)
|
is_already_chosen = any(a.get("action_type") == 'emoji' for a in final_actions)
|
||||||
if not is_already_chosen:
|
if not is_already_chosen:
|
||||||
logger.info(f"{self.log_prefix}强制添加100%概率动作: {action_name}")
|
if random.random() < global_config.emoji.emoji_chance:
|
||||||
|
logger.info(f"{self.log_prefix}根据概率 '{global_config.emoji.emoji_chance}' 添加 emoji 动作")
|
||||||
final_actions.append({
|
final_actions.append({
|
||||||
"action_type": action_name,
|
"action_type": 'emoji',
|
||||||
"reasoning": "根据100%概率设置强制添加",
|
"reasoning": f"根据概率 {global_config.emoji.emoji_chance} 自动添加",
|
||||||
"action_data": {},
|
"action_data": {},
|
||||||
"action_message": self.get_latest_message(used_message_id_list),
|
"action_message": self.get_latest_message(used_message_id_list),
|
||||||
"available_actions": available_actions,
|
"available_actions": available_actions,
|
||||||
|
|||||||
@@ -143,9 +143,12 @@ class EmojiAction(BaseAction):
|
|||||||
chosen_emotion = chosen_emotion.strip().replace('"', "").replace("'", "")
|
chosen_emotion = chosen_emotion.strip().replace('"', "").replace("'", "")
|
||||||
logger.info(f"{self.log_prefix} LLM选择的情感: {chosen_emotion}")
|
logger.info(f"{self.log_prefix} LLM选择的情感: {chosen_emotion}")
|
||||||
|
|
||||||
if chosen_emotion in emotion_map:
|
# 使用模糊匹配来查找最相关的情感标签
|
||||||
emoji_base64, emoji_description = random.choice(emotion_map[chosen_emotion])
|
matched_key = next((key for key in emotion_map if chosen_emotion in key), None)
|
||||||
logger.info(f"{self.log_prefix} 找到匹配情感 '{chosen_emotion}' 的表情包: {emoji_description}")
|
|
||||||
|
if matched_key:
|
||||||
|
emoji_base64, emoji_description = random.choice(emotion_map[matched_key])
|
||||||
|
logger.info(f"{self.log_prefix} 找到匹配情感 '{chosen_emotion}' (匹配到: '{matched_key}') 的表情包: {emoji_description}")
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"{self.log_prefix} LLM选择的情感 '{chosen_emotion}' 不在可用列表中, 将随机选择一个表情包"
|
f"{self.log_prefix} LLM选择的情感 '{chosen_emotion}' 不在可用列表中, 将随机选择一个表情包"
|
||||||
|
|||||||
Reference in New Issue
Block a user