mirror of https://github.com/Mai-with-u/MaiBot.git
加入时间!
parent
4ae0ef1ede
commit
eacd02fcef
|
|
@ -20,7 +20,9 @@ logger = get_logger("pfc_action_planner")
|
|||
# --- 定义 Prompt 模板 ---
|
||||
|
||||
# Prompt(1): 首次回复或非连续回复时的决策 Prompt
|
||||
PROMPT_INITIAL_REPLY = """{persona_text}。现在你在参与一场QQ私聊,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以回复,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
PROMPT_INITIAL_REPLY = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。现在你在参与一场QQ私聊,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以回复,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
|
||||
【当前对话目标】
|
||||
{goals_str}
|
||||
|
|
@ -54,7 +56,9 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
||||
# Prompt(2): 上一次成功回复后,决定继续发言时的决策 Prompt
|
||||
PROMPT_FOLLOW_UP = """{persona_text}。现在你在参与一场QQ私聊,刚刚你已经回复了对方,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以继续发送新消息,可以等待,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
PROMPT_FOLLOW_UP = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。现在你在参与一场QQ私聊,刚刚你已经回复了对方,请根据以下【所有信息】审慎且灵活的决策下一步行动,可以继续发送新消息,可以等待,可以倾听,可以调取知识,甚至可以屏蔽对方:
|
||||
|
||||
【当前对话目标】
|
||||
{goals_str}
|
||||
|
|
@ -88,7 +92,9 @@ block_and_ignore: 更加极端的结束对话方式,直接结束对话并在
|
|||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
||||
# 新增:Prompt(3): 决定是否在结束对话前发送告别语
|
||||
PROMPT_END_DECISION = """{persona_text}。刚刚你决定结束一场 QQ 私聊。
|
||||
PROMPT_END_DECISION = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。刚刚你决定结束一场 QQ 私聊。
|
||||
|
||||
【你们之前的聊天记录】
|
||||
{chat_history_text}
|
||||
|
|
@ -187,6 +193,10 @@ class ActionPlanner:
|
|||
log_msg = "使用 PROMPT_INITIAL_REPLY (首次/非连续回复决策)"
|
||||
logger.debug(f"[私聊][{self.private_name}] {log_msg}")
|
||||
|
||||
current_time_value = "获取时间失败" # 默认值
|
||||
if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str:
|
||||
current_time_value = observation_info.current_time_str
|
||||
|
||||
prompt = prompt_template.format(
|
||||
persona_text=persona_text,
|
||||
goals_str=goals_str if goals_str.strip() else "- 目前没有明确对话目标,请考虑设定一个。",
|
||||
|
|
@ -197,6 +207,7 @@ class ActionPlanner:
|
|||
chat_history_text=chat_history_text if chat_history_text.strip() else "还没有聊天记录。",
|
||||
retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。",
|
||||
retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。",
|
||||
current_time_str=current_time_value # 新增:传入当前时间字符串
|
||||
)
|
||||
logger.debug(f"[私聊][{self.private_name}] 发送到LLM的最终提示词:\n------\n{prompt}\n------")
|
||||
except KeyError as fmt_key_err:
|
||||
|
|
@ -235,8 +246,11 @@ class ActionPlanner:
|
|||
|
||||
if initial_action == "end_conversation":
|
||||
try:
|
||||
time_str_for_end_decision = "获取时间失败"
|
||||
if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str:
|
||||
time_str_for_end_decision = observation_info.current_time_str
|
||||
final_action, final_reason = await self._handle_end_conversation_decision(
|
||||
persona_text, chat_history_text, initial_reason
|
||||
persona_text, chat_history_text, initial_reason,time_str_for_end_decision
|
||||
)
|
||||
except Exception as end_dec_err:
|
||||
logger.error(f"[私聊][{self.private_name}] 处理结束对话决策时出错: {end_dec_err}")
|
||||
|
|
@ -446,11 +460,11 @@ class ActionPlanner:
|
|||
# --- Helper method for handling end_conversation decision ---
|
||||
|
||||
async def _handle_end_conversation_decision(
|
||||
self, persona_text: str, chat_history_text: str, initial_reason: str
|
||||
self, persona_text: str, chat_history_text: str, initial_reason: str, current_time_str: str
|
||||
) -> Tuple[str, str]:
|
||||
"""处理结束对话前的告别决策"""
|
||||
logger.info(f"[私聊][{self.private_name}] 初步规划结束对话,进入告别决策...")
|
||||
end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text)
|
||||
end_decision_prompt = PROMPT_END_DECISION.format(persona_text=persona_text, chat_history_text=chat_history_text,current_time_str=current_time_str)
|
||||
logger.debug(f"[私聊][{self.private_name}] 发送到LLM的结束决策提示词:\n------\n{end_decision_prompt}\n------")
|
||||
llm_start_time = time.time()
|
||||
end_content, _ = await self.llm.generate_response_async(end_decision_prompt)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import asyncio
|
|||
import datetime
|
||||
import traceback
|
||||
from typing import Dict, Any, Optional, Set, List
|
||||
from dateutil import tz
|
||||
|
||||
# 导入日志记录器
|
||||
from src.common.logger_manager import get_logger
|
||||
|
|
@ -44,6 +45,19 @@ install(extra_lines=3)
|
|||
# 获取当前模块的日志记录器
|
||||
logger = get_logger("pfc_conversation")
|
||||
|
||||
try:
|
||||
from ...config.config import global_config
|
||||
except ImportError:
|
||||
logger.error("无法在 conversation.py 中导入 global_config,时区可能不准确!")
|
||||
global_config = None
|
||||
TIME_ZONE = tz.tzlocal() # 使用本地时区作为后备
|
||||
else:
|
||||
# 确保 global_config.TIME_ZONE 存在且有效,否则使用默认值
|
||||
configured_tz = getattr(global_config, 'TIME_ZONE', 'Asia/Shanghai') # 使用 getattr 安全访问
|
||||
TIME_ZONE = tz.gettz(configured_tz)
|
||||
if TIME_ZONE is None: # 如果 gettz 返回 None,说明时区字符串无效
|
||||
logger.error(f"配置的时区 '{configured_tz}' 无效,将使用默认时区 'Asia/Shanghai'")
|
||||
TIME_ZONE = tz.gettz('Asia/Shanghai')
|
||||
|
||||
class Conversation:
|
||||
"""
|
||||
|
|
@ -347,6 +361,30 @@ class Conversation:
|
|||
while self.should_continue:
|
||||
loop_iter_start_time = time.time() # 记录本次循环开始时间
|
||||
logger.debug(f"[私聊][{self.private_name}] 开始新一轮循环迭代 ({loop_iter_start_time:.2f})")
|
||||
try:
|
||||
# 重新获取 TIME_ZONE 以防在 __init__ 中导入失败
|
||||
if 'TIME_ZONE' not in locals() or TIME_ZONE is None:
|
||||
from dateutil import tz
|
||||
try:
|
||||
from ...config.config import global_config
|
||||
except ImportError:
|
||||
global_config = None
|
||||
TIME_ZONE = tz.tzlocal()
|
||||
else:
|
||||
configured_tz = getattr(global_config, 'TIME_ZONE', 'Asia/Shanghai')
|
||||
TIME_ZONE = tz.gettz(configured_tz)
|
||||
if TIME_ZONE is None: TIME_ZONE = tz.gettz('Asia/Shanghai')
|
||||
|
||||
current_time = datetime.datetime.now(TIME_ZONE)
|
||||
if self.observation_info: # 确保 observation_info 存在
|
||||
time_str = current_time.strftime("%Y-%m-%d %H:%M:%S %Z%z") # 包含时区信息的格式
|
||||
self.observation_info.current_time_str = time_str
|
||||
logger.debug(f"[私聊][{self.private_name}] 更新 ObservationInfo 当前时间: {time_str}")
|
||||
else:
|
||||
logger.warning(f"[私聊][{self.private_name}] ObservationInfo 未初始化,无法更新当前时间。")
|
||||
except Exception as time_update_err:
|
||||
logger.error(f"[私聊][{self.private_name}] 更新 ObservationInfo 当前时间时出错: {time_update_err}")
|
||||
# --- 更新时间代码结束 ---
|
||||
|
||||
# --- 处理忽略状态 ---
|
||||
if self.ignore_until_timestamp and loop_iter_start_time < self.ignore_until_timestamp:
|
||||
|
|
@ -389,7 +427,9 @@ class Conversation:
|
|||
logger.debug(f"[私聊][{self.private_name}] 调用 ActionPlanner.plan...")
|
||||
# 传入当前观察信息、对话信息和上次成功回复的动作类型
|
||||
action, reason = await self.action_planner.plan(
|
||||
self.observation_info, self.conversation_info, self.conversation_info.last_successful_reply_action
|
||||
self.observation_info,
|
||||
self.conversation_info,
|
||||
self.conversation_info.last_successful_reply_action
|
||||
)
|
||||
planning_duration = time.time() - planning_start_time
|
||||
logger.debug(
|
||||
|
|
@ -663,6 +703,12 @@ class Conversation:
|
|||
# retry_count for checker starts from 0
|
||||
current_retry_for_checker = reply_attempt_count - 1
|
||||
|
||||
|
||||
current_time_value_for_check = "获取时间失败"
|
||||
if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str:
|
||||
current_time_value_for_check = observation_info.current_time_str
|
||||
|
||||
|
||||
logger.debug(f"{log_prefix} 调用 ReplyChecker 检查...")
|
||||
# 调用 ReplyChecker 的 check 方法
|
||||
is_suitable, check_reason, need_replan_from_checker = await self.reply_checker.check(
|
||||
|
|
@ -670,6 +716,7 @@ class Conversation:
|
|||
goal=current_goal_str,
|
||||
chat_history=chat_history_for_check, # 传递列表形式的历史记录
|
||||
chat_history_text=chat_history_text_for_check, # 传递文本形式的历史记录
|
||||
current_time_str=current_time_value_for_check, # 新增:传递时间字符串
|
||||
retry_count=current_retry_for_checker,
|
||||
)
|
||||
logger.info(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import datetime
|
||||
import time
|
||||
import traceback
|
||||
from dateutil import tz
|
||||
from typing import List, Optional, Dict, Any, Set
|
||||
|
||||
from maim_message import UserInfo
|
||||
from src.common.logger import get_module_logger
|
||||
from src.plugins.utils.chat_message_builder import build_readable_messages
|
||||
|
|
@ -12,6 +13,17 @@ from .chat_states import NotificationHandler, NotificationType, Notification
|
|||
|
||||
logger = get_module_logger("observation_info")
|
||||
|
||||
try:
|
||||
from ...config.config import global_config
|
||||
except ImportError:
|
||||
# 如果路径不对,可能需要调整 '...' 的数量
|
||||
# 或者在后面实际使用 TIME_ZONE 的地方导入
|
||||
logger.warning("无法在 observation_info.py 中直接导入 global_config,时区将在使用时获取")
|
||||
global_config = None # 设置为 None,后面检查
|
||||
TIME_ZONE = tz.tzlocal() # 备选:使用本地时区
|
||||
else:
|
||||
TIME_ZONE = tz.gettz(global_config.TIME_ZONE if global_config else 'Asia/Shanghai') # 使用配置的时区,提供默认值
|
||||
|
||||
|
||||
class ObservationInfoHandler(NotificationHandler):
|
||||
"""ObservationInfo的通知处理器"""
|
||||
|
|
@ -138,6 +150,9 @@ class ObservationInfo:
|
|||
# 其他状态
|
||||
self.is_typing: bool = False # 是否正在输入 (未来可能用到)
|
||||
self.changed: bool = False # 状态是否有变化 (用于优化)
|
||||
|
||||
# 用于存储格式化的当前时间
|
||||
self.current_time_str: Optional[str] = None
|
||||
|
||||
# 关联对象
|
||||
self.chat_observer: Optional[ChatObserver] = None
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ReplyChecker:
|
|||
self.max_retries = 3 # 最大重试次数
|
||||
|
||||
async def check(
|
||||
self, reply: str, goal: str, chat_history: List[Dict[str, Any]], chat_history_text: str, retry_count: int = 0
|
||||
self, reply: str, goal: str, chat_history: List[Dict[str, Any]], chat_history_text: str,current_time_str: str, retry_count: int = 0
|
||||
) -> Tuple[bool, str, bool]:
|
||||
"""检查生成的回复是否合适
|
||||
|
||||
|
|
@ -85,7 +85,9 @@ class ReplyChecker:
|
|||
logger.error(f"[私聊][{self.private_name}]检查回复时出错: 类型={type(e)}, 值={e}")
|
||||
logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 打印详细的回溯信息
|
||||
|
||||
prompt = f"""你是一个聊天逻辑检查器,请检查以下回复或消息是否合适:
|
||||
prompt_template = """
|
||||
当前时间:{current_time_str}
|
||||
你是一个聊天逻辑检查器,请检查以下回复或消息是否合适:
|
||||
|
||||
当前对话目标:{goal}
|
||||
最新的对话记录:
|
||||
|
|
@ -121,6 +123,16 @@ class ReplyChecker:
|
|||
|
||||
注意:请严格按照JSON格式输出,不要包含任何其他内容。"""
|
||||
|
||||
prompt = prompt_template.format(
|
||||
current_time_str=current_time_str, # 使用传入的参数
|
||||
goal=goal,
|
||||
chat_history_text=chat_history_text,
|
||||
reply=reply
|
||||
)
|
||||
|
||||
# 调用 LLM
|
||||
content, _ = await self.llm.generate_response_async(prompt)
|
||||
|
||||
try:
|
||||
content, _ = await self.llm.generate_response_async(prompt)
|
||||
logger.debug(f"[私聊][{self.private_name}]检查回复的原始返回: {content}")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@ logger = get_logger("reply_generator")
|
|||
# --- 定义 Prompt 模板 ---
|
||||
|
||||
# Prompt for direct_reply (首次回复)
|
||||
PROMPT_DIRECT_REPLY = """{persona_text}。现在你在参与一场QQ私聊,请根据以下信息生成一条回复:
|
||||
PROMPT_DIRECT_REPLY = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。现在你在参与一场QQ私聊,请根据以下信息生成一条回复:
|
||||
|
||||
当前对话目标:{goals_str}
|
||||
|
||||
|
|
@ -52,7 +54,9 @@ PROMPT_DIRECT_REPLY = """{persona_text}。现在你在参与一场QQ私聊,请
|
|||
请直接输出回复内容,不需要任何额外格式。"""
|
||||
|
||||
# Prompt for send_new_message (追问/补充)
|
||||
PROMPT_SEND_NEW_MESSAGE = """{persona_text}。现在你在参与一场QQ私聊,**刚刚你已经发送了一条或多条消息**,现在请根据以下信息再发一条新消息:
|
||||
PROMPT_SEND_NEW_MESSAGE = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。现在你在参与一场QQ私聊,**刚刚你已经发送了一条或多条消息**,现在请根据以下信息再发一条新消息:
|
||||
|
||||
当前对话目标:{goals_str}
|
||||
|
||||
|
|
@ -82,7 +86,9 @@ PROMPT_SEND_NEW_MESSAGE = """{persona_text}。现在你在参与一场QQ私聊
|
|||
请直接输出回复内容,不需要任何额外格式。"""
|
||||
|
||||
# Prompt for say_goodbye (告别语生成)
|
||||
PROMPT_FAREWELL = """{persona_text}。你在参与一场 QQ 私聊,现在对话似乎已经结束,你决定再发一条最后的消息来圆满结束。
|
||||
PROMPT_FAREWELL = """
|
||||
当前时间:{current_time_str}
|
||||
{persona_text}。你在参与一场 QQ 私聊,现在对话似乎已经结束,你决定再发一条最后的消息来圆满结束。
|
||||
|
||||
最近的聊天记录:
|
||||
{chat_history_text}
|
||||
|
|
@ -215,14 +221,26 @@ class ReplyGenerator:
|
|||
|
||||
# --- 格式化最终的 Prompt ---
|
||||
try: # <--- 增加 try-except 块处理可能的 format 错误
|
||||
prompt = prompt_template.format(
|
||||
persona_text=persona_text,
|
||||
goals_str=goals_str,
|
||||
chat_history_text=chat_history_text,
|
||||
retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。",
|
||||
retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。",
|
||||
last_rejection_info=last_rejection_info_str, # <--- 新增传递上次拒绝原因
|
||||
)
|
||||
current_time_value = "获取时间失败" # 默认值
|
||||
if observation_info and hasattr(observation_info, 'current_time_str') and observation_info.current_time_str:
|
||||
current_time_value = observation_info.current_time_str
|
||||
if action_type == "say_goodbye":
|
||||
prompt = prompt_template.format(
|
||||
persona_text=persona_text,
|
||||
chat_history_text=chat_history_text,
|
||||
current_time_str=current_time_value # 添加时间
|
||||
)
|
||||
|
||||
else:
|
||||
prompt = prompt_template.format(
|
||||
persona_text=persona_text,
|
||||
goals_str=goals_str,
|
||||
chat_history_text=chat_history_text,
|
||||
retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。",
|
||||
retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。",
|
||||
last_rejection_info=last_rejection_info_str, # <--- 新增传递上次拒绝原因
|
||||
current_time_str=current_time_value # 新增:传入当前时间字符串
|
||||
)
|
||||
except KeyError as e:
|
||||
logger.error(
|
||||
f"[私聊][{self.private_name}]格式化 Prompt 时出错,缺少键: {e}。请检查 Prompt 模板和传递的参数。"
|
||||
|
|
|
|||
Loading…
Reference in New Issue