mirror of
https://github.com/Nezumi-2711/astrbot_plugin_qq_group_daily_analysis.git
synced 2026-09-22 05:31:52 +00:00
feat(分析功能): 添加人格设定的配置选项,并更新相关提示词构建逻辑
feat(persona): 自动检测当前群聊激活的对话所绑定的性格
This commit is contained in:
@@ -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", "")
|
||||
|
||||
Reference in New Issue
Block a user