Files
Mofox-Core/src/chat/utils/smart_prompt.py

956 lines
44 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
智能Prompt系统 - 完全重构版本
基于原有DefaultReplyer的完整功能集成使用新的参数结构
解决实现质量不高、功能集成不完整和错误处理不足的问题
"""
import asyncio
import time
from datetime import datetime
from dataclasses import dataclass, field
from typing import Dict, Any, Optional, List, Literal, Tuple
from src.chat.utils.prompt_builder import global_prompt_manager, Prompt
from src.common.logger import get_logger
from src.config.config import global_config
from src.chat.utils.chat_message_builder import (
build_readable_messages,
get_raw_msg_before_timestamp_with_chat,
build_readable_messages_with_id,
replace_user_references_sync,
)
from src.person_info.person_info import get_person_info_manager
from src.plugin_system.core.tool_use import ToolExecutor
from src.chat.utils.prompt_utils import PromptUtils
from src.chat.utils.prompt_parameters import SmartPromptParameters
logger = get_logger("smart_prompt")
@dataclass
class ChatContext:
"""聊天上下文信息"""
chat_id: str = ""
platform: str = ""
is_group: bool = False
user_id: str = ""
user_nickname: str = ""
group_id: Optional[str] = None
timestamp: datetime = field(default_factory=datetime.now)
class SmartPromptBuilder:
"""重构的智能提示词构建器 - 统一错误处理和功能集成"""
def __init__(self):
# 移除缓存相关初始化
pass
async def build_context_data(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""并行构建完整的上下文数据"""
# 并行执行所有构建任务
start_time = time.time()
timing_logs = {}
try:
# 准备构建任务
tasks = []
task_names = []
# 初始化预构建参数,使用新的结构
pre_built_params = {}
if params.expression_habits_block:
pre_built_params['expression_habits_block'] = params.expression_habits_block
if params.relation_info_block:
pre_built_params['relation_info_block'] = params.relation_info_block
if params.memory_block:
pre_built_params['memory_block'] = params.memory_block
if params.tool_info_block:
pre_built_params['tool_info_block'] = params.tool_info_block
if params.knowledge_prompt:
pre_built_params['knowledge_prompt'] = params.knowledge_prompt
if params.cross_context_block:
pre_built_params['cross_context_block'] = params.cross_context_block
# 根据新的参数结构确定要构建的项
if params.enable_expression and not pre_built_params.get('expression_habits_block'):
tasks.append(self._build_expression_habits(params))
task_names.append("expression_habits")
if params.enable_memory and not pre_built_params.get('memory_block'):
tasks.append(self._build_memory_block(params))
task_names.append("memory_block")
if params.enable_relation and not pre_built_params.get('relation_info_block'):
tasks.append(self._build_relation_info(params))
task_names.append("relation_info")
# 添加mai_think上下文构建任务
if not pre_built_params.get('mai_think'):
tasks.append(self._build_mai_think_context(params))
task_names.append("mai_think_context")
if params.enable_tool and not pre_built_params.get('tool_info_block'):
tasks.append(self._build_tool_info(params))
task_names.append("tool_info")
if params.enable_knowledge and not pre_built_params.get('knowledge_prompt'):
tasks.append(self._build_knowledge_info(params))
task_names.append("knowledge_info")
if params.enable_cross_context and not pre_built_params.get('cross_context_block'):
tasks.append(self._build_cross_context(params))
task_names.append("cross_context")
# 性能优化:根据任务数量动态调整超时时间
base_timeout = 10.0 # 基础超时时间
task_timeout = 2.0 # 每个任务的超时时间
timeout_seconds = min(
max(base_timeout, len(tasks) * task_timeout), # 根据任务数量计算超时
30.0 # 最大超时时间
)
# 性能优化:限制并发任务数量,避免资源耗尽
max_concurrent_tasks = 5 # 最大并发任务数
if len(tasks) > max_concurrent_tasks:
# 分批执行任务
results = []
for i in range(0, len(tasks), max_concurrent_tasks):
batch_tasks = tasks[i:i+max_concurrent_tasks]
batch_names = task_names[i:i+max_concurrent_tasks]
batch_results = await asyncio.wait_for(
asyncio.gather(*batch_tasks, return_exceptions=True),
timeout=timeout_seconds
)
results.extend(batch_results)
else:
# 一次性执行所有任务
results = await asyncio.wait_for(
asyncio.gather(*tasks, return_exceptions=True),
timeout=timeout_seconds
)
# 处理结果并收集性能数据
context_data = {}
for i, result in enumerate(results):
task_name = task_names[i] if i < len(task_names) else f"task_{i}"
if isinstance(result, Exception):
logger.error(f"构建任务{task_name}失败: {str(result)}")
elif isinstance(result, dict):
# 结果格式: {component_name: value}
context_data.update(result)
# 记录耗时过长的任务
if task_name in timing_logs and timing_logs[task_name] > 8.0:
logger.warning(f"构建任务{task_name}耗时过长: {timing_logs[task_name]:.2f}s")
# 添加预构建的参数
for key, value in pre_built_params.items():
if value:
context_data[key] = value
except asyncio.TimeoutError:
logger.error(f"构建超时 ({timeout_seconds}s)")
context_data = {}
# 添加预构建的参数,即使在超时情况下
for key, value in pre_built_params.items():
if value:
context_data[key] = value
# 构建聊天历史 - 根据模式不同
if params.prompt_mode == "s4u":
await self._build_s4u_chat_context(context_data, params)
else:
await self._build_normal_chat_context(context_data, params)
# 补充基础信息
context_data.update({
'keywords_reaction_prompt': params.keywords_reaction_prompt,
'extra_info_block': params.extra_info_block,
'time_block': params.time_block or f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
'identity': params.identity_block,
'schedule_block': params.schedule_block,
'moderation_prompt': params.moderation_prompt_block,
'reply_target_block': params.reply_target_block,
'mood_state': params.mood_prompt,
'action_descriptions': params.action_descriptions,
})
total_time = time.time() - start_time
if timing_logs:
timing_str = "; ".join([f"{name}: {time:.2f}s" for name, time in timing_logs.items()])
logger.info(f"构建任务耗时: {timing_str}")
logger.debug(f"构建完成,总耗时: {total_time:.2f}s")
return context_data
async def _build_s4u_chat_context(self, context_data: Dict[str, Any], params: SmartPromptParameters) -> None:
"""构建S4U模式的聊天上下文 - 使用新参数结构"""
if not params.message_list_before_now_long:
return
# 使用共享工具构建分离历史
core_dialogue, background_dialogue = await self._build_s4u_chat_history_prompts(
params.message_list_before_now_long,
params.target_user_info.get("user_id") if params.target_user_info else ""
)
context_data['core_dialogue_prompt'] = core_dialogue
context_data['background_dialogue_prompt'] = background_dialogue
async def _build_normal_chat_context(self, context_data: Dict[str, Any], params: SmartPromptParameters) -> None:
"""构建normal模式的聊天上下文 - 使用新参数结构"""
if not params.chat_talking_prompt_short:
return
context_data['chat_info'] = f"""群里的聊天内容:
{params.chat_talking_prompt_short}"""
async def _build_s4u_chat_history_prompts(
self,
message_list_before_now: List[Dict[str, Any]],
target_user_id: str
) -> Tuple[str, str]:
"""构建S4U风格的分离对话prompt - 完整实现"""
core_dialogue_list = []
background_dialogue_list = []
bot_id = str(global_config.bot.qq_account)
# 过滤消息分离bot和目标用户的对话 vs 其他用户的对话
for msg_dict in message_list_before_now:
try:
msg_user_id = str(msg_dict.get("user_id"))
reply_to = msg_dict.get("reply_to", "")
_platform, reply_to_user_id = self._parse_reply_target(reply_to)
if (msg_user_id == bot_id and reply_to_user_id == target_user_id) or msg_user_id == target_user_id:
# bot 和目标用户的对话
core_dialogue_list.append(msg_dict)
else:
# 其他用户的对话
background_dialogue_list.append(msg_dict)
except Exception as e:
logger.error(f"处理消息记录时出错: {msg_dict}, 错误: {e}")
# 构建背景对话 prompt
background_dialogue_prompt = ""
if background_dialogue_list:
latest_25_msgs = background_dialogue_list[-int(global_config.chat.max_context_size * 0.5) :]
background_dialogue_prompt_str = build_readable_messages(
latest_25_msgs,
replace_bot_name=True,
timestamp_mode="normal",
truncate=True,
)
background_dialogue_prompt = f"这是其他用户的发言:\n{background_dialogue_prompt_str}"
# 构建核心对话 prompt
core_dialogue_prompt = ""
if core_dialogue_list:
core_dialogue_list = core_dialogue_list[-int(global_config.chat.max_context_size * 2) :] # 限制消息数量
core_dialogue_prompt_str = build_readable_messages(
core_dialogue_list,
replace_bot_name=True,
merge_messages=False,
timestamp_mode="normal",
read_mark=0.0,
truncate=True,
show_actions=True,
)
core_dialogue_prompt = core_dialogue_prompt_str
return core_dialogue_prompt, background_dialogue_prompt
async def _build_mai_think_context(self, params: SmartPromptParameters) -> Any:
"""构建mai_think上下文 - 完全继承DefaultReplyer功能"""
from src.mais4u.mai_think import mai_thinking_manager
# 获取mai_think实例
mai_think = mai_thinking_manager.get_mai_think(params.chat_id)
# 设置mai_think的上下文信息
mai_think.memory_block = params.memory_block or ""
mai_think.relation_info_block = params.relation_info_block or ""
mai_think.time_block = params.time_block or f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
# 设置聊天目标信息
if params.is_group_chat:
chat_target_1 = await global_prompt_manager.get_prompt_async("chat_target_group1")
chat_target_2 = await global_prompt_manager.get_prompt_async("chat_target_group2")
else:
chat_target_name = "对方"
if params.chat_target_info:
chat_target_name = (
params.chat_target_info.get("person_name") or
params.chat_target_info.get("user_nickname") or "对方"
)
chat_target_1 = await global_prompt_manager.format_prompt(
"chat_target_private1", sender_name=chat_target_name
)
chat_target_2 = await global_prompt_manager.format_prompt(
"chat_target_private2", sender_name=chat_target_name
)
mai_think.chat_target = chat_target_1
mai_think.chat_target_2 = chat_target_2
mai_think.chat_info = params.chat_talking_prompt_short or ""
mai_think.mood_state = params.mood_prompt or ""
mai_think.identity = params.identity_block or ""
mai_think.sender = params.sender
mai_think.target = params.target
# 返回mai_think实例以便后续使用
return mai_think
def _parse_reply_target_id(self, reply_to: str) -> str:
"""解析回复目标中的用户ID"""
if not reply_to:
return ""
# 复用_parse_reply_target方法的逻辑
sender, _ = self._parse_reply_target(reply_to)
if not sender:
return ""
# 获取用户ID
person_info_manager = get_person_info_manager()
person_id = person_info_manager.get_person_id_by_person_name(sender)
if person_id:
user_id = person_info_manager.get_value_sync(person_id, "user_id")
return str(user_id) if user_id else ""
async def _build_expression_habits(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建表达习惯 - 使用共享工具类完全继承DefaultReplyer功能"""
# 检查是否允许在此聊天流中使用表达
use_expression, _, _ = global_config.expression.get_expression_config_for_chat(params.chat_id)
if not use_expression:
return {"expression_habits_block": ""}
from src.chat.express.expression_selector import expression_selector
style_habits = []
grammar_habits = []
# 使用从处理器传来的选中表达方式
# LLM模式调用LLM选择5-10个然后随机选5个
try:
selected_expressions = await expression_selector.select_suitable_expressions_llm(
params.chat_id,
params.chat_talking_prompt_short,
max_num=8,
min_num=2,
target_message=params.target
)
except Exception as e:
logger.error(f"选择表达方式失败: {e}")
selected_expressions = []
if selected_expressions:
logger.debug(f"使用处理器选中的{len(selected_expressions)}个表达方式")
for expr in selected_expressions:
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
expr_type = expr.get("type", "style")
if expr_type == "grammar":
grammar_habits.append(f"{expr['situation']}时,使用 {expr['style']}")
else:
style_habits.append(f"{expr['situation']}时,使用 {expr['style']}")
else:
logger.debug("没有从处理器获得表达方式,将使用空的表达方式")
# 不再在replyer中进行随机选择全部交给处理器处理
style_habits_str = "\n".join(style_habits)
grammar_habits_str = "\n".join(grammar_habits)
# 动态构建expression habits块
expression_habits_block = ""
expression_habits_title = ""
if style_habits_str.strip():
expression_habits_title = (
"你可以参考以下的语言习惯,当情景合适就使用,但不要生硬使用,以合理的方式结合到你的回复中:"
)
expression_habits_block += f"{style_habits_str}\n"
if grammar_habits_str.strip():
expression_habits_title = (
"你可以选择下面的句法进行回复,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式使用:"
)
expression_habits_block += f"{grammar_habits_str}\n"
if style_habits_str.strip() and grammar_habits_str.strip():
expression_habits_title = "你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,以合理的方式结合到你的回复中。"
return {"expression_habits_block": f"{expression_habits_title}\n{expression_habits_block}"}
async def _build_memory_block(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建记忆块 - 使用共享工具类完全继承DefaultReplyer功能"""
if not global_config.memory.enable_memory:
return {"memory_block": ""}
from src.chat.memory_system.memory_activator import MemoryActivator
from src.chat.memory_system.vector_instant_memory import VectorInstantMemoryV2
instant_memory = None
# 初始化记忆激活器
try:
memory_activator = MemoryActivator()
# 获取长期记忆
running_memories = await memory_activator.activate_memory_with_chat_history(
target_message=params.target,
chat_history_prompt=params.chat_talking_prompt_short
)
except Exception as e:
logger.error(f"激活记忆失败: {e}")
running_memories = []
# 处理瞬时记忆
if global_config.memory.enable_instant_memory:
# 使用异步记忆包装器(最优化的非阻塞模式)
try:
from src.chat.memory_system.async_instant_memory_wrapper import get_async_instant_memory
# 获取异步记忆包装器
async_memory = get_async_instant_memory(params.chat_id)
# 后台存储聊天历史(完全非阻塞)
async_memory.store_memory_background(params.chat_talking_prompt_short)
# 快速检索记忆最大超时2秒
instant_memory = await async_memory.get_memory_with_fallback(params.target, max_timeout=2.0)
logger.info(f"异步瞬时记忆:{instant_memory}")
except ImportError:
# 如果异步包装器不可用,尝试使用异步记忆管理器
try:
from src.chat.memory_system.async_memory_optimizer import (
retrieve_memory_nonblocking,
store_memory_nonblocking,
)
# 异步存储聊天历史(非阻塞)
asyncio.create_task(
store_memory_nonblocking(chat_id=params.chat_id, content=params.chat_talking_prompt_short)
)
# 尝试从缓存获取瞬时记忆
instant_memory = await retrieve_memory_nonblocking(chat_id=params.chat_id, query=params.target)
# 如果没有缓存结果,快速检索一次
if instant_memory is None:
try:
# 使用VectorInstantMemoryV2实例
instant_memory_system = VectorInstantMemoryV2(chat_id=params.chat_id, retention_hours=1)
instant_memory = await asyncio.wait_for(
instant_memory_system.get_memory_for_context(params.target), timeout=1.5
)
except asyncio.TimeoutError:
logger.warning("瞬时记忆检索超时,使用空结果")
instant_memory = ""
logger.info(f"向量瞬时记忆:{instant_memory}")
except ImportError:
# 最后的fallback使用原有逻辑但加上超时控制
logger.warning("异步记忆系统不可用,使用带超时的同步方式")
# 使用VectorInstantMemoryV2实例
instant_memory_system = VectorInstantMemoryV2(chat_id=params.chat_id, retention_hours=1)
# 异步存储聊天历史
asyncio.create_task(instant_memory_system.store_message(params.chat_talking_prompt_short))
# 带超时的记忆检索
try:
instant_memory = await asyncio.wait_for(
instant_memory_system.get_memory_for_context(params.target),
timeout=1.0, # 最保守的1秒超时
)
except asyncio.TimeoutError:
logger.warning("瞬时记忆检索超时,跳过记忆获取")
instant_memory = ""
except Exception as e:
logger.error(f"瞬时记忆检索失败: {e}")
instant_memory = ""
logger.info(f"同步瞬时记忆:{instant_memory}")
except Exception as e:
logger.error(f"瞬时记忆系统异常: {e}")
instant_memory = ""
# 构建记忆字符串,即使某种记忆为空也要继续
memory_str = ""
has_any_memory = False
# 添加长期记忆
if running_memories:
if not memory_str:
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
for running_memory in running_memories:
memory_str += f"- {running_memory['content']}\n"
has_any_memory = True
# 添加瞬时记忆
if instant_memory:
if not memory_str:
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
memory_str += f"- {instant_memory}\n"
has_any_memory = True
# 注入视频分析结果引导语
memory_str = self._inject_video_prompt_if_needed(params.target, memory_str)
# 只有当完全没有任何记忆时才返回空字符串
return {"memory_block": memory_str if has_any_memory else ""}
def _inject_video_prompt_if_needed(self, target: str, memory_str: str) -> str:
"""统一视频分析结果注入逻辑"""
if target and ("[视频内容]" in target or "好的,我将根据您提供的" in target):
video_prompt_injection = "\n请注意,以上内容是你刚刚观看的视频,请以第一人称分享你的观后感,而不是在分析一份报告。"
return memory_str + video_prompt_injection
return memory_str
async def _build_relation_info(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建关系信息 - 使用共享工具类"""
try:
relation_info = await PromptUtils.build_relation_info(
params.chat_id,
params.reply_to
)
return {"relation_info_block": relation_info}
except Exception as e:
logger.error(f"构建关系信息失败: {e}")
return {"relation_info_block": ""}
async def _build_tool_info(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建工具信息 - 使用共享工具类完全继承DefaultReplyer功能"""
if not params.enable_tool:
return {"tool_info_block": ""}
if not params.reply_to:
return {"tool_info_block": ""}
sender, text = PromptUtils.parse_reply_target(params.reply_to)
if not text:
return {"tool_info_block": ""}
from src.plugin_system.core.tool_use import ToolExecutor
# 使用工具执行器获取信息
try:
tool_executor = ToolExecutor(chat_id=params.chat_id)
tool_results, _, _ = await tool_executor.execute_from_chat_message(
sender=sender,
target_message=text,
chat_history=params.chat_talking_prompt_short,
return_details=False
)
if tool_results:
tool_info_str = "以下是你通过工具获取到的实时信息:\n"
for tool_result in tool_results:
tool_name = tool_result.get("tool_name", "unknown")
content = tool_result.get("content", "")
result_type = tool_result.get("type", "tool_result")
tool_info_str += f"- 【{tool_name}{result_type}: {content}\n"
tool_info_str += "以上是你获取到的实时信息,请在回复时参考这些信息。"
logger.info(f"获取到 {len(tool_results)} 个工具结果")
return {"tool_info_block": tool_info_str}
else:
logger.debug("未获取到任何工具结果")
return {"tool_info_block": ""}
except Exception as e:
logger.error(f"工具信息获取失败: {e}")
return {"tool_info_block": ""}
async def _build_knowledge_info(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建知识信息 - 使用共享工具类完全继承DefaultReplyer功能"""
if not params.reply_to:
logger.debug("没有回复对象,跳过获取知识库内容")
return {"knowledge_prompt": ""}
sender, content = PromptUtils.parse_reply_target(params.reply_to)
if not content:
logger.debug("回复对象内容为空,跳过获取知识库内容")
return {"knowledge_prompt": ""}
logger.debug(f"获取知识库内容,元消息:{params.chat_talking_prompt_short[:30]}...,消息长度: {len(params.chat_talking_prompt_short)}")
# 从LPMM知识库获取知识
try:
# 检查LPMM知识库是否启用
if not global_config.lpmm_knowledge.enable:
logger.debug("LPMM知识库未启用跳过获取知识库内容")
return {"knowledge_prompt": ""}
from src.plugins.built_in.knowledge.lpmm_get_knowledge import SearchKnowledgeFromLPMMTool
from src.plugin_system.apis import llm_api
from src.config.config import model_config
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
bot_name = global_config.bot.nickname
prompt = await global_prompt_manager.format_prompt(
"lpmm_get_knowledge_prompt",
bot_name=bot_name,
time_now=time_now,
chat_history=params.chat_talking_prompt_short,
sender=sender,
target_message=content,
)
_, _, _, _, tool_calls = await llm_api.generate_with_model_with_tools(
prompt,
model_config=model_config.model_task_config.tool_use,
tool_options=[SearchKnowledgeFromLPMMTool.get_tool_definition()],
)
if tool_calls:
tool_executor = ToolExecutor(chat_id=params.chat_id)
result = await tool_executor.execute_tool_call(tool_calls[0], SearchKnowledgeFromLPMMTool())
if not result or not result.get("content"):
logger.debug("从LPMM知识库获取知识失败返回空知识...")
return {"knowledge_prompt": ""}
found_knowledge_from_lpmm = result.get("content", "")
logger.debug(
f"从LPMM知识库获取知识相关信息{found_knowledge_from_lpmm[:100]}...,信息长度: {len(found_knowledge_from_lpmm)}"
)
return {"knowledge_prompt": f"你有以下这些**知识**\n{found_knowledge_from_lpmm}\n请你**记住上面的知识**,之后可能会用到。\n"}
else:
logger.debug("从LPMM知识库获取知识失败可能是从未导入过知识返回空知识...")
return {"knowledge_prompt": ""}
except Exception as e:
logger.error(f"获取知识库内容时发生异常: {str(e)}")
return {"knowledge_prompt": ""}
async def _build_cross_context(self, params: SmartPromptParameters) -> Dict[str, Any]:
"""构建跨群上下文 - 使用共享工具类"""
try:
cross_context = await PromptUtils.build_cross_context(
params.chat_id,
params.prompt_mode,
params.target_user_info
)
return {"cross_context_block": cross_context}
except Exception as e:
logger.error(f"构建跨群上下文失败: {e}")
return {"cross_context_block": ""}
def _parse_reply_target(self, target_message: str) -> Tuple[str, str]:
"""解析回复目标消息 - 使用共享工具类"""
return PromptUtils.parse_reply_target(target_message)
class SmartPrompt:
"""重构的智能提示词核心类 - 移除缓存机制和依赖检查,简化架构"""
def __init__(
self,
template_name: Optional[str] = None,
parameters: Optional[SmartPromptParameters] = None,
):
self.parameters = parameters or SmartPromptParameters()
self.template_name = template_name or self._get_default_template()
self.builder = SmartPromptBuilder()
def _get_default_template(self) -> str:
"""根据模式选择默认模板"""
if self.parameters.prompt_mode == "s4u":
return "s4u_style_prompt"
elif self.parameters.prompt_mode == "normal":
return "normal_style_prompt"
else:
return "default_expressor_prompt"
async def build_prompt(self) -> str:
"""构建最终的Prompt文本 - 移除缓存机制和依赖检查"""
# 参数验证
errors = self.parameters.validate()
if errors:
logger.error(f"参数验证失败: {', '.join(errors)}")
raise ValueError(f"参数验证失败: {', '.join(errors)}")
start_time = time.time()
try:
# 构建基础上下文的完整映射
context_data = await self.builder.build_context_data(self.parameters)
# 检查关键上下文数据
if not context_data or not isinstance(context_data, dict):
logger.error("构建的上下文数据无效")
raise ValueError("构建的上下文数据无效")
# 获取模板
template = await self._get_template()
if not template:
logger.error("无法获取模板")
raise ValueError("无法获取模板")
# 根据模式传递不同的参数
if self.parameters.prompt_mode == "s4u":
result = await self._build_s4u_prompt(template, context_data)
elif self.parameters.prompt_mode == "normal":
result = await self._build_normal_prompt(template, context_data)
else:
result = await self._build_default_prompt(template, context_data)
# 记录性能数据
total_time = time.time() - start_time
logger.debug(f"SmartPrompt构建完成模式: {self.parameters.prompt_mode}, 耗时: {total_time:.2f}s")
return result
except asyncio.TimeoutError as e:
logger.error(f"构建Prompt超时: {e}")
raise TimeoutError(f"构建Prompt超时: {e}")
except Exception as e:
logger.error(f"构建Prompt失败: {e}")
raise RuntimeError(f"构建Prompt失败: {e}")
async def _get_template(self) -> Optional[Prompt]:
"""获取模板"""
try:
return await global_prompt_manager.get_prompt_async(self.template_name)
except Exception as e:
logger.error(f"获取模板 {self.template_name} 失败: {e}")
raise RuntimeError(f"获取模板 {self.template_name} 失败: {e}")
async def _build_s4u_prompt(self, template: Prompt, context_data: Dict[str, Any]) -> str:
"""构建S4U模式的完整Prompt - 使用新参数结构"""
params = {
**context_data,
'expression_habits_block': context_data.get('expression_habits_block', ''),
'tool_info_block': context_data.get('tool_info_block', ''),
'knowledge_prompt': context_data.get('knowledge_prompt', ''),
'memory_block': context_data.get('memory_block', ''),
'relation_info_block': context_data.get('relation_info_block', ''),
'extra_info_block': self.parameters.extra_info_block or context_data.get('extra_info_block', ''),
'cross_context_block': context_data.get('cross_context_block', ''),
'identity': self.parameters.identity_block or context_data.get('identity', ''),
'action_descriptions': self.parameters.action_descriptions or context_data.get('action_descriptions', ''),
'sender_name': self.parameters.sender,
'mood_state': self.parameters.mood_prompt or context_data.get('mood_state', ''),
'background_dialogue_prompt': context_data.get('background_dialogue_prompt', ''),
'time_block': context_data.get('time_block', ''),
'core_dialogue_prompt': context_data.get('core_dialogue_prompt', ''),
'reply_target_block': context_data.get('reply_target_block', ''),
'reply_style': global_config.personality.reply_style,
'keywords_reaction_prompt': self.parameters.keywords_reaction_prompt or context_data.get('keywords_reaction_prompt', ''),
'moderation_prompt': self.parameters.moderation_prompt_block or context_data.get('moderation_prompt', ''),
}
return await global_prompt_manager.format_prompt(self.template_name, **params)
async def _build_normal_prompt(self, template: Prompt, context_data: Dict[str, Any]) -> str:
"""构建Normal模式的完整Prompt - 使用新参数结构"""
params = {
**context_data,
'expression_habits_block': context_data.get('expression_habits_block', ''),
'tool_info_block': context_data.get('tool_info_block', ''),
'knowledge_prompt': context_data.get('knowledge_prompt', ''),
'memory_block': context_data.get('memory_block', ''),
'relation_info_block': context_data.get('relation_info_block', ''),
'extra_info_block': self.parameters.extra_info_block or context_data.get('extra_info_block', ''),
'cross_context_block': context_data.get('cross_context_block', ''),
'identity': self.parameters.identity_block or context_data.get('identity', ''),
'action_descriptions': self.parameters.action_descriptions or context_data.get('action_descriptions', ''),
'schedule_block': self.parameters.schedule_block or context_data.get('schedule_block', ''),
'time_block': context_data.get('time_block', ''),
'chat_info': context_data.get('chat_info', ''),
'reply_target_block': context_data.get('reply_target_block', ''),
'config_expression_style': global_config.personality.reply_style,
'mood_state': self.parameters.mood_prompt or context_data.get('mood_state', ''),
'keywords_reaction_prompt': self.parameters.keywords_reaction_prompt or context_data.get('keywords_reaction_prompt', ''),
'moderation_prompt': self.parameters.moderation_prompt_block or context_data.get('moderation_prompt', ''),
}
return await global_prompt_manager.format_prompt(self.template_name, **params)
async def _build_default_prompt(self, template: Prompt, context_data: Dict[str, Any]) -> str:
"""构建默认模式的Prompt - 使用新参数结构"""
params = {
'expression_habits_block': context_data.get('expression_habits_block', ''),
'relation_info_block': context_data.get('relation_info_block', ''),
'chat_target': "",
'time_block': context_data.get('time_block', ''),
'chat_info': context_data.get('chat_info', ''),
'identity': self.parameters.identity_block or context_data.get('identity', ''),
'chat_target_2': "",
'reply_target_block': context_data.get('reply_target_block', ''),
'raw_reply': self.parameters.target,
'reason': "",
'mood_state': self.parameters.mood_prompt or context_data.get('mood_state', ''),
'reply_style': global_config.personality.reply_style,
'keywords_reaction_prompt': self.parameters.keywords_reaction_prompt or context_data.get('keywords_reaction_prompt', ''),
'moderation_prompt': self.parameters.moderation_prompt_block or context_data.get('moderation_prompt', ''),
}
return await global_prompt_manager.format_prompt(self.template_name, **params)
# 工厂函数 - 简化创建 - 更新参数结构
def create_smart_prompt(
chat_id: str = "",
sender_name: str = "",
target_message: str = "",
reply_to: str = "",
**kwargs
) -> SmartPrompt:
"""快速创建智能Prompt实例的工厂函数 - 使用新参数结构"""
# 使用新的参数结构
parameters = SmartPromptParameters(
chat_id=chat_id,
sender=sender_name,
target=target_message,
reply_to=reply_to,
**kwargs
)
return SmartPrompt(parameters=parameters)
class SmartPromptHealthChecker:
"""SmartPrompt健康检查器 - 移除依赖检查"""
@staticmethod
async def check_system_health() -> Dict[str, Any]:
"""检查系统健康状态 - 移除依赖检查"""
health_status = {
"status": "healthy",
"components": {},
"issues": []
}
try:
# 检查配置
try:
from src.config.config import global_config
health_status["components"]["config"] = "ok"
# 检查关键配置项
if not hasattr(global_config, 'personality') or not hasattr(global_config.personality, 'prompt_mode'):
health_status["issues"].append("缺少personality.prompt_mode配置")
health_status["status"] = "degraded"
if not hasattr(global_config, 'memory') or not hasattr(global_config.memory, 'enable_memory'):
health_status["issues"].append("缺少memory.enable_memory配置")
except Exception as e:
health_status["components"]["config"] = f"failed: {str(e)}"
health_status["issues"].append("配置加载失败")
health_status["status"] = "unhealthy"
# 检查Prompt模板
try:
required_templates = ["s4u_style_prompt", "normal_style_prompt", "default_expressor_prompt"]
for template_name in required_templates:
try:
await global_prompt_manager.get_prompt_async(template_name)
health_status["components"][f"template_{template_name}"] = "ok"
except Exception as e:
health_status["components"][f"template_{template_name}"] = f"failed: {str(e)}"
health_status["issues"].append(f"模板{template_name}加载失败")
health_status["status"] = "degraded"
except Exception as e:
health_status["components"]["prompt_templates"] = f"failed: {str(e)}"
health_status["issues"].append("Prompt模板检查失败")
health_status["status"] = "unhealthy"
return health_status
except Exception as e:
return {
"status": "unhealthy",
"components": {},
"issues": [f"健康检查异常: {str(e)}"]
}
@staticmethod
async def run_performance_test() -> Dict[str, Any]:
"""运行性能测试"""
test_results = {
"status": "completed",
"tests": {},
"summary": {}
}
try:
# 创建测试参数
test_params = SmartPromptParameters(
chat_id="test_chat",
sender="test_user",
target="test_message",
reply_to="test_user:test_message",
prompt_mode="s4u"
)
# 测试不同模式下的构建性能
modes = ["s4u", "normal", "minimal"]
for mode in modes:
test_params.prompt_mode = mode
smart_prompt = SmartPrompt(parameters=test_params)
# 运行多次测试取平均值
times = []
for _ in range(3):
start_time = time.time()
try:
await smart_prompt.build_prompt()
end_time = time.time()
times.append(end_time - start_time)
except Exception as e:
times.append(float('inf'))
logger.error(f"性能测试失败 (模式: {mode}): {e}")
# 计算统计信息
valid_times = [t for t in times if t != float('inf')]
if valid_times:
avg_time = sum(valid_times) / len(valid_times)
min_time = min(valid_times)
max_time = max(valid_times)
test_results["tests"][mode] = {
"avg_time": avg_time,
"min_time": min_time,
"max_time": max_time,
"success_rate": len(valid_times) / len(times)
}
else:
test_results["tests"][mode] = {
"avg_time": float('inf'),
"min_time": float('inf'),
"max_time": float('inf'),
"success_rate": 0
}
# 计算总体统计
all_avg_times = [test["avg_time"] for test in test_results["tests"].values() if test["avg_time"] != float('inf')]
if all_avg_times:
test_results["summary"] = {
"overall_avg_time": sum(all_avg_times) / len(all_avg_times),
"fastest_mode": min(test_results["tests"].items(), key=lambda x: x[1]["avg_time"])[0],
"slowest_mode": max(test_results["tests"].items(), key=lambda x: x[1]["avg_time"])[0]
}
return test_results
except Exception as e:
return {
"status": "failed",
"tests": {},
"summary": {},
"error": str(e)
}