This commit is contained in:
wess09 2026-03-28 18:08:15 +08:00
parent 0fc74c701d
commit 85a2eb416c
3 changed files with 76 additions and 30 deletions

76
alas.py
View File

@ -298,39 +298,50 @@ class AzurLaneAutoScript:
from module.base.utils import save_image
from module.handler.sensitive_info import (handle_sensitive_image,
handle_sensitive_logs)
if self.config.Error_SaveError:
# LLM Error Analysis - 放到最前面,防止后面截图保存时二次崩溃导致分析没跑
try:
if hasattr(self, 'config') and getattr(self.config, 'Error_LlmAnalysis', False):
from module.llm import analyze_exception
import sys
_, exc_value, _ = sys.exc_info()
if exc_value is not None:
analyze_exception(self.config, exc_value)
except Exception as e:
logger.error(f'LLM Analysis failed: {e}')
if getattr(self.config, 'Error_SaveError', False):
config_folder = pathlib.Path(f"./log/error/{self.config_name}")
folder = config_folder.joinpath(str(int(time.time() * 1000)))
folder.mkdir(parents=True, exist_ok=True)
logger.warning(f'保存错误日志: {folder}')
for data in self.device.screenshot_deque:
image_time = datetime.strftime(data['time'], '%Y-%m-%d_%H-%M-%S-%f')
image = handle_sensitive_image(data['image'])
save_image(image, f'{folder}/{image_time}.png')
with open(logger.log_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
start = 0
for index, line in enumerate(lines):
line = line.strip(' \r\t\n')
if re.match('^═{15,}$', line):
start = index
lines = lines[start - 2:]
lines = handle_sensitive_logs(lines)
with open(f'{folder}/log.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
self.keep_last_errlog(config_folder, self.config.Error_SaveErrorCount)
# LLM Error Analysis
if self.config.Error_LlmAnalysis:
try:
from module.llm import analyze_exception
import sys
_, exc_value, _ = sys.exc_info()
if exc_value is not None:
analyze_exception(self.config, exc_value)
except Exception as e:
logger.error(f'LLM Analysis failed: {e}')
try:
# 只在已经初始化了设备时才尝试保存截图,避免按需初始化时二次崩溃
if 'device' in self.__dict__:
for data in self.device.screenshot_deque:
image_time = datetime.strftime(data['time'], '%Y-%m-%d_%H-%M-%S-%f')
image = handle_sensitive_image(data['image'])
save_image(image, f'{folder}/{image_time}.png')
except Exception as e:
logger.error(f"Save error screenshot failed: {e}")
try:
with open(logger.log_file, 'r', encoding='utf-8') as f:
lines = f.readlines()
start = 0
for index, line in enumerate(lines):
line = line.strip(' \r\t\n')
if re.match('^═{15,}$', line):
start = index
lines = lines[start - 2:]
lines = handle_sensitive_logs(lines)
with open(f'{folder}/log.txt', 'w', encoding='utf-8') as f:
f.writelines(lines)
except Exception as e:
logger.error(f"Save error logs failed: {e}")
self.keep_last_errlog(config_folder, getattr(self.config, 'Error_SaveErrorCount', 0))
def restart(self):
from module.handler.login import LoginHandler
@ -844,6 +855,15 @@ class AzurLaneAutoScript:
logger.error("调度器循环中发生意外的全局异常!")
import traceback
logger.error(traceback.format_exc()) # 打印完整的错误堆栈
# 即使没有达到重启或失败上限,也第一时间自动请求分析崩溃原因
try:
if hasattr(self, 'config') and getattr(self.config, 'Error_LlmAnalysis', False):
from module.llm import analyze_exception
analyze_exception(self.config, e)
except Exception as ex:
logger.error(f'LLM Analysis failed: {ex}')
logger.warning(
f">>> 这是第 {consecutive_global_failures} 次连续全局失败,共 {MAX_GLOBAL_FAILURES} 次。"
)

View File

@ -456,7 +456,7 @@
},
"LlmModel": {
"type": "input",
"value": "Nvidia/deepseek-ai/deepseek-v3.1",
"value": "Nvidia/qwen/qwen2.5-coder-32b-instruct",
"valuetype": "str"
}
},

View File

@ -2,6 +2,10 @@ import traceback
import os
from module.logger import logger
import hashlib
_analyzed_errors_cache = {}
def analyze_exception(config, e):
"""
Analyze the exception using LLM.
@ -12,6 +16,26 @@ def analyze_exception(config, e):
"""
if not hasattr(config, 'Error_LlmAnalysis') or not config.Error_LlmAnalysis:
return
tb = ''.join(traceback.format_exception(type(e), e, e.__traceback__))
# 错误去重:计算当前堆栈报错的哈希值,如果最近已经分析过,直接跳过,防止浪费 API 和时间
error_hash = hashlib.md5(tb.encode('utf-8')).hexdigest()
if error_hash in _analyzed_errors_cache:
cached_result = _analyzed_errors_cache[error_hash]
model = getattr(config, 'Error_LlmModel', 'gpt-4o-mini')
logger.hr('LLM 错误分析', level=1)
logger.info('该错误已被 LLM 分析过,直接复用上次的分析结果以节省 API...')
logger.info(f"\n[LLM 分析报告 (由 {model} 提供, 复用缓存)]\n{cached_result}\n")
logger.hr('LLM 分析结束', level=1)
return
# 加入缓存占位符,防止等待期间如果又疯狂连续报错导致重复发送请求
_analyzed_errors_cache[error_hash] = "该错误正在被 LLM 分析中或上次分析失败,暂无结果。"
# 控制缓存大小,防止内存泄漏(保留最近 50 个报错记录)
if len(_analyzed_errors_cache) > 50:
_analyzed_errors_cache.clear()
_analyzed_errors_cache[error_hash] = "该错误正在被 LLM 分析中或上次分析失败,暂无结果。"
api_key = getattr(config, 'Error_LlmApiKey', '')
api_base = getattr(config, 'Error_LlmApiBase', 'https://api.openai.com/v1')
@ -75,7 +99,9 @@ def analyze_exception(config, e):
)
analysis = response.choices[0].message.content.strip()
logger.info(f"\n[LLM 分析报告]\n{analysis}\n")
# 覆写真正的成果进字典
_analyzed_errors_cache[error_hash] = analysis
logger.info(f"\n[LLM 分析报告 (由 {model} 提供)]\n{analysis}\n")
logger.hr('LLM 分析结束', level=1)
except ImportError: