飞控日志分析的全部人工经验都在这一个文件夹里维护。 Web 站点(浏览器端 Pyodide 引擎、边缘函数 LLM 层)和未来的平台 MCP 服务 都只消费这里派生出的产物,不在各自代码里另存一份阈值 / 根因 / 提示词。
按飞控固件族分目录:px4/ 与 ardupilot/ 都已接线进构建产物,两边保持同样的结构。
族是怎么配对的:
knowledge/<族名>/配knowledge/engine/providers/<族名>.py——两边 同名即一族,构建脚本按这条规则扫描(web/scripts/build-knowledge.mjs的FAMILIES), 引擎按 provider 的log_type取自己那一套规则 / 数据 / 故障库。加一种日志格式 = 加一个目录 + 一个同名适配器,构建脚本零改动(2026-09 之前这些路径硬编码成knowledge/px4/,于是ardupilot/那批规则写了却没人跑)。
每个固件族目录下再按受众分文档:
knowledge/
── 与固件族无关:各族共用一份(2026-09 从 px4/ 提出)──
llm/ ⓑ 给 AI 看的(第四层:LLM 只做翻译与组装)
gjb841-system-prompt.md GJB-841 思考范式
report-empty.md 无 finding 时的固定结论文案
rules-template.yml 可抄的骨架:一条检查经验(复制成 px4/rules/<名字>.yaml)
plot-template.yml 可抄的骨架:一份绘图预设(复制成 px4/plot/<名字>.yml)
px4/
── 知识本体(不是文档,是经验与字典)──
rules/*.yaml 检查经验:一条经验一个 YAML,判定阈值就写在各自经验里
(31 条经验;failsafe.yaml 一个文件装了 6 条同构经验)
fault-kb.yaml 故障树:标签 → 根因 / 排查步骤 / 禁忌 / 风险等级
facts.yaml 事实层的数据声明:字段名 / 码值 / 飞行阶段分组 / slot 执行顺序,
以及「关键数据」的展示清单(中文名 / 单位 / 顺序)与兜底取数
meta/<tag>.json 固件元数据(生成物):字段字典 + 参数字典(见 meta/README.md)
── 文档(按受众分)──
CLAUDE.md ⓐ 给 AI 与维护者:设计动机、四类经验 → 四种载体、执行链路、
实施状态与落地差异
(子目录 CLAUDE.md:动 `px4/` 下的经验与文档时会自动进上下文,不发布到网站)
ardupilot/ 的知识源自上游 ardupilot-mcp
(MIT),署名与偏差记录在它自己的 ATTRIBUTION.md。结构与 px4/ 刻意不对齐:
ardupilot/
rules/*.yaml 16 个文件共 43 条经验(上游 16 项检查全部迁移);
facts.yaml 码表(飞行模式 / ERR 子系统 / EV 事件 / 机架)与规则元数据
ATTRIBUTION.md 上游署名、迁了什么、改了什么
PENDING.md 剩余待办:占位那一条、FRAME_CLASS 3 号冲突、曲线预设、真实样本
CLAUDE.md 给 AI 与维护者
docs/SOURCES.md 上游 198 条假设审计(引用文本,逐字保留)
它缺 meta/、plot/、fault-kb.yaml(llm/ 与两个 *-template.yml 在 knowledge/ 根下,
各族共用,不算它缺)——这三样都是可选的:没有 meta/ 就不做单位换算、没有 plot/ 就没有
曲线预设、没有 fault-kb.yaml 就不参与第三层故障检索,构建期都不会报错。
这批阈值仍然没有任何一条经过真实 .bin 日志验证:仓库里还没有真实样本,端到端证据
来自 tools/dev/apm_make_sample.py 的合成日志与 tools/engine/check_apm_e2e.py(push 门禁)。
剩下的待办在 ardupilot/PENDING.md。
引擎源码不在这里:operators.py(算子注册表)、engine.py(规则框架 + 报告数据层)在 knowledge/engine/
(浏览器与本地工具共用同一份)。这个目录只放经验与字典——"算完怎么判定",不放"怎么算"。
此处曾同时放这两类东西,2026-09 分开。
站点内容不在这里:给人读的展示内容(guide/skills/mcp,不做计算——与上面的"经验与字典"是两回事)
真源在 web/content/,直接入库,web/lib/{skills,mcp}.ts 运行期读盘。
以前 skills/ 与 mcp/ 挂在本目录、由 sync-content 构建期拷过去;真源唯一化后已退役。
skills/<slug>/
SKILL.md 给 AI 看:YAML frontmatter + 指令正文
README.md 给人看:站点详情页「概述」Tab
CHANGELOG.md 给人看:站点「版本历史」Tab,同时是 version / updatedAt 的唯一真源
SKILL.md 按 Agent Skills 规范写(依据:https://agentskills.io/specification,
即 Anthropic 官方仓库 anthropics/skills README 指向的规范站)。硬约束:
| 项 | 约束 |
|---|---|
name |
1–64 字符,仅小写字母数字与单个连字符,不以连字符开头/结尾;必须等于目录名;不得含保留字 claude / anthropic |
description |
同时写清「做什么」与「什么时候用」(正文要等触发后才加载,"何时用"只能写在这里);≤ 200 字符 |
| 顶层字段 | 只允许 name / description / license / compatibility / metadata / allowed-tools |
metadata |
string→string 映射:列表写成 "A, B"、数字写成 "4.6",不能放数组或数字 |
| 正文 | 建议 < 500 行 |
站点卡片需要的字段(分类、平台、标签、评分…)一律收进 metadata,不散在顶层。
这样每个目录拷进 .claude/skills/<slug>/ 就能被 Claude 直接加载,不必先过一遍我们的站点。
description 的长度取的是严值:规范站给 1024,support.claude.com 给 200,取 200 两边都过。
metadata.seed_rating / seed_downloads 是过渡期的种子值——社区指标最终会迁到
EdgeOne KV(站点已按「种子 + KV 增量」合并)。迁走后这两项从 SKILL.md 删除,
它们本来就不属于 Skill 的内容。
README.md / CHANGELOG.md 与规范的关系:规范建议"可分发的技能包"里不要带这类文件
(省 token、避免与指令混淆)。这里是发布前的源目录,三份文件用途已经分开,
站点三个 Tab 各读一份,所以保留。将来若做「下载为 skill zip」的打包脚本,只打
SKILL.md,这两份留在仓库不进包。
改完跑:
pnpm web:check:skills # 合规校验(CI 里也跑,见 tools/ci/check_all.py)三份文件都能在仓库里直接改、提 PR:详情页每个内容 Tab 右上角就是那一份文件的编辑入口
(地址集中在 web/lib/constants.ts 的 CONTENT_REPO,换仓库只改一处)。
mcp/<slug>/
server.json 给机器看:MCP Registry manifest(上游在哪、怎么装、走什么传输)
README.md 给人看:站点详情页「概述」Tab + 卡片展示字段
CHANGELOG.md 给人看:站点「版本历史」Tab,同时是 version / updatedAt 的唯一真源
两者最容易互相抄错的地方:
Skill(skills/) |
MCP(mcp/) |
|
|---|---|---|
| 规范 | Agent Skills(agentskills.io) | MCP Registry(registry.modelcontextprotocol.io) |
| 规范文件 | SKILL.md |
server.json |
name |
kebab 小写,必须等于目录名 | 反向 DNS io.github.<owner>/<repo> |
description |
≤ 200(两份官方口径取严值) | ≤ 100 |
| 独有概念 | 渐进披露、allowed-tools |
tools / transport / readOnly / packages[] |
两边的 name 规则互斥——同一个名字不可能同时满足。所以两个目录各有各的守卫
(check-skill-spec.mjs / check-mcp-spec.mjs),别合成一个,合了必然有一边是错的。
server.json 必填 $schema / name / description / version / packages[];
packages[] 每项要 registryType / identifier / version / transport.type
(stdio / streamable-http / sse)。
上游没核实的条目不填 manifest——填了就是编造,客户端照着装会装到一个不存在的包。
在 README frontmatter 写 upstream_status: "pending" 可以暂时免交 server.json:
校验会放过这一条,但每次都把它列在 SKIP 行里,不会悄悄变成永久状态。
核实后补上 server.json,并删掉 upstream_status 与 frontmatter 里的 transport
(传输方式只以 server.json 的 packages[].transport 为真源,留两份会被判双真源)。
改完跑:
pnpm web:check:mcp「知识库」分组的两个页面在 web/content/guide/ 里(构建期生成、入库,/guide 站内可见):
- 「如何编写知识规则」
rule-schema.mdx:由build-knowledge.mjs自动生成(算子目录与内置变量表从knowledge/engine/源码派生),唯一一份。 - 「现有规则清单」
rule-catalogue.mdx:构建时从rules/*.yaml现读现算,只出网站这一份。
| 我要…… | 看 / 改 |
|---|---|
| 了解整套规则体系为什么这么设计 | px4/CLAUDE.md(给 AI 与维护者的设计上下文) |
| 新增 / 改一个 Skill | web/content/skills/<slug>/ 三份文件(结构见上节),改完 pnpm web:check:skills |
| 新增 / 改一个 MCP 条目 | web/content/mcp/<slug>/(结构见上节;规范与 Skill 不同),改完 pnpm web:check:mcp |
| 写一条新规则 / 改一条现有规则 | 站内 /guide/rule-schema(字段、算子、常见坑;构建期生成,仓库里不留拷贝) |
| 弄清自己这类经验该写在哪 | px4/CLAUDE.md 的「四类经验 → 四种载体」 |
| 查现在有哪些规则、各自读什么字段、什么条件触发 | 网站 /guide/rule-catalogue(构建时从 rules/*.yaml 生成,仓库里不留拷贝) |
| 只是想"用网页看"这些内容 | 站点 /guide 的「知识库」分组(怎么写规则 / 现有规则两页) |
| 调一条阈值 | 直接改 px4/rules/<那条经验>.yaml 的 threshold 与 triggers[].expr |
| 改字段绑定 / 码值 / 阶段分组 / slot 执行顺序 / 关键数据的名字与顺序 | px4/facts.yaml(引擎不含业务数据,全在这里) |
| 加一条故障模式(根因 / 排查步骤) | px4/fault-kb.yaml(trigger_tags 必须是引擎会产出的标签) |
| 加一个可复用计算步骤 | knowledge/engine/operators.py(@operator 声明 in/out arity),再在经验的 compute 里引用 |
| 改 AI 报告口径 | llm/*.md(与固件族无关,在 knowledge/ 根下) |
| 同步固件元数据 | python tools/dev/fetch_px4_uorb_msg.py --tags ... → 生成物在 px4/meta/<tag>.json |
cd web && node scripts/build-knowledge.mjs # 构建;校验失败会直接报错(或 pnpm build:kb)
# 等价回归:6 条真实日志与冻结基线逐字段比对(不依赖 Node)
python tools/engine/compare_baseline.py
# 生成产物是否真的可执行(不只是语法)
python tools/calibrate/check-artifact.py
# 字段引用与版本错配(字段名写错时引擎只会静默取到 None,这条能揪出来)
python tools/calibrate/lint-rules.py
# 单条经验为什么不触发:逐节点打印
python tools/dev/check_rules_compute.py tools/testdata/logs/<log>.ulg <rule_id>改完算子后,记得重跑 python tools/px4/gen-rule-reference.py(把算子目录与内置变量表注入
「如何编写知识规则」页)。规则清单不用手动跑——build:kb 会从 rules/*.yaml 现读现算,
直接生成网站页面;产物是否与知识源一致,用 --check 比对(CI 用,不一致则退出码 1):
pnpm web:build:kb # 生成全部产物(= node scripts/build-knowledge.mjs)
pnpm web:build:kb -- --check # 只比对不写入:任一产物与 knowledge/ 不一致就退出码 1生成产物(提交进仓库,EdgeOne 直接 next build 也有得用):
web/workers/analysis-engine.generated.ts(内联 knowledge/engine/operators.py + knowledge/engine/engine.py + rules/*.yaml)、
web/workers/fault-kb.generated.json、
web/lib/knowledge/prompts.generated.js、
rules-editor-schema.generated.json(编辑器用,见下节;与固件族无关,落在 knowledge/ 根)。
指南的两页生成物(rule-catalogue.mdx / rule-schema.mdx)也入库,就在 web/content/guide/。
rules/*.yaml 的 schema 已配在 .vscode/settings.json(需要扩展 redhat.vscode-yaml):
写规则时键名会补全、拼错会当场飘红。那份 schema 同样是生成物——词表从 facts.yaml
与 knowledge/engine/ 派生,手改它就等于造出第二份真源(改了 facts.yaml 而它没跟上时,
IDE 会拿旧词表去纠正新写法,比没有提示更糟)。
它只管键名 / 枚举 / 类型这类「纯形状」的问题。compute 表达式内部的语法、算子名、
字段存在性它查不了——那些在字符串里,仍然只有 pnpm web:build:kb 能查。
铁律:生成物不要手改;所有数值判断只在 px4/rules/*.yaml + 引擎框架里发生,
LLM 只做翻译与组装。