273 lines
11 KiB
Python
273 lines
11 KiB
Python
import json
|
||
from typing import Dict, Any
|
||
from rich.traceback import install
|
||
from src.llm_models.utils_model import LLMRequest
|
||
from src.config.config import global_config
|
||
from src.common.logger_manager import get_logger
|
||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||
from src.individuality.individuality import individuality
|
||
from src.chat.focus_chat.planners.action_manager import ActionManager
|
||
from src.chat.normal_chat.normal_prompt import prompt_builder
|
||
from src.chat.message_receive.message import MessageThinking
|
||
from json_repair import repair_json
|
||
|
||
logger = get_logger("normal_chat_planner")
|
||
|
||
install(extra_lines=3)
|
||
|
||
|
||
def init_prompt():
|
||
Prompt(
|
||
"""
|
||
你的自我认知是:
|
||
{self_info_block}
|
||
|
||
注意,除了下面动作选项之外,你在聊天中不能做其他任何事情,这是你能力的边界,现在请你选择合适的action:
|
||
|
||
{action_options_text}
|
||
|
||
重要说明:
|
||
- "no_action" 表示只进行普通聊天回复,不执行任何额外动作
|
||
- "change_to_focus_chat" 表示当聊天变得热烈、自己回复条数很多或需要深入交流时,正常回复消息并切换到focus_chat模式进行更深入的对话
|
||
- 其他action表示在普通回复的基础上,执行相应的额外动作
|
||
|
||
你必须从上面列出的可用action中选择一个,并说明原因。
|
||
你的决策必须以严格的 JSON 格式输出,且仅包含 JSON 内容,不要有任何其他文字或解释。
|
||
|
||
{moderation_prompt}
|
||
|
||
当前聊天上下文:
|
||
{chat_context}
|
||
|
||
基于以上聊天上下文和用户的最新消息,选择最合适的action。
|
||
|
||
请你以下面格式输出你选择的action:
|
||
{{
|
||
"action": "action_name",
|
||
"reasoning": "说明你做出该action的原因",
|
||
"参数1": "参数1的值",
|
||
"参数2": "参数2的值",
|
||
"参数3": "参数3的值",
|
||
...
|
||
}}
|
||
|
||
请输出你的决策 JSON:""",
|
||
"normal_chat_planner_prompt",
|
||
)
|
||
|
||
Prompt(
|
||
"""
|
||
action_name: {action_name}
|
||
描述:{action_description}
|
||
参数:
|
||
{action_parameters}
|
||
动作要求:
|
||
{action_require}""",
|
||
"normal_chat_action_prompt",
|
||
)
|
||
|
||
|
||
class NormalChatPlanner:
|
||
def __init__(self, log_prefix: str, action_manager: ActionManager):
|
||
self.log_prefix = log_prefix
|
||
# LLM规划器配置
|
||
self.planner_llm = LLMRequest(
|
||
model=global_config.model.planner,
|
||
max_tokens=1000,
|
||
request_type="normal_chat.planner", # 用于normal_chat动作规划
|
||
)
|
||
|
||
self.action_manager = action_manager
|
||
|
||
async def plan(self, message: MessageThinking, sender_name: str = "某人") -> Dict[str, Any]:
|
||
"""
|
||
Normal Chat 规划器: 使用LLM根据上下文决定做出什么动作。
|
||
|
||
参数:
|
||
message: 思考消息对象
|
||
sender_name: 发送者名称
|
||
"""
|
||
|
||
action = "no_action" # 默认动作改为no_action
|
||
reasoning = "规划器初始化默认"
|
||
action_data = {}
|
||
|
||
try:
|
||
# 设置默认值
|
||
nickname_str = ""
|
||
for nicknames in global_config.bot.alias_names:
|
||
nickname_str += f"{nicknames},"
|
||
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
|
||
|
||
personality_block = individuality.get_personality_prompt(x_person=2, level=2)
|
||
identity_block = individuality.get_identity_prompt(x_person=2, level=2)
|
||
|
||
self_info = name_block + personality_block + identity_block
|
||
|
||
# 获取当前可用的动作
|
||
current_available_actions = self.action_manager.get_using_actions()
|
||
|
||
# 如果没有可用动作或只有no_action动作,直接返回no_action
|
||
if not current_available_actions or (
|
||
len(current_available_actions) == 1 and "no_action" in current_available_actions
|
||
):
|
||
logger.debug(f"{self.log_prefix}规划器: 没有可用动作或只有no_action动作,返回no_action")
|
||
return {
|
||
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning},
|
||
"chat_context": "",
|
||
"action_prompt": "",
|
||
}
|
||
|
||
# 构建normal_chat的上下文 (使用与normal_chat相同的prompt构建方法)
|
||
chat_context = await prompt_builder.build_prompt(
|
||
message_txt=message.processed_plain_text,
|
||
sender_name=sender_name,
|
||
chat_stream=message.chat_stream,
|
||
)
|
||
|
||
# 构建planner的prompt
|
||
prompt = await self.build_planner_prompt(
|
||
self_info_block=self_info,
|
||
chat_context=chat_context,
|
||
current_available_actions=current_available_actions,
|
||
)
|
||
|
||
if not prompt:
|
||
logger.warning(f"{self.log_prefix}规划器: 构建提示词失败")
|
||
return {
|
||
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning},
|
||
"chat_context": chat_context,
|
||
"action_prompt": "",
|
||
}
|
||
|
||
# 使用LLM生成动作决策
|
||
try:
|
||
content, reasoning_content, model_name = await self.planner_llm.generate_response(prompt)
|
||
logger.debug(f"{self.log_prefix}规划器原始响应: {content}")
|
||
|
||
# 解析JSON响应
|
||
try:
|
||
# 尝试修复JSON
|
||
fixed_json = repair_json(content)
|
||
action_result = json.loads(fixed_json)
|
||
|
||
action = action_result.get("action", "no_action")
|
||
reasoning = action_result.get("reasoning", "未提供原因")
|
||
|
||
# 提取其他参数作为action_data
|
||
action_data = {k: v for k, v in action_result.items() if k not in ["action", "reasoning"]}
|
||
|
||
# 验证动作是否在可用动作列表中,或者是特殊动作
|
||
if action not in current_available_actions and action != "change_to_focus_chat":
|
||
logger.warning(f"{self.log_prefix}规划器选择了不可用的动作: {action}, 回退到no_action")
|
||
action = "no_action"
|
||
reasoning = f"选择的动作{action}不在可用列表中,回退到no_action"
|
||
action_data = {}
|
||
|
||
except json.JSONDecodeError as e:
|
||
logger.warning(f"{self.log_prefix}规划器JSON解析失败: {e}, 内容: {content}")
|
||
action = "no_action"
|
||
reasoning = "JSON解析失败,使用默认动作"
|
||
action_data = {}
|
||
|
||
except Exception as e:
|
||
logger.error(f"{self.log_prefix}规划器LLM调用失败: {e}")
|
||
action = "no_action"
|
||
reasoning = "LLM调用失败,使用默认动作"
|
||
action_data = {}
|
||
|
||
except Exception as outer_e:
|
||
logger.error(f"{self.log_prefix}规划器异常: {outer_e}")
|
||
chat_context = "无法获取聊天上下文" # 设置默认值
|
||
prompt = "" # 设置默认值
|
||
action = "no_action"
|
||
reasoning = "规划器出现异常,使用默认动作"
|
||
action_data = {}
|
||
|
||
logger.debug(f"{self.log_prefix}规划器决策动作:{action}, 动作信息: '{action_data}', 理由: {reasoning}")
|
||
|
||
# 恢复到默认动作集
|
||
self.action_manager.restore_actions()
|
||
logger.debug(
|
||
f"{self.log_prefix}规划后恢复到默认动作集, 当前可用: {list(self.action_manager.get_using_actions().keys())}"
|
||
)
|
||
|
||
action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning}
|
||
|
||
plan_result = {
|
||
"action_result": action_result,
|
||
"chat_context": chat_context,
|
||
"action_prompt": prompt,
|
||
}
|
||
|
||
return plan_result
|
||
|
||
async def build_planner_prompt(
|
||
self,
|
||
self_info_block: str,
|
||
chat_context: str,
|
||
current_available_actions: Dict[str, Any],
|
||
) -> str:
|
||
"""构建 Normal Chat Planner LLM 的提示词"""
|
||
try:
|
||
# 构建动作选项文本
|
||
action_options_text = ""
|
||
|
||
# 添加特殊的change_to_focus_chat动作
|
||
action_options_text += "action_name: change_to_focus_chat\n"
|
||
action_options_text += (
|
||
" 描述:当聊天变得热烈、自己回复条数很多或需要深入交流时使用,正常回复消息并切换到focus_chat模式\n"
|
||
)
|
||
action_options_text += " 参数:\n"
|
||
action_options_text += " 动作要求:\n"
|
||
action_options_text += " - 聊天上下文中自己的回复条数较多(超过3-4条)\n"
|
||
action_options_text += " - 对话进行得非常热烈活跃\n"
|
||
action_options_text += " - 用户表现出深入交流的意图\n"
|
||
action_options_text += " - 话题需要更专注和深入的讨论\n\n"
|
||
|
||
for action_name, action_info in current_available_actions.items():
|
||
action_description = action_info.get("description", "")
|
||
action_parameters = action_info.get("parameters", {})
|
||
action_require = action_info.get("require", [])
|
||
|
||
# 格式化参数
|
||
parameters_text = ""
|
||
for param_name, param_desc in action_parameters.items():
|
||
parameters_text += f" - {param_name}: {param_desc}\n"
|
||
|
||
# 格式化要求
|
||
require_text = ""
|
||
for req in action_require:
|
||
require_text += f" - {req}\n"
|
||
|
||
# 构建单个动作的提示
|
||
action_prompt = await global_prompt_manager.format_prompt(
|
||
"normal_chat_action_prompt",
|
||
action_name=action_name,
|
||
action_description=action_description,
|
||
action_parameters=parameters_text,
|
||
action_require=require_text,
|
||
)
|
||
action_options_text += action_prompt + "\n\n"
|
||
|
||
# 审核提示
|
||
moderation_prompt = "请确保你的回复符合平台规则,避免不当内容。"
|
||
|
||
# 使用模板构建最终提示词
|
||
prompt = await global_prompt_manager.format_prompt(
|
||
"normal_chat_planner_prompt",
|
||
self_info_block=self_info_block,
|
||
action_options_text=action_options_text,
|
||
moderation_prompt=moderation_prompt,
|
||
chat_context=chat_context,
|
||
)
|
||
|
||
return prompt
|
||
|
||
except Exception as e:
|
||
logger.error(f"{self.log_prefix}构建Planner提示词失败: {e}")
|
||
return ""
|
||
|
||
|
||
init_prompt()
|