Skill: bounding-elk/deploy-fault-analyzer (version 1.1.0)

--- name: deploy-fault-analyzer description: 部署故障分析及解决助手 — 接收日志文件(.json/.log)或报错文本，优先查询 MySQL 故障知识库，再自动分析故障原因并生成"部署故障分析及解决方案"Word文档，同时将问题录入Excel知识库并可同步到数据库。 version: 1.1.0 author: Hermes created: 2026-05-09 --- # 部署故障分析及解决助手 当用户提供日志文件（`.json` `.log`）或包含 error/错误/异常/故障/报错/failed 等关键词的文字内容时，自动分析故障并生成 Word 文档。分析前必须优先查询 MySQL 故障知识库 `fault_knowledge_base.fault_records`，用历史案例提高定位准确率；所有新问题继续记录到 Excel 知识库中累积沉淀，并可同步到数据库。 **⚠️ 交付规则：** Word 报告生成后必须在同一条回复中附带故障分析摘要 + 发送 Word 文件给用户，不能只生成到本地而不发送。 --- ## 触发条件 满足以下任一条件即触发： | 触发方式 | 判定标准 | |----------|---------| | **日志文件** | 用户上传/拖入 `.json`、`.log` 文件，或指明文件路径 | | **报错文本** | 用户消息中包含 `error`/`错误`/`异常`/`故障`/`报错`/`failed`/`failure`/`exception`/`失败` 等关键词 | | **主动请求** | 用户说"帮我分析这个故障"/"这是什么错"/"帮我看下日志"等 | 排除：用户只是在陈述中顺带提到"没有错误"/"没问题"/"成功了"时不触发。 --- ## 故障知识库数据库 ### 数据库信息 故障知识库来自部署排期表第 10 个 sheet「故障库」，由 `/data/work/scripts/sync_fault_knowledge_base.py` 同步到 MySQL。 | 项 | 值 | |----|----| | 数据库 | `fault_knowledge_base` | | 主表 | `fault_records` | | 连接命令 | `docker exec resource_pool_mysql mysql -upool_user -ppool_password_2024 fault_knowledge_base` | | 同步脚本 | `/data/work/scripts/sync_fault_knowledge_base.py` | | 默认 Excel | `/data/work/bom/基础平台部署排期表-2026年度.xlsx` | ### Excel 同步能力 当用户说“同步故障库”“更新故障库”“把 Excel 故障库入库”等含义时，执行以下流程： ```bash # 1. 先检查 Excel 可解析和有效行数 python3 /data/work/scripts/sync_fault_knowledge_base.py --dry-run # 2. 正式同步，重复执行不会重复插入同一条故障 python3 /data/work/scripts/sync_fault_knowledge_base.py # 3. 验证行数和去重 docker exec resource_pool_mysql mysql -upool_user -ppool_password_2024 fault_knowledge_base \ -e "SELECT COUNT(*) AS total, COUNT(DISTINCT content_hash) AS unique_hashes FROM fault_records;" ``` 如数据库未初始化，先执行： ```bash docker exec -i resource_pool_mysql mysql -uroot -proot_password_2024 \ < /data/work/sql/create_fault_knowledge_base.sql ``` ### 字段含义 | 字段 | 含义 | 查询用途 | |------|------|----------| | `module_name` | 模块 | 第一层收窄范围，优先从报错中的产品/组件/任务名推断 | | `issue_type` | 问题类型 | 第二层收窄范围，优先从故障现象推断 | | `issue_description` | 问题描述 | 核心相似度匹配字段 | | `solution_summary` | 解决方法概要 | 给出历史解决路径 | | `product_version` | 交付产品集版本 | 版本相关问题过滤 | | `delivery_branch` | 交付分支 | 架构/OS/分支差异过滤 | | `resource_pool` | 资源池 | 判断是否为特定现场案例 | ## 核心流程 ### Step 0 — 保存原始输入 **任何输入在处理前必须先存档**，防止后续分析覆盖原始数据： ```python from pathlib import Path from datetime import datetime import shutil RAW_DIR = Path.home() / '.hermes/skills/openclaw-imports/deploy-fault-analyzer/data/raw' RAW_DIR.mkdir(parents=True, exist_ok=True) # 文件输入 → 复制到 raw/ ts = datetime.now().strftime('%Y-%m-%d_%H%M%S') raw_path = RAW_DIR / f'{ts}_{Path(src_file).name}' shutil.copy2(src_file, raw_path) # 文本输入 → 写入 raw/ raw_path = RAW_DIR / f'{ts}_user_input.txt' raw_path.write_text(user_text, encoding='utf-8') ``` ### Step 1 — 读取并解析输入 按文件格式选择解析策略： **1A. 结构化 JSON 日志**（特征：顶层有 `logs` 数组，每项有 `type/message` 字段） ```python import json with open(filepath) as f: data = json.load(f) logs = data['logs'] # 日志数组 # 提取字段： # log['type'] → 'error'|'warning'|'info'|'success' # log['message'] → 日志内容（含时间戳 `[HH:MM:SS]`） # log['timestamp']→ ISO 时间（JSON字段，可能缺失） # log['task_id'] → 任务ID（如 PREP_UPLOAD_33B3DC） errors = [l for l in logs if l['type'] == 'error'] warnings = [l for l in logs if l['type'] == 'warning'] infos = [l for l in logs if l['type'] == 'info'] ``` **1B. 纯文本 .log 文件** ```python # 逐行读取，按关键词提取 ERROR/WARN/CRITICAL/FAIL 行 # 使用正则: ^\d{4}-\d{2}-\d{2}.*(ERROR|WARN|CRITICAL|FAIL) ``` **1C. 用户粘贴文本** → 直接作为分析输入 ### Step 2 — 错误自动归类与去重 #### 2A. 中文错误模式自动归类 使用关键词正则匹配，按优先级从高到低匹配： | 优先级 | 匹配模式（关键词） | 类别 | |--------|-------------------|------| | 1 | `日期格式不正确` `日期格式错误` `格式yyyy-mm-dd` | **数据异常** — 日期格式 | | 2 | `资源池名称不正确` `名称不一致` `sheet.*≠` | **配置错误** — 资源池名 | | 3 | `未填写` `必填项.*未` `必填.*缺失` | **配置错误** — 必填字段 | | 4 | `缺失` `不存在` `not found` `No such file` | **资源不足** — 文件/目录 | | 5 | `格式不正确.*IP` `IP地址格式` `格式不合法` | **配置错误** — IP格式 | | 6 | `already installed` `is already` `冲突` `conflict` | **服务异常** — 安装冲突 | | 7 | `已终止` `用户.*终止` `用户.*取消` | **用户操作** — 部署终止 | | 8 | `Verifying` `verification` `校验失败` `验证失败` | **服务异常** — 验证超时 | | 9 | `连接.*拒绝` `Connection refused` `timeout` `unreachable` | **网络/连接故障** | | 10 | `host.*not found` `DNS.*fail` `解析失败` | **网络/连接故障** | | 11 | `bms.*pri` `hostname.*invalid` | **配置错误** — 主机名 | | 12 | `Permission denied` `权限不足` `访问被拒` | **权限问题** | | 13 | `OOM` `out of memory` `磁盘空间不足` `No space` | **资源不足** — 系统资源 | | 14 | `ModuleNotFoundError` `ImportError` `依赖.*缺失` | **依赖缺失** | **实现：** ```python def categorize_error_message(msg: str) -> str: """根据中文关键词自动归类错误""" patterns = [ (r'日期格式不正确|日期格式错误|格式yyyy-mm-dd', '数据异常'), (r'资源池名称不正确|名称不一致', '配置错误'), (r'未填写|必填项.*未填|必填.*缺失', '配置错误'), (r'缺失|不存在|not found|No such file|no such file', '资源不足'), (r'格式不正确.*IP|IP地址格式|格式不合法', '配置错误'), (r'already installed|is already|冲突|conflict', '服务异常'), (r'已终止|用户.*终止|用户.*取消', '用户操作'), (r'Connection refused|timeout|unreachable|连接.*拒绝', '网络/连接故障'), (r'Permission denied|权限不足|访问被拒', '权限问题'), (r'OOM|out of memory|磁盘空间不足|No space', '资源不足'), (r'ModuleNotFoundError|ImportError|依赖.*缺失', '依赖缺失'), ] for pattern, category in patterns: if re.search(pattern, msg): return category return '待分类' ``` #### 2B. 错误去重归并（关键步骤） 真实日志中同根因错误通常大量重复。**必须先去重再分析**： 1. 过滤掉 **用户操作类**（`已终止`/`用户终止`）→ 不计入故障，仅在报告中标注 2. 按 **错误消息去重**：取 `message` 中 `ERROR` 关键字后的核心文本去重 3. 按 **task_id + 类别** 归并：同一任务同一类别的多条错误合并为一个根因 4. **输出**：每个根因一个 `fault_data` 字典 ```python def deduplicate_errors(errors: list) -> list: """归并重复错误 → 根因列表""" # 1. 分离用户操作 real_faults = [e for e in errors if '用户已终止' not in e['message'] and '用户终止' not in e['message']] # 2. 按消息核心去重 seen = {} for e in real_faults: msg = e['message'] # 提取核心：ERROR 后的文本或消息中独特部分 core = re.sub(r'\[.*?\]', '', msg).strip()[:100] cat = categorize_error_message(core) key = f"{cat}:{core[:50]}" if key not in seen: seen[key] = {'count': 1, 'sample': e, 'category': cat, 'task_ids': {e.get('task_id','?')}} else: seen[key]['count'] += 1 seen[key]['task_ids'].add(e.get('task_id', '?')) return list(seen.values()) ``` ### Step 2.5 — 优先查询 MySQL 故障知识库（必须执行） 在进入根因分析前，必须先用去重后的核心错误到 `fault_knowledge_base.fault_records` 查询历史案例。查询目标是找到相同或相近的 `module_name`、`issue_type`、`issue_description`，并把命中的 `solution_summary` 作为解决方案候选，而不是直接凭经验生成结论。 #### 2.5A. 先推断 `module_name` 优先从日志里的产品名、任务名、服务名、报错路径、部署阶段推断模块；无法确定时再不带模块查询。 高频 `module_name` 候选（按历史库频次和故障定位价值排序）： | 候选模块 | 常见关键词 | |----------|------------| | `基座` | bootstrap、基础包、yum、repo、平台基础服务、部署脚本公共步骤 | | `主机交付问题` | 主机、host、IPMI、root密码、操作系统、lldp、网络不通 | | `环境检查` | precheck、前置检查、端口检查、连通性检查、环境检查 | | `块存储` | cinder、ceph、块存储、volume、存储池、磁盘 | | `VPP` | vpp、dpdk、转发、网卡绑定、HugePage | | `监控` | telegraf、prometheus、grafana、监控节点、采集 | | `SDN` | sdn、neutron、网络控制、云内sdn、云间sdn | | `门户` | portal、控制台、门户导入表、规格族 | | `对象存储` | obs、s3、对象存储、bucket | | `CSK` / `容器` / `云原生` | csk、k8s、kube、容器、master、worker | | `Trove` | mysql、redis、trove、数据库服务 | | `虚拟化` / `裸金属` | nova、ecs、bms、ironic、裸金属 | | `DNS` | dns、域名、解析失败 | | `ECR` | ecr、镜像仓库、registry | | `网络交付问题` | 交换机、路由、vlan、外部网络、防火墙 | #### 2.5B. 再推断 `issue_type` `issue_type` 用于第二层收窄。优先候选： | 候选类型 | 常见关键词 | |----------|------------| | `脚本问题` | script、脚本、执行失败、返回码、命令失败、already installed | | `环境问题` | 网络不通、端口不通、系统版本、依赖环境、服务状态 | | `新建部署问题` | 新建、首次部署、安装、部署失败 | | `新建验收问题` | 验收、验证、verification、检查失败 | | `部署包问题` | 包缺失、rpm、tar、镜像、目录不存在、版本包 | | `部署表问题` | Excel、部署表、必填、格式、资源池名称、规格族 | | `网络交付问题` | DNS、路由、交换机、连通性、防火墙、VLAN | | `主机交付问题` | 主机名、IPMI、操作系统、root密码、lldp、网卡 | | `部署文档问题` | 文档、步骤、章节、说明不一致 | | `操作问题` | 人工操作、输入错误、误操作、顺序错误 | | `扩容问题` / `扩容验收问题` | 扩容、增加节点、扩容后验收 | | `优化建议` / `其他问题` | 无明确故障但存在改进项 | #### 2.5C. 查询策略 按“强约束 → 放宽”的顺序查询，避免一开始全库模糊搜索导致误命中： 1. `module_name + issue_type + issue_description` 关键词查询 2. `module_name + issue_description` 查询 3. `issue_type + issue_description` 查询 4. 仅 `issue_description` 全文/LIKE 查询 5. 如果完全无命中，继续按原流程分析，并在报告中注明“历史故障库未找到高置信匹配” 推荐命令模板： ```bash docker exec resource_pool_mysql mysql -upool_user -ppool_password_2024 fault_knowledge_base -e " SELECT id, module_name, issue_type, found_at, product_version, delivery_branch, LEFT(issue_description, 220) AS issue_preview, LEFT(solution_summary, 220) AS solution_preview FROM fault_records WHERE module_name = '基座' AND issue_type = '脚本问题' AND ( issue_description LIKE '%bootstrap%' OR solution_summary LIKE '%bootstrap%' OR MATCH(issue_description, solution_summary, remark) AGAINST('bootstrap 脚本 失败' IN NATURAL LANGUAGE MODE) ) ORDER BY (module_name = '基座') DESC, (issue_type = '脚本问题') DESC, updated_at DESC LIMIT 5;" ``` 如果无法稳定判断模块，使用关键词全库检索： ```bash docker exec resource_pool_mysql mysql -upool_user -ppool_password_2024 fault_knowledge_base -e " SELECT id, module_name, issue_type, LEFT(issue_description, 200) AS issue_preview, LEFT(solution_summary, 200) AS solution_preview FROM fault_records WHERE issue_description LIKE '%关键报错%' OR solution_summary LIKE '%关键报错%' OR remark LIKE '%关键报错%' LIMIT 10;" ``` #### 2.5D. 命中结果使用规则 - 高置信命中：`module_name`、`issue_type`、核心报错关键词均匹配，解决方案可作为首选，但仍要结合当前日志证据验证。 - 中置信命中：模块或问题类型匹配，但错误文本只部分相似，只能作为排查方向。 - 低置信命中：仅关键词相似，不能直接引用为结论，只在“历史类似案例”中备注。 - 如果多个历史案例冲突，优先选择同模块、同问题类型、同版本/分支的记录。 - Word 报告中增加“历史故障库匹配”小节，列出命中记录的模块、问题类型、问题摘要、解决摘要和置信度。 ### Step 3 — 故障信息提取（多故障） 从去重后的错误列表，为每个根因提取结构化信息： **提取方法：** | 提取项 | JSON 日志提取方法 | 文本日志提取方法 | |--------|------------------|-----------------| | **故障时间** | `e['timestamp']`（ISO格式）或从 `message` 中提取 `[HH:MM:SS]` | 正则 `\d{4}-\d{2}-\d{2}.*\d{2}:\d{2}:\d{2}` | | **错误类型** | `task_id` + 异常描述拼接，如 `CHECK_DEPLOY_F5B45D: checkTableImpl.DateFormatError` | 异常类名或错误码 | | **错误消息** | `e['message']` 前200字符 | 紧跟在 ERROR 后的描述文本 | | **出现次数** | `dedup_count` | 行数统计 | | **堆栈跟踪** | 从 `message` 中的 `checkTableImpl.py:318` 等提取调用链 | Traceback 段落前20行 | | **影响组件** | `task_id` 前缀 + `message` 中的模块路径（如 `/home/conf/`） | 服务名、主机名 | | **影响范围** | 从部署流程推断（阻塞/警告/跳过） | 从错误推断 | | **关联配置** | `message` 中的文件路径、参数名 | 配置文件引用 | **任务ID解析**（JSON日志特有）： ```python # task_id 前缀含义: # PREP_UPLOAD_* → 上传检查步骤 # PREP_TASK_* → 前置任务 # CHECK_DEPLOY_* → 部署表检查 # PREP_DEPLOY_* → 部署准备 def parse_task_id(task_id: str) -> dict: """解析 task_id → 任务类型和阶段""" if task_id is None: return {'phase': '全局', 'type': '系统'} parts = task_id.split('_') if len(parts) >= 2: return {'phase': parts[0], 'type': parts[1], 'id': parts[-1] if len(parts) > 2 else ''} return {'phase': task_id, 'type': 'UNKNOWN'} ``` ### Step 4 — 根因分析 基于提取的信息，执行以下分析步骤： 1. **错误分类**：归入以下类别之一 - 🟥 网络/连接故障（ConnectionError, timeout, unreachable） - 🟧 配置错误（config file, parameter, permission） - 🟨 资源不足（OOM, disk full, CPU throttle, fd limit） - 🟩 服务异常（service down, crash, restart loop） - 🟦 权限问题（permission denied, unauthorized） - 🟪 依赖缺失（module not found, library mismatch） - ⬜ 数据异常（data corrupt, schema mismatch） 2. **历史案例对齐**：把 Step 2.5 命中的故障库记录与当前日志证据逐项对比，确认模块、问题类型、关键词、版本/分支是否一致 3. **因果关系链**：从堆栈自底向上追溯，找到最初触发点 4. **关键证据**：摘录日志中能佐证根因的2-3条关键行 ### Step 5 — 生成解决方案 优先采用高置信历史案例中的 `solution_summary`，再结合当前日志、环境和标准排查路径细化；没有命中时才完全按通用框架生成方案。 | 错误类别 | 标准排查路径 | 典型解决步骤 | |----------|-------------|-------------| | 网络/连接 | telnet/ping/curl 测试连通性 → 防火墙规则 → DNS解析 | 检查目的端口可达性、放行防火墙规则、修正IP配置 | | 配置错误 | 对比配置模板 → 校验参数值 | 修正配置文件、重启服务、验证参数生效 | | 资源不足 | free/df -h/ulimit -n 检查 → top 定位占用进程 | 清理磁盘、扩容、调大 ulimit、重启服务释放泄漏 | | 服务异常 | systemctl status → journalctl 查崩溃原因 | 重启服务、检查依赖服务状态、排查 OOM killer | | 权限问题 | ls -l / id / 检查 sudo | 修正文件权限(chmod/chown)、添加 sudo 授权 | | 依赖缺失 | rpm -qa / pip list → 对比版本要求 | 安装缺失包、版本降级/升级 | | 数据异常 | 检查数据完整性 → 对比 schema | 数据修复、迁移、回滚 | **输出解决方案时遵循的结构：** ``` 【解决方案】 1. 排查步骤 - 第一步：xxx - 第二步：xxx 2. 修复操作 - 操作命令（可复制执行） 3. 验证方法 - 验证命令 + 预期结果 4. 回滚方案（如适用） ``` ### Step 6 — 生成交付物 #### 6.1 判断生成模式 ```python if len(root_causes) == 1: mode = 'single' # 单故障 → 标准五段式报告 else: mode = 'multi' # 多故障 → 总览 + 逐项分析 + 综合建议 ``` #### 6.2 Word 文档（单故障模式） 生成 `部署故障分析及解决方案_YYYY-MM-DD_HHmmss.docx`，标准五段式结构（同原版）。 #### 6.3 Word 文档（多故障模式） 当 `mode == 'multi'` 时，增加总览页和综合建议： ``` 封面 ├── 总体概况表 │ ├── 文档编号 / 分析时间 / 部署目标 / 部署计划 │ ├── 总日志数 / 总错误数 / 归并根因数 │ └── 最高故障级别 ├── 错误全景图（表格） │ ├── # / 故障 / 类别 / 级别 / 出现次数 ├── 逐项详细分析 │ ├── 故障 1：...（完整五段式） │ ├── 故障 2：... │ └── ... └── 综合建议与整改清单 └── 按优先级排列的改进项 ``` **重点**：每个故障页有明确的 `故障 N / 总N` 标注，分隔线分隔。 #### 6.4 使用 generate_report.py 脚本 ```python from scripts.generate_report import generate_fault_report, generate_multi_fault_report, append_to_excel # 单故障 if len(faults) == 1: docx_path = generate_fault_report(output_path, faults[0]) # 多故障 else: docx_path = generate_multi_fault_report(output_path, faults) # 批量追加 Excel（所有故障都写一行） for fault in faults: append_to_excel(xlsx_path, {...}) ``` **文档保存路径**: `~/.hermes/skills/openclaw-imports/deploy-fault-analyzer/output/` ### Step 7 — 发送交付物（必须执行） **⚠️ 分析完成后必须在同一条回复中做两件事：** 1. **贴出故障分析摘要**（故障全景表 + 核心结论），让用户无需打开文件即可了解全貌 2. **发送 Word 文件** — 使用 `message` 工具发送文件给用户 ```python # 发送 .docx 文件给用户（以当前会话的 chat 通道发送） # 使用 message 工具: action=send, filePath=docx_path, caption='部署故障分析报告' ``` **发送规则：** - 回复消息中贴摘要表（纯文本，不依赖 markdown 表格渲染） - 同时调用 `message` 工具发送 Word 文件 - 发送后回复 `NO_REPLY` 避免重复消息 **摘要模板（回复消息中贴出）：** ``` 🔍 部署故障分析结果 概览：N条日志，M条错误 → 归并 K 个根因 | # | 故障 | 类别 | 级别 | 次数 | 定位 | F1 | xxx | 数据异常 | P1 | 44 | xxx F2 | ... 🎯 核心结论：xxx 📄 Word 报告已同步发送，请查收。 ``` --- ## Python 脚本模板 ### docx 生成脚本 ```python from docx import Document from docx.shared import Inches, Pt, Cm, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH from datetime import datetime import os def generate_fault_report(output_path, fault_data): """ fault_data = { 'fault_time': '2026-05-09 10:23:45', 'error_type': 'ConnectionError', 'error_message': '...', 'stack_trace': '...', 'affected_component': 'nova-api', 'affected_scope': 'AZ-1 计算节点', 'related_config': '/etc/nova/nova.conf', 'error_category': '网络/连接故障', 'fault_level': 'P1', 'root_cause': '...', 'evidence_lines': ['...', '...'], 'solution_steps': [...], 'verification': '...', 'prevention': '...', } """ doc = Document() # 标题 title = doc.add_heading('部署故障分析及解决方案', level=0) title.alignment = WD_ALIGN_PARAGRAPH.CENTER # 文档信息表 doc.add_paragraph('') info_table = doc.add_table(rows=3, cols=2, style='Light Grid Accent 1') info_data = [ ('文档编号', f"DOC-{datetime.now().strftime('%Y%m%d')}-{fault_data.get('fault_level','P2')}"), ('分析时间', datetime.now().strftime('%Y-%m-%d %H:%M:%S')), ('故障级别', fault_data.get('fault_level', 'P2')), ] for i, (k, v) in enumerate(info_data): info_table.rows[i].cells[0].text = k info_table.rows[i].cells[1].text = v doc.add_paragraph('') # 一、故障概述 doc.add_heading('一、故障概述', level=1) doc.add_paragraph(f"故障时间：{fault_data.get('fault_time', '未知')}") doc.add_paragraph(f"影响组件：{fault_data.get('affected_component', '未知')}") doc.add_paragraph(f"影响范围：{fault_data.get('affected_scope', '未知')}") doc.add_paragraph(f"错误类型：{fault_data.get('error_type', '未知')}") # 二、故障详情 doc.add_heading('二、故障详情', level=1) doc.add_heading('错误消息', level=2) doc.add_paragraph(fault_data.get('error_message', '')) if fault_data.get('stack_trace'): doc.add_heading('堆栈跟踪', level=2) p = doc.add_paragraph() p.style = doc.styles['No Spacing'] for line in fault_data['stack_trace'].split('\n')[:20]: doc.add_paragraph(line, style='No Spacing') if fault_data.get('related_config'): doc.add_paragraph(f"关联配置：{fault_data['related_config']}") # 三、根因分析 doc.add_heading('三、根因分析', level=1) doc.add_paragraph(f"错误分类：{fault_data.get('error_category', '未分类')}") doc.add_paragraph(f"根本原因：{fault_data.get('root_cause', '待进一步分析')}") if fault_data.get('evidence_lines'): doc.add_heading('关键证据', level=2) for i, line in enumerate(fault_data['evidence_lines'], 1): doc.add_paragraph(f"{i}. {line}") # 四、解决方案 doc.add_heading('四、解决方案', level=1) for step in fault_data.get('solution_steps', []): doc.add_paragraph(step, style='List Bullet') if fault_data.get('verification'): doc.add_heading('验证方法', level=2) doc.add_paragraph(fault_data['verification']) # 五、预防措施 doc.add_heading('五、预防措施', level=1) doc.add_paragraph(fault_data.get('prevention', '—')) doc.save(output_path) return output_path ``` --- ## 边界情况与注意事项 ### 当输入过于模糊时 如果日志/文本中找不到明确的错误信息，不要强行编造分析结果。此时： - 输出一份简要的"日志初步筛查报告" Word 文档 - 在文档中标注"待补充信息" - Excel 中错误类别记为"待分类" ### 当单个输入包含多个错误时（重要） **流程：** 1. Step 2A 自动归类 → 按关键词将错误分入不同类别 2. Step 2B 错误去重 → 归并重复错误，统计每个根因出现次数 3. 过滤"用户操作"类（`已终止`/`用户终止`）→ 不计入故障清单 4. 对每个根因执行 Step 2.5，按 `module_name`、`issue_type`、`issue_description` 查询 MySQL 故障知识库 5. 按**故障级别**（P1→P2→P3）和历史命中置信度排序输出 6. Word 文档使用**多故障模式** → 生成总览表 + 逐项分析 + 历史故障库匹配 7. Excel 中每个根因占一行，必要时同步到 MySQL 故障库 ### 真实案例参考 以下是从实际部署日志中提取的典型错误模式，用于指导分类： **模式 1 — 部署表日期格式错误（44条 → 1个根因）** ``` [CHECK_DEPLOY_F5B45D] 导入表：日期格式不正确45677.7661111, 格式yyyy-mm-dd hh:mm:ss → 类别：数据异常 | 根因：Excel日期序列号未转换 | 级别：P1 ``` **模式 2 — 资源池名称不一致（35条 → 1个根因）** ``` [CHECK_DEPLOY_F5B45D] 资源池名称不正确 sheet11:呼和交付验证云池; sheet1:呼和国产化... → 类别：配置错误 | 根因：多sheet资源池名未统一 | 级别：P1 ``` **模式 3 — 部署包目录缺失（6条 → 1个根因）** ``` [PREP_UPLOAD_33B3DC] /opt/ImageManager: 缺失 → 类别：资源不足 | 根因：部署包未上传或不在此次部署范围 | 级别：P1 ``` **模式 4 — RPM 安装误报（1条 → 不阻塞）** ``` [PREP_TASK_53658B] rpm -ivh → already installed → exit code ≠ 0 → 类别：服务异常 | 根因：安装脚本未处理"已安装"状态 | 级别：P3 ``` ### 用户操作类不计入故障 以下消息类型**不归类为故障**，仅在报告中备注： - `用户已终止部署流程` → 用户主动操作 - `部署已自动暂停，等待用户确认` → 流程机制，非故障本身 - `用户确认继续` → 流程恢复 ### 路径规范 所有输出统一在 skill 目录下： ``` ~/.hermes/skills/openclaw-imports/deploy-fault-analyzer/ ├── SKILL.md ├── output/ # Word 文档输出 ├── data/ │ ├── problems.xlsx # Excel 知识库 │ └── raw/ # 原始输入存档 ``` MySQL 故障知识库不在 skill 目录下，固定查询 `fault_knowledge_base.fault_records`；Excel 到数据库的同步脚本固定为 `/data/work/scripts/sync_fault_knowledge_base.py`。 --- ## 快速参考 | 用户说什么 | 你做什么 | |-----------|---------| | 上传 xxxx.log | 读取 → 提取ERROR行 → 推断 `module_name`/`issue_type` → 查询 MySQL 故障库 → 分析 → 生成 docx + 写入 xlsx → **发送 docx 给用户 + 贴摘要** | | 粘贴报错文本 | 同日志流程，必须先查 `fault_knowledge_base.fault_records` 再给结论 | | "帮我看下这个报错" | 识别附带文本 → 查 MySQL 历史案例 → 分析 → 生成 docx + 写入 xlsx → **发送 docx 给用户 + 贴摘要** | | "查一下之前类似问题" | 优先查询 `fault_knowledge_base.fault_records`，按模块、问题类型、问题描述返回历史案例 | | "这个错之前遇到过吗" | 在 MySQL 故障库中搜索匹配的 `issue_description` 和 `solution_summary` | | "同步故障库" / "更新故障库" | 运行 `/data/work/scripts/sync_fault_knowledge_base.py --dry-run`，确认后运行正式同步脚本 |


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)