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

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

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

View File

@@ -1606,9 +1606,8 @@ class SignInPlugin(PluginBase):
return {"success": True, "message": f"城市注册请求已处理: {city}"}
else:
return {"success": False, "message": "未知的工具名称"}
return None
except Exception as e:
logger.error(f"LLM工具执行失败: {e}")
return {"success": False, "message": f"执行失败: {str(e)}"}

View File

@@ -322,7 +322,7 @@ class WeatherPlugin(PluginBase):
"""执行LLM工具调用供AIChat插件调用"""
try:
if tool_name != "query_weather":
return {"success": False, "message": "未知的工具名称"}
return None
# 从 arguments 中获取用户信息
user_wxid = arguments.get("user_wxid", from_wxid)

View File

@@ -345,7 +345,7 @@ class ZImageTurbo(PluginBase):
async def execute_llm_tool(self, tool_name: str, arguments: dict, bot: WechatHookClient, from_wxid: str) -> dict:
"""执行LLM工具调用供AIChat插件调用"""
if tool_name != "generate_image":
return {"success": False, "message": "未知的工具名称"}
return None
try:
prompt = arguments.get("prompt", "")