已合并
add Pytorch skills #157
add Pytorch skills #157
已合并
gys创建于 7月1日
共 321 个文件变更+43200-0
@@ -0,0 +1 @@
1+../../../graph-log-analyzer/SKILL.md
@@ -0,0 +1 @@
1+../../../graph-mcp-integration/SKILL.md
@@ -0,0 +1 @@
1+../../../graph-mode-diagnostics/SKILL.md
@@ -0,0 +1 @@
1+../../../graph-mode-expert/SKILL.md
@@ -0,0 +1 @@
1+../../../graph-performance-profiling/SKILL.md
@@ -0,0 +1,185 @@
1+# NPU Graph Skill — NPU 图模式技能套件
2+ 
3+面向 Ascend NPU 图模式(NPUGraph/ACLGraph)的 AI Agent 驱动分析工作流。将「依赖个人经验手工排查」转变为「结构化、可追溯、可复用」的标准化流程。
4+ 
5+5 个子技能统一装在 `NPU-graph-skill/` 下,**一个入口自动路由**,安装时全部注册。
6+ 
7+## 功能描述
8+ 
9+torch_npu 的图模式(NPUGraph)对标 CUDA Graphs,通过 capture → compile → replay 三阶段将逐算子 dispatch 替换为单次 ACL 图执行,是 torch.compile 和大模型推理的核心加速手段。调用链跨越 **Python API → C++ 核心 → op-plugin 算子 → ACL 运行时** 四层,问题定位门槛极高。
10+ 
11+本技能套件核心能力:
12+ 
13+- **知识问答 + 代码导航**:根据代码结构树快速定位涉及文件,解释运行机制
14+- **端到端问题诊断**:5 阶段系统诊断,五层架构(L5→L1)定位到具体文件和行号
15+- **源码管理**:自动拉取 torch_npu + op-plugin 仓库,按需切换分支
16+- **日志分析**:脚本自动解析 TORCH_NPU_LOGS,匹配 16+ 已知异常模式
17+- **性能分析**:基准对比、瓶颈定位、ACL Profile 分析
18+- **DFX 知识沉淀**:每次诊断后提示记录到知识库,自增长案例库
19+ 
20+### 支持的问题类型
21+ 
22+| 类型 | 特征 | 判定方法 |
23+|------|------|----------|
24+| 图捕获失败 | `capture_begin` / `aclmdlRICaptureBegin` 报错 | 检查图内不允许的操作、Stream 状态、嵌套捕获 |
25+| 图重放失败 | `replay` / `aclmdlRIExecuteAsync` 报错 | 检查图编译状态、输入 shape 一致性、LRU 淘汰 |
26+| 图内 OOM | `NPUCachingAllocator` / `aclrtMalloc` 失败 | 检查捕获期间是否有新分配、内存碎片化、池耗尽 |
27+| 动态形状 update 失败 | `update_graph` / shape mismatch 报错 | 检查参数合法性、TND+dropout fallback、CPU tensor 要求 |
28+| 编译 hang/失败 | `torch.compile(backend="npugraphs")` 卡死或报错 | 检查 graph_breaks、Dynamo 异常、图分区逻辑 |
29+| 性能下降 | 图重放耗时增大、QPS 下降 | P50/P99 延迟对比、ACL Profile 分析、回归检测 |
30+| 日志异常 | TORCH_NPU_LOGS 出现 ERROR/FATAL | 结构化解析 + 16 个已知模式匹配 |
31+ 
32+### 结论类型
33+ 
34+| 结论 | 说明 | 后续动作 |
35+|------|------|----------|
36+| 用户代码问题 | 图捕获期间有不允许的操作(CPU 同步、条件分支等) | 修改用户代码 |
37+| 框架 Bug(已定位) | torch_npu 侧代码有误,修改方案明确 | 按修改方案修复 |
38+| 框架 Bug(待定位) | 问题在 torch_npu 侧但根因未明 | 进入深度诊断 |
39+| op-plugin 算子问题 | 定位到 op-plugin 侧算子实现有误 | 转 op-plugin 团队 |
40+| ACL 运行时问题 | 定位到 CANN 驱动层 | 转 CANN 团队 |
41+| 环境/配置问题 | CANN 版本、Stream 配置、环境变量等 | 修正配置 |
42+| 设计限制 | 当前 ACL 图不支持的能力(如嵌套捕获) | 记录限制,评估 workaround |
43+ 
44+## 技能总览
45+ 
46+| 技能 | 职责 | 触发场景 |
47+|------|------|---------|
48+| **graph-mode-expert** ★ | 知识问答 + 代码导航 + 仓库管理 + 自验证 | "NPUGraph 怎么工作"/"capture_begin 做了什么" |
49+| **graph-mode-diagnostics** ★ | 端到端问题诊断(唯一诊断入口) | 捕获/重放/OOM/update 失败、任何报错 |
50+| graph-performance-profiling | 性能分析:回归检测、瓶颈定位、ACL Profile | "图模式慢"/profile/benchmark/QPS 下降 |
51+| graph-log-analyzer | 日志结构化解析 + 异常模式匹配 | TORCH_NPU_LOGS/日志分析 |
52+| graph-mcp-integration | MCP 工具评估、配置、自建 MCP 开发 | 配置 MCP/Context7/Playwright |
53+ 
54+### 技能路由关系
55+ 
56+```
57+用户提问
58+ │
59+ ├── 知识问答/代码导航 ──→ graph-mode-expert
60+ │ ├── Phase 0: 仓库准备
61+ │ ├── Phase 1: 知识问答(关键词→文件→源码→回答)
62+ │ └── Phase 3: 自验证
63+ │
64+ ├── 报错/异常/崩溃 ──→ graph-mode-diagnostics
65+ │ ├── Phase 1: 分类(捕获/重放/内存/Update/编译)
66+ │ ├── Phase 2: 分阶段诊断(2A-2E)
67+ │ ├── Phase 3: 模式匹配(14+ known patterns)
68+ │ ├── Phase 4: 深度追踪(源码级根因)
69+ │ └── Phase 5: 结构化报告
70+ │
71+ ├── 日志分析 ──→ graph-log-analyzer
72+ │ └── 脚本解析 → 模式匹配 → 时间线重建 → 报告
73+ │
74+ ├── 性能问题 ──→ graph-performance-profiling
75+ │ └── 基准建立 → 对比 → 瓶颈定位 → ACL Profile
76+ │
77+ └── MCP 配置 ──→ graph-mcp-integration
78+ └── 评估 → 配置 → 验证
79+ 
80+所有会话结束后 ──→ DFX 知识沉淀(提示记录到 knowledge/history.md)
81+```
82+ 
83+## 目录结构
84+ 
85+```
86+NPU-graph-skill/
87+├── SKILL.md # ★ 总入口(Gate 0 路由 + 中文默认)
88+├── README.md # ★ 本文件
89+├── .claude/skills/ # Claude Code 自动发现(6 个入口)
90+│
91+├── knowledge/ # ★ DFX 知识沉淀
92+│ ├── CASE_TEMPLATE.md # 案例录入标准模板
93+│ └── history.md # 累积案例库(随使用增长)
94+│
95+├── graph-mode-expert/ # 知识问答 + 代码导航
96+│ ├── SKILL.md
97+│ ├── README.md
98+│ ├── references/
99+│ │ ├── code-structure.md # 完整代码结构树(关键词→文件)
100+│ │ ├── knowledge-faq.md # 常见问题速查
101+│ │ ├── code-repo-setup.md # 仓库准备详细流程
102+│ │ ├── architecture.md # 架构设计
103+│ │ └── skill-design.md # 技能设计原理
104+│ ├── scripts/check_graph_health.py # 环境健康检查
105+│ └── templates/analysis-report-template.md
106+│
107+├── graph-mode-diagnostics/ # 端到端问题诊断(唯一入口)
108+│ ├── SKILL.md
109+│ ├── references/
110+│ │ ├── domain-context.md # 五层架构/源文件/约束
111+│ │ ├── known-patterns.md # 14+ 已知问题模式
112+│ │ ├── error-code-map.md # ACL 错误码→根因
113+│ │ └── diagnostic-checklist.md # 诊断检查清单
114+│ └── scripts/
115+│ ├── analyze_graph_dump.py
116+│ └── diagnose_npu_fault.py
117+│
118+├── graph-performance-profiling/ # 性能分析
119+│ └── SKILL.md
120+│
121+├── graph-log-analyzer/ # 日志分析
122+│ ├── SKILL.md
123+│ ├── patterns/ (error/timeline/memory)
124+│ └── scripts/parse_npu_logs.py
125+│
126+└── graph-mcp-integration/ # MCP 集成
127+ ├── SKILL.md
128+ ├── mcp-configs.json
129+ ├── setup-guide.md
130+ └── references/mcp-evaluation.md
131+```
132+ 
133+## 快速开始
134+ 
135+直接在对话中输入图模式相关问题,自动路由到对应技能:
136+ 
137+```
138+NPUGraph 的 capture_begin 内部做了什么?
139+我的图模式报错了:[贴堆栈]
140+TORCH_NPU_LOGS 输出 200MB 帮我分析
141+图模式比 eager 慢怎么排查?
142+```
143+ 
144+### DFX 知识沉淀
145+ 
146+每次问答/诊断结束后,Agent 会自动提示「是否记录到知识库」。选 Y 后案例按标准模板追加到 `knowledge/history.md`,分类计数自动更新。如果发现新问题模式,还会提示是否同步更新 `known-patterns.md`。
147+ 
148+## 辅助脚本
149+ 
150+| 脚本 | 用途 | 位置 |
151+|------|------|------|
152+| `parse_npu_logs.py` | 日志结构化解析(模块分布/时间线/异常模式) | graph-log-analyzer/scripts/ |
153+| `analyze_graph_dump.py` | 图 dump JSON 分析(节点/边/算子分布/基线对比) | graph-mode-diagnostics/scripts/ |
154+| `check_graph_health.py` | 环境健康检查(CANN/设备/内存/NPUGraph 功能) | graph-mode-expert/scripts/ |
155+ 
156+## 参考文件
157+ 
158+| 文件 | 内容 | 何时查阅 |
159+|------|------|----------|
160+| `graph-mode-expert/references/code-structure.md` | 完整代码结构树(L5→L1,18+ 关键词索引) | 知识问答 / 代码导航 |
161+| `graph-mode-expert/references/knowledge-faq.md` | 常见问题速查 | 知识问答 |
162+| `graph-mode-diagnostics/references/domain-context.md` | 五层架构、源文件、使能方式、约束 | 诊断前了解领域 |
163+| `graph-mode-diagnostics/references/known-patterns.md` | 14+ 已知问题模式 | 诊断 Phase 3 模式匹配 |
164+| `graph-mode-diagnostics/references/error-code-map.md` | ACL 错误码 → 根因映射 | 看到 ACL 错误码时 |
165+| `graph-mode-diagnostics/references/diagnostic-checklist.md` | 诊断检查清单 | 诊断全流程 |
166+| `graph-log-analyzer/patterns/` | E/T/M-Series 异常模式 | 日志分析 |
167+| `graph-mcp-integration/references/mcp-evaluation.md` | MCP 工具评估与推荐 | 配置 MCP 工具前 |
168+| `knowledge/history.md` | 累积案例库 | 新诊断前查历史 |
169+| `knowledge/CASE_TEMPLATE.md` | 案例录入模板 | 记录新案例时 |
170+ 
171+## 维护指南
172+ 
173+### 新增案例
174+ 
175+诊断结束后选择记录,Agent 自动追加到 `knowledge/history.md` 对应分类。
176+ 
177+### 新增已知模式
178+ 
179+- **问题模式**: `graph-mode-diagnostics/references/known-patterns.md`,按 P-CAP/P-REP/P-MEM/P-UPD/P-COM 系列
180+- **日志模式**: `graph-log-analyzer/patterns/` 下的对应文件,按 E/T/M 系列
181+- **错误码**: `graph-mode-diagnostics/references/error-code-map.md`
182+ 
183+## 作者
184+ 
185+YCH
@@ -0,0 +1,41 @@
1+---
2+name: npu-graph-skill
3+description: >-
4+ NPU 图模式技能套件。Use when user asks about NPU graph mode —
5+ NPUGraph, ACLGraph, capture/replay, graph compile, graph OOM,
6+ graph update, FA3 graph, graph performance, graph logs, MCP config.
7+ Covers torch_npu full stack (Python API -> C++ core -> op-plugin -> ACL runtime).
8+---
9+ 
10+# NPU Graph Skill
11+ 
12+## 语言
13+ 
14+**所有回答默认使用中文。**
15+ 
16+---
17+ 
18+## Gate 0: 按用户意图路由
19+ 
20+| 用户意图 | 加载技能 | 触发示例 |
21+|---------|---------|---------|
22+| 知识问答、概念解释、代码导航 | `npu-graph-mode-expert` | "NPUGraph 怎么工作"/"capture_begin 做了什么" |
23+| 报错/异常/崩溃/OOM | `npu-graph-mode-diagnostics` | "图捕获报错"/"replay failed"/"OOM" |
24+| 性能问题/变慢/回归 | `npu-graph-performance-profiling` | "图模式慢"/"QPS下降"/"profile" |
25+| 日志分析 | `npu-graph-log-analyzer` | "TORCH_NPU_LOGS"/"帮我分析日志" |
26+| MCP 工具配置 | `npu-graph-mcp-integration` | "配置MCP"/"启用Context7" |
27+ 
28+## 手递手规则
29+ 
30+```
31+expert ───→ diagnostics (报错) → log-analyzer (日志量大)
32+ ├→ profiling (性能) → diagnostics (根因是bug)
33+ └→ log-analyzer (日志)
34+ 
35+log-analyzer → diagnostics (日志指出异常)
36+ └→ profiling (日志指出性能异常)
37+```
38+ 
39+## DFX: 知识沉淀
40+ 
41+所有子技能在会话结束后**自动提示用户**是否将问答/诊断结果记录到 [knowledge/history.md](knowledge/history.md)。详见 [knowledge/CASE_TEMPLATE.md](knowledge/CASE_TEMPLATE.md)。
@@ -0,0 +1,97 @@
1+---
2+name: npu-graph-log-analyzer
3+description: >-
4+ Use when user provides TORCH_NPU_LOGS output, NPUGraph log files,
5+ or logs containing [NPUGRAPH] tags that need structured analysis.
6+ Use keywords: "TORCH_NPU_LOGS", "日志分析", "parse log", "log analysis",
7+ "解析日志", "模块标签", "NPUGRAPH log".
8+ NOT for diagnosing the root cause from logs — handoff findings to
9+ npu-graph-mode-diagnostics for full diagnosis.
10+---
11+ 
12+# Graph Log Analyzer — 图模式日志分析
13+ 
14+## The Iron Law
15+ 
16+```
17+日志分析必须先自动化扫描已知模式,再人工介入异常点。
18+禁止未解析就全文阅读——100MB 日志人工读不完,必漏关键信息。
19+```
20+ 
21+---
22+ 
23+## When to Use
24+ 
25+| 场景 | 输入 |
26+|------|------|
27+| 图模式运行后有大量日志 | 日志文件 (`.log`, `.txt`) |
28+| TORCH_NPU_LOGS 输出需分析 | stdout/stderr 文本 |
29+| 需还原图执行时间线 | 带时间戳的日志 |
30+| 周期性错误需模式匹配 | 多次运行的历史日志 |
31+ 
32+---
33+ 
34+## Phase 1: 结构化解析
35+ 
36+**用脚本**:
37+```bash
38+python scripts/parse_npu_logs.py <log_file> --output json --summary
39+# 输出: 模块分布 + 日志级别分布 + 时间范围 + 错误/警告列表 + 时间线
40+```
41+ 
42+**手动快速定位**(脚本不可用时):
43+```bash
44+grep -E "ERROR|FATAL" <log_file> # 只看错误
45+grep "\[NPUGRAPH\]" <log_file> | grep -v DEBUG # 去噪
46+grep "\[Capture\]" <log_file> # 只看特定阶段
47+```
48+ 
49+---
50+ 
51+## Phase 2: 异常模式匹配
52+ 
53+详见 `patterns/error-patterns.md`、`patterns/timeline-patterns.md`、`patterns/memory-patterns.md`
54+ 
55+**快速索引**:
56+ 
57+| 模式 ID | 描述 | 关键特征 |
58+|---------|------|----------|
59+| E-001 | 图捕获中断 | `Capture FAILED` + 无 `capture_end` |
60+| E-002 | 重放状态异常 | `Replay ERROR: graph not in captured state` |
61+| E-003 | 内存分配失败 | `Memory ERROR: aclrtMalloc failed` |
62+| E-007 | 图编译失败 | `Compile ERROR: aclmdl build failed` |
63+| T-001 | 捕获耗时长 | Capture 时间戳间隔 > 预期 |
64+| T-002 | 重放耗时长 | Replay 时间戳间隔 > 预期 |
65+| M-001 | 内存碎片化 | 大量小 alloc + 小 free |
66+| M-002 | 内存泄漏 | alloc > free + 累积增长 |
67+ 
68+## Phase 3: 时间线重建
69+ 
70+从日志还原图执行时间线:warmup → capture → compile → replay × N → cache
71+ 
72+**异常检测**:捕获 >10s? / 编译 >30s? / 重放 P99 > P50 × 3? / 首次重放 >> 后续?
73+ 
74+## Phase 4: 跨日志对比
75+ 
76+```bash
77+python scripts/parse_npu_logs.py log_v1.txt log_v2.txt --diff
78+# 对比:模块分布变化 / 错误增减 / 重放耗时分布 / 新模块标签
79+```
80+ 
81+## Phase 5: 报告
82+ 
83+```markdown
84+## 日志分析报告
85+### 概况: 文件/总行数/时间跨度/错误数/警告数
86+### 模块分布: | 模块 | 行数 | 占比 |
87+### 异常模式: [E-00X] 模式名: N次, L<行号> → 影响 → 建议
88+### 结论: 核心问题 + 下一步
89+```
90+ 
91+## Handoff
92+ 
93+| 条件 | 移交技能 |
94+|------|----------|
95+| 日志指出捕获/重放/OOM 失败 | `NPU-graph-mode-diagnostics` |
96+| 日志指出性能异常 | `NPU-graph-performance-profiling` |
97+| 日志正常但仍有问题 | `NPU-graph-mode-diagnostics` |
@@ -0,0 +1,21 @@
1+# 错误日志模式 — E-Series
2+ 
3+| 模式 ID | 模式名称 | 正则匹配 | 说明 | 关联阶段 |
4+|---------|---------|---------|------|---------|
5+| E-001 | 捕获中断 | `Capture.*FAILED\|capture.*error` + 后续无 `capture_end` | 图捕获过程中断 | Capture |
6+| E-002 | 重放状态异常 | `Replay.*graph not in captured state\|state.*invalid` | 图不在可重放状态 | Replay |
7+| E-003 | 内存分配失败 | `Memory.*aclrtMalloc failed\|GE_DEVICE_MEMORY_ALLOC` | NPU 内存不足 | Memory |
8+| E-004 | Update 参数非法 | `Update.*invalid.*param\|shape.*mismatch.*update` | update 参数类型/形状错误 | Update |
9+| E-005 | 图 LRU 淘汰 | `TreeManager.*evicting.*graph` | LRU 淘汰了正在使用的图 | Tree/Cache |
10+| E-006 | Stream 不匹配 | `stream.*mismatch\|Stream.*different` | 捕获和重放 Stream 不一致 | Capture/Replay |
11+| E-007 | 图编译失败 | `Compile.*aclmdl.*failed\|build.*error` | ACL 图编译出错 | Compile |
12+| E-008 | HCCL UnmapMem | `Memory.*HCCL.*UnmapMem\|HCCL.*free.*crash` | HCCL 通信内存被意外释放 | Memory |
13+| E-009 | FA3 图不支持 | `FA3.*not supported.*graph\|flash_attn.*fallback` | FA3 graph handler 判定不支持 | Capture |
14+| E-010 | 注册 fake 缺失 | `register_fake.*not found\|no fake impl` | 自定义 op 缺少 fake tensor 实现 | Compile/Dynamo |
15+ 
16+## 如何使用
17+ 
18+当 `parse_npu_logs.py` 检测到以上任一模式,会输出:
19+- 模式 ID
20+- 匹配行号和上下文
21+- 建议的诊断方向
@@ -0,0 +1,24 @@
1+# 内存异常模式 — M-Series
2+ 
3+| 模式 ID | 模式名称 | 检测条件 | 说明 |
4+|---------|---------|---------|------|
5+| M-001 | 内存碎片化 | 大量小 alloc/free (< 1MB),但无大块连续 alloc 成功 | NPUCachingAllocator 池碎片化 |
6+| M-002 | 内存泄漏 | alloc_count - free_count 随时间单调递增 | 图 replay 后未释放临时内存 |
7+| M-003 | 跨 Stream 双倍 | 两个 Stream 各自的 peak > 总内存 / 2 | 各 Stream 独立内存池 |
8+| M-004 | 图捕获时分配 | Capture 期间出现 alloc 事件 | 违反捕获规则 (P-CAP-005) |
9+| M-005 | 池耗尽 | reserved ≈ total, allocated << total | 池预留大量未使用内存 |
10+| M-006 | 峰值接近上限 | peak > total × 0.95 | OOM 风险高 |
11+ 
12+## 内存日志分析命令
13+ 
14+```bash
15+# 统计 alloc/free 数量
16+grep "\[Memory\].*alloc" <log> | wc -l
17+grep "\[Memory\].*free" <log> | wc -l
18+ 
19+# 统计各 size 范围的 alloc
20+grep -oP 'alloc \d+ bytes' <log> | awk '{sum[$2]++} END {for (s in sum) print s, sum[s]}'
21+ 
22+# 查看峰值前后日志
23+grep -B 20 -A 10 "max_memory\|peak" <log>
24+```
@@ -0,0 +1,22 @@
1+# 时间线异常模式 — T-Series
2+ 
3+| 模式 ID | 模式名称 | 检测条件 | 说明 |
4+|---------|---------|---------|------|
5+| T-001 | 捕获耗时长 | Capture 时间戳间隔 > 预期 | 模型复杂度正常时为正常现象 |
6+| T-002 | 重放 P99 异常 | P99 延迟 > P50 × 3 | 间歇性瓶颈(Stream 竞争/调度) |
7+| T-003 | 编译耗时长 | Compile 跨度 > 30s | ACL 编译异常,检查算子上限/带宽 |
8+| T-004 | 首次重放显著慢 | replay#1 >> replay#N | 编译在首次重放触发,正常 |
9+| T-005 | 无 warmup 直接测 | 前几次 replay 偏慢 | 缺少 warmup,正常 |
10+| T-006 | 重复编译 | Compile 事件出现多次 | 相同 shape 编译了多次(pool miss) |
11+| T-007 | 重放间隔递增 | replay 时间逐渐变长 | 可能的内存碎片或资源泄漏 |
12+| T-008 | 捕获-编译间隙大 | capture_end → compile 间隔 > 1s | 编译调度延迟 |
13+ 
14+## 基准参考值
15+ 
16+以下是基于 Ascend 910B 的经验值,实际可能因模型和 CANN 版本不同:
17+ 
18+| 阶段 | 预期范围 | 告警阈值 |
19+|------|---------|---------|
20+| 捕获 | < 模型 eager 前向耗时 × 2 | > 10s |
21+| 编译 | 取决于图规模 | > 30s |
22+| 重放 (稳态) | < eager 前向耗时 | > eager × 1.5 |
@@ -0,0 +1,276 @@
1+#!/usr/bin/env python3
2+"""
3+NPU 图模式日志解析器 — TORCH_NPU_LOGS 结构化分析
4+ 
5+用法:
6+ python parse_npu_logs.py <log_file> [--output json|text] [--summary] [--diff <other_log>]
7+ python parse_npu_logs.py --test
8+ 
9+输出:
10+ - 模块分布统计
11+ - 按日志级别分类
12+ - 时间线提取
13+ - 异常模式匹配
14+"""
15+ 
16+import re
17+import sys
18+import json
19+import argparse
20+from collections import defaultdict, Counter
21+from dataclasses import dataclass, field
22+from datetime import datetime
23+from typing import Optional
24+ 
25+ 
26+# ── 数据结构 ───────────────────────────────────────────
27+ 
28+@dataclass
29+class LogEntry:
30+ line_no: int
31+ timestamp: Optional[str]
32+ module: str
33+ action: str
34+ level: str
35+ message: str
36+ 
37+@dataclass
38+class LogSummary:
39+ total_lines: int
40+ modules: dict # {module: count}
41+ by_level: dict # {level: count}
42+ errors: list
43+ warnings: list
44+ timeline: list
45+ matched_patterns: list
46+ 
47+ 
48+# ── 正则模式 ───────────────────────────────────────────
49+ 
50+LOG_LINE_RE = re.compile(
51+ r'\[(?P<timestamp>\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}[.\d]*)\]\s*'
52+ r'\[NPUGRAPH\]\[(?P<module>\w+)\]\[(?P<action>\w+)\]\s*'
53+ r'(?P<message>.*)'
54+)
55+ 
56+# 简化的模块+动作提取(兼容无时间戳格式)
57+SIMPLE_RE = re.compile(
58+ r'\[NPUGRAPH\]\[(?P<module>\w+)\](?:\[(?P<action>\w+)\])?\s*'
59+ r'(?P<message>.*)'
60+)
61+ 
62+# 日志级别
63+LEVEL_RE = re.compile(r'\b(DEBUG|INFO|WARNING|WARN|ERROR|FATAL)\b', re.IGNORECASE)
64+ 
65+# ── 异常模式定义 ────────────────────────────────────────
66+ 
67+ERROR_PATTERNS = [
68+ ('E-001', r'Capture.*(?:FAILED|failed|error)', '图捕获中断'),
69+ ('E-002', r'Replay.*(?:graph not in|captured state|invalid state)', '重放状态异常'),
70+ ('E-003', r'Memory.*(?:aclrtMalloc failed|GE_DEVICE_MEMORY_ALLOC|alloc.*failed)', '内存分配失败'),
71+ ('E-004', r'Update.*(?:invalid.*param|shape.*mismatch|param.*error)', 'Update 参数非法'),
72+ ('E-005', r'TreeManager.*evicting.*graph', '图 LRU 淘汰'),
73+ ('E-006', r'(?:stream|Stream).*(?:mismatch|different|wrong)', 'Stream 不匹配'),
74+ ('E-007', r'Compile.*(?:aclmdl.*failed|build.*error|compilation.*error)', '图编译失败'),
75+ ('E-008', r'(?:HCCL|hccl).*(?:UnmapMem|free.*crash)', 'HCCL UnmapMem'),
76+ ('E-009', r'FA3.*(?:not supported|fallback|unsupported).*graph', 'FA3 图不支持'),
77+ ('E-010', r'register_fake.*(?:not found|no fake impl|missing)', '注册 fake 缺失'),
78+]
79+ 
80+ 
81+# ── 核心逻辑 ────────────────────────────────────────────
82+ 
83+def parse_log(filepath: str) -> list[LogEntry]:
84+ """解析日志文件,返回 LogEntry 列表"""
85+ entries = []
86+ with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
87+ for i, line in enumerate(f, 1):
88+ line = line.rstrip()
89+ if not line:
90+ continue
91+ 
92+ # 尝试完整格式
93+ m = LOG_LINE_RE.match(line)
94+ if m:
95+ entries.append(LogEntry(
96+ line_no=i,
97+ timestamp=m.group('timestamp'),
98+ module=m.group('module'),
99+ action=m.group('action'),
100+ level=_extract_level(m.group('message'), m.group('action')),
101+ message=m.group('message'),
102+ ))
103+ continue
104+ 
105+ # 尝试简化格式
106+ m = SIMPLE_RE.match(line)
107+ if m:
108+ entries.append(LogEntry(
109+ line_no=i,
110+ timestamp=None,
111+ module=m.group('module'),
112+ action=m.group('action') or 'UNKNOWN',
113+ level=_extract_level(m.group('message'), m.group('action') or ''),
114+ message=m.group('message'),
115+ ))
116+ return entries
117+ 
118+ 
119+def _extract_level(message: str, action: str = '') -> str:
120+ # 优先从 action 字段提取(如 [Memory][ERROR])
121+ for candidate in (action, message):
122+ m = LEVEL_RE.search(candidate)
123+ if m:
124+ lvl = m.group(1).upper()
125+ return 'WARNING' if lvl == 'WARN' else lvl
126+ return 'INFO'
127+ 
128+ 
129+def summarize(entries: list[LogEntry]) -> LogSummary:
130+ """生成摘要"""
131+ modules = Counter(e.module for e in entries)
132+ by_level = Counter(e.level for e in entries)
133+ 
134+ errors = [e for e in entries if e.level in ('ERROR', 'FATAL')]
135+ warnings = [e for e in entries if e.level == 'WARNING']
136+ 
137+ # 提取时间线
138+ timeline = [
139+ {'line': e.line_no, 'ts': e.timestamp, 'module': e.module,
140+ 'action': e.action, 'level': e.level, 'msg': e.message[:120]}
141+ for e in entries
142+ if e.level in ('ERROR', 'FATAL', 'WARNING') or e.action in ('BEGIN', 'END', 'START', 'STOP')
143+ ]
144+ 
145+ # 模式匹配
146+ matched = []
147+ for entry in errors + warnings:
148+ full_text = f"[{entry.module}][{entry.action}] {entry.message}"
149+ for pattern_id, pattern_re, desc in ERROR_PATTERNS:
150+ if re.search(pattern_re, full_text, re.IGNORECASE):
151+ matched.append({
152+ 'pattern_id': pattern_id,
153+ 'description': desc,
154+ 'line': entry.line_no,
155+ 'message': entry.message[:200],
156+ })
157+ break
158+ 
159+ return LogSummary(
160+ total_lines=len(entries),
161+ modules=dict(modules.most_common()),
162+ by_level=dict(by_level),
163+ errors=[{'line': e.line_no, 'msg': e.message[:200]} for e in errors],
164+ warnings=[{'line': e.line_no, 'msg': e.message[:200]} for e in warnings],
165+ timeline=timeline,
166+ matched_patterns=matched,
167+ )
168+ 
169+ 
170+def print_summary(s: LogSummary):
171+ """文本格式输出摘要"""
172+ print(f"=== 日志分析摘要 ===")
173+ print(f"总行数: {s.total_lines}")
174+ print(f"模块分布: {json.dumps(s.modules, ensure_ascii=False)}")
175+ print(f"级别分布: {json.dumps(s.by_level, ensure_ascii=False)}")
176+ 
177+ print(f"\n--- 错误 ({len(s.errors)}) ---")
178+ for e in s.errors[:20]:
179+ print(f" L{e['line']}: {e['msg']}")
180+ 
181+ print(f"\n--- 警告 ({len(s.warnings)}) ---")
182+ for w in s.warnings[:10]:
183+ print(f" L{w['line']}: {w['msg']}")
184+ 
185+ if s.matched_patterns:
186+ print(f"\n--- 匹配到的异常模式 ---")
187+ seen = set()
188+ for m in s.matched_patterns:
189+ if m['pattern_id'] not in seen:
190+ seen.add(m['pattern_id'])
191+ print(f" [{m['pattern_id']}] {m['description']}")
192+ print(f" 位置: L{m['line']}")
193+ print(f" 内容: {m['message'][:150]}")
194+ 
195+ if s.timeline:
196+ print(f"\n--- 关键事件时间线 ({len(s.timeline)} 条目) ---")
197+ for t in s.timeline[:30]:
198+ ts = t['ts'] or 'N/A'
199+ print(f" {ts} [{t['module']}][{t['action']}] {t['level']}: {t['msg'][:100]}")
200+ 
201+ 
202+# ── 主入口 ──────────────────────────────────────────────
203+ 
204+def main():
205+ parser = argparse.ArgumentParser(description='NPU 图模式日志解析器')
206+ parser.add_argument('logfile', nargs='?', help='日志文件路径')
207+ parser.add_argument('--output', choices=['json', 'text'], default='text')
208+ parser.add_argument('--summary', action='store_true', default=True)
209+ parser.add_argument('--test', action='store_true', help='自检')
210+ args = parser.parse_args()
211+ 
212+ if args.test:
213+ _self_test()
214+ return
215+ 
216+ if not args.logfile:
217+ parser.print_help()
218+ sys.exit(1)
219+ 
220+ entries = parse_log(args.logfile)
221+ if not entries:
222+ print("警告: 未找到 NPUGRAPH 日志条目。请确认日志格式是否正确。", file=sys.stderr)
223+ print("期望格式: [NPUGRAPH][Module][Action] message", file=sys.stderr)
224+ sys.exit(1)
225+ 
226+ summary = summarize(entries)
227+ 
228+ if args.output == 'json':
229+ print(json.dumps({
230+ 'total_lines': summary.total_lines,
231+ 'modules': summary.modules,
232+ 'by_level': summary.by_level,
233+ 'errors': summary.errors,
234+ 'warnings': summary.warnings,
235+ 'matched_patterns': summary.matched_patterns,
236+ }, ensure_ascii=False, indent=2))
237+ else:
238+ print_summary(summary)
239+ 
240+ 
241+def _self_test():
242+ """自检"""
243+ test_log = """
244+[2026-06-10 10:00:00.100] [NPUGRAPH][TreeManager][WARMUP] Warmup iteration start
245+[2026-06-10 10:00:01.000] [NPUGRAPH][Capture][BEGIN] capture_begin on stream 0x1
246+[2026-06-10 10:00:03.500] [NPUGRAPH][Capture][END] capture_end success
247+[2026-06-10 10:00:03.600] [NPUGRAPH][Compile][START] ACL graph compilation
248+[2026-06-10 10:00:05.000] [NPUGRAPH][Compile][END] compilation complete
249+[2026-06-10 10:00:05.100] [NPUGRAPH][Replay][INFO] replay() #1 success, 12.3ms
250+[2026-06-10 10:00:05.150] [NPUGRAPH][Memory][ERROR] aclrtMalloc failed: device memory insufficient
251+[2026-06-10 10:00:05.200] [NPUGRAPH][Replay][ERROR] Replay ERROR: graph not in captured state
252+[2026-06-10 10:00:05.300] [NPUGRAPH][TreeManager][EVICT] evicting graph key=hash_abc123
253+"""
254+ import tempfile, os
255+ with tempfile.NamedTemporaryFile(mode='w', suffix='.log', delete=False) as f:
256+ f.write(test_log)
257+ testpath = f.name
258+ 
259+ try:
260+ entries = parse_log(testpath)
261+ print(f"[SELF TEST] 解析条目数: {len(entries)} (期望 9)")
262+ assert len(entries) == 9, f"FAIL: 期望 9 条目,实际 {len(entries)}"
263+ 
264+ summary = summarize(entries)
265+ assert summary.total_lines == 9
266+ assert len(summary.errors) == 2
267+ assert len(summary.matched_patterns) >= 2
268+ print(f"[SELF TEST] PASSED — 所有断言通过")
269+ print(f" 错误数: {len(summary.errors)}")
270+ print(f" 匹配模式: {[m['pattern_id'] for m in summary.matched_patterns]}")
271+ finally:
272+ os.unlink(testpath)
273+ 
274+ 
275+if __name__ == '__main__':
276+ main()
@@ -0,0 +1,70 @@
1+---
2+name: npu-graph-mcp-integration
3+description: >-
4+ Use when user asks about configuring or setting up MCP tools for NPU graph
5+ mode development — Context7, Playwright, Greptile, or custom NPU MCP servers.
6+ Use keywords: "配置MCP", "setup mcp", "启用Context7", "mcp工具",
7+ "add mcp", "MCP server".
8+ NOT for using MCP tools in diagnosis — this skill only handles configuration.
9+---
10+ 
11+# Graph MCP Integration — MCP 工具集成
12+ 
13+## The Iron Law
14+ 
15+```
16+MCP 工具是辅助,不是替代。诊断的核心永远是理解 NPUGraph 五层架构,MCP 只是加速信息检索。
17+```
18+ 
19+---
20+ 
21+## 推荐工具
22+ 
23+| 优先级 | 工具 | 价值 |
24+|--------|------|------|
25+| P0 | **Context7** | 查询 CANN/PyTorch API 文档 |
26+| P0 | **NPU-Log-Parser** (自建) | 结构化解析日志 |
27+| P1 | **Playwright** | 捕获 CANN Profiler 页面 |
28+| P2 | **Graph-Dump-Analyzer** (自建) | 分析 graph.debug_dump() JSON |
29+ 
30+MCP 工具评估详情 → [references/mcp-evaluation.md](references/mcp-evaluation.md)
31+ 
32+---
33+ 
34+## 快速配置
35+ 
36+### Context7 (文档查询)
37+ 
38+```json
39+{ "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp"] } } }
40+```
41+ 
42+### Playwright (浏览器自动化)
43+ 
44+```json
45+{ "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest"] } } }
46+```
47+ 
48+### NPU-Log-Parser (自建)
49+ 
50+```json
51+{ "mcpServers": { "npu-log-parser": { "command": "python", "args": ["-m", "npu_mcp.log_parser"], "cwd": "${workspaceFolder}/skills/NPU-graph-skill/graph-mcp-integration" } } }
52+```
53+ 
54+解析脚本位于 [../graph-log-analyzer/scripts/parse_npu_logs.py](../graph-log-analyzer/scripts/parse_npu_logs.py)。
55+ 
56+---
57+ 
58+## 自建 MCP 规范
59+ 
60+1. **Python stdio 协议** — 兼容 Claude Code MCP 客户端
61+2. **JSON 输入输出** — 确保结构化
62+3. **错误隔离** — MCP crash 不影响主诊断流程
63+ 
64+---
65+ 
66+## 注意事项
67+ 
68+- NPU 开发环境通常在内网,Context7 等云端 MCP 可能不可用
69+- MCP 需要 Python 3.10+
70+- 日志可能含敏感信息,MCP 只做本地解析
@@ -0,0 +1,46 @@
1+{
2+ "_comment": "NPU 图模式开发推荐 MCP 配置。复制需要的条目到项目根目录 .mcp.json",
3+ "mcpServers": {
4+ "context7": {
5+ "command": "npx",
6+ "args": ["-y", "@upstash/context7-mcp"],
7+ "description": "实时文档查询 — CANN/PyTorch/C++ API"
8+ },
9+ "playwright": {
10+ "command": "npx",
11+ "args": ["@playwright/mcp@latest"],
12+ "description": "浏览器自动化 — CANN Profiler Web UI"
13+ },
14+ "greptile": {
15+ "url": "https://api.greptile.com/mcp",
16+ "headers": {
17+ "Authorization": "Bearer ${GREPTILE_API_TOKEN}"
18+ },
19+ "description": "代码库语义搜索"
20+ },
21+ "npu-log-parser": {
22+ "command": "python",
23+ "args": ["-m", "npu_mcp.log_parser"],
24+ "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts",
25+ "description": "自建 — TORCH_NPU_LOGS 结构化解析",
26+ "env": {
27+ "PYTHONPATH": "${workspaceFolder}/NPUGRAPH_SKILL/scripts"
28+ }
29+ },
30+ "graph-dump-analyzer": {
31+ "command": "python",
32+ "args": ["-m", "npu_mcp.graph_dump_analyzer"],
33+ "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts",
34+ "description": "自建 — graph.debug_dump() JSON 分析",
35+ "env": {
36+ "PYTHONPATH": "${workspaceFolder}/NPUGRAPH_SKILL/scripts"
37+ }
38+ },
39+ "npu-smi": {
40+ "command": "python",
41+ "args": ["-m", "npu_mcp.npu_smi"],
42+ "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/scripts",
43+ "description": "自建 — npu-smi 命令封装,结构化设备状态查询"
44+ }
45+ }
46+}
@@ -0,0 +1,142 @@
1+# MCP 服务器评估报告 — NPU 图模式开发
2+ 
3+> 评估日期:2026-06-10
4+> 评估目标:筛选对 NPU 图模式开发/调试/性能分析最有价值的 MCP 服务器和开源工具
5+ 
6+---
7+ 
8+## 1. 当前 MCP 状态
9+ 
10+| 项目 | 状态 |
11+|------|------|
12+| 全局 `claude.json` 中 MCP 配置 | **全部为空** — 未启用任何 MCP 服务器 |
13+| 项目级 `.mcp.json` | **不存在** |
14+| 可用但未启用的 MCP 插件 | 15 个(见下方) |
15+| 远程连接机制 | SSH + openai.yaml(非 MCP,用于连接 NPU 远程服务器) |
16+ 
17+**结论**: 当前 NPU 图模式开发完全依赖 SKILL 文件 + Bash 工具,零 MCP 集成。这是最大的效率提升空间。
18+ 
19+---
20+ 
21+## 2. 可用 MCP 服务器评估
22+ 
23+### 2.1 高价值(建议立即启用)
24+ 
25+| MCP 服务器 | 评分 | 用途 | NPU 图模式场景 |
26+|------------|------|------|----------------|
27+| **Context7** | ★★★★★ | 实时文档查询 | 查询 CANN API、PyTorch C++ 文档、ACL 头文件定义。避免手动查网页 |
28+| **Playwright** | ★★★★☆ | 浏览器自动化 | 捕获 CANN Profiler Web UI 截图、操作 MSCCL 配置页面、导出性能报告 |
29+| **Greptile** | ★★★★☆ | 代码库语义理解 | 理解 pytorch/ 仓库中 NPUGraph 调用链、追踪 op-plugin 注册关系 |
30+ 
31+### 2.2 中等价值(有条件启用)
32+ 
33+| MCP 服务器 | 评分 | 用途 | NPU 图模式场景 |
34+|------------|------|------|----------------|
35+| **GitHub** | ★★★☆☆ | Issue/PR 自动化 | 实际仓库在 gitcode.com,非 GitHub。但上游 PyTorch PR 分析可用 |
36+| **Serena** | ★★★☆☆ | AI 编码代理 | 辅助重构 NPUGraph 代码、生成测试用例 |
37+| **Linear** | ★★★☆☆ | 项目管理 | 如果团队用 Linear 跟踪 NPUGraph bug |
38+ 
39+### 2.3 低价值(不推荐)
40+ 
41+| MCP 服务器 | 原因 |
42+|------------|------|
43+| GitLab, Asana, Discord, Telegram, iMessage, FakeChat | 与 NPU 图开发无关 |
44+| Firebase, Terraform, Laravel Boost | 不适用场景 |
45+| Docker | CANN 环境通常直接安装在 NPU 服务器上,不容器化 |
46+ 
47+---
48+ 
49+## 3. 推荐的自定义 MCP 设计(高优先级)
50+ 
51+现有通用 MCP 无法覆盖 NPU 图模式的核心需求,建议自建以下专用 MCP:
52+ 
53+### 3.1 NPU-Log-Parser MCP ★★★★★
54+ 
55+```
56+功能:解析 TORCH_NPU_LOGS 输出,提取结构化信息
57+输入:日志文件路径 或 直接读取日志流
58+输出:
59+ - 按模块(aclgraph/memory/dispatch)分类的事件列表
60+ - 时间线(capture_begin → capture_end → replay → execute)
61+ - 错误码 → 错误描述映射表
62+ - 异常模式自动标注(如 "memcpy size mismatch", "aclmdlExecute failed")
63+实现方式:Python stdio MCP server
64+核心依赖:正则匹配引擎 + ACL 错误码表
65+```
66+ 
67+### 3.2 Graph-Dump-Analyzer MCP ★★★★★
68+ 
69+```
70+功能:解析 graph.debug_dump(path) 导出的 JSON,结构化分析
71+输入:JSON dump 文件路径
72+输出:
73+ - 图拓扑结构(节点数、边数、层数)
74+ - 算子类型分布统计
75+ - 异常结构检测(孤立节点、环、超长链)
76+ - 与上次成功 dump 的 diff
77+实现方式:Python stdio MCP server
78+核心依赖:JSON schema 定义 + 图算法(networkx)
79+```
80+ 
81+### 3.3 NPU-SMI MCP ★★★★☆
82+ 
83+```
84+功能:封装 npu-smi 命令,提供结构化设备状态查询
85+输入:查询类型(memory/process/temperature/power)
86+输出:JSON 格式的设备状态
87+场景:调试 OOM 时自动查询内存占用;性能分析时监控功耗
88+实现方式:Python stdio MCP server,封装 npu-smi 命令行
89+```
90+ 
91+### 3.4 CANN-Doc-Query MCP ★★★☆☆
92+ 
93+```
94+功能:查询 CANN 离线文档(HTML/PDF),快速定位 API 说明
95+输入:API 名称(如 aclmdlRICaptureBegin)
96+输出:函数签名、参数说明、返回值、错误码、示例代码
97+实现方式:Python MCP + 本地文档索引(meilisearch 或 simple grep)
98+注意:如果 Context7 已能满足需求,此 MCP 优先级降低
99+```
100+ 
101+---
102+ 
103+## 4. 开源工具补充
104+ 
105+这些不是 MCP,但对图模式开发有直接帮助:
106+ 
107+| 工具 | 用途 | 集成方式 |
108+|------|------|----------|
109+| **networkx** | 图拓扑分析 | Python 脚本 |
110+| **rich** | 终端美化输出(日志高亮、进度条) | Python 脚本 |
111+| **pydantic** | 结构化日志/Analytics 数据验证 | Python 脚本 |
112+| **watchfiles** | 监控日志文件变化,实时触发分析 | Python 脚本 |
113+| **graphviz** | 生成图捕获拓扑的 SVG 可视化 | Python 脚本 |
114+ 
115+---
116+ 
117+## 5. 推荐实施优先级
118+ 
119+```
120+Phase 1 (本周):
121+ ├── 启用 Context7 MCP
122+ └── 创建 NPU-Log-Parser MCP 原型
123+ 
124+Phase 2 (本月):
125+ ├── 启用 Playwright MCP
126+ ├── 创建 Graph-Dump-Analyzer MCP
127+ └── 创建 NPU-SMI MCP
128+ 
129+Phase 3 (下月):
130+ ├── 启用 Greptile MCP
131+ ├── 评估 CANN-Doc-Query MCP 必要性
132+ └── 建立 MCP 回归测试
133+```
134+ 
135+---
136+ 
137+## 6. 风险与注意事项
138+ 
139+1. **Context7 可能无法访问 CANN 内部文档** — CANN 文档多为离线 HTML,Context7 可能索引不到。需要验证。
140+2. **NPU 环境隔离** — MCP server 运行在开发机上,但 NPU 在远程服务器上。需要通过 SSH 转发或 API 代理解决。
141+3. **日志量级** — `TORCH_NPU_LOGS=+aclgraph,+dispatch` 输出可达 GB 级,日志解析器需要流式处理。
142+4. **安全** — `npu-smi` 查询包含硬件信息,MCP 接口需限制暴露范围。
@@ -0,0 +1,84 @@
1+# MCP 安装配置指南
2+ 
3+## 前提条件
4+ 
5+- Claude Code CLI 已安装
6+- Python 3.10+ (自建 MCP 需要)
7+- Node.js 18+ (Context7, Playwright 需要)
8+ 
9+## 启用通用 MCP
10+ 
11+### Context7 — 推荐最先启用
12+ 
13+在 Claude Code 对话中:
14+```
15+/add-mcp context7
16+```
17+ 
18+或手动创建项目根目录 `.mcp.json`:
19+```json
20+{
21+ "mcpServers": {
22+ "context7": {
23+ "command": "npx",
24+ "args": ["-y", "@upstash/context7-mcp"]
25+ }
26+ }
27+}
28+```
29+ 
30+验证:
31+```
32+帮我用 Context7 查询 PyTorch torch.cuda.CUDAGraph API
33+```
34+ 
35+### Playwright
36+ 
37+```
38+/add-mcp playwright
39+```
40+ 
41+用于操作 CANN Profiler Web 页面。
42+ 
43+### Greptile(可选,需要 token)
44+ 
45+需要先注册 https://greptile.com 获取 API token。
46+ 
47+## 自建 MCP 安装
48+ 
49+### NPU-Log-Parser
50+ 
51+```bash
52+# 1. 安装依赖
53+pip install mcp
54+ 
55+# 2. 测试运行
56+python NPUGRAPH_SKILL/scripts/parse_npu_logs.py --test
57+ 
58+# 3. 配置为 MCP server
59+# 在 .mcp.json 中添加
60+{
61+ "npu-log-parser": {
62+ "command": "python",
63+ "args": ["-m", "npu_mcp.log_parser"],
64+ "cwd": "${workspaceFolder}/NPUGRAPH_SKILL/MCP"
65+ }
66+}
67+```
68+ 
69+### Graph-Dump-Analyzer
70+ 
71+TODO: v0.2.0
72+ 
73+### NPU-SMI
74+ 
75+TODO: v0.3.0
76+ 
77+## 故障排查
78+ 
79+| 问题 | 解决方案 |
80+|------|---------|
81+| `npx` 不可用 | 安装 Node.js 18+ |
82+| Python `mcp` 库找不到 | `pip install mcp` |
83+| Context7 查询不到 CANN 文档 | CANN 是离线文档,Context7 可能无法索引 |
84+| MCP server 超时 | 检查网络代理,MCP 默认 60s 超时 |
@@ -0,0 +1,166 @@
1+---
2+name: npu-graph-mode-diagnostics
3+description: >-
4+ Use when user reports NPU graph mode errors, crashes, or exceptions —
5+ capture failure (aclmdlRICaptureBegin), replay failure (aclmdlRIExecuteAsync),
6+ graph OOM, dynamic shape update failure, graph_breaks, compile hang,
7+ or any error with [NPUGRAPH]/ACLGraph tags.
8+ Covers all four enablement modes (NPUGraph/graph()/make_graphed_callables/torch.compile).
9+ NOT for performance analysis or log-only analysis — handoff to respective skills.
10+---
11+ 
12+# Graph Mode Diagnostics — 图模式问题诊断
13+ 
14+## The Iron Law
15+ 
16+```
17+在未完成 L5→L1 层级定位并识别到具体失败点之前,禁止提出修复方案。
18+先定位,再修复。永远是先定位,再修复。
19+```
20+ 
21+---
22+ 
23+## When to Use
24+ 
25+| 症状 | 错误特征 |
26+|------|----------|
27+| 图捕获阶段报错 | `aclmdlRICaptureBegin` / `capture_begin()` 失败 |
28+| 图重放阶段报错 | `aclmdlRIExecuteAsync` / `replay()` 失败 |
29+| 图内 OOM | `NPUCachingAllocator` / `aclrtMalloc` 失败 |
30+| 动态形状 update 失败 | `update_graph()` / `NPUGraphUpdateError` |
31+| graph_breaks 异常多 | Dynamo 图断裂数超出预期 |
32+| 图编译 hang | `torch.compile(backend="npugraphs")` 卡死 |
33+ 
34+**领域知识(五层架构、源文件、使能方式、约束)→ [domain-context.md](references/domain-context.md)**
35+ 
36+---
37+ 
38+## Phase 1: 分类 — 确定问题阶段
39+ 
40+```
41+┌─ 错误发生在哪个阶段?
42+│
43+├── 捕获阶段 (Capture) ──→ Phase 2A 特征:aclmdlRICaptureBegin/End 失败
44+├── 重放阶段 (Replay) ──→ Phase 2B 特征:aclmdlRIExecuteAsync 失败
45+├── 内存阶段 (Memory) ──→ Phase 2C 特征:OOM / allocator 错误
46+├── Update 阶段 ──→ Phase 2D 特征:update_graph() 失败
47+└── 编译阶段 (Compile) ──→ Phase 2E 特征:torch.compile 卡死 / Dynamo 错误
48+```
49+ 
50+**检查清单**:
51+- [ ] 记录完整错误栈(Python + C++)
52+- [ ] 记录 `TORCH_NPU_LOGS` 输出(至少 `aclgraph` 模块)
53+- [ ] 确认使能方式(1/2/3/4)
54+- [ ] 确认 CANN 版本和 NPU 型号
55+ 
56+---
57+ 
58+## Phase 2: 分阶段诊断
59+ 
60+### 2A — 捕获阶段
61+ 
62+**先检查常见原因**:图内 CPU 同步操作(`.item()`, `.cpu()`, `print()`)→ 条件分支 → 嵌套捕获 → Stream 异常
63+ 
64+**定位层级**:
65+- L4 (Python): `graphs.py` capture_begin/end 逻辑、LRU cache、graph pool
66+- L2 (C++): `NPUGraph.cpp` ACLGraph 包装、ACL 状态机
67+- L1 (ACL): `aclmdlRICaptureBegin` 返回值、CANN 兼容性
68+ 
69+**获取诊断信息**:
70+```bash
71+export TORCH_NPU_LOGS=+aclgraph,+dispatch
72+export TORCH_NPU_GRAPHS_LOG_LEVEL=DEBUG
73+export TORCH_NPUGRAPH_GC=1
74+```
75+ 
76+### 2B — 重放阶段
77+ 
78+**三连问**:图是否正确捕获?→ 是否需要 update?→ 图是否被意外释放(LRU/reset/跨线程)?
79+ 
80+**输入验证**:检查 shape/stride/dtype/device/contiguous 与捕获时一致。
81+ 
82+**ACL 诊断**:`g.debug_dump()` 导出图结构,对照 ACL 错误码。
83+ 
84+### 2C — 内存阶段
85+ 
86+**四连问**:捕获期间分配了新内存?→ 内存池耗尽?→ 碎片化?→ 跨 Stream 内存访问?
87+ 
88+### 2D — Update 阶段
89+ 
90+**前提检查**:TND + keep_prob < 1.0 → 必须 fallback。CPU tensor 参数、lvalue 传递。
91+ 
92+**常见原因**:shape 超出预留空间 → dtype 不匹配 → 非法参数 → 并发竞态。
93+ 
94+### 2E — 编译阶段
95+ 
96+```bash
97+TORCH_LOGS=graph_breaks python script.py # 查看图断裂原因
98+torch._dynamo.explain(model)(inputs) # Dynamo 分析
99+torch.compile(backend="aot_eager") # 绕过图优化定位
100+```
101+ 
102+---
103+ 
104+## Phase 3: 模式匹配
105+ 
106+先查 [known-patterns.md](references/known-patterns.md)(14+ 模式)再深入源码:
107+- 捕获: 4 个模式 | 重放: 5 个 | 内存: 3 个 | 性能: 2 个
108+ 
109+## Phase 4: 深度追踪
110+ 
111+已知模式无法匹配时,按路径深入:
112+- Python: `graphs.py` → `_graph_tree.py` → `utils/_graph_tree.py`
113+- C++: `NPUGraph.cpp` → `NPUGraph.h`
114+- ACL: CANN headers + `/var/log/npu/` 驱动日志
115+ 
116+## Phase 5: 报告
117+ 
118+```markdown
119+## 问题诊断报告
120+### 1. 问题概况
121+- 错误信息/阶段/层级/严重程度
122+### 2. 根因分析
123+- 直接原因/触发条件/影响范围
124+### 3. 修复建议
125+- 短期/长期方案 + 文件定位
126+### 4. 验证步骤
127+```
128+ 
129+---
130+ 
131+## Red Flags
132+ 
133+| 症状 | 误区 | 正确做法 |
134+|------|------|----------|
135+| 报错含 "memory" | 直接加 `empty_cache()` | 先查分配栈 |
136+| 捕获阶段失败 | 直接改 op 实现 | 先确认 op 是否在 allowlist |
137+| shape mismatch | 直接增大预留空间 | 先确认 update 调用是否正确 |
138+| 偶尔成功偶尔失败 | 认为是硬件不稳定 | 检查竞态(LRU/多线程/Stream) |
139+| torch.compile hang | 强行 kill | 先 dump Dynamo 中间状态 |
140+ 
141+## Handoff
142+ 
143+| 条件 | 移交技能 |
144+|------|----------|
145+| 确认是缺失功能 | `requirements` |
146+| 根因确认需修代码 | `implementation` |
147+| 修复完成需验证 | `verification` |
148+| 性能相关(非功能 bug) | `NPU-graph-performance-profiling` |
149+| 日志量太大需自动分析 | `NPU-graph-log-analyzer` |
150+| MCP 工具配置 | `NPU-graph-mcp-integration` |
151+ 
152+---
153+ 
154+## DFX: 知识沉淀
155+ 
156+诊断完成并输出 Phase 5 报告后,**必须执行**:
157+ 
158+1. **问用户**:「是否将此诊断案例记录到知识库?[Y/n]」
159+2. 如 Y → 按 [../knowledge/CASE_TEMPLATE.md](../knowledge/CASE_TEMPLATE.md) 格式生成条目,包含:
160+ - 症状原文(从 Phase 1 检查清单提取)
161+ - 层级定位(从 Phase 4 深度追踪提取)
162+ - 根因 + 解决方案(从 Phase 5 报告提取)
163+3. 追加到 [../knowledge/history.md](../knowledge/history.md) 对应分类下
164+4. 更新分类总览计数
165+5. 如该案例匹配了 known-patterns.md 中的模式 → 在条目中标注关联模式编号
166+6. 如该案例**不匹配**任何已知模式 → 提示用户:「发现新问题模式,是否同步更新 known-patterns.md?」
@@ -0,0 +1,70 @@
1+# 图模式诊断检查清单
2+ 
3+> 每次诊断必须完成以下检查项,不可跳过。
4+ 
5+---
6+ 
7+## Phase 1: 分类
8+ 
9+- [ ] 记录完整错误栈(Python traceback + C++ 层错误码)
10+- [ ] 记录 TORCH_NPU_LOGS 输出片段(至少前后 50 行)
11+- [ ] 确认使能方式(NPUGraph / graph() / make_graphed_callables / torch.compile)
12+- [ ] 确认 CANN 版本、PyTorch 版本、NPU 型号
13+- [ ] 确定问题阶段(Capture / Replay / Memory / Update / Compile)
14+ 
15+## Phase 2: 阶段诊断
16+ 
17+### 捕获阶段
18+- [ ] 检查图内有无 .item() / .cpu() / print(tensor)
19+- [ ] 检查有无 data-dependent 条件分支
20+- [ ] 检查是否嵌套捕获
21+- [ ] 检查 Stream 是否正确
22+- [ ] 检查是否首次运行触发 lazy init
23+- [ ] 用 `TORCH_LOGS=graph_breaks` 确认图完整性
24+ 
25+### 重放阶段
26+- [ ] 检查输入 shape/stride/dtype 与捕获时一致性
27+- [ ] 检查设备(device)是否正确
28+- [ ] 检查内存连续性(contiguous)
29+- [ ] 检查是否需要 update_graph()
30+- [ ] 检查图是否被 LRU 淘汰
31+- [ ] 检查图对象生命周期
32+- [ ] 检查 Graph Pool 状态
33+- [ ] 用 `debug_dump()` 导出图结构
34+ 
35+### 内存阶段
36+- [ ] 检查 `torch.npu.memory_allocated()` 和 `max_memory_allocated()`
37+- [ ] 检查 `memory_reserved()` vs `memory_allocated()`
38+- [ ] 检查 NPUCachingAllocator 统计信息
39+- [ ] 确认 empty_cache() 是否会影响 HCCL
40+- [ ] 检查跨 Stream 内存占用
41+ 
42+### Update 阶段
43+- [ ] 检查 TND + Dropout 是否已 fallback
44+- [ ] 检查 CPU Tensor 参数正确性
45+- [ ] 检查 seed/offset shape (1x1)
46+- [ ] 检查 C++ 宏参数是否 lvalue
47+ 
48+### 编译阶段
49+- [ ] 检查 graph_breaks 数量和是否合理
50+- [ ] 用 `torch._dynamo.explain()` 分析
51+- [ ] 尝试 `backend="aot_eager"` 对比
52+ 
53+## Phase 3: 模式匹配
54+ 
55+- [ ] 检查 [known-patterns.md](known-patterns.md) 找到匹配模式
56+- [ ] 检查 [error-code-map.md](error-code-map.md) 定位错误码
57+ 
58+## Phase 4: 深度追踪
59+ 
60+- [ ] Python 层:`graphs.py` / `_graph_tree.py` 源码
61+- [ ] C++ 层:`NPUGraph.cpp` 源码
62+- [ ] ACL 层:CANN 文档
63+- [ ] 检查是否有相关 issue/PR
64+ 
65+## Phase 5: 报告
66+ 
67+- [ ] 填写问题概况(阶段、层级、严重程度)
68+- [ ] 明确根因(直接原因 + 触发条件)
69+- [ ] 提出修复方案(短期 + 长期)
70+- [ ] 编写验证步骤
@@ -0,0 +1,43 @@
1+# 图模式领域知识
2+ 
3+> 从 `SKILL.md` Domain Context 拆分,诊断时按需查阅。
4+ 
5+## 五层架构
6+ 
7+```
8+L5 用户代码 model.forward(), 训练循环, torch.compile 包装
9+L4 Python API npu/graphs.py, npu/_graph_tree.py, utils/_graph_tree.py
10+L3 Graph Tree NPUGraphTreeManager, NPUGraphNode, NPUWarmupNode
11+L2 C++ 核心 NPUGraph.cpp/h, NPUCachingAllocator, aclmdlRICaptureBegin/End
12+L1 ACL 运行时 ACL DVPP, ACL Runtime, CANN 驱动层
13+L0 NPU 硬件/驱动 Ascend 910B/910B2/910C 硬件, 固件, HCCL
14+```
15+ 
16+## 关键源文件
17+ 
18+| 文件 | 路径(相对 pytorch/torch_npu/) |
19+|------|-------------------------------|
20+| NPUGraph Python API | `npu/graphs.py` |
21+| Graph Tree 管理 | `npu/_graph_tree.py` |
22+| Graph Tree 工具 | `utils/_graph_tree.py` |
23+| C++ NPUGraph 核心 | `csrc/core/npu/NPUGraph.h`, `NPUGraph.cpp` |
24+| C++ 内存管理 | `csrc/core/npu/NPUCachingAllocator.h` |
25+| FA3 图处理 | `npu/_npugraph_handlers/_fa3_graph_handler.py` |
26+| Inductor 图分区 | `_inductor/npu_fusion_attention_graph.py` |
27+ 
28+## 四种使能方式
29+ 
30+```
31+方式1: torch_npu.npu.NPUGraph → 底层 API,手动 capture_begin/end
32+方式2: torch_npu.npu.graph(g) → 上下文管理器,自动 Stream 同步
33+方式3: torch_npu.npu.make_graphed_callables → 高级封装,安全子图
34+方式4: torch.compile(backend="npugraphs") → Dynamo + NPUGraph Tree
35+```
36+ 
37+## 关键约束速查
38+ 
39+1. **图捕获期间不能有 CPU-GPU 同步**(`.item()`, `.cpu()`, `print(tensor)`)
40+2. **动态形状**需要 update 机制,非所有 op 支持
41+3. **TND + Dropout** (`keep_prob < 1.0`) 必须 fallback
42+4. **捕获中禁止分配新 NPU 内存**,必须预分配
43+5. **Stream 隔离**:捕获和重放必须在同一 Stream
@@ -0,0 +1,40 @@
1+# ACL 错误码 → 根因映射
2+ 
3+> 快速查询 ACL 运行时错误码的含义和最可能根因
4+> 基于 CANN 7.0+ 错误码表
5+ 
6+---
7+ 
8+## ACL Runtime 错误 (ACL_ERROR_RT_*)
9+ 
10+| 错误码 | 宏 | 含义 | NPU 图模式常见场景 |
11+|--------|-----|------|-------------------|
12+| `0x100000` | `ACL_ERROR_RT_MODEL_EXECUTE_FAILED` | 模型执行失败 | 图重放失败(最常见),检查图是否已正确编译 |
13+| `0x100001` | `ACL_ERROR_RT_INVALID_DEVICE` | 无效设备 | device_id 错误,或 NPU 未就绪 |
14+| `0x100002` | `ACL_ERROR_RT_MEMORY_ALLOC_FAILED` | 内存分配失败 | 图内 OOM |
15+| `0x100003` | `ACL_ERROR_RT_STREAM_CONTEXT` | Stream 上下文错误 | 捕获和重放 Stream 不一致 |
16+| `0x100004` | `ACL_ERROR_RT_MODEL_NOT_INIT` | 模型未初始化 | 图未编译就 replay |
17+ 
18+## ACL Graph 错误 (ACL_ERROR_GE_*)
19+ 
20+| 错误码 | 宏 | 含义 | NPU 图模式常见场景 |
21+|--------|-----|------|-------------------|
22+| `0x200000` | `ACL_ERROR_GE_DEVICE_MEMORY_ALLOC_FAILED` | GE 设备内存分配失败 | 图编译时内存不足 |
23+| `0x200001` | `ACL_ERROR_GE_EXECUTOR_NOT_INIT` | 执行器未初始化 | 图编译未完成就尝试执行 |
24+| `0x200002` | `ACL_ERROR_GE_GRAPH_NOT_INIT` | 图未初始化 | capture 未完成就调用 replay |
25+| `0x200003` | `ACL_ERROR_GE_PARAM_INVALID` | 参数无效 | update 时 shape/stride 参数非法 |
26+ 
27+## 快速查询流程
28+ 
29+```
30+看到错误码 →
31+ 1. 查本表找到对应宏
32+ 2. 查看 NPU 图模式常见场景列
33+ 3. 如匹配 → 跳转到 graph-mode-diagnostics 对应 Phase
34+ 4. 如不匹配 → 查 CANN 官方文档
35+```
36+ 
37+## 备注
38+ 
39+- 错误码会随 CANN 版本变化,以运行环境的 `acl.h` 为准
40+- 完整错误码表参见 CANN 安装目录 `include/acl.h` 和 `include/ge/ge_error_codes.h`
@@ -0,0 +1,212 @@
1+# 已知问题模式 — NPU 图模式
2+ 
3+> 继承自 `.npugraphs/skills/debugging/known-patterns.md`,扩展为 14+ 个模式
4+> 最后更新: 2026-06-10
5+ 
6+---
7+ 
8+## 捕获阶段 (Capture Phase)
9+ 
10+### P-CAP-001: 图内 CPU 同步操作
11+ 
12+**症状**: `capture_begin()` 后某个 op 触发 `RuntimeError: Cannot .item() during graph capture`
13+ 
14+**根因**: `.item()`, `.cpu()`, `print(tensor)`, `.numpy()` 等触发 CPU-GPU/NPU 同步,ACLGraph 不允许。
15+ 
16+**检测**:
17+```python
18+# 在 capture 代码块中搜索
19+grep -n "\.item()\|\.cpu()\|print(" model.py
20+```
21+ 
22+**修复**: 将同步操作移到图外,或用图内等效操作替代。
23+ 
24+---
25+ 
26+### P-CAP-002: 嵌套捕获
27+ 
28+**症状**: `RuntimeError: Capture already in progress`
29+ 
30+**根因**: ACLGraph 不支持嵌套捕获。在已开始捕获的 Stream 上再次 `capture_begin()`。
31+ 
32+**常见触发**:
33+- `make_graphed_callables` 内部嵌套
34+- torch.compile 递归触发图捕获
35+- 多线程同时捕获
36+ 
37+---
38+ 
39+### P-CAP-003: 图内条件分支
40+ 
41+**症状**: graph_breaks 异常多,或捕获结果与预期不符
42+ 
43+**根因**: `if x > 0: y = f(x) else: y = g(x)` 在图捕获中只走一个分支,后续重放不会切换。
44+ 
45+**检测**:
46+```bash
47+TORCH_LOGS=graph_breaks python script.py
48+```
49+ 
50+---
51+ 
52+### P-CAP-004: Stream 不匹配
53+ 
54+**症状**: `capture_begin()` 后 op 不在同一 Stream 执行
55+ 
56+**根因**: 多 Stream 场景下,capture 在 Stream A 开始,但某个 op 在 Stream B 运行。
57+ 
58+---
59+ 
60+### P-CAP-005: 首次运行触发的 lazy init
61+ 
62+**症状**: 捕获阶段触发了 NPU 内存分配
63+ 
64+**根因**: 某些 op 首次调用时做 lazy initialization(如 weight init, kernel cache build),触发 `aclrtMalloc`。ACLGraph 在捕获期间禁止新内存分配。
65+ 
66+**修复**: 在捕获前先 eager 跑一次触发所有 lazy init。
67+ 
68+---
69+ 
70+## 重放阶段 (Replay Phase)
71+ 
72+### P-REP-001: 输入 Shape 不匹配
73+ 
74+**症状**: `RuntimeError: Graph input shape mismatch`
75+ 
76+**根因**: 重放时输入的 shape/stride 与捕获时不一致,且未调用 `update_graph()`。
77+ 
78+**检测**:
79+```python
80+# 对比捕获时和重放时的 shape
81+print(g.captured_input_meta)
82+print(x.shape, x.stride())
83+```
84+ 
85+---
86+ 
87+### P-REP-002: 图已被 LRU 淘汰
88+ 
89+**症状**: `RuntimeError: Graph has been evicted` 或 `aclmdlRIExecuteAsync failed with error 0x...`
90+ 
91+**根因**: Graph pool 有 LRU 限制,长期不用的图被淘汰。
92+ 
93+**检测**: 日志中出现 `[NPUGRAPH][TreeManager] evicting graph key=<hash>`
94+ 
95+---
96+ 
97+### P-REP-003: 图生命周期结束
98+ 
99+**症状**: `RuntimeError: Graph object has been destroyed`
100+ 
101+**根因**: Python 端 NPUGraph 对象被 GC/del,但 C++ 端 ACLGraph 仍在使用。
102+ 
103+---
104+ 
105+### P-REP-004: 并发重放冲突
106+ 
107+**症状**: 偶尔成功偶尔失败,无固定模式
108+ 
109+**根因**: 多线程同时 replay 同一个图,ACLGraph 不支持并发重放。
110+ 
111+---
112+ 
113+### P-REP-005: ACL 执行超时
114+ 
115+**症状**: `aclmdlRIExecuteAsync` 超时(hang)
116+ 
117+**根因**:
118+- 图内有同步等待(如 HCCL allreduce 未完成)
119+- ACL 任务调度死锁
120+- NPU 硬件异常
121+ 
122+---
123+ 
124+## 内存阶段 (Memory Phase)
125+ 
126+### P-MEM-001: 图内内存分配
127+ 
128+**症状**: 捕获阶段 `aclrtMalloc` 失败
129+ 
130+**根因**: 参见 P-CAP-005(lazy init)
131+ 
132+---
133+ 
134+### P-MEM-002: 内存碎片化
135+ 
136+**症状**: `memory_allocated` 不大但 `aclrtMalloc` 失败
137+ 
138+**根因**: NPUCachingAllocator 池碎片化,大块连续内存不足。
139+ 
140+**检测**:
141+```python
142+print(torch.npu.memory_stats())
143+```
144+ 
145+---
146+ 
147+### P-MEM-003: 跨 Stream 内存不共享
148+ 
149+**症状**: 两个 Stream 上的图各自占用大量内存,总量接近上限
150+ 
151+**根因**: NPU 内存池默认按 Stream 隔离。
152+ 
153+---
154+ 
155+### P-MEM-004: empty_cache + HCCL UnmapMem 崩溃
156+ 
157+**症状**: `torch.npu.empty_cache()` 后程序 SIGSEGV
158+ 
159+**根因**: 详见 `docs/analysis/empty_cache_hccl_unmap_crash.md`。HCCL 通信内存被 empty_cache 释放后,HCCL 仍尝试访问。
160+ 
161+---
162+ 
163+## Update 阶段 (Update Phase)
164+ 
165+### P-UPD-001: TND + Dropout Fallback 缺失
166+ 
167+**症状**: FA3 图 update 失败,`keep_prob < 1.0`
168+ 
169+**根因**: Training dropout 场景下 TND 不支持 ACLGraph update。
170+ 
171+---
172+ 
173+### P-UPD-002: CPU Tensor 参数错误
174+ 
175+**症状**: `actual_seq_qlen`/`actual_seq_kvlen` 不在 CPU 上
176+ 
177+**根因**: Update 参数要求 CPU Tensor 传入。
178+ 
179+---
180+ 
181+### P-UPD-003: Dropout Seed/Offset shape 错误
182+ 
183+**症状**: `seed`/`offset` 参数 shape 不是 `[1]` 或 `[1, 1]`
184+ 
185+**根因**: ACL FA 接口要求 1x1 Tensor。
186+ 
187+---
188+ 
189+## 编译阶段 (Compile Phase)
190+ 
191+### P-COM-001: Inductor 图分区异常
192+ 
193+**症状**: `torch.compile(backend="npugraphs")` 分区结果不合理
194+ 
195+**根因**: Inductor 的 `npu_fusion_attention_graph.py` 分区逻辑与模型不匹配。
196+ 
197+---
198+ 
199+### P-COM-002: Dynamo graph_break 过多
200+ 
201+**症状**: 期望单个子图的模型,实际产生 20+ 个子图
202+ 
203+**根因**: 模型中有 Dynamo 不支持的 Python 结构(data-dependent control flow 等)。
204+ 
205+---
206+ 
207+## 使用说明
208+ 
209+1. 先按阶段(Capture / Replay / Memory / Update / Compile)缩小范围
210+2. 再按症状匹配模式 ID
211+3. 匹配到模式后,直接按"修复"步骤操作
212+4. 未匹配的,进入 `graph-mode-diagnostics` Phase 4 深度追踪
@@ -0,0 +1,155 @@
1+#!/usr/bin/env python3
2+"""
3+图 Dump 分析器 — 解析 graph.debug_dump() 导出的 ACL 图 JSON
4+ 
5+用法:
6+ python analyze_graph_dump.py <dump.json> [--baseline <baseline.json>] [--output json|text]
7+ 
8+功能:
9+ - 节点/边统计
10+ - 算子类型分布
11+ - 与基线对比(regression 检测)
12+ - 结构异常检测(孤立节点/环/超长链)
13+"""
14+ 
15+import json
16+import sys
17+import argparse
18+from collections import Counter
19+from typing import Optional
20+ 
21+ 
22+def load_dump(filepath: str) -> dict:
23+ with open(filepath, 'r') as f:
24+ return json.load(f)
25+ 
26+ 
27+def analyze(dump: dict) -> dict:
28+ """分析单个 dump"""
29+ nodes = dump.get('nodes', dump.get('graph', {}).get('nodes', []))
30+ edges = dump.get('edges', dump.get('graph', {}).get('edges', []))
31+ 
32+ op_types = Counter()
33+ for n in nodes:
34+ op_type = n.get('type', n.get('op_type', 'Unknown'))
35+ op_types[op_type] += 1
36+ 
37+ return {
38+ 'node_count': len(nodes),
39+ 'edge_count': len(edges),
40+ 'op_type_distribution': dict(op_types.most_common(20)),
41+ 'unique_op_count': len(op_types),
42+ 'density': len(edges) / max(len(nodes) * (len(nodes) - 1), 1),
43+ }
44+ 
45+ 
46+def detect_anomalies(dump: dict) -> list[dict]:
47+ """检测结构异常"""
48+ nodes = dump.get('nodes', dump.get('graph', {}).get('nodes', []))
49+ edges = dump.get('edges', dump.get('graph', {}).get('edges', []))
50+ anomalies = []
51+ 
52+ node_ids = {n.get('id', n.get('name', '')) for n in nodes}
53+ sources = {e.get('source', e.get('src', '')) for e in edges}
54+ targets = {e.get('target', e.get('dst', '')) for e in edges}
55+ 
56+ # 孤立节点(无入边也无出边)
57+ connected = sources | targets
58+ isolated = node_ids - connected - {'input', 'output', 'INPUT', 'OUTPUT'}
59+ if isolated:
60+ anomalies.append({
61+ 'type': 'isolated_nodes',
62+ 'severity': 'WARNING',
63+ 'description': f'发现 {len(isolated)} 个孤立节点(非输入/输出)',
64+ 'nodes': list(isolated)[:10],
65+ })
66+ 
67+ # 无出边节点(非输出)
68+ no_output = node_ids - sources - {'output', 'OUTPUT'}
69+ if len(no_output) > 1:
70+ anomalies.append({
71+ 'type': 'dead_end_nodes',
72+ 'severity': 'INFO',
73+ 'description': f'发现 {len(no_output)} 个无出边节点',
74+ })
75+ 
76+ # 节点数异常(过大图可能有问题)
77+ if len(nodes) > 10000:
78+ anomalies.append({
79+ 'type': 'large_graph',
80+ 'severity': 'WARNING',
81+ 'description': f'图中有 {len(nodes)} 节点,可能需要分区优化',
82+ })
83+ 
84+ return anomalies
85+ 
86+ 
87+def diff_dumps(current: dict, baseline: dict) -> dict:
88+ """对比两个 dump"""
89+ ca = analyze(current)
90+ ba = analyze(baseline)
91+ 
92+ cur_ops = set(ca['op_type_distribution'].keys())
93+ bas_ops = set(ba['op_type_distribution'].keys())
94+ 
95+ return {
96+ 'node_diff': ca['node_count'] - ba['node_count'],
97+ 'edge_diff': ca['edge_count'] - ba['edge_count'],
98+ 'new_ops': list(cur_ops - bas_ops),
99+ 'removed_ops': list(bas_ops - cur_ops),
100+ 'op_counts_current': ca['op_type_distribution'],
101+ 'op_counts_baseline': ba['op_type_distribution'],
102+ }
103+ 
104+ 
105+def main():
106+ parser = argparse.ArgumentParser(description='图 Dump 分析器')
107+ parser.add_argument('dumpfile', help='graph.debug_dump() 导出的 JSON 文件')
108+ parser.add_argument('--baseline', help='基线 dump JSON(用于对比)')
109+ parser.add_argument('--output', choices=['json', 'text'], default='text')
110+ args = parser.parse_args()
111+ 
112+ try:
113+ dump = load_dump(args.dumpfile)
114+ except (json.JSONDecodeError, FileNotFoundError) as e:
115+ print(f"错误: 无法加载 {args.dumpfile}: {e}", file=sys.stderr)
116+ sys.exit(1)
117+ 
118+ result = analyze(dump)
119+ anomalies = detect_anomalies(dump)
120+ 
121+ if args.output == 'json':
122+ output = {'analysis': result, 'anomalies': anomalies}
123+ if args.baseline:
124+ baseline = load_dump(args.baseline)
125+ output['diff'] = diff_dumps(dump, baseline)
126+ print(json.dumps(output, ensure_ascii=False, indent=2))
127+ else:
128+ print("=== 图 Dump 分析 ===")
129+ print(f"节点数: {result['node_count']}")
130+ print(f"边数: {result['edge_count']}")
131+ print(f"图密度: {result['density']:.4f}")
132+ print(f"唯一算子类型: {result['unique_op_count']}")
133+ print(f"\n算子类型分布 (Top-10):")
134+ for op, count in list(result['op_type_distribution'].items())[:10]:
135+ print(f" {op}: {count}")
136+ 
137+ if anomalies:
138+ print(f"\n结构异常:")
139+ for a in anomalies:
140+ print(f" [{a['severity']}] {a['type']}: {a['description']}")
141+ 
142+ if args.baseline:
143+ baseline = load_dump(args.baseline)
144+ diff = diff_dumps(dump, baseline)
145+ print(f"\n与基线对比:")
146+ print(f" 节点变化: {diff['node_diff']:+d}")
147+ print(f" 边变化: {diff['edge_diff']:+d}")
148+ if diff['new_ops']:
149+ print(f" 新增算子: {diff['new_ops']}")
150+ if diff['removed_ops']:
151+ print(f" 移除算子: {diff['removed_ops']}")
152+ 
153+ 
154+if __name__ == '__main__':
155+ main()
@@ -0,0 +1,202 @@
1+#!/usr/bin/env python3
2+"""
3+NPU 单卡故障诊断 — 纯被动模式,不触发算子下发,不分配 GPU 内存
4+所有诊断信息存入 device_logs/<device_id>/ 子文件夹
5+"""
6+ 
7+import os
8+import sys
9+import subprocess
10+import json
11+from datetime import datetime
12+ 
13+ 
14+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
15+LOG_ROOT = os.path.join(SCRIPT_DIR, "..", "device_logs")
16+ 
17+ 
18+def run_cmd(cmd: list, timeout: int = 10) -> str:
19+ try:
20+ r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
21+ return r.stdout.strip() or r.stderr.strip()
22+ except subprocess.TimeoutExpired:
23+ return "TIMEOUT"
24+ except FileNotFoundError:
25+ return "CMD_NOT_FOUND"
26+ except Exception as e:
27+ return f"ERROR: {e}"
28+ 
29+ 
30+def save_log(log_dir: str, filename: str, content: str):
31+ """保存单个日志文件"""
32+ if not content:
33+ return
34+ filepath = os.path.join(log_dir, filename)
35+ with open(filepath, "w", encoding="utf-8", errors="replace") as f:
36+ f.write(content)
37+ 
38+ 
39+def save_json(log_dir: str, filename: str, data):
40+ """保存 JSON 格式文件"""
41+ filepath = os.path.join(log_dir, filename)
42+ with open(filepath, "w", encoding="utf-8") as f:
43+ json.dump(data, f, ensure_ascii=False, indent=2, default=str)
44+ 
45+ 
46+def save_log_lines(log_dir: str, filename: str, lines: list):
47+ """保存行列表"""
48+ if not lines:
49+ return
50+ filepath = os.path.join(log_dir, filename)
51+ with open(filepath, "w", encoding="utf-8", errors="replace") as f:
52+ f.write("\n".join(str(l) for l in lines))
53+ 
54+ 
55+def diagnose_card(device_id: int, log_dir: str):
56+ """诊断单卡,所有结果写入 log_dir"""
57+ 
58+ # ─── 1. npu-smi ───────────────────────────────────
59+ smi_dir = os.path.join(log_dir, "npu_smi")
60+ os.makedirs(smi_dir, exist_ok=True)
61+ 
62+ save_log(smi_dir, "health.txt",
63+ run_cmd(["npu-smi", "info", "-t", "health", "-i", str(device_id)]))
64+ save_log(smi_dir, "error_counters.txt",
65+ run_cmd(["npu-smi", "info", "-t", "error", "-i", str(device_id)]))
66+ save_log(smi_dir, "board_info.txt",
67+ run_cmd(["npu-smi", "info", "-t", "board", "-i", str(device_id)]))
68+ save_log(smi_dir, "processes.txt",
69+ run_cmd(["npu-smi", "info", "-t", "proc", "-i", str(device_id)]))
70+ save_log(smi_dir, "firmware.txt",
71+ run_cmd(["npu-smi", "info", "-t", "firmware", "-i", str(device_id)]))
72+ save_log(smi_dir, "temperature.txt",
73+ run_cmd(["npu-smi", "info", "-t", "temp", "-i", str(device_id)]))
74+ save_log(smi_dir, "power.txt",
75+ run_cmd(["npu-smi", "info", "-t", "power", "-i", str(device_id)]))
76+ 
77+ # ─── 2. 驱动故障日志 ──────────────────────────────
78+ log_src = f"/var/log/npu/device-{device_id}"
79+ driver_dir = os.path.join(log_dir, "driver_logs")
80+ if os.path.isdir(log_src):
81+ os.makedirs(driver_dir, exist_ok=True)
82+ error_keywords = ("error", "fault", "exception", "timeout",
83+ "reset", "fail", "abnormal", "hang", "ecc", "assert")
84+ 
85+ for fname in sorted(os.listdir(log_src)):
86+ fpath = os.path.join(log_src, fname)
87+ try:
88+ with open(fpath, "r", errors="replace") as f:
89+ lines = f.readlines()
90+ # 完整日志
91+ save_log_lines(driver_dir, f"{fname}.full", [l.strip() for l in lines[-500:]])
92+ # 过滤后的关键行
93+ filtered = [l.strip() for l in lines
94+ if any(kw in l.lower() for kw in error_keywords)]
95+ save_log_lines(driver_dir, f"{fname}.errors", filtered[-200:])
96+ except Exception as e:
97+ save_log(driver_dir, f"{fname}.error", str(e))
98+ else:
99+ save_log(log_dir, "driver_logs_missing.txt", f"directory not found: {log_src}")
100+ 
101+ # ─── 3. 内核日志 ──────────────────────────────────
102+ dmesg_out = run_cmd(["dmesg"], timeout=5)
103+ if dmesg_out and dmesg_out != "CMD_NOT_FOUND":
104+ lines = dmesg_out.split("\n")
105+ save_log_lines(log_dir, "dmesg_full.txt", [l.strip() for l in lines[-1000:]])
106+ filtered = [l.strip() for l in lines
107+ if any(kw in l.lower() for kw in
108+ ("npu", "ascend", "hisi", "davinci", "pcie", "hbm", "ts", "aicore"))]
109+ save_log_lines(log_dir, "dmesg_errors.txt", filtered[-200:])
110+ 
111+ # ─── 4. cron 巡检日志 ─────────────────────────────
112+ cron_log = "/var/log/npu/cron_npu.log"
113+ if os.path.isfile(cron_log):
114+ try:
115+ with open(cron_log, "r", errors="replace") as f:
116+ lines = f.readlines()
117+ # 完整日志
118+ save_log_lines(log_dir, "cron_full.txt",
119+ [l.strip() for l in lines[-1000:]])
120+ # 过滤当前设备的关键行
121+ filtered = [l.strip() for l in lines
122+ if str(device_id) in l and
123+ any(kw in l.lower() for kw in
124+ ("error", "fault", "fail", "reset", "timeout"))]
125+ save_log_lines(log_dir, "cron_errors.txt", filtered[-200:])
126+ except Exception as e:
127+ save_log(log_dir, "cron_error.txt", str(e))
128+ 
129+ # ─── 5. Python 侧只读查询 ─────────────────────────
130+ py_info = {"device_id": device_id}
131+ try:
132+ import torch
133+ import torch_npu
134+ 
135+ py_info["device_count"] = torch.npu.device_count()
136+ if device_id < torch.npu.device_count():
137+ py_info["visible"] = True
138+ py_info["device_name"] = torch.npu.get_device_name(device_id)
139+ py_info["capability"] = torch.npu.get_device_capability(device_id)
140+ props = torch.npu.get_device_properties(device_id)
141+ py_info["total_memory_mb"] = props.total_memory / 1024**2
142+ 
143+ torch.npu.set_device(device_id)
144+ py_info["mem_allocated_mb"] = torch.npu.memory_allocated() / 1024**2
145+ py_info["mem_reserved_mb"] = torch.npu.memory_reserved() / 1024**2
146+ py_info["max_mem_allocated_mb"] = torch.npu.max_memory_allocated() / 1024**2
147+ 
148+ stats = torch.npu.memory_stats(device_id)
149+ if stats:
150+ py_info["alloc_count"] = stats.get("allocation.all.allocated", "N/A")
151+ py_info["free_count"] = stats.get("allocation.all.freed", "N/A")
152+ py_info["segment_count"] = stats.get("segment.all.current", "N/A")
153+ else:
154+ py_info["visible"] = False
155+ except ImportError:
156+ py_info["visible"] = "torch_npu not importable"
157+ except Exception as e:
158+ py_info["python_error"] = str(e)
159+ 
160+ save_json(log_dir, "python_info.json", py_info)
161+ 
162+ # ─── 6. 诊断摘要 ──────────────────────────────────
163+ summary = {
164+ "device_id": device_id,
165+ "timestamp": datetime.now().isoformat(),
166+ "log_directory": log_dir,
167+ "files": [],
168+ }
169+ for root, dirs, files in os.walk(log_dir):
170+ for fname in files:
171+ rel = os.path.relpath(os.path.join(root, fname), log_dir)
172+ size = os.path.getsize(os.path.join(root, fname))
173+ summary["files"].append({"file": rel, "size_bytes": size})
174+ 
175+ save_json(log_dir, "summary.json", summary)
176+ 
177+ return summary
178+ 
179+ 
180+def print_summary(summary: dict):
181+ """命令行输出摘要"""
182+ print(f"\n=== 诊断完成 ===")
183+ print(f"设备: NPU {summary['device_id']}")
184+ print(f"时间: {summary['timestamp']}")
185+ print(f"输出: {summary['log_directory']}")
186+ print(f"文件数: {len(summary['files'])}")
187+ print(f"\n目录结构:")
188+ for f in summary["files"]:
189+ size_kb = f["size_bytes"] / 1024
190+ print(f" {f['file']} ({size_kb:.1f} KB)")
191+ 
192+ 
193+if __name__ == "__main__":
194+ device_id = int(sys.argv[1]) if len(sys.argv) > 1 else 0
195+ 
196+ log_dir = os.path.join(LOG_ROOT, f"device_{device_id}")
197+ os.makedirs(log_dir, exist_ok=True)
198+ 
199+ print(f"诊断 NPU {device_id},输出目录: {log_dir}")
200+ 
201+ summary = diagnose_card(device_id, log_dir)
202+ print_summary(summary)
@@ -0,0 +1,77 @@
1+# Graph Mode Expert — 图模式专家技能
2+ 
3+> NPU 图模式全能技能:知识问答 + 问题诊断 + 修复方案 + 自验证
4+ 
5+## 这个技能能做什么
6+ 
7+1. **拉取代码仓** — 自动拉取 torch_npu 和 op-plugin 仓库,支持本地源码和远程 clone
8+2. **知识问答** — 回答图模式任何概念问题,根据代码结构树快速定位涉及文件
9+3. **问题诊断** — 5 步系统分析法:概要定位→堆栈流程树→根因→方案→报告
10+4. **自验证** — 环境可用时自行测试验证修复方案
11+ 
12+## 快速使用
13+ 
14+### 知识问答
15+ 
16+```
17+"NPUGraph 的 capture_begin 内部做了什么?"
18+"图 tree 是怎么管理多图的?"
19+"FA3 update 机制有什么限制?"
20+"为什么捕获期间不能分配新内存?"
21+```
22+ 
23+### 问题分析
24+ 
25+```
26+"我的图模式报错了,日志在 xxx.log,帮我分析"
27+"torch.compile(backend='npugraphs') hang 住了"
28+"图重放阶段 OOM,怎么排查?"
29+```
30+ 
31+### 分析报告
32+ 
33+分析结果会输出到 `analysis/<问题简述>_analysis.md`,包含:
34+- 问题概要定位(一句话)
35+- 报错定位堆栈流程树(精确到文件:函数:行号)
36+- 根因总结
37+- 修改方案
38+- 测试建议
39+ 
40+## 文件结构
41+ 
42+```
43+graph-mode-expert/
44+├── SKILL.md # 技能主体(Phase 0-3)
45+├── README.md # 本文件
46+├── references/
47+│ └── code-structure.md # ★ 完整代码结构树 (关键词→文件索引)
48+└── templates/
49+ └── analysis-report-template.md # 分析报告模板
50+```
51+ 
52+## 关键设计
53+ 
54+| 特性 | 说明 |
55+|------|------|
56+| **代码仓准备** | 先问本地路径,没有再拉取;询问 torch_npu 分支 |
57+| **代码结构树** | 18+ 个关键词索引,覆盖 L4→L1 全链路 |
58+| **5 步分析** | 概要→堆栈树→根因→方案→报告 |
59+| **分析输出** | 放入 `analysis/` 文件夹的结构化 Markdown 报告 |
60+| **自验证** | 环境可用时自动测试 |
61+ 
62+## 依赖
63+ 
64+- 参考 `NPU-graph-mode-diagnostics` 的已知模式和错误码表
65+- 使用 `scripts/check_graph_health.py` 做环境健康检查
66+- 使用 `../graph-log-analyzer/scripts/parse_npu_logs.py` 做日志分析
67+ 
68+## Checklist
69+ 
70+- [x] 代码仓拉取逻辑
71+- [x] 知识问答流程
72+- [x] 问题诊断流程(5步)
73+- [x] 分析报告模板
74+- [x] 代码结构树(关键词索引)
75+- [x] 自验证流程
76+- [x] 与 NPU-graph-mode-diagnostics 互补
77+- [x] Red Flags 和 Handoff 规则
@@ -0,0 +1,114 @@
1+---
2+name: npu-graph-mode-expert
3+description: >-
4+ Use when user asks conceptual questions about NPU graph mode —
5+ how NPUGraph/ACLGraph works, capture/replay lifecycle, graph tree management,
6+ update mechanism, FA3 graph integration, super kernel, memory model.
7+ Also use when user needs code navigation — locating specific files/functions
8+ in torch_npu graph mode codebase.
9+ NOT for error/crash diagnosis — handoff to npu-graph-mode-diagnostics.
10+---
11+ 
12+# Graph Mode Expert — 图模式知识专家
13+ 
14+## The Iron Law
15+ 
16+```
17+分析问题前必须拿到两样东西:
18+ 1. 完整报错堆栈(Python traceback + C++ error code)
19+ 2. 复现用例(能跑的最小脚本)
20+ 
21+没有这两样不开始分析。绝不凭空猜测根因。
22+```
23+ 
24+**报错/异常/崩溃 → 立即 handoff 到 `NPU-graph-mode-diagnostics`,本技能只做知识问答和代码导航。**
25+ 
26+---
27+ 
28+## Phase 0: 代码仓准备
29+ 
30+涉及代码分析时,先确认源码可用。详细步骤 → [code-repo-setup.md](references/code-repo-setup.md)。
31+ 
32+核心原则:先问用户本地路径,没有再拉取;拉取前确认分支。
33+ 
34+---
35+ 
36+## Phase 1: 知识问答
37+ 
38+### 处理流程
39+ 
40+```
41+Step 1: 从关键词提取技术点
42+ ↓ 使用 code-structure.md 的「关键词→文件」索引
43+Step 2: 定位涉及的文件(精确到文件+关键函数)
44+ ↓
45+Step 3: 读取关键源码
46+ ↓
47+Step 4: 回答,包含:
48+ - 要点概述 (2-3句话)
49+ - 涉及的代码层级和文件
50+ - 关键调用链/数据流
51+ - 相关约束和注意事项
52+```
53+ 
54+常见问题的回答要点见 [knowledge-faq.md](references/knowledge-faq.md)。
55+ 
56+---
57+ 
58+## Phase 3: 自验证
59+ 
60+仅当环境有 NPU 设备且修改方案已实施时使用。
61+ 
62+```
63+Step 1: python scripts/check_graph_health.py → 确认环境健康
64+Step 2: python <repro_script.py> → 确认问题不再复现
65+Step 3: 运行相关测试套件
66+Step 4: 输出 [PASS/FAIL] 验证结论 + 性能影响
67+```
68+ 
69+---
70+ 
71+## 知识库索引
72+ 
73+| 参考文档 | 路径 | 用途 |
74+|---------|------|------|
75+| 完整代码结构树 | [code-structure.md](references/code-structure.md) | 关键词→文件快速定位 |
76+| 常见问题速查 | [knowledge-faq.md](references/knowledge-faq.md) | 知识问答参考 |
77+| 代码仓准备 | [code-repo-setup.md](references/code-repo-setup.md) | clone/分支切换详细流程 |
78+| 已知问题模式 | [known-patterns.md](../graph-mode-diagnostics/references/known-patterns.md) | 14+ 已知模式匹配 |
79+| ACL 错误码映射 | [error-code-map.md](../graph-mode-diagnostics/references/error-code-map.md) | 错误码→根因 |
80+| 分析报告模板 | [analysis-report-template.md](templates/analysis-report-template.md) | 标准化报告输出 |
81+ 
82+## Handoff
83+ 
84+| 条件 | 移交技能 | 原因 |
85+|------|---------|------|
86+| 报错/异常/崩溃/OOM | `NPU-graph-mode-diagnostics` | 进入端到端诊断流程 |
87+| 性能问题/变慢 | `NPU-graph-performance-profiling` | 进入性能分析 |
88+| 日志量太大需自动分析 | `NPU-graph-log-analyzer` | 进入日志分析 |
89+| MCP 工具配置 | `NPU-graph-mcp-integration` | 进入 MCP 集成 |
90+ 
91+---
92+ 
93+## Red Flags
94+ 
95+| 误区 | 正确做法 |
96+|------|---------|
97+| 看到 "memory" 就加 `empty_cache()` | 先查分配栈,确认是泄漏还是不足 |
98+| 看到 "shape mismatch" 就改 padding | 先确认 update 机制是否正确调用 |
99+| 看到 graph_break 就改模型 | 先确认是否是真的问题(有的断裂是预期的) |
100+| 看到 ACL 错误码不查表 | 先查 error-code-map.md |
101+| 没看到复现用例就分析 | 先要复现用例,否则分析无意义 |
102+| 看到报错就开始诊断 | handoff 到 diagnostics,不要自己分析 |
103+ 
104+---
105+ 
106+## DFX: 知识沉淀
107+ 
108+每次问答/诊断结束后,**必须执行**:
109+ 
110+1. **问用户**:「是否将此问题记录到知识库?[Y/n]」
111+2. 如 Y → 按 [../knowledge/CASE_TEMPLATE.md](../knowledge/CASE_TEMPLATE.md) 格式生成条目
112+3. 追加到 [../knowledge/history.md](../knowledge/history.md) 对应分类下
113+4. 更新分类总览计数
114+5. 如发现新的问题模式 → 提示用户:「是否同步更新 known-patterns.md?」
@@ -0,0 +1,147 @@
1+# NPUGRAPH_SKILL 架构设计
2+ 
3+> 版本: v0.1.0 | 日期: 2026-06-10 | 状态: 设计阶段
4+ 
5+---
6+ 
7+## 1. 设计目标
8+ 
9+在现有 `.npugraphs/` 技能体系基础上,构建一个**专注于图模式问题诊断与解决**的技能增强层。核心差异化:
10+ 
11+| 维度 | .npugraphs/ (现有) | NPUGRAPH_SKILL (本系统) |
12+|------|-------------------|------------------------|
13+| **定位** | 通用 NPUGraph 开发全流程 | 图模式**问题诊断**专项 |
14+| **粒度** | 粗粒度(debugging/requirements 覆盖所有) | 细粒度(捕获/重放/内存/性能 分技能) |
15+| **工具链** | 依赖 Bash 手动执行 | MCP 自动化 + Python 脚本 |
16+| **日志** | 手动 grep | 结构化解析 + 模式匹配 |
17+| **性能** | 不在 scope 内 | 独立性能分析技能 |
18+| **输出** | Markdown 报告 | 结构化报告 + Timeline 可视化 |
19+ 
20+## 2. 技能体系
21+ 
22+```
23+NPUGRAPH_SKILL/
24+│
25+├── NPUGRAPH_SKILL_DOC.md # 总入口文档(本文件同级)
26+│
27+├── DOC/ # 设计文档
28+│ ├── architecture.md # 本文件:架构设计
29+│ ├── mcp-evaluation.md # MCP 服务器评估
30+│ └── skill-design.md # 技能设计原理
31+│
32+├── SKILL/ # ===== 技能定义 =====
33+│ │
34+│ ├── graph-mode-diagnostics/ # ★ 核心技能:图模式问题诊断
35+│ │ ├── SKILL.md # 主技能文件
36+│ │ └── references/
37+│ │ ├── known-patterns.md # 已知问题模式(扩展版)
38+│ │ ├── error-code-map.md # ACL 错误码 → 根因映射
39+│ │ └── diagnostic-checklist.md # 诊断检查清单
40+│ │
41+│ ├── graph-performance-profiling/ # 性能分析技能
42+│ │ ├── SKILL.md
43+│ │ └── references/
44+│ │ ├── profiling-methodology.md
45+│ │ └── baseline-metrics.md
46+│ │
47+│ ├── graph-log-analyzer/ # 日志分析技能
48+│ │ ├── SKILL.md
49+│ │ └── patterns/
50+│ │ ├── error-patterns.md # 错误日志模式
51+│ │ ├── timeline-patterns.md # 时间线异常模式
52+│ │ └── memory-patterns.md # 内存异常模式
53+│ │
54+│ └── graph-mcp-integration/ # MCP 工具集成
55+│ └── SKILL.md # 如何配置和使用 MCP
56+│
57+├── MCP/ # MCP 配置
58+│ ├── mcp-configs.json # 所有相关 MCP server 定义
59+│ └── setup-guide.md # MCP 安装配置指南
60+│
61+└── scripts/ # 实用脚本
62+ ├── parse_npu_logs.py # 日志解析器
63+ ├── analyze_graph_dump.py # 图 dump 分析器
64+ └── check_graph_health.py # 图健康检查
65+```
66+ 
67+## 3. 技能触发规则
68+ 
69+```
70+用户问题 → 关键词匹配 → 路由到对应技能
71+│
72+├── "图捕获失败" / "capture failed" / "aclmdlRICaptureBegin"
73+│ → graph-mode-diagnostics (捕获阶段)
74+│
75+├── "重放出错" / "replay error" / "aclmdlRIExecuteAsync failed"
76+│ → graph-mode-diagnostics (重放阶段)
77+│
78+├── "OOM" / "内存泄漏" / "memory" / "allocator"
79+│ → graph-mode-diagnostics (内存阶段)
80+│
81+├── "慢" / "性能下降" / "regression" / "profile" / "耗时"
82+│ → graph-performance-profiling
83+│
84+├── "日志分析" / "parse log" / "TORCH_NPU_LOGS"
85+│ → graph-log-analyzer
86+│
87+├── "配置MCP" / "setup mcp" / "mcp工具"
88+│ → graph-mcp-integration
89+│
90+└── 模糊/综合问题 → graph-mode-diagnostics (全流程)
91+```
92+ 
93+## 4. 与现有 .npugraphs/ 的关系
94+ 
95+```
96+.npugraphs/skills/ NPUGRAPH_SKILL/SKILL/
97+├── debugging/ ←→ graph-mode-diagnostics/ (互补增强)
98+├── requirements/ ← 独立(需求分析不在此系统 scope)
99+├── implementation/ ← 独立(编码实现不在此系统 scope)
100+├── verification/ ←→ graph-performance-profiling/ (验证阶段增强)
101+├── torch-npu-logs-registration/ ←→ graph-log-analyzer/ (日志分析互补)
102+└── graph-issue-solver/ ←→ graph-mode-diagnostics/ (问题单求解互补)
103+```
104+ 
105+**关键原则**:不重复 `.npugraphs/` 已有能力,只做**增强和特化**。
106+ 
107+## 5. 数据流
108+ 
109+```
110+┌──────────────┐
111+│ 用户报告问题 │
112+└──────┬───────┘
113+ ▼
114+┌──────────────────┐ MCP: Context7 ┌─────────────────┐
115+│ graph-mode- │◄───查询文档──────────►│ CANN/PyTorch 文档│
116+│ diagnostics │ └─────────────────┘
117+└──────┬───────────┘
118+ │ 需要日志分析?
119+ ▼
120+┌──────────────────┐ scripts/ ┌─────────────────┐
121+│ graph-log- │───parse_npu_logs.py──►│ 结构化日志报告 │
122+│ analyzer │ └─────────────────┘
123+└──────┬───────────┘
124+ │ 需要性能分析?
125+ ▼
126+┌──────────────────┐ MCP: Playwright ┌─────────────────┐
127+│ graph- │───Profiler截图────────►│ 性能分析报告 │
128+│ performance- │ └─────────────────┘
129+│ profiling │
130+└──────┬───────────┘
131+ │
132+ ▼
133+┌──────────────────┐
134+│ 综合诊断报告 │
135+│ + 修复建议 │
136+│ + 验证步骤 │
137+└──────────────────┘
138+```
139+ 
140+## 6. 扩展计划
141+ 
142+| 版本 | 内容 |
143+|------|------|
144+| v0.1.0 | 核心技能定义 + MCP 评估 + 脚本原型 |
145+| v0.2.0 | MCP 实际集成(Context7 + NPU-Log-Parser) |
146+| v0.3.0 | Graph-Dump-Analyzer MCP + 性能分析脚本完善 |
147+| v1.0.0 | 全量测试覆盖 + 与 .npugraphs/ 无缝集成 |
@@ -0,0 +1,30 @@
1+# 代码仓准备详细流程
2+ 
3+> 从 `SKILL.md` Phase 0 拆分。
4+ 
5+## 规则
6+ 
7+每次对话开始时,如果涉及代码分析,必须先确认源码可用:
8+ 
9+1. 先问用户:是否有本地 torch_npu 和 op-plugin 源码?路径是?
10+2. 如果用户提供了本地路径:
11+ - 验证路径存在且包含关键文件(torch_npu/npu/graphs.py 等)
12+ - 记录为 `$TORCH_NPU_ROOT` 和 `$OP_PLUGIN_ROOT`
13+3. 如果用户没有本地源码:
14+ - 告知用户将拉取以下仓库:
15+ - `git clone https://gitcode.com/Ascend/pytorch.git` (torch_npu)
16+ - `git clone https://gitcode.com/Ascend/op-plugin.git -b master` (op-plugin)
17+ - 询问用户:torch_npu 需要特定分支吗?[默认: master]
18+ - 拉取后按分支切换
19+ - op-plugin 默认 master 分支
20+4. 拉取完成后,验证关键文件存在
21+ 
22+## 仓库验证检查清单
23+ 
24+```
25+□ torch_npu/npu/graphs.py — 主 API 文件
26+□ torch_npu/npu/_graph_tree.py — 图树管理
27+□ torch_npu/csrc/core/npu/NPUGraph.cpp — C++ 核心
28+□ torch_npu/csrc/npu/Graph.cpp — pybind 绑定
29+□ op-plugin/ops/ — 算子实现
30+```
@@ -0,0 +1,336 @@
1+# NPU 图模式完整代码结构树
2+ 
3+> 从 torch_npu Python API → C++ 核心 → op-plugin 的完整文件索引
4+> 用于问题快速定位:根据关键词/报错信息定位到具体文件和函数
5+ 
6+---
7+ 
8+## 一、总览架构图
9+ 
10+```
11+┌──────────────────────────────────────────────────────────────────┐
12+│ 用户代码 (L5) │
13+│ model.forward() / train loop / torch.compile(backend="npugraphs")│
14+└──────────────────────────┬───────────────────────────────────────┘
15+ │
16+┌──────────────────────────▼───────────────────────────────────────┐
17+│ Python API 层 (L4) │
18+│ │
19+│ npu/graphs.py (1189行) npu/npugraph_ex/ (55行) │
20+│ ├── NPUGraph (继承 C++_NPUGraph) ├── inference/ │
21+│ ├── graph (上下文管理器) └── scope/ │
22+│ ├── make_graphed_callables() │
23+│ ├── _GraphDispatchMode │
24+│ └── _GraphDispatchRecord │
25+│ │
26+│ npu/__init__.py ← 导出: NPUGraph, graph, make_graphed_callables │
27+└──────────────────────────┬───────────────────────────────────────┘
28+ │
29+┌──────────────────────────▼───────────────────────────────────────┐
30+│ Graph Tree 层 (L3) │
31+│ │
32+│ npu/_graph_tree.py (2560行) utils/_graph_tree.py (380行) │
33+│ ├── NPUGraphTreeManager ├── NpugraphsBackend │
34+│ ├── NPUGraphNode ├── npugraphify() │
35+│ ├── NPUWarmupNode ├── npugraphify_impl() │
36+│ ├── GraphID ├── _apply_npugraph_tree_methods │
37+│ ├── OutputAliasInfo └── check_for_skip() │
38+│ ├── TreeManagerContainer │
39+│ └── ExecutionState / CompilationMode │
40+│ │
41+│ Handler 层: npu/_npugraph_handlers/ (5文件, 582行) │
42+│ ├── __init__.py ← 自动注册入口 │
43+│ ├── npugraph_handler.py ← NpuGraphOpHandler 基类 + 注册表 │
44+│ ├── _fa3_graph_handler.py ← FA3 前后向 handler │
45+│ ├── ifa_handler.py ← IFA v1/v2 handler │
46+│ └── simple_handler.py ← 默认 handler │
47+└──────────────────────────┬───────────────────────────────────────┘
48+ │
49+┌──────────────────────────▼───────────────────────────────────────┐
50+│ C++ 核心层 (L2) │
51+│ │
52+│ core/npu/NPUGraph.h (134行) core/npu/NPUGraph.cpp (371行) │
53+│ ├── class NPUGraph ├── capture_begin/end 实现 │
54+│ ├── MempoolId_t ├── replay() 实现 │
55+│ ├── NPUTaskGroupHandle ├── reset() 实现 │
56+│ └── 图状态 + 生成器状态管理 └── 图内存池管理 │
57+│ │
58+│ core/npu/NPUGraphsUtils.h/cpp ← CaptureStatus 枚举 + 流状态 │
59+│ core/npu/interface/AclInterface ← aclmdlRI* 动态加载 │
60+│ core/npu/interface/SkInterface ← aclsk* 超级内核 API │
61+│ │
62+│ core/npu/NPUCachingAllocator.h/cpp ← 图内存池 (PrivatePool) │
63+│ ├── beginAllocateToPool() ↔ 对应 capture_begin │
64+│ ├── endAllocateToPool() ↔ 对应 capture_end │
65+│ ├── releasePool() ↔ 对应 reset() │
66+│ └── graph_pools / graph_pools_freeable │
67+│ │
68+│ aten/NPUGeneratorImpl.h/cpp ← RNG 状态图安全 │
69+│ ├── capture_prologue/epilogue │
70+│ └── replay_prologue │
71+│ │
72+│ csrc/npu/Graph.cpp (834行) ← pybind11 绑定 │
73+│ ├── THNPGraph_init() ← 所有 Python 绑定入口 │
74+│ └── _NPUGraph / _graph_pool_handle / _graph_task_group_* │
75+└──────────────────────────┬───────────────────────────────────────┘
76+ │
77+┌──────────────────────────▼───────────────────────────────────────┐
78+│ ACL 运行时层 (L1) │
79+│ │
80+│ libascendcl.so: │
81+│ ├── aclmdlRICaptureBegin / GetInfo / End │
82+│ ├── aclmdlRIExecuteAsync │
83+│ ├── aclmdlRIDestroy │
84+│ ├── aclmdlRICaptureTaskGrpBegin/End │
85+│ ├── aclmdlRICaptureTaskUpdateBegin/End │
86+│ └── aclmdlRIDebugPrint / DebugJsonPrint │
87+│ │
88+│ libascendsk.so: │
89+│ ├── aclskOptimize │
90+│ ├── aclskScopeBegin / ScopeEnd │
91+│ └── aclskOption 结构体 │
92+└──────────────────────────┬───────────────────────────────────────┘
93+ │
94+┌──────────────────────────▼───────────────────────────────────────┐
95+│ NPU 硬件/驱动层 (L0) │
96+│ Ascend 910B / 910B2 / 910C / 310B1 系列 │
97+└──────────────────────────────────────────────────────────────────┘
98+```
99+ 
100+---
101+ 
102+## 二、完整文件清单(按层级)
103+ 
104+### L4 — Python API 层
105+ 
106+| 文件 | 路径 | 行数 | 核心符号 | 用途 |
107+|------|------|------|----------|------|
108+| `graphs.py` | `torch_npu/npu/graphs.py` | 1189 | `NPUGraph`, `graph`, `_GraphDispatchMode`, `make_graphed_callables`, `graph_pool_handle`, `super_kernel_scope_begin/end` | **主 API 文件**:三种使能方式 |
109+| `__init__.py` | `torch_npu/npu/__init__.py` | — | 导出 `NPUGraph`, `graph`, `make_graphed_callables` | 公开 API 导出 |
110+| `npugraph_ex/__init__.py` | `torch_npu/npu/npugraph_ex/__init__.py` | 33 | `compile_fx()`, `register_replacement()` | npugraph_ex 后端入口 |
111+| `inference/__init__.py` | `torch_npu/npu/npugraph_ex/inference/__init__.py` | 16 | `cache_compile()` | 推理缓存编译 |
112+| `scope/__init__.py` | `torch_npu/npu/npugraph_ex/scope/__init__.py` | 6 | `limit_core_num()` | 图编译 scope |
113+ 
114+### L3 — Graph Tree + Handler 层
115+ 
116+| 文件 | 路径 | 行数 | 核心符号 | 用途 |
117+|------|------|------|----------|------|
118+| `_graph_tree.py` | `torch_npu/npu/_graph_tree.py` | 2560 | `NPUGraphTreeManager`, `NPUGraphNode`, `NPUWarmupNode`, `GraphID`, `OutputAliasInfo`, `TreeManagerContainer`, `ExecutionState`, `CompilationMode` | **最大文件**:多图树管理 |
119+| `_graph_tree.py` | `torch_npu/utils/_graph_tree.py` | 380 | `NpugraphsBackend`, `npugraphify()`, `check_for_skip()` | Dynamo 后端桥接 |
120+| `__init__.py` | `torch_npu/npu/_npugraph_handlers/__init__.py` | 32 | 导入所有 handler 触发注册 | Handler 自动注册 |
121+| `npugraph_handler.py` | `torch_npu/npu/_npugraph_handlers/npugraph_handler.py` | 212 | `NpuGraphOpHandler`, `register_npu_graph_handler`, `_NPU_GRAPH_OP_HANDLERS` | Handler 基类 + 注册表 |
122+| `_fa3_graph_handler.py` | `torch_npu/npu/_npugraph_handlers/_fa3_graph_handler.py` | 184 | `FA3ForwardHandler`, `FA3BackwardHandler`, `_FA3TensorListOutHandler` | FA3 图 handler |
123+| `ifa_handler.py` | `torch_npu/npu/_npugraph_handlers/ifa_handler.py` | 122 | `_IFAv1DefaultHandler`, `_IFAv2DefaultHandler` | IFA handler |
124+| `simple_handler.py` | `torch_npu/npu/_npugraph_handlers/simple_handler.py` | 32 | `_SimpleGraphHandler` | 默认 handler |
125+ 
126+### L3 — Inductor 图编译层
127+ 
128+| 文件 | 路径 | 行数 | 核心符号 | 用途 |
129+|------|------|------|----------|------|
130+| `graph.py` | `torch_npu/_inductor/graph.py` | 550 | `patch_codegen_with_cpp_wrapper()` | Inductor 代码生成补丁 |
131+| `npu_fusion_attention_graph.py` | `torch_npu/_inductor/fx_passes/pattern_match/npu_fusion_attention_graph.py` | 256 | `NpuGraphAttentionFunction`, `npu_fusion_attention_graph()` | FA 图融合 pass |
132+| `ascend_graph_pass.py` | `torch_npu/_inductor/fx_passes/ascend_custom_passes/ascend_graph_pass.py` | 2483 | `AscendCustomPostPass` | 图优化 passes 集合 |
133+| `joint_graph.py` | `torch_npu/_inductor/fx_passes/joint_graph.py` | 14 | `patch_constant_fold_uniform_value()` | 常量折叠补丁 |
134+| `graph_match_pass.py` | `torch_npu/_inductor/fx_passes/graph_match_pass.py` | 17 | — | 图模式匹配 |
135+| `graph_build.py` | `torch_npu/_inductor/dvm/graph_build.py` | 173 | `DvmCodegenInterpreter` | DVM 图构建 |
136+| `graph_fusion.py` | `torch_npu/_inductor/dvm/graph_fusion.py` | 447 | `DvmGraphFusionPatch`, `dvm_graph_fusion()` | DVM 图融合 |
137+| `graph_fusion.py` | `torch_npu/_inductor/mfusion/graph_fusion.py` | 963 | `MFusionPatch`, `mfusion_graph_fusion()` | MFusion 图融合 |
138+| `subgraph_registry.py` | `torch_npu/_inductor/mfusion/subgraph_registry.py` | 67 | `register()`, `get()`, `pop()` | MFusion 子图注册 |
139+ 
140+### L2 — C++ 核心层
141+ 
142+| 文件 | 路径 | 行数 | 核心符号 | 用途 |
143+|------|------|------|----------|------|
144+| `NPUGraph.h` | `torch_npu/csrc/core/npu/NPUGraph.h` | 134 | `class NPUGraph`, `MempoolId_t`, `NPUTaskGroupHandle` | C++ 图类声明 |
145+| `NPUGraph.cpp` | `torch_npu/csrc/core/npu/NPUGraph.cpp` | 371 | `capture_begin/end`, `replay`, `reset` 实现 | C++ 图类实现 |
146+| `NPUGraphsUtils.h` | `torch_npu/csrc/core/npu/NPUGraphsUtils.h` | 107 | `CaptureId_t`, `CaptureStatus`, `currentStreamCaptureStatus` | 图工具函数 |
147+| `NPUGraphsUtils.cpp` | `torch_npu/csrc/core/npu/NPUGraphsUtils.cpp` | — | 流状态查询实现 | 图工具实现 |
148+| `AclInterface.h` | `torch_npu/csrc/core/npu/interface/AclInterface.h` | ~300 | `AclmdlRICaptureBegin/End`, `AclmdlRIExecuteAsync` 等 | ACL API C++ 包装声明 |
149+| `AclInterface.cpp` | `torch_npu/csrc/core/npu/interface/AclInterface.cpp` | ~1200 | 动态加载 aclmdlRI* 函数 | ACL API 动态加载 |
150+| `SkInterface.h` | `torch_npu/csrc/core/npu/interface/SkInterface.h` | — | `AclskOptimize`, `AclskScopeBegin/End` | 超级内核 API 声明 |
151+| `SkInterface.cpp` | `torch_npu/csrc/core/npu/interface/SkInterface.cpp` | — | 动态加载 aclsk* 函数 | 超级内核 API 加载 |
152+| `NPUCachingAllocator.h` | `torch_npu/csrc/core/npu/NPUCachingAllocator.h` | — | `MemPool`, `MemPoolContext`, `beginAllocateToPool` 等 | 图内存池声明 |
153+| `NPUCachingAllocator.cpp` | `torch_npu/csrc/core/npu/NPUCachingAllocator.cpp` | — | `PrivatePool`, `graph_pools` | 图内存池实现 |
154+| `NPUGeneratorImpl.h` | `torch_npu/csrc/aten/NPUGeneratorImpl.h` | — | `NPUGeneratorState`, `capture_prologue/epilogue` | RNG 图安全管理 |
155+| `NPUGeneratorImpl.cpp` | `torch_npu/csrc/aten/NPUGeneratorImpl.cpp` | — | 生成器状态管理实现 | RNG 图安全实现 |
156+| `Graph.h` | `torch_npu/csrc/npu/Graph.h` | — | `THNPGraph_init()` 声明 | pybind 绑定声明 |
157+| `Graph.cpp` | `torch_npu/csrc/npu/Graph.cpp` | 834 | `THNPGraph_init()` 实现, `_NPUGraph` 绑定 | **pybind 绑定入口** |
158+| `OpCommand.cpp` | `torch_npu/csrc/framework/OpCommand.cpp` | — | aclop 捕捉检查 | 图内 aclop 拦截 |
159+| `NPUWorkspaceAllocator.cpp` | `torch_npu/csrc/core/npu/NPUWorkspaceAllocator.cpp` | — | 工作空间分配器 | 图感知工作空间 |
160+| `ProcessGroupHCCL.cpp` | `torch_npu/csrc/distributed/ProcessGroupHCCL.cpp` | — | HCCL 捕捉状态检查 | 分布式图兼容 |
161+ 
162+### op-plugin 层
163+ 
164+| 文件 | 路径 | 用途 |
165+|------|------|------|
166+| RNG 算子 | `op_plugin/ops/opapi/*KernelNpuOpApi.cpp` | 图感知的随机数算子 |
167+| FA 算子 | `op_plugin/ops/opapi/FlashAttentionKernelNpuOpApi.cpp` | 图感知的 FlashAttention |
168+| 惰性融合 | `op_plugin/ops/dvm/lazy_fusion_ops.cpp` | DVM 图融合算子 |
169+| ATB 创建 | `op_plugin/utils/custom_functions/atb/AtbCommon.h/cpp` | 图感知 ATB 算子创建 |
170+| ACL 图示例 | `third_party/op-plugin/examples/kernel_extension_aclgraph/` | ACL 图扩展参考实现 |
171+ 
172+### Dynamo 补丁和配置
173+ 
174+| 文件 | 路径 | 用途 |
175+|------|------|------|
176+| `dynamo_patches.py` | `torch_npu/_init/patches/dynamo_patches.py` | `apply_npugraph_tree_patch()` |
177+| `__init__.py` | `torch_npu/dynamo/__init__.py` | `NPUGRAPH_EX_BACKEND` 注册 |
178+| `config.py` | `torch_npu/_inductor/config.py` | cudagraph 兼容性引用 |
179+| `utils.py` | `torch_npu/_inductor/utils.py` | `get_first_incompatible_cudagraph_node` |
180+| `transfer_to_npu.py` | `torch_npu/contrib/transfer_to_npu.py` | `CUDAGraph` → `NPUGraph` 映射 |
181+ 
182+### 测试文件
183+ 
184+| 文件 | 路径 | 行数 | 用途 |
185+|------|------|------|------|
186+| `test_graph_tree.py` | `test/npu/test_graph_tree.py` | 1210 | 图树核心测试 |
187+| `test_npugraph_ex.py` | `test/dynamo/test_npugraph_ex.py` | 192 | npugraph_ex 测试 |
188+| `test_npugraph_handler.py` | `test/npu/test_npugraph_handler.py` | 60 | Handler 测试 |
189+| `test_aclgraph_update.py` | `test/npu/test_aclgraph_update.py` | 594 | ACL 图 update 测试 |
190+| `test_aclgraph_super_kernel.py` | `test/npu/test_aclgraph_super_kernel.py` | 396 | 超级内核测试 |
191+| `test_fav3_aclgraph_update.py` | `test/npu/test_fav3_aclgraph_update.py` | 234 | FA3 update 测试 |
192+| `test_aclgraph_dfx.py` | `test/npu/test_aclgraph_dfx.py` | 244 | DFX 测试 |
193+| `test_aclgraph_multi_stream.py` | `test/npu/test_aclgraph_multi_stream.py` | 135 | 多流图测试 |
194+| `test_compile_aclgraph_update.py` | `test/_inductor/test_compile_aclgraph_update.py` | 143 | Inductor 图 update |
195+ 
196+---
197+ 
198+## 三、关键调用链
199+ 
200+### 3.1 图捕获流程
201+ 
202+```
203+用户代码: g.capture_begin()
204+ │
205+ ▼
206+graphs.py: NPUGraph.capture_begin()
207+ │ 参数校验(auto_dispatch, mode, super_kernel等)
208+ │ 调用 C++ _capture_begin()
209+ ▼
210+Graph.cpp (pybind): _NPUGraph.capture_begin()
211+ │ 解析 Python 参数 → C++ CaptureMode 枚举
212+ │ 调用 cpp_obj->capture_begin()
213+ ▼
214+NPUGraph.cpp: NPUGraph::capture_begin()
215+ │ 1. 检查 TASK_QUEUE_ENABLE
216+ │ 2. apply_cache_op_info(stream) — 设置 Stream 属性
217+ │ 3. 注册默认生成器状态(capture_prologue)
218+ │ 4. 选择/创建 MemPool
219+ │ 5. NPUCachingAllocator::beginAllocateToPool()
220+ │ 6. 等待 HCCL 事件查询
221+ │ 7. AclmdlRICaptureBegin() → 进入 ACL 运行时
222+ │ 8. AclmdlRICaptureGetInfo() → 确认状态
223+ ▼
224+AclInterface.cpp: AclmdlRICaptureBegin()
225+ │ 调用动态加载的 libascendcl.so 中的 aclmdlRICaptureBegin
226+ ▼
227+ACL 运行时: aclmdlRICaptureBegin
228+ │ 进入图捕获模式
229+ │ 后续所有 ACL 调用被记录而非立即执行
230+```
231+ 
232+### 3.2 图重放流程
233+ 
234+```
235+用户代码: g.replay()
236+ │
237+ ▼
238+graphs.py: NPUGraph.replay()
239+ │ 调用 C++ replay()
240+ ▼
241+NPUGraph.cpp: NPUGraph::replay()
242+ │ 1. 设置 device guard
243+ │ 2. replay_prologue() — 更新生成器状态
244+ │ 3. AclmdlRIExecuteAsync() → 异步执行
245+ │ 4. 如果阻塞模式:同步等待
246+ ▼
247+ACL 运行时: aclmdlRIExecuteAsync
248+ │ 在 NPU 上执行捕获的图
249+```
250+ 
251+### 3.3 Graph Tree 流程 (torch.compile)
252+ 
253+```
254+torch.compile(backend="npugraphs")
255+ │
256+ ▼
257+Dynamo → FX Graph → AOT Autograd
258+ │
259+ ▼
260+utils/_graph_tree.py: NpugraphsBackend
261+ │ npugraphify() — 将编译后的模块注册到树管理器
262+ ▼
263+npu/_graph_tree.py: NPUGraphTreeManager
264+ │ 1. 创建 NPUGraphNode (每个动态形状变体)
265+ │ 2. NPUWarmupNode → 预热运行
266+ │ 3. 触发 capture_begin() → 用户代码 → capture_end()
267+ │ 4. 编译图 → replay 使用
268+ │ 5. LRU 管理缓存图
269+```
270+ 
271+### 3.4 FA3 Update 流程
272+ 
273+```
274+用户代码: model(input_with_new_shape)
275+ │
276+ ▼
277+npu/_graph_tree.py: NPUGraphTreeManager
278+ │ 检测 shape 变化
279+ │ 调用 update_graph()
280+ ▼
281+graphs.py: _GraphDispatchMode
282+ │ 查找 NpuGraphOpHandler
283+ ▼
284+_fa3_graph_handler.py: FA3ForwardHandler
285+ │ update_args() — 更新 TND/形状参数
286+ │ 检查 TND + dropout fallback
287+ ▼
288+C++: graph_task_update_begin/end → aclmdlRICaptureTaskUpdateBegin/End
289+```
290+ 
291+---
292+ 
293+## 四、关键词 → 文件快速索引
294+ 
295+| 关键词/报错 | 第一优先级文件 | 第二优先级 |
296+|-------------|---------------|-----------|
297+| `capture_begin` / `capture_end` | `graphs.py` → `NPUGraph.cpp` | `AclInterface.cpp` |
298+| `replay` / `ExecuteAsync` | `graphs.py` → `NPUGraph.cpp` | `AclInterface.cpp` |
299+| `aclmdlRICaptureBegin` | `AclInterface.h/.cpp` | `NPUGraph.cpp` |
300+| `aclmdlRIExecuteAsync` | `AclInterface.h/.cpp` | `NPUGraph.cpp` (replay) |
301+| `graph tree` / `TreeManager` | `_graph_tree.py` | `utils/_graph_tree.py` |
302+| `NPUGraphTreeManager` | `npu/_graph_tree.py` | — |
303+| `make_graphed_callables` | `graphs.py` (L889) | `NPUGraph.cpp` |
304+| `super_kernel` / `aclsk` | `SkInterface.h/.cpp` | `graphs.py`, `Graph.cpp` |
305+| `OOM` / `allocator` | `NPUCachingAllocator.cpp` | `NPUGraph.cpp` (pool 管理) |
306+| `Memory pool` / `mempool` | `NPUCachingAllocator.cpp` | `NPUGraph.h` (MempoolId_t) |
307+| `NpuGraphOpHandler` / handler | `_npugraph_handlers/npugraph_handler.py` | `_fa3_graph_handler.py` |
308+| `FA3` / `flash attention` | `_fa3_graph_handler.py` | `npu_fusion_attention_graph.py` |
309+| `IFA` / `infer attention` | `ifa_handler.py` | `FlashAttentionKernelNpuOpApi.cpp` |
310+| `update_graph` / `update` | `graphs.py` (_GraphDispatchMode) | `_fa3_graph_handler.py` |
311+| `graph_task_group` | `Graph.cpp` (pybind) | `NPUGraph.cpp`, `AclInterface.cpp` |
312+| `graph_task_update` | `Graph.cpp` (pybind) | `NPUGraph.cpp` |
313+| `Generator` / `RNG` / `seed` | `NPUGeneratorImpl.h/.cpp` | `graphs.py` |
314+| `register_fake` / `fake tensor` | Dynamo 注册 | `issues/custom-op-register-fake/` |
315+| `HCCL` / `ProcessGroup` | `ProcessGroupHCCL.cpp` | `NPUGraphsUtils.h` |
316+| `Stream` capture status | `NPUGraphsUtils.h/.cpp` | `graphs.py` |
317+| `debug_dump` / `dump` | `NPUGraph.cpp` | `Graph.cpp` (pybind) |
318+| `npu_fusion_attention_graph` | `npu_fusion_attention_graph.py` | `ascend_graph_pass.py` |
319+| `graph_breaks` / Dynamo | — | `utils/_graph_tree.py` |
320+| `npugraph_ex` | `npugraph_ex/__init__.py` | `dynamo/__init__.py` |
321+| `is_current_stream_capturing` | `NPUGraphsUtils.cpp` | `graphs.py` |
322+| `empty_cache` crash | `NPUCachingAllocator.cpp` | `docs/analysis/empty_cache_hccl_unmap_crash.md` |
323+| `save_npugraph_tensor` / `print` | `graphs.py` (调试工具) | — |
324+| `compile` / inductor graph | `graph.py`, `ascend_graph_pass.py` | `dvm/graph_fusion.py` |
325+ 
326+---
327+ 
328+## 五、各使能方式的文件路径差异
329+ 
330+| 使能方式 | 入口文件 | Tree层 | Handler层 | C++核心 |
331+|---------|---------|--------|-----------|---------|
332+| 1. `NPUGraph()` 底层 | `graphs.py` (NPUGraph类) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` |
333+| 2. `graph(g)` 上下文 | `graphs.py` (graph类) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` |
334+| 3. `make_graphed_callables` | `graphs.py` (L889) | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` |
335+| 4. `torch.compile(backend="npugraphs")` | `utils/_graph_tree.py` | `_graph_tree.py` | `_npugraph_handlers/` | `NPUGraph.cpp` |
336+| 5. `torch.compile(backend="npugraph_ex")` | `npugraph_ex/` | 不经过 Tree | 不经过 Handler | `NPUGraph.cpp` |
@@ -0,0 +1,18 @@
1+# 知识领域速查
2+ 
3+> 从 `SKILL.md` 拆分,知识问答时按需查阅。
4+ 
5+## 常见问题→回答要点→涉及文件
6+ 
7+| 用户问 | 回答要点 | 涉及文件 |
8+|--------|---------|---------|
9+| "NPUGraph 怎么工作" | 四种使能方式 + capture→compile→replay 生命周期 | `graphs.py`, `NPUGraph.cpp` |
10+| "图捕获期间发生了什么" | Stream 属性设置 → 内存池切换 → ACL capture begin → 记录算子 | `NPUGraph.cpp: capture_begin()` |
11+| "图重放和直接执行的差异" | ACL 图一次性编译 → 后续重放跳过逐算子 dispatch | `NPUGraph.cpp: replay()` |
12+| "update 机制是什么" | 动态形状时更新指针/形状参数,不重新捕获 | `_fa3_graph_handler.py`, `graphs.py:_GraphDispatchMode` |
13+| "graph tree 怎么管理多图" | warmup→capture→compile→cache,LRU 淘汰 | `_graph_tree.py` |
14+| "为什么会有 graph_breaks" | Dynamo 图断裂原因:data-dependent control flow 等 | `utils/_graph_tree.py: check_for_skip()` |
15+| "FA3 的图模式有什么特殊" | TND + dropout fallback、CPU tensor 参数限制 | `_fa3_graph_handler.py` |
16+| "图和内存的关系" | 图捕获期间禁止新分配,专用 MemPool | `NPUCachingAllocator.cpp` |
17+| "super kernel 是什么" | aclsk 编译器优化编译图,多算子融合 | `SkInterface.cpp`, `Graph.cpp` |
18+| "怎么调试图内数据" | save_npugraph_tensor / print_npugraph_tensor / debug_dump | `graphs.py` (调试工具) |
@@ -0,0 +1,89 @@
1+# 技能设计原理
2+ 
3+> 记录 NPUGRAPH_SKILL 中每个技能的设计决策和权衡。
4+ 
5+---
6+ 
7+## 1. 设计原则
8+ 
9+### PR-001: 不重复原则
10+ 
11+**规则**: 如果 `.npugraphs/` 已有该能力,NPUGRAPH_SKILL 不做。
12+ 
13+**举例**:
14+- `.npugraphs/` 已有 `implementation` (TDD 编码) → NPUGRAPH_SKILL 不设编码技能
15+- `.npugraphs/` 已有 `code-analysis` (源码分析) → NPUGRAPH_SKILL 不设源码分析技能
16+- `.npugraphs/` 已有 `requirements` (需求设计) → NPUGRAPH_SKILL 不设需求分析技能
17+ 
18+### PR-002: 单文件核心 + 参考资料分离
19+ 
20+**规则**: 每个技能的核心逻辑在 `SKILL.md` 中自包含,参考资料放到 `references/`。
21+ 
22+**原因**: 跟从 `.npugraphs/` 的已证明模式。Claude Code 加载 `SKILL.md` 到上下文,按需读取 `references/`。
23+ 
24+### PR-003: 脚本优先于手动操作
25+ 
26+**规则**: 能自动化的操作提供 Python 脚本,在技能中标注 `python scripts/xxx.py` 而非 `grep | awk | sort`。
27+ 
28+**原因**: Bash 管道易出错且不好维护。Python 脚本可测试、可复用、可被 MCP 包装。
29+ 
30+### PR-004: 结构化输出
31+ 
32+**规则**: 技能输出带有明确的字段(Phase、时间线、错误码),JSON 可序列化。
33+ 
34+**原因**: 方便后续 MCP 集成和跨技能数据传递。
35+ 
36+---
37+ 
38+## 2. 技能设计决策
39+ 
40+### graph-mode-diagnostics
41+ 
42+| 决策点 | 选择 | 原因 |
43+|--------|------|------|
44+| 诊断流程 | 5 阶段(分类→定位→模式→追踪→报告) | 继承自 `.npugraphs/debugging`,保持一致 |
45+| 阶段划分 | Capture / Replay / Memory / Update / Compile | 对标图执行生命周期 |
46+| 是否含修复 | 否(handoff 到 implementation) | 遵循 Iron Law:先定位再修复 |
47+ 
48+### graph-performance-profiling
49+ 
50+| 决策点 | 选择 | 原因 |
51+|--------|------|------|
52+| 指标选用 | P50/P99 而非 mean/std | 性能分析的标准实践,P99 更反映长尾 |
53+| 对比方式 | 同脚本两次运行再对比 | 避免跨环境差异(CANN 版本等) |
54+| warmup 次数 | 默认 10 次 | 足够消除首次编译开销 |
55+ 
56+### graph-log-analyzer
57+ 
58+| 决策点 | 选择 | 原因 |
59+|--------|------|------|
60+| 模式命名 | E-/T-/M- 三系列 | E=错误, T=时间线, M=内存,可以无限扩展 |
61+| 解析器实现 | Python 而非 Bash | 支持流式处理、JSON 输出、可 MCP 化 |
62+| 异常检测 | 正则匹配 + 后续 ML | v0.1.0 用正则足够,v2.0 可升级 ML |
63+ 
64+### graph-mcp-integration
65+ 
66+| 决策点 | 选择 | 原因 |
67+|--------|------|------|
68+| MCP 启用顺序 | Context7 → NPU-Log-Parser → Playwright → 其他 | 先启用最快的(文档查询),再自建核心工具 |
69+| 自建 MCP | Python stdio 协议 | 开发简单,与 Claude Code MCP 客户端兼容 |
70+ 
71+---
72+ 
73+## 3. 与 `.npugraphs/` 的差异
74+ 
75+| 维度 | .npugraphs/ | NPUGRAPH_SKILL |
76+|------|------------|----------------|
77+| 语言 | 英文为主 | 中文为主(面向中文团队) |
78+| 触发方式 | `/command` slash 命令 | 关键词 + 上下文匹配 |
79+| MCP 集成 | 无 | 4 个 MCP 推荐 + 3 个自建 |
80+| 脚本 | 无独立脚本目录 | `scripts/` 含 3 个 Python 脚本 |
81+| 分发方式 | 复制 `.npugraphs/` 目录 | 需额外复制 `NPUGRAPH_SKILL/` 和 `NPUGRAPH_SKILL_DOC.md` |
82+ 
83+---
84+ 
85+## 4. 待决策事项
86+ 
87+1. **是否需要独立 git 仓库?** `.npugraphs/` 是独立仓库,NPUGRAPH_SKILL 目前放在 `wtd/` 下。如果要独立分发,需要拆出来。
88+2. **是否需要安装脚本?** 目前手动创建目录。如果有 Cursor rule 安装需求,参考 `.npugraphs/install.sh` 模式。
89+3. **MCP 自建优先级** — NPU-Log-Parser MCP 是否要先于 Playwright?
@@ -0,0 +1,189 @@
1+#!/usr/bin/env python3
2+"""
3+图模式健康检查 — 检查环境配置是否正确,快速诊断常见配置问题
4+ 
5+用法:
6+ python check_graph_health.py [--verbose]
7+ 
8+检查项:
9+ 1. CANN 环境完整性
10+ 2. NPU 设备状态
11+ 3. torch_npu 版本
12+ 4. NPUGraph 功能正常性
13+ 5. 日志环境变量
14+ 6. 图 Pool 状态
15+"""
16+ 
17+import os
18+import sys
19+import argparse
20+import subprocess
21+from datetime import datetime
22+ 
23+ 
24+def check(label: str, passed: bool, detail: str = '', critical: bool = False) -> dict:
25+ return {
26+ 'label': label,
27+ 'passed': passed,
28+ 'detail': detail,
29+ 'critical': critical,
30+ }
31+ 
32+ 
33+def run_checks(verbose: bool = False) -> list[dict]:
34+ results = []
35+ 
36+ # 1. CANN 环境
37+ cann_home = os.environ.get('ASCEND_HOME', os.environ.get('ASCEND_TOOLKIT_HOME', ''))
38+ results.append(check(
39+ 'ASCEND_HOME 已设置',
40+ bool(cann_home),
41+ cann_home or '未设置——CANN 环境可能不完整',
42+ critical=True,
43+ ))
44+ 
45+ # 2. Python 环境
46+ try:
47+ import torch
48+ torch_ver = torch.__version__
49+ results.append(check('PyTorch 已安装', True, f'版本: {torch_ver}'))
50+ except ImportError:
51+ results.append(check('PyTorch 已安装', False, 'torch 未安装', critical=True))
52+ return results
53+ 
54+ try:
55+ import torch_npu
56+ npu_ver = getattr(torch_npu, '__version__', 'unknown')
57+ results.append(check('torch_npu 已安装', True, f'版本: {npu_ver}'))
58+ except ImportError:
59+ results.append(check('torch_npu 已安装', False, 'torch_npu 未安装', critical=True))
60+ return results
61+ 
62+ # 3. NPU 设备
63+ try:
64+ device_count = torch.npu.device_count()
65+ if device_count > 0:
66+ device_name = torch.npu.get_device_name(0)
67+ results.append(check(
68+ 'NPU 设备可用',
69+ True,
70+ f'设备数: {device_count}, 型号: {device_name}',
71+ critical=True,
72+ ))
73+ else:
74+ results.append(check('NPU 设备可用', False, '未检测到 NPU 设备', critical=True))
75+ except Exception as e:
76+ results.append(check('NPU 设备可用', False, str(e), critical=True))
77+ 
78+ # 4. NPUGraph 导入
79+ try:
80+ from torch_npu.npu import NPUGraph, graph, make_graphed_callables
81+ results.append(check('NPUGraph API 可用', True, 'NPUGraph/graph/make_graphed_callables'))
82+ except ImportError as e:
83+ results.append(check('NPUGraph API 可用', False, str(e)))
84+ 
85+ # 5. 日志环境变量
86+ log_vars = {
87+ 'TORCH_NPU_LOGS': os.environ.get('TORCH_NPU_LOGS', '(未设置)'),
88+ 'TORCH_NPU_GRAPHS_LOG_LEVEL': os.environ.get('TORCH_NPU_GRAPHS_LOG_LEVEL', 'INFO(默认)'),
89+ 'TORCH_NPU_GRAPHS_DEBUG': os.environ.get('TORCH_NPU_GRAPHS_DEBUG', '0(默认)'),
90+ 'TORCH_NPUGRAPH_GC': os.environ.get('TORCH_NPUGRAPH_GC', '0(默认)'),
91+ }
92+ results.append(check(
93+ '日志环境变量',
94+ True,
95+ '; '.join(f'{k}={v}' for k, v in log_vars.items()),
96+ ))
97+ 
98+ # 6. NPUGraph 基础功能测试
99+ if device_count > 0:
100+ try:
101+ x = torch.randn(10, 10, device='npu')
102+ g = NPUGraph()
103+ g.capture_begin()
104+ y = x + 1
105+ g.capture_end()
106+ g.replay()
107+ torch.npu.synchronize()
108+ results.append(check('NPUGraph 基础捕获/重放', True, '简单图正常'))
109+ except Exception as e:
110+ results.append(check('NPUGraph 基础捕获/重放', False, str(e)))
111+ 
112+ try:
113+ with graph() as g:
114+ y = x * 2
115+ torch.npu.synchronize()
116+ results.append(check('context manager graph() 可用', True, '上下文管理器模式正常'))
117+ except Exception as e:
118+ results.append(check('context manager graph() 可用', False, str(e)))
119+ 
120+ # 7. 内存信息
121+ try:
122+ allocated = torch.npu.memory_allocated() / 1024**2
123+ reserved = torch.npu.memory_reserved() / 1024**2
124+ total = torch.npu.get_device_properties(0).total_memory / 1024**2
125+ results.append(check(
126+ '内存状态',
127+ True,
128+ f'已分配: {allocated:.0f}MB / 已预留: {reserved:.0f}MB / 总容量: {total:.0f}MB',
129+ ))
130+ except Exception as e:
131+ results.append(check('内存状态', False, str(e)))
132+ 
133+ # 8. CANN 版本
134+ try:
135+ result = subprocess.run(
136+ ['npu-smi', 'info', '-t', 'board', '-i', '0'],
137+ capture_output=True, text=True, timeout=10
138+ )
139+ results.append(check(
140+ 'npu-smi 可用',
141+ result.returncode == 0,
142+ result.stdout.strip()[:200] if result.returncode == 0 else result.stderr.strip()[:200],
143+ ))
144+ except FileNotFoundError:
145+ results.append(check('npu-smi 可用', False, 'npu-smi 命令未找到'))
146+ except Exception as e:
147+ results.append(check('npu-smi 可用', False, str(e)))
148+ 
149+ return results
150+ 
151+ 
152+def print_report(results: list[dict], verbose: bool = False):
153+ """打印健康报告"""
154+ passed = sum(1 for r in results if r['passed'])
155+ failed = sum(1 for r in results if not r['passed'])
156+ critical_failed = sum(1 for r in results if not r['passed'] and r['critical'])
157+ 
158+ print(f"=== NPU 图模式健康检查 ===")
159+ print(f"时间: {datetime.now().isoformat()}")
160+ print(f"结果: {passed} 通过, {failed} 失败 ({critical_failed} 严重)")
161+ print()
162+ 
163+ for r in results:
164+ icon = 'PASS' if r['passed'] else ('CRIT' if r['critical'] else 'FAIL')
165+ print(f" [{icon}] {r['label']}")
166+ if (not r['passed'] or verbose) and r['detail']:
167+ print(f" {r['detail']}")
168+ 
169+ print()
170+ if critical_failed > 0:
171+ print("结论: 环境存在严重问题,需优先解决 CRIT 项")
172+ sys.exit(1)
173+ elif failed > 0:
174+ print("结论: 存在非严重问题,建议解决 FAIL 项")
175+ else:
176+ print("结论: 环境健康,NPUGraph 功能正常")
177+ 
178+ 
179+def main():
180+ parser = argparse.ArgumentParser(description='NPU 图模式健康检查')
181+ parser.add_argument('--verbose', '-v', action='store_true', help='显示详细信息')
182+ args = parser.parse_args()
183+ 
184+ results = run_checks(verbose=args.verbose)
185+ print_report(results, verbose=args.verbose)
186+ 
187+ 
188+if __name__ == '__main__':
189+ main()