refactor(chatter): 将主动思考的Prompt模板化
将 `proactive_thinking_executor.py` 中的硬编码Prompt字符串重构为独立的 `Prompt` 对象。 这次重构主要有以下几个好处: - **提高可读性**:将大段的文本从业务逻辑中分离,让代码结构更清晰。 - **便于维护**:以后要调整 Prompt 的时候,直接修改模板对象就行,不用在函数里大海捞针了。 - **提升复用性**:虽然现在还没复用,但以后有类似需求时,这种模式也更容易扩展。
This commit is contained in:
committed by
Windpicker-owo
parent
7534536e22
commit
c83d1ac1d8
@@ -10,6 +10,7 @@ from typing import Any, Literal
|
|||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from src.chat.express.expression_selector import expression_selector
|
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_database_api import get_db_session
|
||||||
from src.common.database.sqlalchemy_models import ChatStreams
|
from src.common.database.sqlalchemy_models import ChatStreams
|
||||||
from src.common.logger import get_logger
|
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")
|
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:
|
class ProactiveThinkingPlanner:
|
||||||
"""主动思考规划器
|
"""主动思考规划器
|
||||||
@@ -167,7 +295,35 @@ class ProactiveThinkingPlanner:
|
|||||||
|
|
||||||
response = None
|
response = None
|
||||||
try:
|
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:
|
if global_config.debug.show_prompt:
|
||||||
logger.info(f"决策提示词:\n{decision_prompt}")
|
logger.info(f"决策提示词:\n{decision_prompt}")
|
||||||
@@ -195,76 +351,6 @@ class ProactiveThinkingPlanner:
|
|||||||
logger.error(f"决策过程失败: {e}", exc_info=True)
|
logger.error(f"决策过程失败: {e}", exc_info=True)
|
||||||
return None
|
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(
|
async def generate_reply(
|
||||||
self, context: dict[str, Any], action: Literal["simple_bubble", "throw_topic"], topic: str | None = None
|
self, context: dict[str, Any], action: Literal["simple_bubble", "throw_topic"], topic: str | None = None
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
@@ -283,7 +369,34 @@ class ProactiveThinkingPlanner:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
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:
|
if global_config.debug.show_prompt:
|
||||||
logger.info(f"回复提示词:\n{reply_prompt}")
|
logger.info(f"回复提示词:\n{reply_prompt}")
|
||||||
@@ -350,77 +463,6 @@ class ProactiveThinkingPlanner:
|
|||||||
logger.warning(f"获取表达方式失败: {e}")
|
logger.warning(f"获取表达方式失败: {e}")
|
||||||
return ""
|
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:
|
def _clean_json_response(self, response: str) -> str:
|
||||||
"""清理LLM响应中的JSON格式标记"""
|
"""清理LLM响应中的JSON格式标记"""
|
||||||
import re
|
import re
|
||||||
|
|||||||
Reference in New Issue
Block a user