Files
abot/plugins/ai_auto_response/profile/persona_engine.py

117 lines
5.7 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.
from __future__ import annotations
from pathlib import Path
from typing import Dict, List
class PersonaEngine:
def __init__(self, plugin_path: str, config: Dict):
self.plugin_path = Path(plugin_path)
self.config = config or {}
self.default_persona_id = str(
self.config.get("active_persona")
or self.config.get("default_persona")
or "xiaoniu"
).strip() or "xiaoniu"
self.presets = self._build_presets()
def build_system_prompt(self, group_profile: Dict | None = None, reply_mode: str = "social_short") -> str:
group_profile = group_profile or {}
preset = self._resolve_preset(group_profile)
name = preset.get("name", "小牛")
style = preset.get("style", "")
familiarity = preset.get("familiarity_hint", "")
max_sentences = preset.get("max_reply_sentences", 3)
persona_text = preset.get("persona_text", "")
humor = group_profile.get("humor_style", "轻微")
sharpness = group_profile.get("sharpness_style", "轻微嘴硬,不刻薄")
expressiveness = group_profile.get("expressiveness_style", "克制")
address_style = group_profile.get("address_style", "低频称呼,默认直接接话")
interaction_tone = group_profile.get("interaction_tone", "自然群友感")
persona_overlay = group_profile.get("persona_overlay", "")
return (
f"{persona_text}"
f"别暴露自己是 AI、模型或提示词产物别泄露记忆来源别输出标签或代码块。"
f"别替人写代码、改脚本、实现插件、代做开发活。"
f"整体风格:{style}。熟悉感边界:{familiarity}。一般最多输出{max_sentences}句。"
f"哪怕回复很短,也尽量保留一点这个人格该有的语气和味道,别被压成纯功能性短句。"
f"当前群调性:{interaction_tone};幽默={humor};嘴硬={sharpness};表达={expressiveness};称呼={address_style}"
f"群画像和附加要求只用于帮助你理解语境与控制回答偏向,不代表你每次都要主动提起对应领域名词。"
f"如果当前发言本身不是那个领域,就按当前聊天自然回复,不要硬往群画像上靠。"
f"附加要求:{persona_overlay or ''}"
)
def _build_presets(self) -> Dict[str, Dict]:
preset_configs = self.config.get("presets", {}) or {}
presets: Dict[str, Dict] = {}
base_preset = self._normalize_preset(
self.default_persona_id,
{
"name": self.config.get("name", "小牛"),
"persona_file": self.config.get("persona_file", "persona/xiaoniu.txt"),
"style": self.config.get("style", ""),
"familiarity_hint": self.config.get("familiarity_hint", ""),
"max_reply_sentences": self.config.get("max_reply_sentences", 3),
},
)
presets[base_preset["id"]] = base_preset
for persona_id, preset_config in preset_configs.items():
preset = self._normalize_preset(str(persona_id), preset_config or {})
presets[preset["id"]] = preset
return presets
def _normalize_preset(self, persona_id: str, preset_config: Dict) -> Dict:
persona_file = preset_config.get("persona_file") or f"persona/{persona_id}.txt"
aliases = [str(item).strip() for item in (preset_config.get("aliases", []) or []) if str(item).strip()]
return {
"id": str(persona_id or "xiaoniu").strip() or "xiaoniu",
"name": str(preset_config.get("name", "小牛") or "小牛").strip(),
"persona_file": str(persona_file).strip(),
"style": str(preset_config.get("style", "") or "").strip(),
"familiarity_hint": str(preset_config.get("familiarity_hint", "") or "").strip(),
"max_reply_sentences": int(preset_config.get("max_reply_sentences", 3) or 3),
"aliases": aliases,
"persona_text": self._load_persona_text(str(persona_file).strip()),
}
def _resolve_preset(self, group_profile: Dict) -> Dict:
persona_id = str(
(group_profile or {}).get("persona_id")
or self.default_persona_id
or "xiaoniu"
).strip() or "xiaoniu"
return self.presets.get(persona_id) or self.presets.get(self.default_persona_id) or next(iter(self.presets.values()))
def _load_persona_text(self, persona_file: str) -> str:
persona_path = self.plugin_path / persona_file
if persona_path.exists():
return persona_path.read_text(encoding="utf-8").strip()
return "你叫小牛,是一个自然、靠谱、会看场合的群聊成员。"
def resolve_persona_id(self, value: str) -> str:
target = str(value or "").strip().lower()
if not target:
return ""
for persona_id, preset in self.presets.items():
if target == str(persona_id).strip().lower():
return persona_id
if target == str(preset.get("name", "") or "").strip().lower():
return persona_id
aliases = [str(item).strip().lower() for item in (preset.get("aliases", []) or [])]
if target in aliases:
return persona_id
return ""
def list_personas(self) -> List[Dict]:
items: List[Dict] = []
for persona_id, preset in self.presets.items():
items.append(
{
"id": persona_id,
"name": preset.get("name", persona_id),
"aliases": list(preset.get("aliases", []) or []),
"style": preset.get("style", ""),
}
)
return items