Diagnose
昇腾问题的核心诊断循环。你是辅助定位工具——fix 是你给的建议,由人手动应用到客户环境,你不自动改生产。
本文是「可执行脊梁」:只保留始终要跑的骨架与权威规则。步骤的展开机制(子步骤/边界/判定细节)见本 skill 的
references/diagnosis-procedure.md;trace 细节(词表/时间戳/证据落盘完整展开)见references/diagnosis-trace.md,二者按需加载。skill 支撑文件一律放本 skill 的references/,不放仓库根references/(那是知识库/先验层)。
何时用
出现训练或推理问题(中断 / 精度 / 性能),且你在能执行 bash 的 agent 中。被打断后续接 → /skill:resume-diagnosis。
紧急情况(生产中断)
客户说“紧急 / 生产挂了 / 先恢复”时,诊断目标从“查根因”变成“先 stabilize”:
- 还是先查知识库——有匹配的 case(比如已知的安全回滚)直接给,这最快。
- 无快速匹配时,按已提供的信息一步步给 stabilize 建议:问最近 24-48h 改过什么;
npu-smi info/hccl top健康;看日志栈尾定位哪层炸;能否先恢复(回滚 checkpoint / 降配 / 重启 daemon)。 - 不钻深度排查、不写 postmortem——事后用
/skill:to-postmortem补。
流程(骨架)
每步只写「做什么 + 何时用」;子步骤与判定细节见
references/diagnosis-procedure.md对应「步骤 N」。核心循环 = 收集 →(数据缺口则取采集面)→ 路由 → 两阶段加载+2.5 reference → 验证 → (未命中)深度排查 → 产出。
先验 trace 相似检测(收集症状后、路由前):扫
traces/*.yaml(全部 status——进行中+已闭环都留在traces/),按症状里的模型/框架/配置名/category 对每个 state 文件的summary/detected_framework/detected_category做词法 grep 匹配。命中且status: in_progress(或feedback_pending) → "本地有同问题进行中<session_id>()。要/skill:resume-diagnosis续接吗?";命中且已resolved/escalated→ "上次同类<session_id>已定位()。参考其结论还是重新定位?";无匹配 → 正常从路由开始。不再泛泛问"有未完成诊断要续接吗"(旧提示对无关 session 是噪音)。
- 收集症状 + 确认框架(全部来自工程师提供):错误/环境变量/版本组合(引擎+CANN+HDK+架构);信息不全就主动问;主动裁剪日志(失败 rank + 栈尾,绝不灌全量 profiler)。→ 展开见 reference 步骤 1。
- 分类 →
triage-tree.yaml(Tier 1):症状匹配分支 → 路由 namespace;triage 决策记 trace;未命中 → 语义兜底triage_semantic;无法分类 → Tier 3。→ 展开见 reference 步骤 2。 - 两阶段加载 Tier 2:阶段一读命中 category 分片索引筛候选(≤5);阶段二按
confidence.score载全文 +quickly_check(primary→fallback) 验证;阶段 2.5 按需取先验 reference(只读active)。→ 展开见 reference 步骤 3。 - 验证 diagnosis checks:顺序对照已提供信息验证;缺信息→追问;mismatch 且有
fix_on_mismatch→提示 fix(先看 severity);无fix_on_mismatch→标excluded_cases试下一个。→ 展开见 reference 步骤 4。 - 深度排查(未命中):先取流程(方法缺口,见下节) → Tier 3 grep
postmortems/;源码分析(疑似框架/算子层且 Tier 3 未覆盖)走scripts/src_fetch.py(见源码分析小节);都没有→诚实说"知识库未覆盖",建议/skill:to-postmortem。→ 展开见 reference 步骤 5。 - 产出:
resolution+ 顶层summary+ 沉淀状态(sedimented) + trace;结果反馈闭环(问 fix 结果回写 confidence + 写feedback_pending)。→ 展开见 reference 步骤 6。
数据资产探询(「数据缺口」消费点——精度 / 性能类先问这一句)
reference 有三个消费点,都由流程里的缺口决定、都不参与候选路由/排序:数据缺口(缺测量数据 → 本节的采集面,在候选加载前)、判断缺口(有候选、缺签名/背景/修复依据 → 步骤 2.5)、方法缺口(候选全未命中、需要"这类问题怎么查" → 步骤 5)。本节只管数据缺口:命中精度或性能类问题、下一步需要测量数据时,先探询对方手上的资产,再决定给「分析」还是给「采集指导」——别默认对方不会采,也别默认对方已有数据。一句话的成本,换掉一整段可能没人需要的接入说明(原则九:上下文与注意力都是预算)。
绑定落在数据上,不写在散文里:category → 探询问句 → 分支 → 词条 的绑定见 references/collect-gates.yaml(本 skill 支撑文件;每个 id 由 verify_references.py 校验存在且 active——散文里硬编码 ref-id 会静默腐化,已有先例)。本节只给交互形态(问什么、何时问):
| category | 探询问句 | 闸门形态 |
|---|---|---|
| precision | 「你已经有 dump 数据 / 分析结果了吗?还是要我给到代码级接入步骤?」 | 探询型:按回答分支 |
| performance | 「你已经有 profiling 数据了吗(采集产物)?还是要我给采集指引?」 | 探询型:按回答分支 |
| interrupt | —(不预先问) | 条件型:日志不足以定位时才给采集指引 |
分支动作与词条不在此重复(改一处即生效,避免散文与数据双源漂移):走闸表的 branches[].action / refs。
三条纪律:
- 探询只问一次、只问一句——问完按对方回答走,不要"顺便把步骤也讲了"。
- 给接入步骤时必须区分改谁:改用户业务代码(加
PrecisionDebugger等)风险低;改框架源码(vLLM/verl 的 runner 等)属"改被测系统",必须标注临时性 + 给回滚方式。 - 命令以客户环境为准 + 先排除采集副作用:具体命令以客户环境的工具版本为准(版本差异以实际输出为准,不照搬示例);采集行为本身可能让问题消失(工具介入的副作用),先排除再下结论——判据词条见闸表
caveat_refs。
展开细节见
references/diagnosis-procedure.md步骤 1。
始终要避的坑(内联,不必读 reference 就知道):
- 两种缺信息,两个时机:①路由信息(症状/框架/版本/平台/部署形态)不全 → 步骤 1 问;②验证候选所需的精确配置值(某
--additional-config字段/量化档/硬件型号)→ 本步按需问。别混、别让用户全量倒;别在确认该字段前把 provisional 结论写成hit(先给低置信假设 + 明确要什么来验证)。 - 版本软匹配:compat 不符只降 confidence、不硬排除(soft match);没填的维度跳过。
- category 决定 quickly_check 形态:interrupt→grep 签名;precision→数值阈值;performance→profiler 指标;别混。
- 活锁 ≠ 组件故障:同一请求/实体以固定节奏(~1s)重复打同一条日志、且计数冻结 → 判"控制循环活锁"(调度/接纳/抢占在反复重试却无法推进),向控制循环上游走——别把"发日志的组件"当故障组件(load/传输后端常只是表象,真实支点在调度/接纳/抢占层)。
- 判别优先追问:多假设并存时,先问能二分命中的那个问题(如"PD prefill 节点是否禁用了抢占?"这类控制/调度维度,而非数据流细节),再补数据流/传输细节——一刀命中,避免在错误维度上堆证据。
- 类别冲突不得二选一:日志/报错签名指向的方向与症状性质冲突时(例:症状是"输出乱码"→ precision,日志却是 store/BM 初始化失败 → interrupt 味),显式声明这是两条线、各自走各自的消费点——不要因为日志里有 interrupt 签名就跳过 precision / performance 侧的数据缺口探询,也不要把类别判给"先看到的那一方"。category 由症状性质定,不由日志签名定。
- 连续失败 ≤2(只计 fix 未解决):同一个问题给过两次 fix、客户应用后都未解决 → 转人工,不试第三个。候选被 quickly_check 排除不计入(那是候选穷尽,不是失败)。
方法缺口(流程加载——候选全部未命中时才走这一步)
何时:所有 Tier 2 候选都未命中、进入步骤 5 深度排查时——此时这一步是必走的(已有候选命中才不走; 跳过它等于把方法面留空)。不在候选之前加载——候选命中时流程用不上, 提前加载只是多花注意力预算(原则九);这是成本论断,不是"早加载更危险" (第四轮对照:流程先行 11/11 未致偏离命中 case,故"会锚定"未获支持,强度如实标为设计判断)。
怎么做(绑定在本 skill 的 references/procedure-gates.yaml 的 kind: procedure 闸门,id 由 verify_references.py 校验):
- 读仓库根先验层的
references/_procedure-index.yaml(选择器,不是内容;注意与上面那句的references/不是同一个目录——skill 支撑文件在skills/diagnose/references/,先验层在仓库根references/):按本轮 category 过滤categories(该列为空 = 不限定类别),用title/summary选一条最贴合的流程——默认一条。若该流程的前提与现场证据明确矛盾(如它要求的数据形态在你手上根本不成立),可换一条:同样受"连续失败 ≤2"约束,并在 trace 记冲突理由; - 按该行的
file打开词条,读content.flow[]全文(step / action / check / when_to_use)——摘要行不算加载:实测只读摘要与不读等效,流程的反直觉判据会被摘要截断(例:摘要写"同步比例 > 0.2 则存在慢卡",漏掉"慢卡 = WTR 最小的卡"); - 按流程执行:用每步的
check当判定口径(阈值、分流条件),跳步要说明理由; - 某步所需数据不在手上(流程要看"逐卡计算耗时"而导出里没有)→ 如实记
gap,不臆断分支结论; - 记 trace:
{action: reference_lookup, ref_id, purpose: procedure}+{action: procedure_follow, ref_id, steps_executed, branch_taken, gap}(字段见references/diagnosis-trace.md)。
流程是参考,不是判词:流程给的是"这类问题怎么查",不是"这次就是这个"。它与现场证据冲突时以证据为准(记
conflict字段), 分支结论仍需数据支撑才进结论; 且流程走通并解决了问题不免除 case 沉淀(方法解决一次不等于这次事故不值得成为 case)。流程错了也要能被发现:跟随流程给出 fix、但工程师回报没解决时,在
attribution事件里写component: reference:<ref-id>——这样"被跟随后仍失败"的流程能进组件失败簇聚合(component_tally.py), 否则流程层只有加载率、没有失败率,错流程会被稳定注入而无人察觉。
severity 闸门(命中后先看这个)
读候选 case 的 severity 字段,决定输出策略:
benign→ 直接给 fixservice-affecting→ 给 fix,但标注fix_side_effects(如 requires-restart),让人协调窗口data-loss-risk(如"checkpoint 可能被污染")→ 不直接给 fix,输出"先停训练、保留现场、通知 owner"。高危 root cause 的正确动作是 halt 不是 patch
每个 fix_on_mismatch 都带 rollback——人应用失败时能回退。
命中时的输出格式(4 段必需 + 2 个按需块)
判读口径:输出的段数与长度应与问题复杂度相关。根因明确、fix 是单个开关时,四段写完即可。 一件事只说一遍——同一信息在"结论先行"与后续分节各写一次就是冗余(盲评对照里这是最被诟病的一点)。
必需四段(顺序即优先级):
- 结论先行——一句话:现象 + 根因 + 改哪个开关(无内部词表)。
- 依据链——每条结论 → 支撑它的证据/检查结果,逐条标强度:
已验证(本轮实际执行过)/推测(依赖推断、未直接验证)/数据(历史积累,非本轮判断)。 命中 case 时,把「命中 case / 路由依据 / 排除链 / 匹配症状 / 版本匹配 / 历史表现」作为本段的子清单列出,不另起一段:- 命中 case 的 id 与统计必写:
<CASE-ID>(confidence<score>,历史命中<hits>/ 误诊<misdiagnoses>)——它是工程师回溯知识库、以及反馈闭环回写 confidence 的锚点,不要省; - 排除链要给出检查明细;材料没给的如实标"未提供",不写"已验证";
- 版本匹配按软匹配口径(compat 不符只降 confidence);历史表现标为「数据」。
- 命中 case 的 id 与统计必写:
- 修复方案——精确命令或 diff 要点 +
rollback+ side-effect(需重启 / 需升级驱动等)+ 应用后如何验证生效。fix_type决定呈现:env-var/config-change直接给可执行命令;code-patch给改动文件 + diff 要点(不可直接执行);pending-investigation给排查建议。 前提未验证时把核验写成修复的第 0 步(例:case 的 compat 是hdk <26.1而客户没报 HDK 版本 → 第 0 步先npu-smi info核版本,前提不成立就转 plan B),不要带着未验证的前提直接开方。 - 可靠度与残余风险——confidence 分档讲明:
>0.8高可信直接应用、0.5–0.8中可信(应用同时备 plan B)、<0.5仅作提示重点靠手动排查。 另给:哪些是推测、触发/不触发面(什么条件用得上这条结论)、follow-up。
两个按需块(不要默认展开):
- 机制原理——源码/流程层面的因果链。展开就讲完整(触发前置条件 → 每步的"为什么" → 因→果闭环),不得压成一句;命中且根因已由依据链说清时不展开。 去 AI 味 ≠ 删机制:机制与可核对性必须保留,只把内部词表/交叉引用翻译成因果白话。
- 时间线——按日志时间排列的可观察事实,只放可观察项、不夹判断;仅当排查跨多轮、或时间先后本身是判据时展开。
每步必写 trace(硬要求)
每个 step 后往 traces/<session_id>.yaml(每个并发诊断一文件;模板见 diagnosis_state.yaml.example)的 trace 数组追加一条。trace 是完整交互轨迹(trajectory)——统一 {role, ...} 结构:
- agent 事件:
{role: agent, step, action: triage|load_index|quickly_check|load_full|run_check|hit|miss|tier3|feedback|reference_lookup|triage_semantic|source_analysis|attribution|resume, output, reason, ...}。output给用户(可精简)、reason记决策依据(关键决策必写);source_analysis必记tool_calls;attribution执行错可加component。 - user 事件:
{role: user, step, content, evidence}——content摘要(短)+evidence完整证据(inline原文 /files相对路径 /sourcesURL /missing缺口)。 - 证据落盘铁律(必走,无例外):短原文 →
inline存完整原文;长命令/配置/日志块/附件 → 先写traces/evidence/<session_id>/<名>.txt完整原文、evidence.files用相对路径引用、inline只留一行"完整原文见 evidence.files" + 关键指纹。禁止只写摘要、或把原文压成指纹塞inline。 - 写前自检:问"用户贴的原文现在在哪?"——答不出"已存在文件"的相对路径或完整
inline→ 证据未落,先落盘再写 trace。 - 时间戳:建 session 写顶层
created_at;每次写 trace 刷新顶层updated_at(含 resume 续接——置顶诊断面板)。 - trace 边界(只记诊断轨迹 + 误诊归因,别混自演进):用户中途提出的流程改进/设计讨论不是本诊断输入(自演进信号)——走
traces/evidence/<session_id>/<session_id>_evnote.md(渐进式披露,正常定位不披露,真要改 SKILL/脚本时才升级为 EV 卡);attribution执行错归因仅限"确实影响本次结论",纯流程改进走 EV 卡。别把改进讨论写成 trace 的 user/agent 事件,也别用source_analysis记 skill 编辑。
完整细节(
KNOWN_ACTIONS词表、外部事实获取落盘、agent 事件两层、反馈闭环格式、词表同步纪律)见references/diagnosis-trace.md。trace 是误诊归因的唯一依据:误诊先读 trace 断 case 错(改库)还是执行错(改 skill)。不写 trace → 无法归因 → 可能改坏正确的 case。
源码分析(深度排查的子步骤,入口在步骤 5)
报错签名指向框架代码/算子名/量化描述表(如 fault kernel_name=QuantBatchMatMulV3、modelslim_config.py 相关 KeyError)且 Tier 3 未覆盖时:
- 按报错背景确定是哪个源码仓,再向其确认版本(
scripts/src_fetch.py --list看已支持仓库:如 vllm-ascend / torch-npu / CANN / mindspeed-* / verl 等,取决于报错签名指向哪——源码分析依赖对应版本,不要猜)。 - 获取源码(统一走
scripts/src_fetch.py确定性入口——本地优先、复用优先):python3 scripts/src_fetch.py <repo> --ref <tag>(--list看已知仓库与 host:vllm-ascend=GitHub、mindspeed-=GitCode、torch-npu=GitCode、verl=GitHub;未知/私有 →--url)。脚本把「clone 到哪 / 同版本复用 / URL 来自哪」从 agent 自觉变成*确定性操作——本地src-code/<org>/<repo>/已有则复用(git -C log -1/describe核对版本),没有则按已知 host 拉取。「不落库」= 源码不随仓库提交、也不写进知识库;分析仍要保留源码(src-code/本地缓存),知识库只记source_ref代码指针。 - grep 定位:搜报错签名/算子名/函数名(如
grep -rn "QuantBatchMatMulV3" vllm_ascend/)→ 读相关文件片段 → 分析根因。 - 追问用户验证:对照预期/复现/补环境信息,验证根因假设。
- follow-up:查知识库是否已覆盖;
gh search issues/prs看上游是否已修复(已修复→fix=升级到修复版本;未修复→根因+workaround);内网不可达→诚实说明无法查证。 - 多层级:根因指向更底层开源仓(torch-npu)→ 同样流程分析其源码(
source_ref指向该仓);CANN 等未开源 → 承认局限,给方向 + 建议联系华为。 - 沉淀:根因清楚且知识库未覆盖 →
/skill:to-postmortem记source_ref: {repo, ref, file, line};顺手沉淀跨事故稳定的结构事实 →/skill:to-reference(software-fact / env-var-table / compat-matrix,判据:"6 个月后/跨版本是否仍成立")。
不要做
- 不要替人决定 root cause——给结构化清单,人执行后贴回结果
- 不要连续尝试第三个 case——两次 fix 未解决即转人工(误诊保护的串联保护;候选被排除不计入)
- 不要把全量 profiler 灌进 context——裁剪到相关 rank + 栈尾
- 不要用 interrupt 的 grep 思路建 precision 的 quickly_check(category 形态不同)
- 不要直接改本 skill / triage / reference 等会进诊断上下文的资产——改进动作必须先产 EV 卡(
scripts/ev_proposal.py --new)再涉及。诊断中发现的流程改进(执行错/摩擦)走attribution(执行错归因喂 component_tally)或 EV 卡(主动设计改进),不混入本诊断 trace。 - 被打断 →
/skill:resume-diagnosis