feat(reply): 引入 reply 和 respond 动作,优化消息回复机制
- 增加 reply 动作,针对单条消息进行深度回复,使用 s4u 模板。 - 增加 respond 动作,统一回应未读消息,使用 normal 模板。 - 更新核心动作插件以支持新动作,确保配置选项可用。 - 优化动作执行逻辑,提升对话流畅性和响应准确性。
This commit is contained in:
@@ -69,14 +69,7 @@ def init_prompt():
|
||||
{keywords_reaction_prompt}
|
||||
{moderation_prompt}
|
||||
不要复读你前面发过的内容,意思相近也不行。
|
||||
不要浮夸,不要夸张修辞,平淡且不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 ),只输出一条回复就好。
|
||||
|
||||
**【!!!绝对禁止!!!】在回复中输出任何格式化标记**:
|
||||
- **核心原则**: 你的回复**只能**包含纯粹的口语化文本。任何看起来像程序指令、系统提示或格式标签的内容都**绝对不允许**出现在你的回复里。
|
||||
- **禁止模仿系统消息**: 绝对禁止输出任何类似 `[回复<xxx>:xxx]`、`[表情包:xxx]`、`[图片:xxx]` 的格式。这些都是系统用于展示消息的方式,不是你应该说的话。
|
||||
- **禁止模仿动作指令**: 绝对禁止输出 `[戳了戳]` 或 `[poke]`。这类互动由名为 `poke_user` 的特殊动作处理,不是文本消息。
|
||||
- **正确提及用户**: 如果想提到某人,直接说“你”或他/她的名字,绝对禁止使用 `[回复<某人>]` 或 `@某人` 的格式。
|
||||
- **正确表达情绪**: 如果想表达笑的情绪,直接说“哈哈”、“嘻嘻”等,绝对禁止使用 `[表情包:笑哭]` 这样的文字。
|
||||
不要浮夸,不要夸张修辞,平淡且不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at,[xx:xxx]系统格式化文字或 @等 ),只输出一条回复就好。
|
||||
|
||||
*你叫{bot_name},也有人叫你{bot_nickname}*
|
||||
|
||||
@@ -138,19 +131,13 @@ def init_prompt():
|
||||
## 规则
|
||||
{safety_guidelines_block}
|
||||
|
||||
注意:在规划回复时,务必确定对方是不是真的在叫自己。聊天时往往有数百甚至数千个用户,请务必认清自己的身份和角色,避免误以为对方在和自己对话而贸然插入回复,导致尴尬局面。
|
||||
你的回复应该是一条简短、完整且口语化的回复。
|
||||
|
||||
--------------------------------
|
||||
{time_block}
|
||||
|
||||
请注意不要输出多余内容(包括前后缀,冒号和引号,at或 @等 )。只输出回复内容。
|
||||
|
||||
**【!!!绝对禁止!!!】在回复中输出任何格式化标记**:
|
||||
- **核心原则**: 你的回复**只能**包含纯粹的口语化文本。任何看起来像程序指令、系统提示或格式标签的内容都**绝对不允许**出现在你的回复里。
|
||||
- **禁止模仿系统消息**: 绝对禁止输出任何类似 `[回复<xxx>:xxx]`、`[表情包:xxx]`、`[图片:xxx]` 的格式。这些都是系统用于展示消息的方式,不是你应该说的话。
|
||||
- **禁止模仿动作指令**: 绝对禁止输出 `[戳了戳]` 或 `[poke]`。这类互动由名为 `poke_user` 的特殊动作处理,不是文本消息。
|
||||
- **正确提及用户**: 如果想提到某人,直接说“你”或他/她的名字,绝对禁止使用 `[回复<某人>]` 或 `@某人` 的格式。
|
||||
- **正确表达情绪**: 如果想表达笑的情绪,直接说“哈哈”、“嘻嘻”等,绝对禁止使用 `[表情包:笑哭]` 这样的文字。
|
||||
请注意不要输出多余内容(包括前后缀,冒号和引号,at,[xx:xxx]系统格式化文字或 @等 )。只输出回复内容。
|
||||
|
||||
{moderation_prompt}
|
||||
|
||||
@@ -178,60 +165,66 @@ If you need to use the search tool, please directly call the function "lpmm_sear
|
||||
name="lpmm_get_knowledge_prompt",
|
||||
)
|
||||
|
||||
# normal 版 prompt 模板(0.9之前的简化模式)
|
||||
# normal 版 prompt 模板(参考 s4u 格式,用于统一回应未读消息)
|
||||
logger.debug("[Prompt模式调试] 正在注册normal_style_prompt模板")
|
||||
Prompt(
|
||||
"""
|
||||
{chat_scene}
|
||||
# 人设:{identity}
|
||||
|
||||
**重要:消息针对性判断**
|
||||
在回应之前,首先分析消息的针对性:
|
||||
1. **直接针对你**:@你、回复你、明确询问你 → 必须回应
|
||||
2. **间接相关**:涉及你感兴趣的话题但未直接问你 → 谨慎参与
|
||||
3. **他人对话**:与你无关的私人交流 → 通常不参与
|
||||
4. **重复内容**:他人已充分回答的问题 → 避免重复
|
||||
## 当前状态
|
||||
- 你现在的心情是:{mood_state}
|
||||
{schedule_block}
|
||||
|
||||
{expression_habits_block}
|
||||
{tool_info_block}
|
||||
{knowledge_prompt}
|
||||
{memory_block}
|
||||
{relation_info_block}
|
||||
{extra_info_block}
|
||||
## 历史记录
|
||||
### 📜 已读历史消息
|
||||
{read_history_prompt}
|
||||
|
||||
{cross_context_block}
|
||||
{identity}
|
||||
如果有人说你是人机,你可以用一种阴阳怪气的口吻来回应
|
||||
{schedule_block}
|
||||
|
||||
### 📬 未读历史消息
|
||||
{unread_history_prompt}
|
||||
|
||||
{notice_block}
|
||||
|
||||
## 表达方式
|
||||
- *你需要参考你的回复风格:*
|
||||
{reply_style}
|
||||
{keywords_reaction_prompt}
|
||||
|
||||
{expression_habits_block}
|
||||
|
||||
{tool_info_block}
|
||||
|
||||
{knowledge_prompt}
|
||||
|
||||
## 其他信息
|
||||
{memory_block}
|
||||
{relation_info_block}
|
||||
|
||||
{extra_info_block}
|
||||
{auth_role_prompt_block}
|
||||
|
||||
{action_descriptions}
|
||||
|
||||
下面是群里最近的聊天内容:
|
||||
--------------------------------
|
||||
## 任务
|
||||
|
||||
*{chat_scene}*
|
||||
|
||||
### 核心任务
|
||||
- 你需要对以上未读历史消息进行统一回应。这些消息可能来自不同的参与者,你需要理解整体对话动态,生成一段自然、连贯的回复。
|
||||
- 你的回复应该能够推动对话继续,可以回应其中一个或多个话题,也可以提出新的观点。
|
||||
|
||||
## 规则
|
||||
{safety_guidelines_block}
|
||||
注意:在规划回复时,务必确定对方是不是真的在叫自己。聊天时往往有数百甚至数千个用户,请务必认清自己的身份和角色,避免误以为对方在和自己对话而贸然插入回复,导致尴尬局面。
|
||||
你的回复应该是一条简短、完整且口语化的回复。
|
||||
|
||||
--------------------------------
|
||||
{time_block}
|
||||
{chat_info}
|
||||
--------------------------------
|
||||
|
||||
{reply_target_block}
|
||||
|
||||
你现在的心情是:{mood_state}
|
||||
{config_expression_style}
|
||||
注意不要复读你前面发过的内容,意思相近也不行。
|
||||
{keywords_reaction_prompt}
|
||||
请注意不要输出多余内容(包括前后缀,冒号和引号,at或 @等 )。只输出回复内容。
|
||||
|
||||
**【!!!绝对禁止!!!】在回复中输出任何格式化标记**:
|
||||
- **核心原则**: 你的回复**只能**包含纯粹的口语化文本。任何看起来像程序指令、系统提示或格式标签的内容都**绝对不允许**出现在你的回复里。
|
||||
- **禁止模仿系统消息**: 绝对禁止输出任何类似 `[回复<xxx>:xxx]`、`[表情包:xxx]`、`[图片:xxx]` 的格式。这些都是系统用于展示消息的方式,不是你应该说的话。
|
||||
- **禁止模仿动作指令**: 绝对禁止输出 `[戳了戳]` 或 `[poke]`。这类互动由名为 `poke_user` 的特殊动作处理,不是文本消息。
|
||||
- **正确提及用户**: 如果想提到某人,直接说“你”或他/她的名字,绝对禁止使用 `[回复<某人>]` 或 `@某人` 的格式。
|
||||
- **正确表达情绪**: 如果想表达笑的情绪,直接说“哈哈”、“嘻嘻”等,绝对禁止使用 `[表情包:笑哭]` 这样的文字。
|
||||
请注意不要输出多余内容(包括前后缀,冒号和引号,at,[xx:xxx]系统格式化文字或 @等 )。只输出回复内容。
|
||||
|
||||
{moderation_prompt}
|
||||
你的核心任务是针对 {reply_target_block} 中提到的内容,{relation_info_block}生成一段紧密相关且能推动对话的回复。你的回复应该:
|
||||
1. 明确回应目标消息,而不是宽泛地评论。
|
||||
2. 可以分享你的看法、提出相关问题,或者开个合适的玩笑。
|
||||
3. 目的是让对话更有趣、更深入。
|
||||
最终请输出一条简短、完整且口语化的回复。
|
||||
|
||||
*你叫{bot_name},也有人叫你{bot_nickname}*
|
||||
|
||||
@@ -371,6 +364,15 @@ class DefaultReplyer:
|
||||
return False, None, None
|
||||
llm_response = None
|
||||
try:
|
||||
# 从available_actions中提取prompt_mode(由action_manager传递)
|
||||
# 如果没有指定,默认使用s4u模式
|
||||
prompt_mode_value: str = "s4u"
|
||||
if available_actions and "_prompt_mode" in available_actions:
|
||||
mode = available_actions.get("_prompt_mode", "s4u")
|
||||
# 确保类型安全
|
||||
if isinstance(mode, str):
|
||||
prompt_mode_value = mode
|
||||
|
||||
# 构建 Prompt
|
||||
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
||||
prompt = await self.build_prompt_reply_context(
|
||||
@@ -380,6 +382,7 @@ class DefaultReplyer:
|
||||
choosen_actions=choosen_actions,
|
||||
enable_tool=enable_tool,
|
||||
reply_message=reply_message,
|
||||
prompt_mode=prompt_mode_value, # 传递prompt_mode
|
||||
)
|
||||
|
||||
if not prompt:
|
||||
@@ -1121,6 +1124,7 @@ class DefaultReplyer:
|
||||
available_actions: dict[str, ActionInfo] | None = None,
|
||||
enable_tool: bool = True,
|
||||
reply_message: DatabaseMessages | None = None,
|
||||
prompt_mode: str = "s4u", # 新增参数:s4u 或 normal
|
||||
) -> str:
|
||||
"""
|
||||
构建回复器上下文
|
||||
@@ -1133,6 +1137,7 @@ class DefaultReplyer:
|
||||
enable_timeout: 是否启用超时处理
|
||||
enable_tool: 是否启用工具调用
|
||||
reply_message: 回复的原始消息
|
||||
prompt_mode: 提示词模式,"s4u"(针对单条消息回复)或"normal"(统一回应未读消息)
|
||||
|
||||
Returns:
|
||||
str: 构建好的上下文
|
||||
@@ -1213,16 +1218,41 @@ class DefaultReplyer:
|
||||
|
||||
target = await replace_user_references_async(target, chat_stream.platform, replace_bot_name=True)
|
||||
|
||||
# (简化)不再对自消息做额外任务段落清理,只通过前置选择逻辑避免自目标
|
||||
|
||||
# 构建action描述 (如果启用planner)
|
||||
# 构建action描述(告诉回复器已选取的动作)
|
||||
action_descriptions = ""
|
||||
if available_actions:
|
||||
action_descriptions = "以下是系统中可用的动作列表。**【重要】**这些动作将由一个独立的决策模型决定是否执行,**并非你的职责**。你只需要了解这些能力的存在,以便更好地理解对话情景,**严禁**在你的回复中模仿、调用或提及这些动作本身。\n"
|
||||
for action_name, action_info in available_actions.items():
|
||||
action_description = action_info.description
|
||||
action_descriptions += f"- {action_name}: {action_description}\n"
|
||||
action_descriptions += "\n"
|
||||
# 过滤掉特殊键(以_开头)
|
||||
action_items = {k: v for k, v in available_actions.items() if not k.startswith("_")}
|
||||
|
||||
# 提取目标消息信息(如果存在)
|
||||
target_msg_info = available_actions.get("_target_message") # type: ignore
|
||||
|
||||
if action_items:
|
||||
if len(action_items) == 1:
|
||||
# 单个动作
|
||||
action_name, action_info = list(action_items.items())[0]
|
||||
action_desc = action_info.description
|
||||
|
||||
# 构建基础决策信息
|
||||
action_descriptions = f"## 决策信息\n\n你已经决定要执行 **{action_name}** 动作({action_desc})。\n\n"
|
||||
|
||||
# 如果有目标消息信息,添加目标消息详情
|
||||
if target_msg_info and isinstance(target_msg_info, dict):
|
||||
import time as time_module
|
||||
sender = target_msg_info.get("sender", "未知用户")
|
||||
content = target_msg_info.get("content", "")
|
||||
msg_time = target_msg_info.get("time", 0)
|
||||
time_str = time_module.strftime("%H:%M:%S", time_module.localtime(msg_time)) if msg_time else "未知时间"
|
||||
|
||||
action_descriptions += f"**目标消息**: {time_str} {sender} 说: {content}\n\n"
|
||||
else:
|
||||
# 多个动作
|
||||
action_descriptions = "## 决策信息\n\n你已经决定同时执行以下动作:\n\n"
|
||||
for action_name, action_info in action_items.items():
|
||||
action_desc = action_info.description
|
||||
action_descriptions += f"- **{action_name}**: {action_desc}\n"
|
||||
action_descriptions += "\n"
|
||||
|
||||
|
||||
# 从内存获取历史消息,避免重复查询数据库
|
||||
from src.plugin_system.apis.chat_api import get_chat_manager
|
||||
@@ -1423,7 +1453,7 @@ class DefaultReplyer:
|
||||
duration_minutes = (now - start_time).total_seconds() / 60
|
||||
remaining_minutes = (end_time - now).total_seconds() / 60
|
||||
schedule_block = (
|
||||
f"你当前正在进行“{activity}”,"
|
||||
f"- 你当前正在进行“{activity}”,"
|
||||
f"计划时间从{start_time.strftime('%H:%M')}到{end_time.strftime('%H:%M')}。"
|
||||
f"这项活动已经开始了{duration_minutes:.0f}分钟,"
|
||||
f"预计还有{remaining_minutes:.0f}分钟结束。"
|
||||
@@ -1431,9 +1461,9 @@ class DefaultReplyer:
|
||||
)
|
||||
|
||||
except (ValueError, AttributeError):
|
||||
schedule_block = f"你当前正在进行“{activity}”。(此为你的当前状态,仅供参考。除非被直接询问,否则不要在对话中主动提及。)"
|
||||
schedule_block = f"- 你当前正在进行“{activity}”。(此为你的当前状态,仅供参考。除非被直接询问,否则不要在对话中主动提及。)"
|
||||
else:
|
||||
schedule_block = f"你当前正在进行“{activity}”。(此为你的当前状态,仅供参考。除非被直接询问,否则不要在对话中主动提及。)"
|
||||
schedule_block = f"- 你当前正在进行“{activity}”。(此为你的当前状态,仅供参考。除非被直接询问,否则不要在对话中主动提及。)"
|
||||
|
||||
moderation_prompt_block = (
|
||||
"请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
|
||||
@@ -1483,11 +1513,21 @@ class DefaultReplyer:
|
||||
|
||||
if sender and target:
|
||||
if is_group_chat:
|
||||
reply_target_block = (
|
||||
f"现在{sender}说的:{target}。引起了你的注意,你想要在群里发言或者回复这条消息。原因是{reply_reason}"
|
||||
)
|
||||
if sender:
|
||||
reply_target_block = (
|
||||
f"现在{sender}的消息:{target}。引起了你的注意,你想要在群里发言或者回复这条消息。"
|
||||
)
|
||||
elif target:
|
||||
reply_target_block = f"现在{target}引起了你的注意,你想要在群里发言或者回复这条消息。"
|
||||
else:
|
||||
reply_target_block = "现在,你想要在群里发言或者回复消息。"
|
||||
else: # private chat
|
||||
reply_target_block = f"现在{sender}说的:{target}。引起了你的注意,针对这条消息回复。原因是{reply_reason}"
|
||||
if sender:
|
||||
reply_target_block = f"现在{sender}的消息:{target}。引起了你的注意,针对这条消息回复。"
|
||||
elif target:
|
||||
reply_target_block = f"现在{target}引起了你的注意,针对这条消息回复。"
|
||||
else:
|
||||
reply_target_block = "现在,你想要回复。"
|
||||
else:
|
||||
reply_target_block = ""
|
||||
|
||||
@@ -1511,7 +1551,7 @@ class DefaultReplyer:
|
||||
available_actions=available_actions,
|
||||
enable_tool=enable_tool,
|
||||
chat_target_info=self.chat_target_info,
|
||||
prompt_mode="s4u",
|
||||
prompt_mode=prompt_mode, # 使用传入的prompt_mode参数
|
||||
message_list_before_now_long=message_list_before_now_long,
|
||||
message_list_before_short=message_list_before_short,
|
||||
chat_talking_prompt_short=chat_talking_prompt_short,
|
||||
@@ -1539,8 +1579,10 @@ class DefaultReplyer:
|
||||
bot_nickname=",".join(global_config.bot.alias_names) if global_config.bot.alias_names else "",
|
||||
)
|
||||
|
||||
# 使用新的统一Prompt系统 - 使用正确的模板名称
|
||||
template_name = "s4u_style_prompt"
|
||||
# 使用新的统一Prompt系统 - 根据prompt_mode选择模板
|
||||
# s4u: 针对单条消息的深度回复
|
||||
# normal: 对未读消息的统一回应
|
||||
template_name = "s4u_style_prompt" if prompt_mode == "s4u" else "normal_style_prompt"
|
||||
|
||||
# 获取模板内容
|
||||
template_prompt = await global_prompt_manager.get_prompt_async(template_name)
|
||||
|
||||
Reference in New Issue
Block a user