Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
@@ -92,12 +92,12 @@ def init_prompt():
|
||||
- {schedule_block}
|
||||
|
||||
## 历史记录
|
||||
### 📜 已读历史消息(仅供参考)
|
||||
### 📜 已读历史消息
|
||||
{read_history_prompt}
|
||||
|
||||
{cross_context_block}
|
||||
|
||||
### 📬 未读历史消息(动作执行对象)
|
||||
### 📬 未读历史消息
|
||||
{unread_history_prompt}
|
||||
|
||||
{notice_block}
|
||||
@@ -129,13 +129,10 @@ def init_prompt():
|
||||
### 核心任务
|
||||
- 你现在的主要任务是和 {sender_name} 聊天。同时,也有其他用户会参与聊天,你可以参考他们的回复内容,但是你现在想回复{sender_name}的发言。
|
||||
|
||||
- {reply_target_block} 你需要生成一段紧密相关的回复。
|
||||
- {reply_target_block} 你需要生成一段紧密相关且与历史消息相关的回复。
|
||||
|
||||
## 规则
|
||||
{safety_guidelines_block}
|
||||
**重要提醒:**
|
||||
- **已读历史消息仅作为当前聊天情景的参考**
|
||||
- **未读历史消息是你需要回应的对象**
|
||||
|
||||
你的回复应该是一条简短、完整且口语化的回复。
|
||||
|
||||
@@ -986,14 +983,9 @@ class DefaultReplyer:
|
||||
else:
|
||||
read_history_prompt = "暂无已读历史消息"
|
||||
|
||||
# 构建未读历史消息 prompt(包含兴趣度)
|
||||
# 构建未读历史消息 prompt
|
||||
unread_history_prompt = ""
|
||||
if unread_messages:
|
||||
# 尝试获取兴趣度评分
|
||||
interest_scores = await self._get_interest_scores_for_messages(
|
||||
[msg.flatten() for msg in unread_messages]
|
||||
)
|
||||
|
||||
unread_lines = []
|
||||
for msg in unread_messages:
|
||||
msg_id = msg.message_id
|
||||
@@ -1029,14 +1021,11 @@ class DefaultReplyer:
|
||||
replace_bot_name=True
|
||||
)
|
||||
|
||||
# 添加兴趣度信息
|
||||
interest_score = interest_scores.get(msg_id, 0.0)
|
||||
interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else ""
|
||||
|
||||
unread_lines.append(f"{msg_time} {sender_name}: {msg_content}{interest_text}")
|
||||
# 不显示兴趣度,replyer只需要关注消息内容本身
|
||||
unread_lines.append(f"{msg_time} {sender_name}: {msg_content}")
|
||||
|
||||
unread_history_prompt_str = "\n".join(unread_lines)
|
||||
unread_history_prompt = f"这是未读历史消息,包含兴趣度评分,请优先对兴趣值高的消息做出动作:\n{unread_history_prompt_str}"
|
||||
unread_history_prompt = f"这是未读历史消息:\n{unread_history_prompt_str}"
|
||||
else:
|
||||
unread_history_prompt = "暂无未读历史消息"
|
||||
|
||||
@@ -1098,9 +1087,6 @@ class DefaultReplyer:
|
||||
# 构建未读历史消息 prompt
|
||||
unread_history_prompt = ""
|
||||
if unread_messages:
|
||||
# 尝试获取兴趣度评分
|
||||
interest_scores = await self._get_interest_scores_for_messages(unread_messages)
|
||||
|
||||
unread_lines = []
|
||||
for msg in unread_messages:
|
||||
msg_id = msg.get("message_id", "")
|
||||
@@ -1135,15 +1121,12 @@ class DefaultReplyer:
|
||||
replace_bot_name=True
|
||||
)
|
||||
|
||||
# 添加兴趣度信息
|
||||
interest_score = interest_scores.get(msg_id, 0.0)
|
||||
interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else ""
|
||||
|
||||
unread_lines.append(f"{msg_time} {sender_name}: {msg_content}{interest_text}")
|
||||
# 不显示兴趣度,replyer只需要关注消息内容本身
|
||||
unread_lines.append(f"{msg_time} {sender_name}: {msg_content}")
|
||||
|
||||
unread_history_prompt_str = "\n".join(unread_lines)
|
||||
unread_history_prompt = (
|
||||
f"这是未读历史消息,包含兴趣度评分,请优先对兴趣值高的消息做出动作:\n{unread_history_prompt_str}"
|
||||
f"这是未读历史消息:\n{unread_history_prompt_str}"
|
||||
)
|
||||
else:
|
||||
unread_history_prompt = "暂无未读历史消息"
|
||||
|
||||
@@ -55,6 +55,7 @@ class StreamContext(BaseDataModel):
|
||||
priority_info: dict | None = None
|
||||
triggering_user_id: str | None = None # 触发当前聊天流的用户ID
|
||||
is_replying: bool = False # 是否正在生成回复
|
||||
processing_message_id: str | None = None # 当前正在规划/处理的目标消息ID,用于防止重复回复
|
||||
|
||||
def add_action_to_message(self, message_id: str, action: str):
|
||||
"""
|
||||
|
||||
@@ -310,7 +310,7 @@ class ChatterPlanFilter:
|
||||
flattened_unread = [msg.flatten() for msg in unread_messages]
|
||||
|
||||
# 尝试获取兴趣度评分(返回以真实 message_id 为键的字典)
|
||||
await self._get_interest_scores_for_messages(flattened_unread)
|
||||
interest_scores = await self._get_interest_scores_for_messages(flattened_unread)
|
||||
|
||||
# 为未读消息分配短 id(保持与 build_readable_messages_with_id 的一致结构)
|
||||
message_id_list = assign_message_ids(flattened_unread)
|
||||
@@ -319,14 +319,17 @@ class ChatterPlanFilter:
|
||||
for idx, msg in enumerate(flattened_unread):
|
||||
mapped = message_id_list[idx]
|
||||
synthetic_id = mapped.get("id")
|
||||
msg.get("message_id") or msg.get("id")
|
||||
real_msg_id = msg.get("message_id") or msg.get("id")
|
||||
msg_time = time.strftime("%H:%M:%S", time.localtime(msg.get("time", time.time())))
|
||||
user_nickname = msg.get("user_nickname", "未知用户")
|
||||
msg_content = msg.get("processed_plain_text", "")
|
||||
|
||||
# 不再显示兴趣度,但保留合成ID供模型内部使用
|
||||
# 同时,为了让模型更好地理解上下文,我们显示用户名
|
||||
unread_lines.append(f"<{synthetic_id}> {msg_time} {user_nickname}: {msg_content}")
|
||||
# 获取兴趣度信息并显示在提示词中
|
||||
interest_score = interest_scores.get(real_msg_id, 0.0)
|
||||
interest_text = f" [兴趣度: {interest_score:.3f}]" if interest_score > 0 else ""
|
||||
|
||||
# 在未读消息中显示兴趣度,让planner优先选择兴趣度高的消息
|
||||
unread_lines.append(f"<{synthetic_id}> {msg_time} {user_nickname}: {msg_content}{interest_text}")
|
||||
|
||||
unread_history_block = "\n".join(unread_lines)
|
||||
else:
|
||||
|
||||
@@ -189,6 +189,37 @@ class ChatterActionPlanner:
|
||||
plan_filter = ChatterPlanFilter(self.chat_id, available_actions)
|
||||
filtered_plan = await plan_filter.filter(reply_not_available, initial_plan)
|
||||
|
||||
# 4.5 检查是否正在处理相同的目标消息,防止重复回复
|
||||
target_message_id = None
|
||||
for action in filtered_plan.decided_actions:
|
||||
if action.action_type in ["reply", "proactive_reply"] and action.action_message:
|
||||
# 提取目标消息ID
|
||||
if hasattr(action.action_message, 'message_id'):
|
||||
target_message_id = action.action_message.message_id
|
||||
elif isinstance(action.action_message, dict):
|
||||
target_message_id = action.action_message.get('message_id')
|
||||
break
|
||||
|
||||
# 如果找到目标消息ID,检查是否已经在处理中
|
||||
if target_message_id and context:
|
||||
if context.processing_message_id == target_message_id:
|
||||
logger.warning(
|
||||
f"目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
|
||||
)
|
||||
# 返回 no_action,避免重复处理
|
||||
from src.common.data_models.info_data_model import ActionPlannerInfo
|
||||
no_action = ActionPlannerInfo(
|
||||
action_type="no_action",
|
||||
reasoning=f"目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
|
||||
action_data={},
|
||||
action_message=None,
|
||||
)
|
||||
return [asdict(no_action)], None
|
||||
else:
|
||||
# 记录当前正在处理的消息ID
|
||||
context.processing_message_id = target_message_id
|
||||
logger.debug(f"开始处理目标消息: {target_message_id}")
|
||||
|
||||
# 5. 使用 PlanExecutor 执行 Plan
|
||||
execution_result = await self.executor.execute(filtered_plan)
|
||||
|
||||
@@ -209,12 +240,20 @@ class ChatterActionPlanner:
|
||||
context.chat_mode = ChatMode.NORMAL
|
||||
await self._sync_chat_mode_to_stream(context)
|
||||
|
||||
# 8. 返回结果
|
||||
# 8. 清理处理标记
|
||||
if context:
|
||||
context.processing_message_id = None
|
||||
logger.debug(f"已清理处理标记,完成规划流程")
|
||||
|
||||
# 9. 返回结果
|
||||
return self._build_return_result(filtered_plan)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"增强版规划流程出错: {e}")
|
||||
self.planner_stats["failed_plans"] += 1
|
||||
# 清理处理标记
|
||||
if context:
|
||||
context.processing_message_id = None
|
||||
return [], None
|
||||
|
||||
async def _normal_mode_flow(self, context: "StreamContext | None") -> tuple[list[dict[str, Any]], Any | None]:
|
||||
@@ -258,6 +297,27 @@ class ChatterActionPlanner:
|
||||
break
|
||||
|
||||
if should_reply and target_message:
|
||||
# 检查是否正在处理相同的目标消息,防止重复回复
|
||||
target_message_id = target_message.message_id
|
||||
if context and context.processing_message_id == target_message_id:
|
||||
logger.warning(
|
||||
f"Normal模式: 目标消息 {target_message_id} 已经在处理中,跳过本次规划以防止重复回复"
|
||||
)
|
||||
# 返回 no_action,避免重复处理
|
||||
from src.common.data_models.info_data_model import ActionPlannerInfo
|
||||
no_action = ActionPlannerInfo(
|
||||
action_type="no_action",
|
||||
reasoning=f"目标消息 {target_message_id} 已经在处理中,跳过以防止重复回复",
|
||||
action_data={},
|
||||
action_message=None,
|
||||
)
|
||||
return [asdict(no_action)], None
|
||||
|
||||
# 记录当前正在处理的消息ID
|
||||
if context:
|
||||
context.processing_message_id = target_message_id
|
||||
logger.debug(f"Normal模式: 开始处理目标消息: {target_message_id}")
|
||||
|
||||
# 达到reply阈值,直接进入回复流程
|
||||
from src.common.data_models.info_data_model import ActionPlannerInfo, Plan
|
||||
from src.plugin_system.base.component_types import ChatType
|
||||
@@ -287,6 +347,11 @@ class ChatterActionPlanner:
|
||||
|
||||
logger.info("Normal模式: 执行reply动作完成")
|
||||
|
||||
# 清理处理标记
|
||||
if context:
|
||||
context.processing_message_id = None
|
||||
logger.debug(f"Normal模式: 已清理处理标记")
|
||||
|
||||
# 无论是否回复,都进行退出normal模式的判定
|
||||
await self._check_exit_normal_mode(context)
|
||||
|
||||
@@ -310,6 +375,9 @@ class ChatterActionPlanner:
|
||||
except Exception as e:
|
||||
logger.error(f"Normal模式流程出错: {e}")
|
||||
self.planner_stats["failed_plans"] += 1
|
||||
# 清理处理标记
|
||||
if context:
|
||||
context.processing_message_id = None
|
||||
return [], None
|
||||
|
||||
async def _check_exit_normal_mode(self, context: "StreamContext | None") -> None:
|
||||
|
||||
@@ -46,8 +46,10 @@ def init_prompts():
|
||||
# 决策流程
|
||||
1. 已读仅供参考,不能对已读执行任何动作。
|
||||
2. 目标消息必须来自未读历史,并使用其前缀 <m...> 作为 target_message_id。
|
||||
3. 优先级:
|
||||
3. **【重要】兴趣度优先原则**:每条未读消息后都标注了 [兴趣度: X.XXX],数值越高表示该消息越值得你关注和回复。在选择回复目标时,**应优先选择兴趣度高的消息**(通常 ≥0.5 表示较高兴趣),除非有特殊情况(如被直接@或提问)。
|
||||
4. 优先级:
|
||||
- 直接针对你:@你、回复你、点名提问、引用你的消息。
|
||||
- **兴趣度高的消息**:兴趣度 ≥0.5 的消息应优先考虑回复。
|
||||
- 与你强相关的话题或你熟悉的问题。
|
||||
- 其他与上下文弱相关的内容最后考虑。
|
||||
{mentioned_bonus}
|
||||
@@ -58,9 +60,9 @@ def init_prompts():
|
||||
|
||||
# 思绪流规范(thinking)
|
||||
- 真实、自然、非结论化,像给自己看的随笔。
|
||||
- 描述你看到/想到/感觉到的过程,不要出现“因此/我决定”等总结词。
|
||||
- 描述你看到/想到/感觉到的过程,不要出现"因此/我决定"等总结词。
|
||||
- 直接使用对方昵称,而不是 <m1>/<m2> 这样的标签。
|
||||
- 禁止出现“兴趣度、分数”等技术术语或内部实现细节。
|
||||
- **禁止出现"兴趣度、分数"等技术术语或内部实现细节**。兴趣度仅用于你内部的决策权重,不要在thinking中提及,而应该用自然语言描述你对消息的感受(如"这个话题挺有意思的"、"我对这个很感兴趣"等)。
|
||||
|
||||
## 可用动作列表
|
||||
{action_options_text}
|
||||
|
||||
Reference in New Issue
Block a user