feat(分析功能): 添加人格设定的配置选项,并更新相关提示词构建逻辑

feat(persona): 自动检测当前群聊激活的对话所绑定的性格
This commit is contained in:
SXP-Simon
2026-03-16 23:46:03 +08:00
parent 35203b7ca2
commit 4cae39d153
5 changed files with 141 additions and 13 deletions
+6
View File
@@ -220,6 +220,12 @@
"description": "最大金句数量",
"default": 5,
"hint": "分析报告中显示的最大金句数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。"
},
"keep_original_persona": {
"description": "是否保持原始人格设定参与回复",
"type": "bool",
"default": false,
"hint": "开启后,若当前会话存在人格设定,将尝试读取其 Prompt 融入系统提示词。尽量保持分析报告的说话倾向与会话人设一致。"
}
}
},
@@ -19,7 +19,7 @@ from ..utils.llm_utils import (
class BaseAnalyzer(ABC):
"""
基础分析器抽象类
定义所有分析器的通用接口流程
定义所有分析器的通用接口 and 流程
"""
def __init__(self, context, config_manager):
@@ -172,10 +172,6 @@ class BaseAnalyzer(ABC):
# 保存调试数据
debug_mode = self.config_manager.get_debug_mode()
logger.info(
f"[Debug] debug_mode={debug_mode}, session_id={session_id}, prompt_len={len(prompt) if prompt else 0}"
) # Added log
if debug_mode and session_id and prompt:
self._save_debug_data(prompt, session_id)
elif debug_mode and not session_id:
@@ -193,14 +189,37 @@ class BaseAnalyzer(ABC):
temperature = self.get_temperature()
provider_id_key = self.get_provider_id_key()
# 获取人格设定
system_prompt = await self._build_system_prompt(umo)
# 如果开启了人格设定且成功获取到 Prompt,我们将其注入到主提示词中,以确保最佳效果
if system_prompt:
logger.info(f"[{self.get_data_type()}分析] 已启用人格设定")
# 在主提示词前添加人格说明,并要求 LLM 保持风格
prompt = (
f"你可以扮演以下人格:\n{system_prompt}\n\n"
f"请在接下来的分析工作中,保持上述人格的角色定位和说话风格。\n"
"--- 任务开始 ---\n"
f"{prompt}"
)
logger.info(f"[{self.get_data_type()}分析] 开始发起 LLM 请求, umo: {umo}")
# [Debug] 记录调试信息
if debug_mode:
logger.debug(
f"[Debug] debug_mode={debug_mode}, umo={umo}, session_id={session_id}, prompt_len={len(prompt) if prompt else 0}"
)
response = await call_provider_with_retry(
self.context,
self.config_manager,
prompt,
max_tokens,
temperature,
umo,
provider_id_key,
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
umo=umo,
provider_id_key=provider_id_key,
system_prompt=system_prompt,
)
if response is None:
@@ -274,3 +293,88 @@ class BaseAnalyzer(ABC):
温度参数
"""
return 0.6
async def _build_system_prompt(self, umo: str | None) -> str | None:
"""
构建带有会话人格的系统提示词
"""
keep = self.config_manager.get_keep_original_persona()
if not keep or not umo:
return None
# 获取人格管理器
persona_mgr = getattr(self.context, "persona_manager", None)
if persona_mgr is None:
return None
persona_prompt = None
try:
# 1. 尝试从 SharedPreferences 获取当前会话选中的人格 ID (类似 /persona 设置的)
from astrbot.api import sp
# resolve_selected_persona 的简化逻辑
session_service_config = await sp.get_async(
scope="umo", scope_id=str(umo), key="session_service_config", default={}
)
persona_id = (
session_service_config.get("persona_id")
if session_service_config
else None
)
if persona_id and persona_id != "[%None]":
# 获取指定人格
persona_obj = await persona_mgr.get_persona(persona_id)
persona_prompt = (
persona_obj.system_prompt
if hasattr(persona_obj, "system_prompt")
else None
)
if persona_prompt:
logger.debug(f"找到会话选定的人格: {persona_id}")
# 2. 如果没有选定人格,尝试获取当前对话的人格 ID (Dialogue Persona)
if not persona_prompt:
conv_mgr = getattr(self.context, "conversation_manager", None)
if conv_mgr:
curr_conv_id = await conv_mgr.get_curr_conversation_id(umo)
if curr_conv_id:
conv_obj = await conv_mgr.get_conversation(umo, curr_conv_id)
if (
conv_obj
and conv_obj.persona_id
and conv_obj.persona_id != "[%None]"
):
persona_obj = await persona_mgr.get_persona(
conv_obj.persona_id
)
persona_prompt = (
persona_obj.system_prompt
if hasattr(persona_obj, "system_prompt")
else None
)
if persona_prompt:
logger.debug(
f"找到对话设定的人格: {conv_obj.persona_id} (conv_id: {curr_conv_id})"
)
# 3. 如果还是没有,回退到 UMO 默认人格
if not persona_prompt:
personality = await persona_mgr.get_default_persona_v3(umo)
if isinstance(personality, dict):
persona_prompt = personality.get("prompt")
else:
persona_prompt = getattr(personality, "prompt", None)
if persona_prompt:
logger.debug("使用 UMO 默认人格设定")
except Exception as e:
logger.warning(f"获取人格设定失败 (umo: {umo}): {e}")
return None
if not isinstance(persona_prompt, str) or not persona_prompt.strip():
return None
# 构建系统提示词,要求 LLM 保持人格设定
system_prompt = persona_prompt.strip()
return system_prompt
@@ -188,6 +188,7 @@ async def call_provider_with_retry(
temperature: float,
umo: str | None = None,
provider_id_key: str | None = None,
system_prompt: str | None = None,
) -> Any | None:
"""
调用LLM提供者,带超时、重试与退避。支持自定义服务商和配置化 Provider 选择。
@@ -200,6 +201,7 @@ async def call_provider_with_retry(
temperature: 采样温度
umo: 指定使用的模型唯一标识符
provider_id_key: 配置中的 provider_id 键名(如 'topic_provider_id'),用于选择特定的 Provider
system_prompt: 系统提示词
Returns:
LLM生成的结果,失败时返回None
@@ -231,6 +233,11 @@ async def call_provider_with_retry(
f"[LLM 调用] Prompt 前100字符: {prompt[:100] if prompt else 'None'}..."
)
if system_prompt:
logger.debug(
f"[LLM 调用] System Prompt 前100字符: {system_prompt[:100]}..."
)
# 检查 prompt 是否为空
if not prompt or not prompt.strip():
logger.error(
@@ -253,6 +260,7 @@ async def call_provider_with_retry(
prompt=prompt,
max_tokens=max_tokens,
temperature=temperature,
system_prompt=system_prompt,
)
# 成功记录
@@ -220,6 +220,10 @@ class ConfigManager:
"""获取金句分析专用 Provider ID"""
return self._get_group("llm").get("golden_quote_provider_id", "")
def get_keep_original_persona(self) -> bool:
"""获取是否保持原始人格设定"""
return self._get_group("analysis_features").get("keep_original_persona", False)
def get_pdf_output_dir(self) -> str:
"""获取PDF输出目录"""
try:
@@ -1237,7 +1237,9 @@ class OneBotAdapter(PlatformAdapter):
f"[群分析相册] Base64 接口 2 (upload_group_album) 失败: {e3}"
)
await self.bot.call_action("upload_qun_album", **params)
logger.info("[群分析相册] Base64 模式 (upload_qun_album) 上传成功")
logger.info(
"[群分析相册] Base64 模式 (upload_qun_album) 上传成功"
)
return True
except Exception as e:
@@ -1294,7 +1296,9 @@ class OneBotAdapter(PlatformAdapter):
for action in actions:
try:
logger.debug(f"[群分析相册] 正在通过 {action} 获取列表 (群: {group_id})...")
logger.debug(
f"[群分析相册] 正在通过 {action} 获取列表 (群: {group_id})..."
)
result = await self.bot.call_action(
action,
group_id=int(group_id), # 确保传整型,对齐 NapCat 等实现
@@ -1329,7 +1333,9 @@ class OneBotAdapter(PlatformAdapter):
if not album_name:
return None
logger.debug(f"[群分析相册] 正在群 {group_id} 中查找名为 '{album_name}' 的相册...")
logger.debug(
f"[群分析相册] 正在群 {group_id} 中查找名为 '{album_name}' 的相册..."
)
albums = await self.get_group_album_list(group_id)
for album in albums:
name = album.get("name") or album.get("album_name", "")