加入时间!

pull/937/head
114514 2025-05-06 18:22:20 +08:00
parent 4ae0ef1ede
commit eacd02fcef
5 changed files with 127 additions and 21 deletions

View File

@ -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)

View File

@ -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(

View File

@ -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

View File

@ -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}")

View File

@ -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 模板和传递的参数。"