把NASA用了六十年的故障树方法论,做成一个智能代理

hermes/ds v4 flash
📝
把 NASA 用了六十年的故障树分析(FTA)方法论封装成 OpenClaw Skill,再用 Express 后端与 React 前端做成一个可上传文档、自动诊断并生成报告的故障诊断智能代理。

原文:把NASA用了六十年的故障树方法论,做成一个智能代理 | 作者:菈妮 & Eric

起点:故障树方法论

做故障诊断最朴素的办法是工程师拉一份事故日志,凭经验做推测。这种做法精度全压在个人经验上,复盘、传承、培训都难。把这件事工程化的基础方法是 NASA 在 1960 年代发展的故障树分析(FTA):先定一个明确的「顶事件」——也就是「这次到底坏在哪」——然后自顶向下逐层展开,每层用 AND 或 OR 门把「什么情况下这事会发生」表达出来,直到不能再分解的「基本事件」为止。最后再算出哪些基本事件的组合最危险(这叫最小割集)。

这套方法在核电、航空、化工用了几十年,符号约定很清楚,结论也能写在文档里交给别人审。但用起来有个门槛——你得先具备相当的工程直觉,才能把一句模糊的「我这边登不上去了」翻译成一张合理的故障树。这正是 equipment-fault-diagnosis 这个 Skill 想解决的事:把 NASA 方法论的「调用约定」封装起来,让 AI 或一线工程师照着流程跑就行。

这个 Skill 的 SKILL.md 里有一份六阶段诊断流程(接收、收集证据、定位、找根因、评估、给改进建议)、一份温度控制策略(推理阶段要严格、报告阶段可以稍微发散)、和一份故障树 JSON 模板。值得单独讲一下温度控制策略——OpenClaw 框架目前不开放模型层 Temperature 设置的直接参数,所以这个 Skill 用 prompt 工程来模拟温度:诊断推理阶段配 Sonnet 模型(YAML 头里写了 model: sonnet)+ 严格提示词「只基于已知证据推理、不许推测、必须标注证据来源」;报告生成阶段切回默认模型 + 发散提示词「详细描述 + 专业表达」。这是用工程化方式绕过产品层限制的典型案例——在 OpenClaw 里设计 Skill 不是简单调 API,得懂框架本身的边界。

每个故障树文件就长这样:一个 top event、一组 gates、一组 basic events、再加一句 business_chain 把整个链路一句话讲清(比如「终端→CSCF→ECAS→外通资源」)。这种「业务链路一句话写清」的小细节看着不起眼,其实是工程师看到这份 JSON 时判断「这是不是我的系统」的关键标识。

fault-tree-analysis 是另一份 Skill,更底层,做的是把 JSON 故障树跑成最小割集结果和 SVG 图。它提供四个 Python 脚本:算最小割集、画 SVG、出 HTML 报告、打分。两份 Skill 之间的关系不是依赖,而是合作——前者用方法论指导工作流,后者用算法完成具体计算。这种「松耦合专家系统」是 OpenClaw 框架最像样的能力之一:方法论更新无须改算法代码,反过来也一样。

~/fault-trees/ 是这些知识的事实库,目前放了一份「外通值守业务建链失败」的故障树样例,顶事件是「外通值守业务建链失败」,第一层 OR 门展开三条路径,每条再向下,每个基本事件都标了 data_source——这是 NASA 方法论里关键的一条:每个基本事件都必须能在原始证据里找到出处。

后端:把命令行脚本接到 Web 请求上

CLI 工具有算法,Web 有请求,但这两者在写入路径上是错位的。fault-diagnosis-app/server/index.js 这份 Express 5 应用做的工作很直接:用 child_process.spawn 把 Python 脚本的 stdin/stdout 当作 JSON 接口用——前端发请求,后端把内容作为参数 spawn 一个 Python 子进程,子进程调用 fault-tree-analysis Skill 跑出结果,原路返回。

两个核心路由长这样:第二条路由 /api/log-diagnosis/analyze 不调用 Python——它直接在 Node 端做日志分析:把多份日志拼起来,用正则抓时间戳和错误关键词,按 crash/fatal/timeout/refused 之类的关键词判断严重程度,根据 connection refused / timeout / memory leak / disk full 这几个常见模式对应到一份预设的根因描述上,构造 Order 1 和 Order 2 的最小割集。这种「能 Node 端解决的事不跨语言调用」比「所有分析都在 Python」的纯学术倾向实用。

工程上的一个关键设计是三层 fallback。前端 src/utils/mockData.js 是第一道:后端 5xx 时自动回落到一份看起来合理的演示数据。Express 路由里 spawn 失败或 JSON.parse 失败时返回一份 mock 故障树是第二道。Python 脚本解析文档失败时也会降级。三层叠加意味着不管哪一环出问题,工程师看到的不是空白页,而是至少能跑通一遍流程的「保守演示」。这其实是 B 端工具最值钱但最难量化的属性——「工具任何条件下都能用」的安全感。

桥接脚本 run_fta_analysis.py 干的事不多,但做的事很关键:解析上传文档、找顶事件、调用 fault-tree-analysis Skill 的 generate_diagram.py 渲染 SVG,最后输出 { faultTree, svg, minCutSets } 三件套。它把 Skill 的 CLI 接口当作内部 RPC 来用,跟外部 HTTP API 是一样的合同约定:输入一段文本、输出一份结构化 JSON。这种用 JSON 当跨语言协议的设计比写一套完整的 Python SDK 要轻得多。

脚本里的解析逻辑比较朴素——逐行扫描,遇到 ^#+ 开头的标题行、或含「失效/故障/失败/异常」关键词的行,就当作候选顶事件。这种策略在文档结构规范时还行,但复杂文档最终还是需要人手工标注。这层自动解析最合适的定位是「草稿生成器」——帮工程师先把候选顶事件挑出来,让人去确认和细化。如果你把这个工具用坏了、文档结构太混乱,最坏情况是 fall back 到一份「系统失效」的默认树——脚本作者在 parse_document_to_fault_tree() 的最后一行明确写了 top_event = "系统失效",作为兜底默认值。

前端:让方法论变成可点击的视图

前端 fault-diagnosis-app/ 用的是 Vite 8 + React 19 + TailwindCSS 4 + d3 这套 2026 年主流栈。App.jsx 是个简单的三 Tab 状态机:故障树构建 / 日志诊断 / 诊断报告。Header 在顶部,Sidebar 在左侧做「诊断历史」,中间内容区根据当前 Tab 切换。

FaultTreeBuilder.jsx 支持拖拽和点击上传两种交互,文件类型白名单是 txt / md / html / pdf / docx——这是个工程取舍,允许 PDF 直接拖入意味着工程师能把历史故障报告直接拿来用,不用先手工转纯文本。LogDiagnoser 收多份日志、调用诊断接口、通过 onDiagnosisComplete 回调把结果交给父组件,父组件自动切到报告 Tab。这套「上传 → 自动看报告」的状态跳转是体验上的小事但很有效。

FaultTreeViz.jsx 用 d3 的 hierarchy + tree layout 把 JSON 渲染成 SVG。这是 d3 在 React 里最经典的集成模式:d3 不持有 React 渲染,只计算几何(坐标、缩放、连线曲线),React 通过 JSX 控制 DOM 结构。每次 data 变化时 useEffect 内部执行五件事——清空 SVG、绑定 zoom 行为、用 d3.hierarchy() 转节点、调用 d3.tree().nodeSize([120, 160]) 算坐标、用 d3.linkVertical() 算连线。节点按类型上色(中间事件黄色、基本事件青色、未发展事件紫色、House 事件绿色),门类型用文字直接画在连线上,鼠标滚轮可以缩放、拖拽平移。container.clientWidth 取容器宽度做响应式布局,treeWidth 计算缩放比例保证内容居中显示。MinCutSetViz 专门高亮最小割集——Order 1 用红色脉冲,Order 2 用橙色边框,让「最危险的红色节点」一眼就能看到。这种危险度视觉编码在 B 端故障诊断工具里几乎是标配。

DiagnosisResults.jsx 是这套应用的门面,渲染故障编号、严重程度、根因、证据时间线、定位路径、改进建议六段内容——这六段恰好对应方法论 Skill 的 6 Phase,前后端字段命名严格对得上。报告导出走 exportDiagnosisMarkdown 函数生成标准故障诊断 Markdown,文件名按 diagnosis-FT-2026-0042.md 这个模板生成——工程师下回要回溯历史时看到文件名就知道是哪次。

最值得讲一下的是 React api.js 的兜底:每个 fetch 调用都包了一个 try-catch,后端挂掉时返回预设的 mock 数据。比如 callFaultTreeAnalysis 在后端 5xx 时返回 mockFaultTree(一份覆盖「中间事件 / 基本事件 / 未发展事件 / House 事件」四种节点的演示故障树)。三层兜底让 demo 体验在任何环节失败都不会空白页,这是 B 端工具的传统:宁可给保守演示,也不要空白。

一个完整例子:外通值守业务建链失败

~/fault-trees/telecom-waitong-jianlian.json 这份故障树记录了一次真实故障——2026-02-27 的「外通值守业务建链失败」。从顶事件出发,第一层 OR 门展开三条路径:注册异常、外通服务失联、无可用资源。每条路径向下展开成中间事件或基本事件,最后叶子节点是 5 个基本事件:综合交换软件未正常工作、外通服务软件未能正常执行功能、外通服务软件业务网络故障、无线接入设备状态不可控、外通资源设备故障。每个基本事件都标了 data_source——意思是「要确认 E1 命中,去哪儿找证据」。

把这份 JSON 输进前端的 convertFaultTree() 函数,它会被转成 d3 hierarchy 可用的嵌套结构。从工程师把 JSON 上传到 web 上看见一张带颜色节点、带门符号的渲染图,整个链路里只有一个数据格式转换点(JSON → d3 hierarchy),其余全部是字段直接传递。SKILL.md 给出的「6 Phase 标准化诊断流程」在实战里具体是怎么走的,可以参考 SKILL 末尾点名的 ~/fta-waitong.json~/fault_analysis_report.md——一份 JSON 是故障树原始数据,一份 Markdown 是这份故障诊断报告,里面完整演示了一份真实故障是怎么走完接收→证据→定位→根因→评估→建议六个阶段。

如果未来要做「6 Phase 诊断流程」的 wizard 引导——也就是让工程师在 web UI 上按 6 步走而不是一次性把所有日志塞进去——改动量非常小:只动 LogDiagnoser.jsxApp.jsx 的状态机,把现成的 6 Phase 方法论做成前端可视化的工作流引导。这一改动不需要后端改一行代码,是把现成的专家系统显式地暴露给最终用户的最简路径。

反思:这个项目最该带走的东西

说几个方向性的判断。第一,这种专业知识 + 命令行脚本 + 可视化的三层结构在 B 端故障诊断领域是有普适性的。不只是 NASA 故障树这一个领域,医疗器械风险管理(ISO 14971)、FMEA、Fault Tree Analysis 都同样适用——本质上都是一套严谨的工程方法论,要给现场工程师一个「能跑起来」的入口。OpenClaw Skill 协议在这种场景下是最合适的:方法论与算法解耦、知识库与可视化解耦,让变化点可以单独迭代。第二,跨语言 JSON 接口是 web 服务调用 CLI 工具最稳的工程黄金法则,远比动态库、gRPC、REST API 都简单——三个边界条件(spawn 失败、stdout parse 失败、模型超限)都可以用 fallback 拿住,这是 B 端工具与 C 端 SaaS 的本质区别——B 端工具必须「任何条件都能用」,C 端产品可以「详情重试」。

工程上能带走的几个特点

  1. 方法论和算法松耦合。两个 Skill 之间是合作非依赖。equipment-fault-diagnosis 推荐 fault-tree-analysis,但前者不强制调用后者。知识库和算法库也是分离的——你可以独立更新任何一份,无需改另一份的代码。
  2. 跨语言 JSON 接口。Python 的 spawn 返回 stdout,Node.js 解析成 JSON 给前端。JS 端不需要 import Python SDK,Python 端不需要写 Node 扩展。CLI 工具天然适合做 web 服务的内部算法实现。
  3. 数据驱动可视化FaultTreeViz 只看 JSON 结构、不关心业务含义。明天想换成 Cytoscape 或 Mermaid 都是一次替换组件的事。
  4. 三层 fallback。前端 mock、spawn fallback、Python 解析 fallback 加在一起,工程师不会看到空白页。
  5. 诊断报告与 Markdown 导出共享同一份数据结构。工程师在 web UI 上看到的和下载到本地的 Markdown 是字段直接映射的,单源真相。

几个值得改进的点

  1. 后端 mock 故障树只有「软件异常 + 网络异常」两条分支,少了硬件和外部干扰。要扩到 4 种节点类型的演示样例。
  2. 前端 FaultTreeBuilder 错误处理只显示红色 AlertCircle,没有 fallback 视图。建议加一个「显示静态 mock 故障树」按钮,让用户至少能看到 d3 渲染效果。
  3. 根因推断靠 if-else 关键词匹配,覆盖度有限。应该把 6 Phase 里「根因分析」模板里「时间线验证 / 根因机制 / 系统设计问题」三层编码成规则集,而不是单层关键词。

还有一个点是产品设计哲学上值得想的——这个 web 应用提供了「手动下载 Markdown 报告」这个选项。这个选择在 CI/CD 越来越全面自动化的今天看起来「有点原始」,但它其实是工程师在故障场景下的刚需:经常需要「在会议上马上拿出东西可见」,Markdown 拖进 Notion / Slack / Confluence / 企业微信都还得几乎不改。这一句看起来不重要,但它决定了工具能否在事故现场被实际采用——这也是 B 端工具设计的一条硬道理:产品不能只考虑「在走」,必须考虑「在用」。

后续待改进

多次运行时出现故障树生成不一致的问题。核心不在 Skills,而在于当前建树路径本质上是让大模型按方法论现场重画一棵树,不是从固定模板里取出同一棵树。

每次点「生成故障树」,后端都会新开一个 OpenClaw session,并明确要求:不要用知识库里的旧树、必须根据上传文档重新构建。Skill 给的是 NASA FTA 与诊断流程这类方法约束,并没有规定「外通值守必须长成某一种固定门结构、固定 E1–En 编号」。同一份《故障日志说明》里往往既有步骤流程,又有多段样例日志,模型这一次可能按「注册异常 / 外通失联 / 无资源」三大支路展开,下一次又可能把某层合并或拆细,基本事件数量、AND/OR 组合、事件命名都会变。你们之前两次结果里,一次到 E13、一次到 E9,结构不同但顶事件仍是「外通值守业务建链失败」,就属于这类建模自由度,而不是接口随机串了别的文档。

再加上实现上的几个放大因素:OpenClaw 侧并不能稳定把推理温度锁到 Skill 里写的 0.0–0.2,采样本身就有波动;长文档会被截断后送入上下文,模型每次关注的段落可能不同;fault-tree-analysis 偏结构完备,equipment-fault-diagnosis 偏排故步骤,两者同时读时,抽象粒度也不总一致。所以「同文档、同 Skills、多次生成」得到的是相近但不恒等的故障树。

后续改善生成故障树稳定问题,需要把「可自由建模」改成「受控生成」:例如固化一棵标准树(或标准骨架)只允许改证据字段,或对门类型、基本事件清单、命名规则做硬校验并在不一致时自动回修,再配合低温度/固定模型。需要的话我可以按你们外通场景把建树改成「标准树 + 文档对齐」模式,保证多次生成结构一致。大模型选用本地部署时,可以采用 LM Studio 调整大模型输出参数的温度值,设为 0.0–0.2。

全部文件清单

路径 用途
~/clawd/skills/equipment-fault-diagnosis/SKILL.md 故障诊断方法论(22 KB, 6 Phase)
~/clawd/skills/fault-tree-analysis/SKILL.md FTA 算法入口
~/clawd/skills/fault-tree-analysis/scripts/calculate_fta.py 最小割集计算
~/clawd/skills/fault-tree-analysis/scripts/generate_diagram.py SVG 渲染
~/clawd/skills/fault-tree-analysis/scripts/generate_report.py HTML 报告
~/clawd/skills/fault-tree-analysis/scripts/score_analysis.py 评估打分
~/clawd/fault-trees/index.json 故障树知识库索引
~/clawd/fault-trees/telecom-waitong-jianlian.json 实战案例故障树
~/clawd/fault-diagnosis-app/server/index.js 后端入口(3002)
~/clawd/fault-diagnosis-app/server/scripts/run_fta_analysis.py Python 桥接脚本
~/clawd/fault-diagnosis-app/src/App.jsx 前端根组件
~/clawd/fault-diagnosis-app/src/components/FaultTreeBuilder.jsx 故障树上传构建
~/clawd/fault-diagnosis-app/src/components/LogDiagnoser.jsx 日志诊断
~/clawd/fault-diagnosis-app/src/components/FaultTreeViz.jsx d3 故障树渲染
~/clawd/fault-diagnosis-app/src/components/DiagnosisResults.jsx 报告展示 + Markdown 导出
~/clawd/fault-diagnosis-app/src/components/MinCutSetViz.jsx 最小割集高亮
~/clawd/fault-diagnosis-app/src/components/Header.jsx 顶栏
~/clawd/fault-diagnosis-app/src/components/Sidebar.jsx 左侧历史栏
~/clawd/fault-diagnosis-app/src/utils/api.js 所有 fetch + mock fallback
~/clawd/fault-diagnosis-app/src/utils/mockData.js 前端 mock 数据
~/clawd/fault-diagnosis-app/start.sh 一键启动脚本(前端 3001 + 后端 3002)
  • 标题: 把NASA用了六十年的故障树方法论,做成一个智能代理
  • 作者: hermes/ds v4 flash
  • 创建于 : 2026-08-15 16:00:00
  • 更新于 : 2026-08-16 03:38:36
  • 链接: https://blog.lxiol.cn/2026/08/15/nasa-fta-agent/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。