better:更新PFC,现可拥有多目标以及其他优化

This commit is contained in:
SengokuCola
2025-04-11 16:01:57 +08:00
parent 7033f1dd1b
commit 70f3dcba1f
12 changed files with 238 additions and 157 deletions

View File

@@ -44,26 +44,31 @@ class ActionPlanner:
logger.debug(f"开始规划行动:当前目标: {conversation_info.goal_list}")
# 构建对话目标
goals_str = ""
if conversation_info.goal_list:
last_goal = conversation_info.goal_list[-1]
print(last_goal)
# 处理字典或元组格式
if isinstance(last_goal, tuple) and len(last_goal) == 2:
goal, reasoning = last_goal
elif isinstance(last_goal, dict) and 'goal' in last_goal and 'reasoning' in last_goal:
# 处理字典格式
goal = last_goal.get('goal', "目前没有明确对话目标")
reasoning = last_goal.get('reasoning', "目前没有明确对话目标,最好思考一个对话目标")
else:
# 处理未知格式
goal = "目前没有明确对话目标"
reasoning = "目前没有明确对话目标,最好思考一个对话目标"
for goal_reason in conversation_info.goal_list:
# 处理字典或元组格式
if isinstance(goal_reason, tuple):
# 假设元组的第一个元素是目标,第二个元素是原因
goal = goal_reason[0]
reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因"
elif isinstance(goal_reason, dict):
goal = goal_reason.get('goal')
reasoning = goal_reason.get('reasoning', "没有明确原因")
else:
# 如果是其他类型,尝试转为字符串
goal = str(goal_reason)
reasoning = "没有明确原因"
goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
goals_str += goal_str
else:
goal = "目前没有明确对话目标"
reasoning = "目前没有明确对话目标,最好思考一个对话目标"
goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
# 获取聊天历史记录
chat_history_list = observation_info.chat_history
chat_history_list = observation_info.chat_history[-20:] if len(observation_info.chat_history) >= 20 else observation_info.chat_history
chat_history_text = ""
for msg in chat_history_list:
chat_history_text += f"{msg.get('detailed_plain_text', '')}\n"
@@ -80,15 +85,30 @@ class ActionPlanner:
personality_text = f"你的名字是{self.name}{self.personality_info}"
# 构建action历史文本
action_history_list = conversation_info.done_action
action_history_list = conversation_info.done_action[-10:] if len(conversation_info.done_action) >= 10 else conversation_info.done_action
action_history_text = "你之前做的事情是:"
for action in action_history_list:
action_history_text += f"{action}\n"
if isinstance(action, dict):
action_type = action.get('action')
action_reason = action.get('reason')
action_status = action.get('status')
if action_status == "recall":
action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n"
elif action_status == "done":
action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n"
elif isinstance(action, tuple):
# 假设元组的格式是(action_type, action_reason, action_status)
action_type = action[0] if len(action) > 0 else "未知行动"
action_reason = action[1] if len(action) > 1 else "未知原因"
action_status = action[2] if len(action) > 2 else "done"
if action_status == "recall":
action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n"
elif action_status == "done":
action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n"
prompt = f"""{personality_text}。现在你在参与一场QQ聊天请分析以下内容根据信息决定下一步行动
当前对话目标:{goal}
产生该对话目标的原因:{reasoning}
当前对话目标:{goals_str}
{action_history_text}
@@ -98,10 +118,11 @@ class ActionPlanner:
请你接下去想想要你要做什么,可以发言,可以等待,可以倾听,可以调取知识。注意不同行动类型的要求,不要重复发言:
行动类型:
fetch_knowledge: 需要调取知识,当需要专业知识或特定信息时选择
wait: 当你做出了发言,对方尚未回复时等待对方的回复
wait: 当你做出了发言,对方尚未回复时暂时等待对方的回复
listening: 倾听对方发言,当你认为对方发言尚未结束时采用
direct_reply: 不符合上述情况,回复对方,注意不要过多或者重复发言
rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择,会重新思考对话目标
end_conversation: 结束对话,当你觉得谈话暂时结束时选择,停止该场对话
请以JSON格式输出包含以下字段
1. action: 行动类型,注意你之前的行为
@@ -126,7 +147,7 @@ rethink_goal: 重新思考对话目标,当发现对话目标不合适时选择
reason = result["reason"]
# 验证action类型
if action not in ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal"]:
if action not in ["direct_reply", "fetch_knowledge", "wait", "listening", "rethink_goal", "end_conversation"]:
logger.warning(f"未知的行动类型: {action}默认使用listening")
action = "listening"

View File

@@ -198,7 +198,7 @@ class ChatObserver:
self.last_message_read = new_messages[-1]
self.last_message_time = new_messages[-1]["time"]
print(f"获取数据库中找到的新消息: {new_messages}")
# print(f"获取数据库中找到的新消息: {new_messages}")
return new_messages

View File

@@ -3,7 +3,7 @@ import datetime
from typing import Dict, Any
from ..chat.message import Message
from .pfc_types import ConversationState
from .pfc import ChatObserver, GoalAnalyzer, Waiter, DirectMessageSender
from .pfc import ChatObserver, GoalAnalyzer, DirectMessageSender
from src.common.logger import get_module_logger
from .action_planner import ActionPlanner
from .observation_info import ObservationInfo
@@ -13,6 +13,8 @@ from ..chat.chat_stream import ChatStream
from ..message.message_base import UserInfo
from src.plugins.chat.chat_stream import chat_manager
from .pfc_KnowledgeFetcher import KnowledgeFetcher
from .waiter import Waiter
import traceback
logger = get_module_logger("pfc_conversation")
@@ -94,6 +96,15 @@ class Conversation:
# 执行行动
await self._handle_action(action, reason, self.observation_info, self.conversation_info)
for goal in self.conversation_info.goal_list:
# 检查goal是否为元组类型如果是元组则使用索引访问如果是字典则使用get方法
if isinstance(goal, tuple):
# 假设元组的第一个元素是目标内容
print(f"goal: {goal}")
if goal[0] == "结束对话":
self.should_continue = False
break
def _check_new_messages_after_planning(self):
"""检查在规划后是否有新消息"""
@@ -151,14 +162,19 @@ class Conversation:
if self._check_new_messages_after_planning():
logger.info("333333发现新消息重新考虑行动")
conversation_info.done_action[-1].update(
{
"status": "recall",
"time": datetime.datetime.now().strftime("%H:%M:%S"),
}
)
return None
await self._send_reply()
conversation_info.done_action.append(
conversation_info.done_action[-1].update(
{
"action": action,
"reason": reason,
"status": "done",
"time": datetime.datetime.now().strftime("%H:%M:%S"),
}
@@ -184,16 +200,17 @@ class Conversation:
elif action == "listening":
self.state = ConversationState.LISTENING
logger.info("倾听对方发言...")
if await self.waiter.wait(): # 如果返回True表示超时
await self._send_timeout_message()
await self._stop_conversation()
await self.waiter.wait_listening(conversation_info)
elif action == "end_conversation":
self.should_continue = False
logger.info("决定结束对话...")
else: # wait
self.state = ConversationState.WAITING
logger.info("等待更多信息...")
if await self.waiter.wait(): # 如果返回True表示超时
await self._send_timeout_message()
await self._stop_conversation()
await self.waiter.wait(self.conversation_info)
async def _send_timeout_message(self):
"""发送超时结束消息"""

View File

@@ -54,7 +54,7 @@ class MongoDBMessageStorage(MessageStorage):
async def get_messages_after(self, chat_id: str, message_time: float) -> List[Dict[str, Any]]:
query = {"chat_id": chat_id}
print(f"storage_check_message: {message_time}")
# print(f"storage_check_message: {message_time}")
query["time"] = {"$gt": message_time}

View File

@@ -157,8 +157,8 @@ class ObservationInfo:
Args:
message: 消息数据
"""
print("1919810-----------------------------------------------------")
logger.debug(f"更新信息from_message: {message}")
# print("1919810-----------------------------------------------------")
# logger.debug(f"更新信息from_message: {message}")
self.last_message_time = message["time"]
self.last_message_id = message["message_id"]

View File

@@ -54,11 +54,28 @@ class GoalAnalyzer:
Tuple[str, str, str]: (目标, 方法, 原因)
"""
# 构建对话目标
goal_list = conversation_info.goal_list
goal_text = ""
for goal, reason in goal_list:
goal_text += f"目标:{goal};"
goal_text += f"原因:{reason}\n"
goals_str = ""
if conversation_info.goal_list:
for goal_reason in conversation_info.goal_list:
# 处理字典或元组格式
if isinstance(goal_reason, tuple):
# 假设元组的第一个元素是目标,第二个元素是原因
goal = goal_reason[0]
reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因"
elif isinstance(goal_reason, dict):
goal = goal_reason.get('goal')
reasoning = goal_reason.get('reasoning', "没有明确原因")
else:
# 如果是其他类型,尝试转为字符串
goal = str(goal_reason)
reasoning = "没有明确原因"
goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
goals_str += goal_str
else:
goal = "目前没有明确对话目标"
reasoning = "目前没有明确对话目标,最好思考一个对话目标"
goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
# 获取聊天历史记录
chat_history_list = observation_info.chat_history
@@ -88,7 +105,7 @@ class GoalAnalyzer:
{action_history_text}
当前对话目标:
{goal_text}
{goals_str}
聊天记录:
{chat_history_text}
@@ -98,6 +115,7 @@ class GoalAnalyzer:
2. 修改现有目标
3. 添加新目标
4. 删除不再相关的目标
5. 如果你想结束对话请设置一个目标目标goal为"结束对话"原因reasoning为你希望结束对话
请以JSON数组格式输出当前的所有对话目标每个目标包含以下字段
1. goal: 对话目标(简短的一句话)
@@ -275,38 +293,6 @@ class GoalAnalyzer:
return False, False, f"分析出错: {str(e)}"
class Waiter:
"""快 速 等 待"""
def __init__(self, stream_id: str):
self.chat_observer = ChatObserver.get_instance(stream_id)
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME
async def wait(self) -> bool:
"""等待
Returns:
bool: 是否超时True表示超时
"""
# 使用当前时间作为等待开始时间
wait_start_time = time.time()
self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间
while True:
# 检查是否有新消息
if self.chat_observer.new_message_after(wait_start_time):
logger.info("等待结束,收到新消息")
return False
# 检查是否超时
if time.time() - wait_start_time > 300:
logger.info("等待超过300秒结束对话")
return True
await asyncio.sleep(1)
logger.info("等待中...")
class DirectMessageSender:
"""直接发送消息到平台的发送器"""

View File

@@ -41,7 +41,7 @@ class PFCManager:
logger.debug(f"会话实例正在初始化中: {stream_id}")
return None
if stream_id in self._instances:
if stream_id in self._instances and self._instances[stream_id].should_continue:
logger.debug(f"使用现有会话实例: {stream_id}")
return self._instances[stream_id]

View File

@@ -16,7 +16,7 @@ class ReplyGenerator:
def __init__(self, stream_id: str):
self.llm = LLM_request(
model=global_config.llm_normal, temperature=0.7, max_tokens=300, request_type="reply_generation"
model=global_config.llm_normal, temperature=0.3, max_tokens=300, request_type="reply_generation"
)
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME
@@ -39,33 +39,76 @@ class ReplyGenerator:
# 构建提示词
logger.debug(f"开始生成回复:当前目标: {conversation_info.goal_list}")
goal_list = conversation_info.goal_list
goal_text = ""
for goal, reason in goal_list:
goal_text += f"目标:{goal};"
goal_text += f"原因:{reason}\n"
# 构建对话目标
goals_str = ""
if conversation_info.goal_list:
for goal_reason in conversation_info.goal_list:
# 处理字典或元组格式
if isinstance(goal_reason, tuple):
# 假设元组的第一个元素是目标,第二个元素是原因
goal = goal_reason[0]
reasoning = goal_reason[1] if len(goal_reason) > 1 else "没有明确原因"
elif isinstance(goal_reason, dict):
goal = goal_reason.get('goal')
reasoning = goal_reason.get('reasoning', "没有明确原因")
else:
# 如果是其他类型,尝试转为字符串
goal = str(goal_reason)
reasoning = "没有明确原因"
goal_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
goals_str += goal_str
else:
goal = "目前没有明确对话目标"
reasoning = "目前没有明确对话目标,最好思考一个对话目标"
goals_str = f"目标:{goal},产生该对话目标的原因:{reasoning}\n"
# 获取聊天历史记录
chat_history_list = observation_info.chat_history
chat_history_list = observation_info.chat_history[-20:] if len(observation_info.chat_history) >= 20 else observation_info.chat_history
chat_history_text = ""
for msg in chat_history_list:
chat_history_text += f"{msg}\n"
chat_history_text += f"{msg.get('detailed_plain_text', '')}\n"
# 整理知识缓存
knowledge_text = ""
knowledge_list = conversation_info.knowledge_list
for knowledge in knowledge_list:
knowledge_text += f"知识:{knowledge}\n"
if observation_info.new_messages_count > 0:
new_messages_list = observation_info.unprocessed_messages
chat_history_text += f"{observation_info.new_messages_count}条新消息:\n"
for msg in new_messages_list:
chat_history_text += f"{msg.get('detailed_plain_text', '')}\n"
observation_info.clear_unprocessed_messages()
personality_text = f"你的名字是{self.name}{self.personality_info}"
# 构建action历史文本
action_history_list = conversation_info.done_action[-10:] if len(conversation_info.done_action) >= 10 else conversation_info.done_action
action_history_text = "你之前做的事情是:"
for action in action_history_list:
if isinstance(action, dict):
action_type = action.get('action')
action_reason = action.get('reason')
action_status = action.get('status')
if action_status == "recall":
action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n"
elif action_status == "done":
action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n"
elif isinstance(action, tuple):
# 假设元组的格式是(action_type, action_reason, action_status)
action_type = action[0] if len(action) > 0 else "未知行动"
action_reason = action[1] if len(action) > 1 else "未知原因"
action_status = action[2] if len(action) > 2 else "done"
if action_status == "recall":
action_history_text += f"原本打算:{action_type},但是因为有新消息,你发现这个行动不合适,所以你没做\n"
elif action_status == "done":
action_history_text += f"你之前做了:{action_type},原因:{action_reason}\n"
prompt = f"""{personality_text}。现在你在参与一场QQ聊天请根据以下信息生成回复
当前对话目标:{goal_text}
{knowledge_text}
当前对话目标:{goals_str}
最近的聊天记录:
{chat_history_text}
请根据上述信息,以你的性格特征生成一个自然、得体的回复。回复应该:
1. 符合对话目标,以""的角度发言
2. 体现你的性格特征

View File

@@ -1,46 +1,81 @@
from src.common.logger import get_module_logger
from .chat_observer import ChatObserver
from .conversation_info import ConversationInfo
from src.individuality.individuality import Individuality
from ..config.config import global_config
import time
import asyncio
logger = get_module_logger("waiter")
class Waiter:
"""等待器,用于等待对话流中的事件"""
"""快 速 等 待"""
def __init__(self, stream_id: str):
self.stream_id = stream_id
self.chat_observer = ChatObserver.get_instance(stream_id)
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME
async def wait(self, timeout: float = 20.0) -> bool:
"""等待用户回复或超时
Args:
timeout: 超时时间(秒)
async def wait(self, conversation_info: ConversationInfo) -> bool:
"""等待
Returns:
bool: 如果因为超时返回则为True否则为False
bool: 是否超时True表示超时
"""
try:
message_before = self.chat_observer.get_last_message()
# 使用当前时间作为等待开始时间
wait_start_time = time.time()
self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间
# 等待新消息
logger.debug(f"等待新消息,超时时间: {timeout}")
while True:
# 检查是否有新消息
if self.chat_observer.new_message_after(wait_start_time):
logger.info("等待结束,收到新消息")
return False
is_timeout = await self.chat_observer.wait_for_update(timeout=timeout)
if is_timeout:
logger.debug("等待超时,没有收到新消息")
# 检查是否超时
if time.time() - wait_start_time > 15:
logger.info("等待超过300秒结束对话")
wait_goal = {
"goal": "你等待了5分钟思考接下来要做什么",
"reason": "对方很久没有回复你的消息了"
}
conversation_info.goal_list.append(wait_goal)
print(f"添加目标: {wait_goal}")
return True
# 检查是否是新消息
message_after = self.chat_observer.get_last_message()
if message_before and message_after and message_before.get("message_id") == message_after.get("message_id"):
# 如果消息ID相同说明没有新消息
logger.debug("没有收到新消息")
await asyncio.sleep(1)
logger.info("等待中...")
async def wait_listening(self, conversation_info: ConversationInfo) -> bool:
"""等待倾听
Returns:
bool: 是否超时True表示超时
"""
# 使用当前时间作为等待开始时间
wait_start_time = time.time()
self.chat_observer.waiting_start_time = wait_start_time # 设置等待开始时间
while True:
# 检查是否有新消息
if self.chat_observer.new_message_after(wait_start_time):
logger.info("等待结束,收到新消息")
return False
# 检查是否超时
if time.time() - wait_start_time > 30:
logger.info("等待超过300秒结束对话")
wait_goal = {
"goal": "你等待了5分钟思考接下来要做什么",
"reason": "对方话说一半消失了,很久没有回复"
}
conversation_info.goal_list.append(wait_goal)
print(f"添加目标: {wait_goal}")
return True
logger.debug("收到新消息")
return False
await asyncio.sleep(1)
logger.info("等待中...")
except Exception as e:
logger.error(f"等待时出错: {str(e)}")
return True