feat(persona): 人格设定功能增强,支持插件指定和会话继承的人格分析

This commit is contained in:
SXP-Simon
2026-04-04 19:40:30 +08:00
parent 5375659769
commit 92a365475c
4 changed files with 131 additions and 63 deletions
+13
View File
@@ -306,6 +306,19 @@ _✨ 一个基于 AstrBot 的智能群聊分析插件,支持 **QQ (OneBot)**
- 机器人根据文件名和 `html_base_url` 拼接成完整链接发送到群里。 - 机器人根据文件名和 `html_base_url` 拼接成完整链接发送到群里。
- **注意**:本插件**不提供** Web 服务器功能,你需要自行使用 Nginx 或 AstrBot 所在的服务器环境来实现静态文件的公网访问。 - **注意**:本插件**不提供** Web 服务器功能,你需要自行使用 Nginx 或 AstrBot 所在的服务器环境来实现静态文件的公网访问。
## 人格设定 (Persona)
插件支持深度的“人格化”分析,让 AI 能够以特定的人设风格(口吻、偏好、语气)来产出摘要和锐评。
### 人格识别优先级
插件在构建分析任务时,会按以下顺序确定最终使用的“人设状态”:
1. **强制插件人格 (优先级最高)**:在配置中开启 `强制使用插件指定人格` 并选择 ID。此时 **全平台、所有群聊** 都会统一使用这一种人设,忽略群聊本身的设置。
2. **继承会话人设 (优先级次之)**:在配置中开启 `继承会话人设风格`。插件会尝试识别当前群聊在 AstrBot 中设置的人格(如通过 `/persona` 指令设置的人设)。如果该群已有人设,分析报告将尽量模拟其说话倾向。
3. **系统默认人设**:若上述开关均关闭,或未识别到有效人设,则回退到当前默认设定。
## 常见问题 (FAQ) ## 常见问题 (FAQ)
### 获取不到带记录的引用消息 ### 获取不到带记录的引用消息
+15 -2
View File
@@ -238,10 +238,23 @@
"hint": "分析报告中显示的最大金句数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。" "hint": "分析报告中显示的最大金句数量,依赖于 LLM 输出的格式化信息质量,模型越好结果越好,可能出现数量不匹配。"
}, },
"keep_original_persona": { "keep_original_persona": {
"description": "是否保持原始人格设定参与回复", "description": "继承会话人设风格",
"type": "bool", "type": "bool",
"default": false, "default": false,
"hint": "开启后,若当前会话存在人格设定,将尝试读取其 Prompt 融入系统提示词。尽量保持分析报告的说话倾向与会话人设一致。" "hint": "【优先级2】开启后将自动尝试识别当前群聊会话(或对话)的人格设定。若该群已设置人设,则分析报告会尽量模拟该人设的口吻进行回复。注意:此开关在“强制使用插件指定人格”关闭时才会生效。"
},
"use_plugin_specific_persona": {
"description": "强制使用插件指定人格",
"type": "bool",
"default": false,
"hint": "【优先级1:核心开关】若开启,将完全忽略当前群聊的人格设定,统一强制使用下方选择的“插件指定人格 ID”进行分析。适用于希望全群分析保持某种统一固定风格(如特定的“记录员”人设)的情况。"
},
"plugin_specific_persona_id": {
"description": "插件指定人格 ID",
"type": "string",
"_special": "select_persona",
"default": "",
"hint": "当“强制使用插件指定人格”开启时,在此处选择系统中已定义的人设。若此处未选择,则仍会尝试识别全局默认人格。"
} }
} }
}, },
@@ -474,85 +474,115 @@ class BaseAnalyzer(ABC, Generic[TDataObject]):
async def _build_system_prompt(self, umo: str | None) -> str | None: async def _build_system_prompt(self, umo: str | None) -> str | None:
""" """
构建带有会话人格的系统提示词 构建带有会话人格的系统提示词,优先级如下:
""" 1. 插件指定的全局人格 (若核心开关开启)
keep = self.config_manager.get_keep_original_persona() 2. 会话/对话选定的人格 (若开启了继承开关)
if not keep or not umo: 3. 当前 UMO 的默认人格 (若开启了继承开关)
return None
# 获取人格管理器 Args:
umo: 用户模型对象标识,用于定位会话上下文
Returns:
最终生成的 System Prompt 字符串,若无则返回 None
"""
# 获取配置
use_specific = self.config_manager.get_use_plugin_specific_persona()
specific_id = self.config_manager.get_plugin_specific_persona_id()
keep_original = self.config_manager.get_keep_original_persona()
# 获取 AstrBot 核心的人格管理器
persona_mgr = getattr(self.context, "persona_manager", None) persona_mgr = getattr(self.context, "persona_manager", None)
if persona_mgr is None: if persona_mgr is None:
return None return None
persona_prompt = None persona_prompt = None
try:
# 1. 尝试从 SharedPreferences 获取当前会话选中的人格 ID (类似 /persona 设置的)
from astrbot.api import sp
# resolve_selected_persona 的简化逻辑 # --- 优先级 1: 插件指定的全局固定人格 ---
session_service_config = await sp.get_async( # 适用于希望所有分析报告都呈现同一种风格的情况
scope="umo", scope_id=str(umo), key="session_service_config", default={} if use_specific and specific_id:
) try:
persona_id = ( persona_obj = await persona_mgr.get_persona(specific_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_prompt = (
persona_obj.system_prompt persona_obj.system_prompt
if hasattr(persona_obj, "system_prompt") if hasattr(persona_obj, "system_prompt")
else None else None
) )
if persona_prompt: if persona_prompt:
logger.debug(f"找到会话选定的人格: {persona_id}") logger.debug(f"已应用插件指定的全局强制人格设定: {specific_id}")
except Exception as e:
logger.warning(f"获取插件指定人格失败 (ID: {specific_id}): {e}")
# 2. 如果没有选定人格,尝试获取当前对话的人格 ID (Dialogue Persona) # --- 优先级 2: 继承当前会话/群聊的原始人格 ---
if not persona_prompt: # 只有在未开启“强制人格”且开启了“继承设定”时生效
conv_mgr = getattr(self.context, "conversation_manager", None) if not persona_prompt and keep_original and umo:
if conv_mgr: try:
curr_conv_id = await conv_mgr.get_curr_conversation_id(umo) # 2.1 尝试获取 SharedPreferences 中会话绑定的 Persona ID (通常是 /persona 命令设置的)
if curr_conv_id: from astrbot.api import sp
conv_obj = await conv_mgr.get_conversation(umo, curr_conv_id)
if ( session_service_config = await sp.get_async(
conv_obj scope="umo",
and conv_obj.persona_id scope_id=str(umo),
and conv_obj.persona_id != "[%None]" key="session_service_config",
): default={},
persona_obj = await persona_mgr.get_persona( )
conv_obj.persona_id 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.2 若无会话绑定,尝试获取当前对话(Dialogue)级别的人格
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
) )
persona_prompt = ( if (
persona_obj.system_prompt conv_obj
if hasattr(persona_obj, "system_prompt") and conv_obj.persona_id
else None and conv_obj.persona_id != "[%None]"
) ):
if persona_prompt: persona_obj = await persona_mgr.get_persona(
logger.debug( conv_obj.persona_id
f"找到对话设定的人格: {conv_obj.persona_id} (conv_id: {curr_conv_id})"
) )
persona_prompt = (
persona_obj.system_prompt
if hasattr(persona_obj, "system_prompt")
else None
)
if persona_prompt:
logger.debug(
f"继承到对话(Dialogue)设定人格: {conv_obj.persona_id}"
)
# 3. 如果还是没有,回退到 UMO 默认人格 # 2.3 若仍无结果,尝试获取 UMO 设定的默认人格
if not persona_prompt: if not persona_prompt:
personality = await persona_mgr.get_default_persona_v3(umo) personality = await persona_mgr.get_default_persona_v3(umo)
if isinstance(personality, dict): if isinstance(personality, dict):
persona_prompt = personality.get("prompt") persona_prompt = personality.get("prompt")
else: else:
persona_prompt = getattr(personality, "prompt", None) persona_prompt = getattr(personality, "prompt", None)
if persona_prompt: if persona_prompt:
logger.debug("使用 UMO 默认人格设定") logger.debug("继承到 UMO 默认人格设定")
except Exception as e: except Exception as e:
logger.warning(f"获取人格设定失败 (umo: {umo}): {e}") logger.warning(f"分析人格回溯识别失败 (umo: {umo}): {e}")
return None
# 检查生成结果
if not isinstance(persona_prompt, str) or not persona_prompt.strip(): if not isinstance(persona_prompt, str) or not persona_prompt.strip():
return None return None
# 构建系统提示词,要求 LLM 保持人格设定 return persona_prompt.strip()
system_prompt = persona_prompt.strip()
return system_prompt
+13 -1
View File
@@ -225,9 +225,21 @@ class ConfigManager:
return self._get_group("llm").get("golden_quote_provider_id", "") return self._get_group("llm").get("golden_quote_provider_id", "")
def get_keep_original_persona(self) -> bool: def get_keep_original_persona(self) -> bool:
"""获取是否保持原始人格设定""" """获取是否继承会话原始人格设定"""
return self._get_group("analysis_features").get("keep_original_persona", False) return self._get_group("analysis_features").get("keep_original_persona", False)
def get_use_plugin_specific_persona(self) -> bool:
"""获取是否强制使用插件指定的人格设定"""
return self._get_group("analysis_features").get(
"use_plugin_specific_persona", False
)
def get_plugin_specific_persona_id(self) -> str:
"""获取插件指定的全局人格 ID (通过 select_persona 接口选择)"""
return self._get_group("analysis_features").get(
"plugin_specific_persona_id", ""
)
def get_pdf_output_dir(self) -> str: def get_pdf_output_dir(self) -> str:
"""获取PDF输出目录""" """获取PDF输出目录"""
from pathlib import Path from pathlib import Path