diff --git a/src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py b/src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py index cea22211c..e172c4600 100644 --- a/src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py +++ b/src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py @@ -10,6 +10,7 @@ from typing import Any, Literal from sqlalchemy import select from src.chat.express.expression_selector import expression_selector +from src.chat.utils.prompt import Prompt from src.common.database.sqlalchemy_database_api import get_db_session from src.common.database.sqlalchemy_models import ChatStreams from src.common.logger import get_logger @@ -20,6 +21,133 @@ from src.plugin_system.apis import message_api, send_api logger = get_logger("proactive_thinking_executor") +# ================================================================================================== +# == Prompt Templates +# ================================================================================================== + +# 决策 Prompt +decision_prompt_template = Prompt( + """你的人设是: +{bot_personality} + +现在是 {current_time},你正在考虑是否要在与 "{stream_name}" 的对话中主动说些什么。 + +【你当前的心情】 +{current_mood} + +【聊天环境信息】 +- 整体印象: {stream_impression} +- 聊天风格: {chat_style} +- 常见话题: {topic_keywords} +- 你的兴趣程度: {interest_score:.2f}/1.0 +{last_decision_text} + +【最近的聊天记录】 +{recent_chat_history} + +请根据以上信息,决定你现在应该做什么: + +**选项1:什么都不做 (do_nothing)** +- 适用场景:气氛不适合说话、最近对话很活跃、没什么特别想说的、或者此时说话会显得突兀。 +- 心情影响:如果心情不好(如生气、难过),可能更倾向于保持沉默。 + +**选项2:简单冒个泡 (simple_bubble)** +- 适用场景:对话有些冷清,你想缓和气氛或开启新的互动。 +- 方式:说一句轻松随意的话,旨在建立或维持连接。 +- 心情影响:心情会影响你冒泡的方式和内容。 + +**选项3:发起一次有目的的互动 (throw_topic)** +- 适用场景:你想延续对话、表达关心、或深入讨论某个具体话题。 +- **【互动类型1:延续约定或提醒】(最高优先级)**:检查最近的聊天记录,是否存在可以延续的互动。例如,如果昨晚的最后一条消息是“晚安”,现在是早上,一个“早安”的回应是绝佳的选择。如果之前提到过某个约定(如“待会聊”),现在可以主动跟进。 +- **【互动类型2:展现真诚的关心】(次高优先级)**:如果不存在可延续的约定,请仔细阅读聊天记录,寻找对方提及的个人状况(如天气、出行、身体、情绪、工作学习等),并主动表达关心。 +- **【互动类型3:开启新话题】**:当以上两点都不适用时,可以考虑开启一个你感兴趣的新话题。 +- 心情影响:心情会影响你想发起互动的方式和内容。 + +请以JSON格式回复你的决策: +{{ + "action": "do_nothing" | "simple_bubble" | "throw_topic", + "reasoning": "你的决策理由(请结合你的心情、聊天环境和对话历史进行分析)", + "topic": "(仅当action=throw_topic时填写)你的互动意图(如:回应晚安并说早安、关心对方的考试情况、讨论新游戏)" +}} + +注意: +1. 兴趣度较低(<0.4)时或者最近聊天很活跃(不到1小时),倾向于 `do_nothing` 或 `simple_bubble`。 +2. 你的心情会影响你的行动倾向和表达方式。 +3. 参考上次决策,避免重复,并可根据上次的互动效果调整策略。 +4. 只有在真的有感而发时才选择 `throw_topic`。 +5. 保持你的人设,确保行为一致性。 +""", + name="proactive_thinking_decision", +) + +# 冒泡回复 Prompt +simple_bubble_reply_prompt_template = Prompt( + """你的人设是: +{bot_personality} + +距离上次对话已经有一段时间了,你决定主动说些什么,轻松地开启新的互动。 + +【你当前的心情】 +{current_mood} + +【聊天环境】 +- 整体印象: {stream_impression} +- 聊天风格: {chat_style} + +【最近的聊天记录】 +{recent_chat_history} +{expression_habits} +请生成一条简短的消息,用于水群。 +【要求】 +1. 风格简短随意(5-20字) +2. 不要提出明确的话题或问题,可以是问候、表达心情或一句随口的话。 +3. 符合你的人设和当前聊天风格。 +4. **你的心情应该影响消息的内容和语气**。 +5. 如果有表达方式参考,在合适时自然使用。 +6. 合理参考历史记录。 +直接输出消息内容,不要解释:""", + name="proactive_thinking_simple_bubble", +) + +# 抛出话题回复 Prompt +throw_topic_reply_prompt_template = Prompt( + """你的人设是: +{bot_personality} + +现在是 {current_time},你决定在与 "{stream_name}" 的对话中主动发起一次互动。 + +【你当前的心情】 +{current_mood} + +【聊天环境】 +- 整体印象: {stream_impression} +- 聊天风格: {chat_style} +- 常见话题: {topic_keywords} + +【最近的聊天记录】 +{recent_chat_history} + +【你的互动意图】 +{topic} +{expression_habits} +【构思指南】 +请根据你的互动意图,生成一条有温度的消息。 +- 如果意图是**延续约定**(如回应“晚安”),请直接生成对应的问候。 +- 如果意图是**表达关心**(如跟进对方提到的事),请生成自然、真诚的关心话语。 +- 如果意图是**开启新话题**,请自然地引入话题。 + +请根据这个意图,生成一条消息,要求: +1. 自然地引入话题或表达关心。 +2. 长度适中(20-50字)。 +3. 可以结合最近的聊天记录,使对话更连贯。 +4. 符合你的人设和当前聊天风格。 +5. **你的心情会影响你的表达方式**。 +6. 如果有表达方式参考,在合适时自然使用。 + +直接输出消息内容,不要解释:""", + name="proactive_thinking_throw_topic", +) + class ProactiveThinkingPlanner: """主动思考规划器 @@ -167,7 +295,35 @@ class ProactiveThinkingPlanner: response = None try: - decision_prompt = self._build_decision_prompt(context) + # 构建上次决策信息 + last_decision_text = "" + if context.get("last_decision"): + last_dec = context["last_decision"] + last_action = last_dec.get("action", "未知") + last_reasoning = last_dec.get("reasoning", "无") + last_topic = last_dec.get("topic") + last_time = last_dec.get("timestamp", "未知") + + last_decision_text = f""" +【上次主动思考的决策】 +- 时间: {last_time} +- 决策: {last_action} +- 理由: {last_reasoning}""" + if last_topic: + last_decision_text += f"\n- 话题: {last_topic}" + + decision_prompt = decision_prompt_template.format( + bot_personality=context["bot_personality"], + current_time=context["current_time"], + stream_name=context["stream_name"], + current_mood=context.get("current_mood", "感觉很平静"), + stream_impression=context["stream_impression"], + chat_style=context["chat_style"], + topic_keywords=context["topic_keywords"] or "暂无", + interest_score=context["interest_score"], + last_decision_text=last_decision_text, + recent_chat_history=context["recent_chat_history"], + ) if global_config.debug.show_prompt: logger.info(f"决策提示词:\n{decision_prompt}") @@ -195,76 +351,6 @@ class ProactiveThinkingPlanner: logger.error(f"决策过程失败: {e}", exc_info=True) return None - def _build_decision_prompt(self, context: dict[str, Any]) -> str: - """构建决策提示词""" - # 构建上次决策信息 - last_decision_text = "" - if context.get("last_decision"): - last_dec = context["last_decision"] - last_action = last_dec.get("action", "未知") - last_reasoning = last_dec.get("reasoning", "无") - last_topic = last_dec.get("topic") - last_time = last_dec.get("timestamp", "未知") - - last_decision_text = f""" -【上次主动思考的决策】 -- 时间: {last_time} -- 决策: {last_action} -- 理由: {last_reasoning}""" - if last_topic: - last_decision_text += f"\n- 话题: {last_topic}" - - return f"""你的人设是: -{context['bot_personality']} - -现在是 {context['current_time']},你正在考虑是否要在与 "{context['stream_name']}" 的对话中主动说些什么。 - -【你当前的心情】 -{context.get("current_mood", "感觉很平静")} - -【聊天环境信息】 -- 整体印象: {context["stream_impression"]} -- 聊天风格: {context["chat_style"]} -- 常见话题: {context["topic_keywords"] or "暂无"} -- 你的兴趣程度: {context["interest_score"]:.2f}/1.0 -{last_decision_text} - -【最近的聊天记录】 -{context["recent_chat_history"]} - -请根据以上信息,决定你现在应该做什么: - -**选项1:什么都不做 (do_nothing)** -- 适用场景:气氛不适合说话、最近对话很活跃、没什么特别想说的、或者此时说话会显得突兀。 -- 心情影响:如果心情不好(如生气、难过),可能更倾向于保持沉默。 - -**选项2:简单冒个泡 (simple_bubble)** -- 适用场景:对话有些冷清,你想缓和气氛或开启新的互动。 -- 方式:说一句轻松随意的话,旨在建立或维持连接。 -- 心情影响:心情会影响你冒泡的方式和内容。 - -**选项3:发起一次有目的的互动 (throw_topic)** -- 适用场景:你想延续对话、表达关心、或深入讨论某个具体话题。 -- **【互动类型1:延续约定或提醒】(最高优先级)**:检查最近的聊天记录,是否存在可以延续的互动。例如,如果昨晚的最后一条消息是“晚安”,现在是早上,一个“早安”的回应是绝佳的选择。如果之前提到过某个约定(如“待会聊”),现在可以主动跟进。 -- **【互动类型2:展现真诚的关心】(次高优先级)**:如果不存在可延续的约定,请仔细阅读聊天记录,寻找对方提及的个人状况(如天气、出行、身体、情绪、工作学习等),并主动表达关心。 -- **【互动类型3:开启新话题】**:当以上两点都不适用时,可以考虑开启一个你感兴趣的新话题。 -- 心情影响:心情会影响你想发起互动的方式和内容。 - -请以JSON格式回复你的决策: -{{ - "action": "do_nothing" | "simple_bubble" | "throw_topic", - "reasoning": "你的决策理由(请结合你的心情、聊天环境和对话历史进行分析)", - "topic": "(仅当action=throw_topic时填写)你的互动意图(如:回应晚安并说早安、关心对方的考试情况、讨论新游戏)" -}} - -注意: -1. 兴趣度较低(<0.4)时或者最近聊天很活跃(不到1小时),倾向于 `do_nothing` 或 `simple_bubble`。 -2. 你的心情会影响你的行动倾向和表达方式。 -3. 参考上次决策,避免重复,并可根据上次的互动效果调整策略。 -4. 只有在真的有感而发时才选择 `throw_topic`。 -5. 保持你的人设,确保行为一致性。 -""" - async def generate_reply( self, context: dict[str, Any], action: Literal["simple_bubble", "throw_topic"], topic: str | None = None ) -> str | None: @@ -283,7 +369,34 @@ class ProactiveThinkingPlanner: return None try: - reply_prompt = await self._build_reply_prompt(context, action, topic) + # 获取表达方式参考 + expression_habits = await self._get_expression_habits( + stream_id=context.get("stream_id", ""), chat_history=context.get("recent_chat_history", "") + ) + + if action == "simple_bubble": + reply_prompt = simple_bubble_reply_prompt_template.format( + bot_personality=context["bot_personality"], + current_mood=context.get("current_mood", "感觉很平静"), + stream_impression=context["stream_impression"], + chat_style=context["chat_style"], + recent_chat_history=context["recent_chat_history"], + expression_habits=expression_habits, + ) + else: # throw_topic + reply_prompt = throw_topic_reply_prompt_template.format( + bot_personality=context["bot_personality"], + current_time=context["current_time"], + stream_name=context["stream_name"], + current_mood=context.get("current_mood", "感觉很平静"), + stream_impression=context["stream_impression"], + chat_style=context["chat_style"], + topic_keywords=context["topic_keywords"] or "暂无", + recent_chat_history=context["recent_chat_history"], + topic=topic, + expression_habits=expression_habits, + ) + if global_config.debug.show_prompt: logger.info(f"回复提示词:\n{reply_prompt}") @@ -350,77 +463,6 @@ class ProactiveThinkingPlanner: logger.warning(f"获取表达方式失败: {e}") return "" - async def _build_reply_prompt( - self, context: dict[str, Any], action: Literal["simple_bubble", "throw_topic"], topic: str | None - ) -> str: - """构建回复提示词""" - # 获取表达方式参考 - expression_habits = await self._get_expression_habits( - stream_id=context.get("stream_id", ""), chat_history=context.get("recent_chat_history", "") - ) - - if action == "simple_bubble": - return f"""你的人设是: -{context['bot_personality']} - -距离上次对话已经有一段时间了,你决定主动说些什么,轻松地开启新的互动。 - -【你当前的心情】 -{context.get("current_mood", "感觉很平静")} - -【聊天环境】 -- 整体印象: {context["stream_impression"]} -- 聊天风格: {context["chat_style"]} - -【最近的聊天记录】 -{context["recent_chat_history"]} -{expression_habits} -请生成一条简短的消息,用于水群。 -【要求】 -1. 风格简短随意(5-20字) -2. 不要提出明确的话题或问题,可以是问候、表达心情或一句随口的话。 -3. 符合你的人设和当前聊天风格。 -4. **你的心情应该影响消息的内容和语气**。 -5. 如果有表达方式参考,在合适时自然使用。 -6. 合理参考历史记录。 -直接输出消息内容,不要解释:""" - - else: # throw_topic - return f"""你的人设是: -{context['bot_personality']} - -现在是 {context['current_time']},你决定在与 "{context['stream_name']}" 的对话中主动发起一次互动。 - -【你当前的心情】 -{context.get("current_mood", "感觉很平静")} - -【聊天环境】 -- 整体印象: {context["stream_impression"]} -- 聊天风格: {context["chat_style"]} -- 常见话题: {context["topic_keywords"] or "暂无"} - -【最近的聊天记录】 -{context["recent_chat_history"]} - -【你的互动意图】 -{topic} -{expression_habits} -【构思指南】 -请根据你的互动意图,生成一条有温度的消息。 -- 如果意图是**延续约定**(如回应“晚安”),请直接生成对应的问候。 -- 如果意图是**表达关心**(如跟进对方提到的事),请生成自然、真诚的关心话语。 -- 如果意图是**开启新话题**,请自然地引入话题。 - -请根据这个意图,生成一条消息,要求: -1. 自然地引入话题或表达关心。 -2. 长度适中(20-50字)。 -3. 可以结合最近的聊天记录,使对话更连贯。 -4. 符合你的人设和当前聊天风格。 -5. **你的心情会影响你的表达方式**。 -6. 如果有表达方式参考,在合适时自然使用。 - -直接输出消息内容,不要解释:""" - def _clean_json_response(self, response: str) -> str: """清理LLM响应中的JSON格式标记""" import re