Merge branch 'dev' of https://github.com/MaiM-with-u/MaiBot into dev
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#Programmable Friendly Conversationalist
|
||||
#Prefrontal cortex
|
||||
# Programmable Friendly Conversationalist
|
||||
# Prefrontal cortex
|
||||
import datetime
|
||||
import asyncio
|
||||
from typing import List, Optional, Tuple, TYPE_CHECKING
|
||||
@@ -29,20 +29,17 @@ logger = get_module_logger("pfc")
|
||||
|
||||
class GoalAnalyzer:
|
||||
"""对话目标分析器"""
|
||||
|
||||
|
||||
def __init__(self, stream_id: str):
|
||||
self.llm = LLM_request(
|
||||
model=global_config.llm_normal,
|
||||
temperature=0.7,
|
||||
max_tokens=1000,
|
||||
request_type="conversation_goal"
|
||||
model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="conversation_goal"
|
||||
)
|
||||
|
||||
self.personality_info = Individuality.get_instance().get_prompt(type = "personality", x_person = 2, level = 2)
|
||||
|
||||
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=2)
|
||||
self.name = global_config.BOT_NICKNAME
|
||||
self.nick_name = global_config.BOT_ALIAS_NAMES
|
||||
self.chat_observer = ChatObserver.get_instance(stream_id)
|
||||
|
||||
|
||||
# 多目标存储结构
|
||||
self.goals = [] # 存储多个目标
|
||||
self.max_goals = 3 # 同时保持的最大目标数量
|
||||
@@ -50,10 +47,10 @@ class GoalAnalyzer:
|
||||
|
||||
async def analyze_goal(self) -> Tuple[str, str, str]:
|
||||
"""分析对话历史并设定目标
|
||||
|
||||
|
||||
Args:
|
||||
chat_history: 聊天历史记录列表
|
||||
|
||||
|
||||
Returns:
|
||||
Tuple[str, str, str]: (目标, 方法, 原因)
|
||||
"""
|
||||
@@ -70,16 +67,16 @@ class GoalAnalyzer:
|
||||
if sender == self.name:
|
||||
sender = "你说"
|
||||
chat_history_text += f"{time_str},{sender}:{msg.get('processed_plain_text', '')}\n"
|
||||
|
||||
|
||||
personality_text = f"你的名字是{self.name},{self.personality_info}"
|
||||
|
||||
|
||||
# 构建当前已有目标的文本
|
||||
existing_goals_text = ""
|
||||
if self.goals:
|
||||
existing_goals_text = "当前已有的对话目标:\n"
|
||||
for i, (goal, _, reason) in enumerate(self.goals):
|
||||
existing_goals_text += f"{i+1}. 目标: {goal}, 原因: {reason}\n"
|
||||
|
||||
existing_goals_text += f"{i + 1}. 目标: {goal}, 原因: {reason}\n"
|
||||
|
||||
prompt = f"""{personality_text}。现在你在参与一场QQ聊天,请分析以下聊天记录,并根据你的性格特征确定多个明确的对话目标。
|
||||
这些目标应该反映出对话的不同方面和意图。
|
||||
|
||||
@@ -107,46 +104,44 @@ class GoalAnalyzer:
|
||||
logger.debug(f"发送到LLM的提示词: {prompt}")
|
||||
content, _ = await self.llm.generate_response_async(prompt)
|
||||
logger.debug(f"LLM原始返回内容: {content}")
|
||||
|
||||
|
||||
# 使用简化函数提取JSON内容
|
||||
success, result = get_items_from_json(
|
||||
content,
|
||||
"goal", "reasoning",
|
||||
required_types={"goal": str, "reasoning": str}
|
||||
content, "goal", "reasoning", required_types={"goal": str, "reasoning": str}
|
||||
)
|
||||
|
||||
|
||||
if not success:
|
||||
logger.error(f"无法解析JSON,重试第{retry + 1}次")
|
||||
continue
|
||||
|
||||
|
||||
goal = result["goal"]
|
||||
reasoning = result["reasoning"]
|
||||
|
||||
|
||||
# 使用默认的方法
|
||||
method = "以友好的态度回应"
|
||||
|
||||
|
||||
# 更新目标列表
|
||||
await self._update_goals(goal, method, reasoning)
|
||||
|
||||
|
||||
# 返回当前最主要的目标
|
||||
if self.goals:
|
||||
current_goal, current_method, current_reasoning = self.goals[0]
|
||||
return current_goal, current_method, current_reasoning
|
||||
else:
|
||||
return goal, method, reasoning
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"分析对话目标时出错: {str(e)},重试第{retry + 1}次")
|
||||
if retry == max_retries - 1:
|
||||
return "保持友好的对话", "以友好的态度回应", "确保对话顺利进行"
|
||||
continue
|
||||
|
||||
|
||||
# 所有重试都失败后的默认返回
|
||||
return "保持友好的对话", "以友好的态度回应", "确保对话顺利进行"
|
||||
|
||||
|
||||
async def _update_goals(self, new_goal: str, method: str, reasoning: str):
|
||||
"""更新目标列表
|
||||
|
||||
|
||||
Args:
|
||||
new_goal: 新的目标
|
||||
method: 实现目标的方法
|
||||
@@ -160,23 +155,23 @@ class GoalAnalyzer:
|
||||
# 将此目标移到列表前面(最主要的位置)
|
||||
self.goals.insert(0, self.goals.pop(i))
|
||||
return
|
||||
|
||||
|
||||
# 添加新目标到列表前面
|
||||
self.goals.insert(0, (new_goal, method, reasoning))
|
||||
|
||||
|
||||
# 限制目标数量
|
||||
if len(self.goals) > self.max_goals:
|
||||
self.goals.pop() # 移除最老的目标
|
||||
|
||||
|
||||
def _calculate_similarity(self, goal1: str, goal2: str) -> float:
|
||||
"""简单计算两个目标之间的相似度
|
||||
|
||||
|
||||
这里使用一个简单的实现,实际可以使用更复杂的文本相似度算法
|
||||
|
||||
|
||||
Args:
|
||||
goal1: 第一个目标
|
||||
goal2: 第二个目标
|
||||
|
||||
|
||||
Returns:
|
||||
float: 相似度得分 (0-1)
|
||||
"""
|
||||
@@ -186,18 +181,18 @@ class GoalAnalyzer:
|
||||
overlap = len(words1.intersection(words2))
|
||||
total = len(words1.union(words2))
|
||||
return overlap / total if total > 0 else 0
|
||||
|
||||
|
||||
async def get_all_goals(self) -> List[Tuple[str, str, str]]:
|
||||
"""获取所有当前目标
|
||||
|
||||
|
||||
Returns:
|
||||
List[Tuple[str, str, str]]: 目标列表,每项为(目标, 方法, 原因)
|
||||
"""
|
||||
return self.goals.copy()
|
||||
|
||||
|
||||
async def get_alternative_goals(self) -> List[Tuple[str, str, str]]:
|
||||
"""获取除了当前主要目标外的其他备选目标
|
||||
|
||||
|
||||
Returns:
|
||||
List[Tuple[str, str, str]]: 备选目标列表
|
||||
"""
|
||||
@@ -215,9 +210,9 @@ class GoalAnalyzer:
|
||||
if sender == self.name:
|
||||
sender = "你说"
|
||||
chat_history_text += f"{time_str},{sender}:{msg.get('processed_plain_text', '')}\n"
|
||||
|
||||
|
||||
personality_text = f"你的名字是{self.name},{self.personality_info}"
|
||||
|
||||
|
||||
prompt = f"""{personality_text}。现在你在参与一场QQ聊天,
|
||||
当前对话目标:{goal}
|
||||
产生该对话目标的原因:{reasoning}
|
||||
@@ -247,7 +242,7 @@ class GoalAnalyzer:
|
||||
"goal_achieved", "stop_conversation", "reason",
|
||||
required_types={"goal_achieved": bool, "stop_conversation": bool, "reason": str}
|
||||
)
|
||||
|
||||
|
||||
if not success:
|
||||
logger.error("无法解析对话分析结果JSON")
|
||||
return False, False, "解析结果失败"
|
||||
@@ -265,14 +260,15 @@ class GoalAnalyzer:
|
||||
|
||||
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.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表示超时)
|
||||
"""
|
||||
@@ -298,7 +294,7 @@ class Waiter:
|
||||
|
||||
class DirectMessageSender:
|
||||
"""直接发送消息到平台的发送器"""
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.logger = get_module_logger("direct_sender")
|
||||
self.storage = MessageStorage()
|
||||
@@ -310,7 +306,7 @@ class DirectMessageSender:
|
||||
reply_to_message: Optional[Message] = None,
|
||||
) -> None:
|
||||
"""直接发送消息到平台
|
||||
|
||||
|
||||
Args:
|
||||
chat_stream: 聊天流
|
||||
content: 消息内容
|
||||
@@ -323,7 +319,7 @@ class DirectMessageSender:
|
||||
user_nickname=global_config.BOT_NICKNAME,
|
||||
platform=chat_stream.platform,
|
||||
)
|
||||
|
||||
|
||||
message = MessageSending(
|
||||
message_id=f"dm{round(time.time(), 2)}",
|
||||
chat_stream=chat_stream,
|
||||
@@ -343,18 +339,17 @@ class DirectMessageSender:
|
||||
try:
|
||||
message_json = message.to_dict()
|
||||
end_point = global_config.api_urls.get(chat_stream.platform, None)
|
||||
|
||||
|
||||
if not end_point:
|
||||
raise ValueError(f"未找到平台:{chat_stream.platform} 的url配置")
|
||||
|
||||
|
||||
await global_api.send_message_REST(end_point, message_json)
|
||||
|
||||
|
||||
# 存储消息
|
||||
await self.storage.store_message(message, message.chat_stream)
|
||||
|
||||
|
||||
self.logger.info(f"直接发送消息成功: {content[:30]}...")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"直接发送消息失败: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
Reference in New Issue
Block a user