refactor ai_auto_response plugin architecture

This commit is contained in:
liuwei
2026-04-09 17:46:30 +08:00
parent cc65378544
commit f580c69736
39 changed files with 4347 additions and 1979 deletions
@@ -0,0 +1,6 @@
from __future__ import annotations
from .group_profile import GroupProfileResolver
from .persona_engine import PersonaEngine
__all__ = ["GroupProfileResolver", "PersonaEngine"]
@@ -0,0 +1,66 @@
from __future__ import annotations
from typing import Dict
class GroupProfileResolver:
def __init__(self, config: Dict):
self.config = config or {}
self.default_profile = self.config.get("default", {}) or {}
self.profiles = self.config.get("profiles", []) or []
def resolve(self, room_id: str, group_name: str = "", group_memory_profile: Dict | None = None) -> Dict:
group_name_lower = str(group_name or "").lower()
for profile in self.profiles:
room_ids = set(profile.get("room_ids", []) or [])
keywords = [str(item).lower() for item in (profile.get("group_name_keywords", []) or [])]
if room_id and room_id in room_ids:
return self._normalize(profile, room_id, group_name, group_memory_profile or {})
if group_name_lower and any(keyword and keyword in group_name_lower for keyword in keywords):
return self._normalize(profile, room_id, group_name, group_memory_profile or {})
return self._normalize(self.default_profile, room_id, group_name, group_memory_profile or {})
@staticmethod
def _normalize(profile: Dict, room_id: str, group_name: str, group_memory_profile: Dict) -> Dict:
focus = list(profile.get("knowledge_focus", []))
configured_domain = str(profile.get("knowledge_domain", "general") or "general")
inferred_domain = str(group_memory_profile.get("inferred_domain", "general") or "general")
inferred_style = group_memory_profile.get("style_profile", {}) or {}
effective_domain = configured_domain
if configured_domain in {"", "general", "casual"} and inferred_domain not in {"", "general"}:
effective_domain = inferred_domain
inferred_focus = list(group_memory_profile.get("focus_topics", []))
merged_focus = []
for item in focus + inferred_focus:
if item and item not in merged_focus:
merged_focus.append(item)
interaction_tone = str(profile.get("interaction_tone", "自然群友感") or "自然群友感")
humor_style = str(profile.get("humor_style", "轻微") or "轻微")
sharpness_style = str(profile.get("sharpness_style", "轻微嘴硬,不刻薄") or "轻微嘴硬,不刻薄")
expressiveness_style = str(profile.get("expressiveness_style", "克制") or "克制")
address_style = str(profile.get("address_style", "低频称呼,默认直接接话") or "低频称呼,默认直接接话")
if configured_domain in {"", "general", "casual"}:
interaction_tone = inferred_style.get("interaction_tone", interaction_tone)
humor_style = inferred_style.get("humor_style", humor_style)
sharpness_style = inferred_style.get("sharpness_style", sharpness_style)
expressiveness_style = inferred_style.get("expressiveness_style", expressiveness_style)
return {
"room_id": room_id,
"group_name": group_name,
"mode": profile.get("mode", "social"),
"persona_overlay": profile.get("persona_overlay", ""),
"interaction_tone": interaction_tone,
"humor_style": humor_style,
"sharpness_style": sharpness_style,
"expressiveness_style": expressiveness_style,
"address_style": address_style,
"knowledge_domain": effective_domain,
"configured_domain": configured_domain,
"knowledge_focus": merged_focus,
"reply_style": profile.get("reply_style", "自然短句"),
"topic_boundaries": profile.get("topic_boundaries", []),
"group_memory_domain": inferred_domain,
"group_memory_summary": group_memory_profile.get("summary_text", ""),
"group_memory_sample_count": group_memory_profile.get("message_sample_count", 0),
"group_memory_style": inferred_style,
}
@@ -0,0 +1,46 @@
from __future__ import annotations
from pathlib import Path
from typing import Dict
class PersonaEngine:
def __init__(self, plugin_path: str, config: Dict):
self.plugin_path = Path(plugin_path)
self.config = config or {}
self.persona_text = self._load_persona()
def build_system_prompt(self, group_profile: Dict | None = None) -> str:
name = self.config.get("name", "小牛")
style = self.config.get("style", "")
familiarity = self.config.get("familiarity_hint", "")
max_sentences = self.config.get("max_reply_sentences", 3)
group_profile = group_profile or {}
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"{self.persona_text}\n\n"
f"补充约束:\n"
f"- 你当前对外名称固定为{name}\n"
f"- 整体风格:{style}\n"
f"- 熟悉感边界:{familiarity}\n"
f"- 一般最多输出{max_sentences}\n"
f"- 优先根据场景决定是答疑、接话还是不说话\n"
f"- 当前群的互动调性:{interaction_tone}\n"
f"- 当前群允许的幽默感:{humor}\n"
f"- 当前群允许的嘴硬/毒舌程度:{sharpness}\n"
f"- 当前群表达松弛度:{expressiveness}\n"
f"- 当前群称呼强度:{address_style}\n"
f"- 当前群人格附加要求:{persona_overlay or ''}\n"
)
def _load_persona(self) -> str:
persona_file = self.config.get("persona_file", "persona/xiaoniu.txt")
persona_path = self.plugin_path / persona_file
if persona_path.exists():
return persona_path.read_text(encoding="utf-8").strip()
return "你叫小牛,是一个自然、靠谱、会看场合的群聊成员。"