feat(chatter):引入可配置的单回复和多回复模式

此提交引入了一个新的配置选项 `chat.enable_multiple_replies`,允许对聊天机器人的对话行为进行更大的控制。

之前,规划器的回复策略是硬编码以支持多回复的。此更改将提示生成重构为根据新的配置设置动态生成。

- 当 `enable_multiple_replies` 为 `true`(默认行为)时,机器人可以在单轮对话中生成多个 `reply` 动作,同时处理多条消息或多个用户。
- 设置为 `false` 时,提示会指示机器人遵循“单目标原则”,强制其仅选择最重要的一条消息进行回复。

这是通过将回复策略和输出格式说明从静态的 `planner_prompts.py` 移动到 `plan_filter.py` 实现的,在那里它们会在被注入主提示模板之前根据条件生成。
This commit is contained in:
tt-P607
2025-11-26 22:30:32 +08:00
parent fd65d8c4eb
commit 6200108391
2 changed files with 122 additions and 66 deletions

View File

@@ -299,6 +299,124 @@ class ChatterPlanFilter:
) )
# Prepare format parameters # Prepare format parameters
# Prepare format parameters
# 根据配置动态生成回复策略和输出格式的提示词部分
if global_config.chat.enable_multiple_replies:
reply_strategy_block = """
# 目标
你的任务是根据当前对话,给出一个或多个动作,构成一次完整的响应组合。
- 主要动作:通常是 reply或respond如需回复
- 辅助动作(可选):如 emoji、poke_user 等,用于增强表达。
# 决策流程
1. 已读仅供参考,不能对已读执行任何动作。
2. 目标消息必须来自未读历史,并使用其前缀 <m...> 作为 target_message_id。
3. 兴趣度优先原则:每条未读消息后都标注了 [兴趣度: X.XXX],数值越高表示该消息越值得你关注和回复。在选择回复目标时,**应优先选择兴趣度高的消息**(通常 ≥0.5 表示较高兴趣),除非有特殊情况(如被直接@或提问)。
4. 优先级:
- 直接针对你:@你、回复你、点名提问、引用你的消息。
- **兴趣度高的消息**:兴趣度 ≥0.5 的消息应优先考虑回复。
- 与你强相关的话题或你熟悉的问题。
- 其他与上下文弱相关的内容最后考虑。
{mentioned_bonus}
5. 多目标:若多人同时需要回应,请在 actions 中并行生成多个 reply每个都指向各自的 target_message_id。
6. 处理无上下文的纯表情包: 对不含任何实质文本、且无紧密上下文互动的纯**表情包**消息(如消息内容仅为“[表情包xxxxx]”),应默认选择 `no_action`。
7. 处理失败消息: 绝不能回复任何指示媒体内容(图片、表情包等)处理失败的消息。如果消息中出现如“[表情包(描述生成失败)]”或“[图片(描述生成失败)]”等文字,必须将其视为系统错误提示,并立即选择`no_action`。
8. 正确决定回复时机: 在决定reply或respond前务必评估当前对话氛围和上下文连贯性。避免在不合适的时机如对方情绪低落、话题不相关等,对方并没有和你对话,贸然插入会很令人讨厌等)进行回复,以免打断对话流或引起误解。如判断当前不适合回复,请选择`no_action`。
9. 认清自己的身份和角色: 在规划回复时,务必确定对方是不是真的在叫自己。聊天时往往有数百甚至数千个用户,请务必认清自己的身份和角色,避免误以为对方在和自己对话而贸然插入回复,导致尴尬局面。
"""
output_format_block = """
## 输出格式(只输出 JSON不要多余文本或代码块
最终输出必须是一个包含 thinking 和 actions 字段的 JSON 对象,其中 actions 必须是一个列表。
示例(单动作):
```json
{{
"thinking": "在这里写下你的思绪流...",
"actions": [
{{
"action_type": "reply",
"reasoning": "选择该动作的详细理由",
"action_data": {{
"target_message_id": "m124",
"content": "回复内容"
}}
}}
]
}}
```
示例(多重动作,并行):
```json
{{
"thinking": "在这里写下你的思绪流...",
"actions": [
{{
"action_type": "reply",
"reasoning": "理由A - 这个消息较早且需要明确回复对象",
"action_data": {{
"target_message_id": "m124",
"content": "对A的回复",
"should_quote_reply": false
}}
}},
{{
"action_type": "reply",
"reasoning": "理由B",
"action_data": {{
"target_message_id": "m125",
"content": "对B的回复",
"should_quote_reply": false
}}
}}
]
}}
```
"""
else:
reply_strategy_block = """
# 目标
你的任务是根据当前对话,**选择一个最需要回应的目标**,并给出一个动作,构成一次完整的响应。
- 主要动作:通常是 reply如需回复
- 辅助动作(可选):如 emoji、poke_user 等,用于增强表达。
# 决策流程
1. 已读仅供参考,不能对已读执行任何动作。
2. 目标消息必须来自未读历史,并使用其前缀 <m...> 作为 target_message_id。
3. **单一目标原则**: 你必须从所有未读消息中,根据**兴趣度**和**优先级**,选择**唯一一个**最值得回应的目标。
4. 兴趣度优先原则:每条未读消息后都标注了 [兴趣度: X.XXX],数值越高表示该消息越值得你关注和回复。在选择回复目标时,**应优先选择兴趣度高的消息**(通常 ≥0.5 表示较高兴趣),除非有特殊情况(如被直接@或提问)。
5. 优先级:
- 直接针对你:@你、回复你、点名提问、引用你的消息。
- **兴趣度高的消息**:兴趣度 ≥0.5 的消息应优先考虑回复。
- 与你强相关的话题或你熟悉的问题。
- 其他与上下文弱相关的内容最后考虑。
{mentioned_bonus}
6. 处理无上下文的纯表情包: 对不含任何实质文本、且无紧密上下文互动的纯**表情包**消息(如消息内容仅为“[表情包xxxxx]”),应默认选择 `no_action`。
7. 处理失败消息: 绝不能回复任何指示媒体内容(图片、表情包等)处理失败的消息。如果消息中出现如“[表情包(描述生成失败)]”或“[图片(描述生成失败)]”等文字,必须将其视为系统错误提示,并立即选择`no_action`。
8. 正确决定回复时机: 在决定reply或respond前务必评估当前对话氛围和上下文连贯性。避免在不合适的时机如对方情绪低落、话题不相关等,对方并没有和你对话,贸然插入会很令人讨厌等)进行回复,以免打断对话流或引起误解。如判断当前不适合回复,请选择`no_action`。
9. 认清自己的身份和角色: 在规划回复时,务必确定对方是不是真的在叫自己。聊天时往往有数百甚至数千个用户,请务必认清自己的身份和角色,避免误以为对方在和自己对话而贸然插入回复,导致尴尬局面。
"""
output_format_block = """
## 输出格式(只输出 JSON不要多余文本或代码块
最终输出必须是一个包含 thinking 和 actions 字段的 JSON 对象,其中 actions 必须是一个**只包含单个动作**的列表。
示例:
```json
{{
"thinking": "在这里写下你的思绪流...",
"actions": [
{{
"action_type": "reply",
"reasoning": "选择该动作的详细理由",
"action_data": {{
"target_message_id": "m124",
"content": "回复内容"
}}
}}
]
}}
```
"""
format_params = { format_params = {
"schedule_block": schedule_block, "schedule_block": schedule_block,
"mood_block": mood_block, "mood_block": mood_block,
@@ -316,6 +434,8 @@ class ChatterPlanFilter:
"custom_prompt_block": custom_prompt_block, "custom_prompt_block": custom_prompt_block,
"bot_name": bot_name, "bot_name": bot_name,
"users_in_chat": users_in_chat_str, "users_in_chat": users_in_chat_str,
"reply_strategy_block": reply_strategy_block,
"output_format_block": output_format_block,
} }
prompt = planner_prompt_template.format(**format_params) prompt = planner_prompt_template.format(**format_params)
return prompt, message_id_list return prompt, message_id_list

View File

@@ -41,26 +41,7 @@ def init_prompts():
{moderation_prompt} {moderation_prompt}
# 目标 {reply_strategy_block}
你的任务是根据当前对话,给出一个或多个动作,构成一次完整的响应组合。
- 主要动作:通常是 reply或respond如需回复
- 辅助动作(可选):如 emoji、poke_user 等,用于增强表达。
# 决策流程
1. 已读仅供参考,不能对已读执行任何动作。
2. 目标消息必须来自未读历史,并使用其前缀 <m...> 作为 target_message_id。
3. 兴趣度优先原则:每条未读消息后都标注了 [兴趣度: X.XXX],数值越高表示该消息越值得你关注和回复。在选择回复目标时,**应优先选择兴趣度高的消息**(通常 ≥0.5 表示较高兴趣),除非有特殊情况(如被直接@或提问)。
4. 优先级:
- 直接针对你:@你、回复你、点名提问、引用你的消息。
- **兴趣度高的消息**:兴趣度 ≥0.5 的消息应优先考虑回复。
- 与你强相关的话题或你熟悉的问题。
- 其他与上下文弱相关的内容最后考虑。
{mentioned_bonus}
5. 多目标:若多人同时需要回应,请在 actions 中并行生成多个 reply每个都指向各自的 target_message_id。
6. 处理无上下文的纯表情包: 对不含任何实质文本、且无紧密上下文互动的纯**表情包**消息(如消息内容仅为“[表情包xxxxx]”),应默认选择 `no_action`。
7. 处理失败消息: 绝不能回复任何指示媒体内容(图片、表情包等)处理失败的消息。如果消息中出现如“[表情包(描述生成失败)]”或“[图片(描述生成失败)]”等文字,必须将其视为系统错误提示,并立即选择`no_action`。
8. 正确决定回复时机: 在决定reply或respond前务必评估当前对话氛围和上下文连贯性。避免在不合适的时机如对方情绪低落、话题不相关等,对方并没有和你对话,贸然插入会很令人讨厌等)进行回复,以免打断对话流或引起误解。如判断当前不适合回复,请选择`no_action`。
9. 认清自己的身份和角色: 在规划回复时,务必确定对方是不是真的在叫自己。聊天时往往有数百甚至数千个用户,请务必认清自己的身份和角色,避免误以为对方在和自己对话而贸然插入回复,导致尴尬局面。
# 思绪流规范thinking # 思绪流规范thinking
- 真实、自然、非结论化,像给自己看的随笔。 - 真实、自然、非结论化,像给自己看的随笔。
@@ -71,52 +52,7 @@ def init_prompts():
## 可用动作列表 ## 可用动作列表
{action_options_text} {action_options_text}
## 输出格式(只输出 JSON不要多余文本或代码块 {output_format_block}
最终输出必须是一个包含 thinking 和 actions 字段的 JSON 对象,其中 actions 必须是一个列表。
示例(单动作):
```json
{{
"thinking": "在这里写下你的思绪流...",
"actions": [
{{
"action_type": "reply",
"reasoning": "选择该动作的详细理由",
"action_data": {{
"target_message_id": "m124",
"content": "回复内容"
}}
}}
]
}}
```
示例(多重动作,并行):
```json
{{
"thinking": "在这里写下你的思绪流...",
"actions": [
{{
"action_type": "reply",
"reasoning": "理由A - 这个消息较早且需要明确回复对象",
"action_data": {{
"target_message_id": "m124",
"content": "对A的回复",
"should_quote_reply": false
}}
}},
{{
"action_type": "reply",
"reasoning": "理由B",
"action_data": {{
"target_message_id": "m125",
"content": "对B的回复",
"should_quote_reply": false
}}
}}
]
}}
```
# 强制规则 # 强制规则
- 每个动作块必须包含 action_type、reasoning 和 action_data 三个字段 - 每个动作块必须包含 action_type、reasoning 和 action_data 三个字段