feat:优化屎山

This commit is contained in:
2025-12-12 18:35:39 +08:00
parent 6156064f56
commit c1983172af
10 changed files with 1257 additions and 582 deletions

View File

@@ -115,6 +115,19 @@ class AutoReply(PluginBase):
logger.error(f"[AutoReply] 初始化失败: {e}")
self.config = None
async def on_disable(self):
"""插件禁用时调用,清理后台判断任务"""
await super().on_disable()
if self.pending_tasks:
for task in self.pending_tasks.values():
task.cancel()
await asyncio.gather(*self.pending_tasks.values(), return_exceptions=True)
self.pending_tasks.clear()
self.judging.clear()
logger.info("[AutoReply] 已清理后台判断任务")
def _load_bot_info(self):
"""加载机器人信息"""
try:
@@ -294,6 +307,8 @@ class AutoReply(PluginBase):
# 直接调用 AIChat 生成回复(基于最新上下文)
await self._trigger_ai_reply(bot, pending.from_wxid)
except asyncio.CancelledError:
raise
except Exception as e:
logger.error(f"[AutoReply] 后台判断异常: {e}")
import traceback
@@ -316,8 +331,7 @@ class AutoReply(PluginBase):
return
# 获取最新的历史记录作为上下文
chat_id = self._normalize_chat_id(from_wxid)
recent_context = await self._get_recent_context_for_reply(chat_id)
recent_context = await self._get_recent_context_for_reply(from_wxid)
if not recent_context:
logger.warning("[AutoReply] 无法获取上下文")
@@ -342,10 +356,10 @@ class AutoReply(PluginBase):
import traceback
logger.error(traceback.format_exc())
async def _get_recent_context_for_reply(self, chat_id: str) -> str:
async def _get_recent_context_for_reply(self, group_id: str) -> str:
"""获取最近的上下文用于生成回复"""
try:
history = await self._get_history(chat_id)
history = await self._get_history(group_id)
if not history:
return ""
@@ -353,35 +367,10 @@ class AutoReply(PluginBase):
count = self.config.get('context', {}).get('messages_count', 5)
recent = history[-count:] if len(history) > count else history
# 构建上下文摘要
context_lines = []
for record in recent:
nickname = record.get('nickname', '未知')
content = record.get('content', '')
if isinstance(content, list):
# 多模态内容,提取文本
for item in content:
if item.get('type') == 'text':
content = item.get('text', '')
break
else:
content = '[图片]'
if len(content) > 50:
content = content[:50] + "..."
context_lines.append(f"{nickname}: {content}")
# 返回最后一条消息作为触发内容AIChat 会读取完整历史)
if recent:
last = recent[-1]
last_content = last.get('content', '')
if isinstance(last_content, list):
for item in last_content:
if item.get('type') == 'text':
return item.get('text', '')
return '[图片]'
return last_content
return ""
# 自动回复触发不再把最后一条用户消息再次发给 AI
# 避免在上下文里出现“同一句话重复两遍”的错觉。
# AIChat 会读取完整历史 recent_history。
return "(自动回复触发)请基于最近群聊内容,自然地回复一句,不要复述提示本身。"
except Exception as e:
logger.error(f"[AutoReply] 获取上下文失败: {e}")
@@ -389,12 +378,13 @@ class AutoReply(PluginBase):
async def _judge_with_small_model(self, from_wxid: str, content: str) -> JudgeResult:
"""使用小模型判断是否需要回复"""
chat_id = self._normalize_chat_id(from_wxid)
chat_state = self._get_chat_state(chat_id)
group_id = from_wxid
state_id = self._normalize_chat_id(group_id)
chat_state = self._get_chat_state(state_id)
# 获取最近消息历史
recent_messages = await self._get_recent_messages(chat_id)
last_bot_reply = await self._get_last_bot_reply(chat_id)
recent_messages = await self._get_recent_messages(group_id)
last_bot_reply = await self._get_last_bot_reply(group_id)
# 构建判断提示词
reasoning_part = ',\n "reasoning": "简短分析原因(20字内)"' if self.config["judge"]["include_reasoning"] else ""
@@ -403,7 +393,7 @@ class AutoReply(PluginBase):
## 当前状态
- 精力: {chat_state.energy:.1f}/1.0
- 上次发言: {self._get_minutes_since_last_reply(chat_id)}分钟前
- 上次发言: {self._get_minutes_since_last_reply(state_id)}分钟前
## 最近对话
{recent_messages}
@@ -531,6 +521,13 @@ class AutoReply(PluginBase):
if not aichat_plugin:
return []
# 优先使用 AIChat 的统一 ContextStore
if hasattr(aichat_plugin, "store") and aichat_plugin.store:
try:
return await aichat_plugin.store.load_group_history(chat_id)
except Exception as e:
logger.debug(f"[AutoReply] ContextStore 获取历史失败: {e}")
# 优先使用 Redis与 AIChat 保持一致)
try:
from utils.redis_cache import get_cache
@@ -548,7 +545,8 @@ class AutoReply(PluginBase):
# 降级到文件存储
if hasattr(aichat_plugin, 'history_dir') and aichat_plugin.history_dir:
history_file = aichat_plugin.history_dir / f"{chat_id}.json"
safe_id = (chat_id or "").replace("@", "_").replace(":", "_")
history_file = aichat_plugin.history_dir / f"{safe_id}.json"
if history_file.exists():
with open(history_file, "r", encoding="utf-8") as f:
return json.load(f)
@@ -558,10 +556,10 @@ class AutoReply(PluginBase):
return []
async def _get_recent_messages(self, chat_id: str) -> str:
"""获取最近消息历史"""
async def _get_recent_messages(self, group_id: str) -> str:
"""获取最近消息历史(群聊)"""
try:
history = await self._get_history(chat_id)
history = await self._get_history(group_id)
if not history:
return "暂无对话历史"
@@ -572,6 +570,12 @@ class AutoReply(PluginBase):
for record in recent:
nickname = record.get('nickname', '未知')
content = record.get('content', '')
if isinstance(content, list):
text_parts = []
for item in content:
if item.get("type") == "text":
text_parts.append(item.get("text", ""))
content = "".join(text_parts).strip() or "[图片]"
# 限制单条消息长度
if len(content) > 100:
content = content[:100] + "..."
@@ -584,17 +588,23 @@ class AutoReply(PluginBase):
return "暂无对话历史"
async def _get_last_bot_reply(self, chat_id: str) -> Optional[str]:
"""获取上次机器人回复"""
async def _get_last_bot_reply(self, group_id: str) -> Optional[str]:
"""获取上次机器人回复(群聊)"""
try:
history = await self._get_history(chat_id)
history = await self._get_history(group_id)
if not history:
return None
# 从后往前查找机器人回复
for record in reversed(history):
if record.get('nickname') == self.bot_nickname:
if record.get('role') == 'assistant' or record.get('nickname') == self.bot_nickname:
content = record.get('content', '')
if isinstance(content, list):
text_parts = []
for item in content:
if item.get("type") == "text":
text_parts.append(item.get("text", ""))
content = "".join(text_parts).strip() or "[图片]"
if len(content) > 100:
content = content[:100] + "..."
return content