@fx-data/ai-diagnostics 0.1.20261016035151 → 0.1.20261026070913

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENT_SETUP.md CHANGED
@@ -1,50 +1,60 @@
1
1
  # FX 数据诊断:Agent 安装与管理
2
2
 
3
- 本文供执行安装与管理的 Agent 读取,不作为日常诊断的前置上下文。包版本为 `0.1.20261016035151`,需要 Node.js `>=22` 及 npm。用户只需表达安装、使用、更新或卸载意图,环境准备和客户端登记由 Agent 完成。
3
+ 本文供执行安装与管理的 Agent 读取,不作为日常诊断的前置上下文。包版本为 `0.1.20261026070913`,需要 Node.js `>=22` 及 npm。用户只需表达安装、使用、更新或卸载意图,环境准备和客户端登记由 Agent 完成。
4
+
5
+ 用户明确要求安装、更新、回退、卸载或检查 FX 数据诊断时,也会触发薄 Skill,并直接执行对应管理命令,不先运行 `context` 或读取诊断规则;只有开始诊断时才按薄 Skill 加载内容。普通聊天不主动加载。
6
+
7
+ 正常路径只完成下列步骤,成功即结束;仅当命令失败或结果不完整时进入异常处理。本工具不要求创建安装清单、读取或写入 `MEMORY.md` 等长期记忆;客户端另有强制要求时仍遵循其规则。
8
+
9
+ 用户提供本地 `.tgz` 时,优先只读取其中的 `package/AGENT_SETUP.md`,并将该包作为安装来源;无需联网查找另一份指引、完整解包或研究 CLI 源码。
4
10
 
5
11
  ## 安装与复用
6
12
 
7
- 1. 识别当前客户端:WorkBuddy、Codex、Pi 分别使用 `workbuddy`、`codex`、`pi`。只登记当前客户端,明确传入 `--agent`,不使用 `all`,无需安装 `find-skills`。其他客户端尚无内置登记支持时说明限制,不冒充已完成安装。
8
- 2. **先查找已有共享安装,再决定是否下载。** 设置了 `FX_DATA_DIAGNOSTICS_HOME` 时优先采用该目录,否则检查下表。
13
+ 1. 识别当前客户端:WorkBuddy、Codex、Pi 分别使用 `workbuddy`、`codex`、`pi`。只登记当前客户端,明确传入 `--agent`,不使用 `all`,无需安装 `find-skills`。技能目录由 CLI 识别,Agent 不自行研究客户端缓存或包内代码。其他客户端尚无内置登记支持时说明限制。
14
+ 2. **合并为一次必要探测:先找共享入口,缺失时再检查现有 Node/npm 和全局包。** 在同一次 shell 调用中按顺序判断;命中已有入口即可停止探测,不逐项调用工具或重复核对。设置了 `FX_DATA_DIAGNOSTICS_HOME` 时优先采用该目录,否则检查下表。
9
15
 
10
16
  | 平台 | 默认共享目录 | 稳定入口(相对共享目录) |
11
17
  |---|---|---|
12
18
  | macOS / Linux | `$XDG_DATA_HOME/fx-data-diagnostics`,未设置时为 `~/.local/share/fx-data-diagnostics` | `bin/fx-data-diagnostics` |
13
19
  | Windows | `%LOCALAPPDATA%\fx-data-diagnostics`,未设置时为 `%USERPROFILE%\AppData\Local\fx-data-diagnostics` | `bin\fx-data-diagnostics.cmd` |
14
20
 
15
- 自定义目录还可从已有薄 Skill 的 `.core-ai-owner.json` 中读取 `home`;只检查用户目录下 `.agents/skills/fx-data-diagnostics` 和 `.workbuddy-ai/skills/fx-data-diagnostics`,不扫描整个用户目录。薄 Skill 也记录了 Node 与稳定脚本的绝对路径。显式目录优先;多个安装无法消歧时说明冲突,不另建副本。
16
- 3. **已有安装直接登记当前 Agent。** 使用其稳定入口运行 `status`,确认 `installed=true`、`package=@fx-data/ai-diagnostics`,再依次执行:
21
+ 自定义目录还可从已有薄 Skill 的 `.core-ai-owner.json` 中读取 `home`;只检查用户目录下 `.agents/skills/fx-data-diagnostics`、`.workbuddy/skills/fx-data-diagnostics` 和 `.workbuddy-ai/skills/fx-data-diagnostics`,不扫描整个用户目录。薄 Skill 也记录了 Node 与稳定脚本的绝对路径。显式目录优先;多个安装无法消歧时说明冲突,不另建副本。
22
+ 3. **已有安装只执行一次登记命令:**
17
23
 
18
24
  ```text
19
25
  <已有稳定入口> install --agent <当前 Agent>
20
- <已有稳定入口> doctor
21
- <已有稳定入口> context
22
26
  ```
23
27
 
24
- 稳定入口复用原 Node,此分支不调用 `npm install`、`npx` 或 `update`。当前 shell 找不到 Node/npm、npm 全局目录没有本包,都不能证明共享安装不存在。Node 路径失效时先修复原运行环境;内容损坏、所有权冲突或权限错误应处理具体错误,不通过第二份安装绕过。
25
- 4. **没有共享安装时才准备首次安装。** 先检查 PATH 中的 `fx-data-diagnostics` 或当前 npm 的全局包目录;已有完整包可直接调用 CLI 登记。确需下载时,优先复用符合 `engines` 要求的已有 Node/npm,不满足时使用客户端托管环境,或从 [Node.js 官方下载](https://nodejs.org/en/download) 获取匹配平台的独立发行包、校验官方校验和后放到用户目录。使用其绝对路径,不替换用户项目的环境,不要求用户手动设置 PATH。
26
- 5. 首次下载和登记如下,`<当前 Agent>` 替换为第 1 步确认的名称:
28
+ 稳定入口复用原 Node,此分支不调用 `npm install`、`npx` 或 `update`,也不需要先运行 `status`。当前 shell 找不到 Node/npm、npm 全局目录没有本包,都不能证明共享安装不存在。
29
+ 4. **没有共享安装时才准备首次安装。** 使用第 2 步的探测结果:PATH 中的 `fx-data-diagnostics` 或当前 npm 全局目录已有完整包时,直接调用 CLI 登记。否则复用符合要求的 Node/npm 安装包;环境不满足才进入下方「环境或安装异常」。下面两步可在同一次 shell 调用中顺序执行,npm 安装成功后才登记;用户提供本地 `.tgz` 时将包名替换为该文件路径:
27
30
 
28
31
  ```text
29
32
  npm install -g @fx-data/ai-diagnostics
30
33
  fx-data-diagnostics install --agent <当前 Agent>
31
- fx-data-diagnostics doctor
32
- fx-data-diagnostics context
33
34
  ```
34
35
 
35
- 全局目录不可写时仅为本次安装指定用户目录的 `--prefix`;现有源找不到本包时仅为本次命令指定 `--registry=https://registry.npmjs.org`,不改全局 npm 配置。已有安装不重复执行 npm 安装命令。
36
- 6. 独立 Node 环境下,用其绝对路径运行配套 `npm-cli.js`(Unix:`lib/node_modules/npm/bin/npm-cli.js`;Windows:`node_modules/npm/bin/npm-cli.js`)。通过同一环境的 `npm root -g` 找到包,沿用自定义 prefix,再用同一 Node 调用包内 `bin/core-ai.mjs` 登记。路径含空格时保留引号;Windows PowerShell 调用带引号的可执行路径需要 `&`。不依赖用户项目目录。
37
- 7. 核对 `doctor` 的完整性、当前客户端登记和 `context` 返回的规则文件可读。失败时同时读取 stderr 中的检查结果。Python 缺失不阻止安装,解析脚本首次需要时再补齐。需要刷新技能时完成客户端支持的刷新(如 Pi 的 `/reload`);无法自动刷新则说明下一步,不把磁盘登记当成已经验证客户端发现。
36
+ 5. **按 `install` 返回结果结束安装。** `ok=true` 且 `checks.integrity`、`checks.skills`、`checks.context` 均为 `ok`,即已完成文件登记和检查。直接反馈,不再执行 `status`、`doctor`、`context` 或 `ls` 复查,不读取 CLI 源码、客户端缓存或诊断规则,不准备 Python/MCP,也不运行示例。结果已包含版本、复用情况、内容入口和阶段耗时。
38
37
 
39
- 安装完成后简短反馈“已启用 FX 数据诊断”、版本、复用或首次下载的结果,以及需要用户执行的刷新动作。将 CLI 返回的 `next_step` 作为默认使用提示:
38
+ 安装完成后用 2–3 句话反馈:版本和首次安装/复用结果;需要的刷新动作;`next_step` 的用法。不附安装过程、目录清单或后续计划。文件登记成功不代表客户端已发现技能;`checks.skills.client_discovery=not_verified` 时只提示刷新或新开聊天,不宣称“已启用”,也不为证明发现而读取客户端缓存。可使用客户端支持的刷新方式(如 Pi 的 `/reload`),无需重新检查安装。默认使用提示为:
40
39
 
41
40
  > 用 FX 数据诊断分析这条 trace 为什么内存超限。
42
41
 
43
42
  提示用户附上实际发生问题的 traceId 或链接。不要自动发起诊断,也不要用虚构 corpId 或整租户时间窗查询作为安装后的默认演示。
44
43
 
44
+ ## 环境或安装异常
45
+
46
+ 仅在命令失败、缺少必要运行环境或返回结果不完整时查看本节;成功结果无需额外检查。
47
+
48
+ - Node/npm 不满足包要求时,使用客户端托管环境,或从 [Node.js 官方下载](https://nodejs.org/en/download) 获取匹配平台的独立发行包,校验官方校验和后放到用户目录。使用绝对路径,不替换用户项目环境,不要求用户手动配置 PATH。
49
+ - 独立 Node 环境下,用其绝对路径运行配套 `npm-cli.js`(Unix:`lib/node_modules/npm/bin/npm-cli.js`;Windows:`node_modules/npm/bin/npm-cli.js`)。通过同一环境的 `npm root -g` 找到包,沿用自定义 prefix,再用同一 Node 调用包内 `bin/core-ai.mjs` 登记。路径含空格时保留引号;Windows PowerShell 调用带引号的可执行路径需要 `&`。不依赖用户项目目录。
50
+ - 全局目录不可写时仅为本次安装指定用户目录的 `--prefix`;现有源找不到本包时仅为本次命令指定 `--registry=https://registry.npmjs.org`,不改全局 npm 配置。
51
+ - 安装失败时同时读取 stderr 中的错误和分项检查。Node 路径失效时修复原运行环境;内容损坏、所有权冲突或权限错误应处理具体错误,不通过第二份安装绕过。需要进一步排错时再运行 `doctor`。
52
+ - 复用旧版时,若 `install` 尚未返回 `checks` 和 `context`,仅此分支补跑稳定入口的 `doctor`、`context` 验证,不因此升级或重新下载。Python 缺失不阻止登记,首次解析材料时再准备。
53
+ - WorkBuddy 的 `.workbuddy/skills` 与 `.workbuddy-ai/skills` 由 CLI 按本机线索选择;无法确认时按 CLI 提示处理,不自行扫描客户端缓存或改成两处同时登记。
54
+
45
55
  ## 按需使用
46
56
 
47
- 明确调用 FX 数据诊断后,按薄 Skill 执行 `context`,读取返回的 `agent_rules`,只加载当前问题需要的 Skill 和引用。固定本次调查的版本与 `pinned_cli`;普通聊天无需预加载诊断内容。
57
+ 用户明确要求用 FX 数据诊断分析问题后,按薄 Skill 执行 `context`,读取返回的 `agent_rules`,只加载当前问题需要的 Skill 和引用。固定本次调查的版本与 `pinned_cli`;普通聊天和管理请求无需预加载诊断内容。
48
58
 
49
59
  已有 SigNoZ MCP 直接复用;实时查询受阻时才按包内配置 Skill 处理,凭据只保存在本机。Python 解析按需准备;独立解释器可通过 `FX_DATA_DIAGNOSTICS_PYTHON` 指定绝对路径。
50
60
 
@@ -58,7 +68,13 @@
58
68
 
59
69
  ## 卸载
60
70
 
61
- 先运行 `status`,说明将一起移除哪些已登记客户端的入口和已安装版本,以及进行中的诊断将无法继续使用包内脚本。用户已明确要求卸载时,说明影响后执行 `uninstall`,不重复请求确认;成功后在原安装环境执行 `npm uninstall -g @fx-data/ai-diagnostics`,沿用自定义 prefix。保留诊断材料和独立 MCP 配置。
71
+ 用户已明确要求卸载时,先说明会一起移除本账户已登记客户端的入口、所有已安装版本和 npm 包,进行中的诊断将无法继续调用包内脚本;随后直接执行稳定入口的 `uninstall`,不重复请求确认,不先运行 `status`、`context` 或读取诊断规则。
72
+
73
+ CLI 使用首次安装记录的 Node/npm 和 prefix,在无网络模式下清理 npm 包,再移除受管副本及薄 Skill。`ok=true`、`complete=true` 即完整卸载成功:用 2–3 句话报告 `removed_skills`、`npm_package` 对应的结果和保留内容后结束,不再运行 `status`、`ls`、`npm uninstall` 或写安装清单/长期记忆。本工具保留的是用户提供的日志、Metric 文件和生成报告,以及独立 MCP 配置;包内规则与知识随包删除,无需扫描或备份其他目录。
74
+
75
+ 若安装未记录可用 npm 来源、返回 `npm_package.status=unconfirmed`,只按返回的 `fallback` 补充清理,不猜测其他 npm 环境或宣称已完整卸载。npm 清理失败时受管状态和入口会保留,按错误修复原环境后重试同一 `uninstall`;需要进一步排错时才运行 `doctor`。
76
+
77
+ 若返回 `npm_package.status=partial`,仅核对 `residual_paths` 列出的疑似 npm 残留,确认归属并清理后重试;不凭目录名直接删除,不扫描其他位置,也不宣称已完整卸载。
62
78
 
63
79
  ## 配置变量
64
80
 
package/README.md CHANGED
@@ -34,8 +34,8 @@
34
34
 
35
35
  > 卸载 FX 数据诊断。
36
36
 
37
- 卸载会删除 npm 包、本机当前账户下的诊断能力包及已登记的各 Agent 入口。诊断材料和独立配置的 SigNoZ 连接会保留。
37
+ 卸载会删除 npm 包、本机当前账户下的诊断能力包(含规则和知识)及已登记的各 Agent 入口。用户提供的日志、Metric 文件和生成报告,以及独立配置的 SigNoZ 连接会保留。
38
38
 
39
39
  ---
40
40
 
41
- **给 Agent:** 安装与管理前读取[Agent 执行说明](https://unpkg.com/@fx-data/ai-diagnostics@0.1.20261016035151/AGENT_SETUP.md);已下载的包中也有同名文件。在线链接不可用时,可从 npm 官方仓库的对应版本包中读取该文件。
41
+ **给 Agent:** 按 [Agent 执行说明](https://unpkg.com/@fx-data/ai-diagnostics@0.1.20261026070913/AGENT_SETUP.md) 完成安装与管理。已有本地包时,优先读取包内 `AGENT_SETUP.md`;本地 `.tgz` 只需读取 `package/AGENT_SETUP.md`。按命令结果简短反馈,成功即结束;只有失败或结果不完整时才排错。
package/bin/core-ai.mjs CHANGED
@@ -17,7 +17,7 @@ const commandName = 'fx-data-diagnostics';
17
17
  const lifecycleProtocol = 2;
18
18
  const defaultRegistry = 'https://registry.npmjs.org';
19
19
  const versionPattern = /^\d+\.\d+\.\d+(?:-[A-Za-z0-9]+(?:[.-][A-Za-z0-9]+)*)?$/;
20
- const agentPaths = { workbuddy: '.workbuddy-ai/skills', codex: '.agents/skills', pi: '.agents/skills' };
20
+ const agentPaths = { workbuddy: '.workbuddy/skills', codex: '.agents/skills', pi: '.agents/skills' };
21
21
 
22
22
  function argumentsFor(argv) {
23
23
  const args = [...argv];
@@ -37,6 +37,30 @@ function argumentsFor(argv) {
37
37
  return { command, options, rest };
38
38
  }
39
39
 
40
+ function commandHelp(argv) {
41
+ const [command = 'help', ...args] = argv;
42
+ const commands = {
43
+ install: { usage: 'install [--agent workbuddy|codex|pi|all] [--home DIR] [--agent-home DIR] [--skills-dir DIR]', description: '为当前 Agent 登记薄 Skill,复用已有共享版本;仅首次登记初始化共享副本。', completion: 'ok=true 且返回的 checks 均通过即结束;简短反馈版本和刷新提示,无需再运行 status、doctor 或 context。' },
44
+ update: { usage: 'update [--version latest|VERSION] [--home DIR] [--registry URL]', description: '下载并校验新版,成功后切换共享版本;默认跟随 latest。' },
45
+ rollback: { usage: 'rollback [--version VERSION] [--home DIR]', description: '切回上次更新前的版本,或明确指定的版本。' },
46
+ uninstall: { usage: 'uninstall [--home DIR]', description: '一次清理本账户已登记入口、受管版本和已记录来源的 npm 包;用户材料与独立 MCP 配置保留。', completion: 'ok=true 且 complete=true 即结束;无需 status、doctor、ls 或额外 npm uninstall。不完整时仅按 npm_package 的 fallback 处理。' },
47
+ status: { usage: 'status [--home DIR]', description: '按需查看共享安装状态;无需作为安装或卸载的前置步骤。' },
48
+ doctor: { usage: 'doctor [--home DIR]', description: '用户要求检查或出现具体异常时,检查完整性和独立能力。' },
49
+ context: { usage: 'context [--home DIR]', description: '开始诊断时获取当前版本和内容路径;管理操作无需预先调用。' },
50
+ session: { usage: 'session [--home DIR]', description: '为诊断材料创建临时目录。' },
51
+ version: { usage: 'version [--home DIR]', description: '显示当前使用的版本。' },
52
+ };
53
+ let target;
54
+ if (['help', '--help', '-h'].includes(command)) target = Object.hasOwn(commands, args[0]) ? args[0] : 'help';
55
+ else if (Object.hasOwn(commands, command) && args.some(arg => arg === '--help' || arg === '-h')) target = command;
56
+ else return null; // metric/python keep their parser or script help arguments.
57
+ const note = '帮助只显示说明,不执行命令。安装和管理成功以命令返回结果为准,简短反馈后停止,无需额外 status 或 doctor。';
58
+ if (target !== 'help') return { ok: true, action: 'help', command: target, ...commands[target], note };
59
+ return { ok: true, action: 'help', name: commandName, display_name: 'FX 数据诊断',
60
+ commands: [...Object.values(commands).map(value => value.usage), 'metric <parser options>', 'python <script> [args...]'],
61
+ registry: defaultRegistry, note };
62
+ }
63
+
40
64
  function defaultHome() {
41
65
  return process.platform === 'win32'
42
66
  ? path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local'), commandName)
@@ -80,7 +104,7 @@ function bundleMetadata(root) {
80
104
  }
81
105
  const pkg = json(path.join(root, 'package.json'));
82
106
  const manifest = json(path.join(root, 'content-manifest.json'));
83
- if (!versionPattern.test(pkg.version) || manifest.version !== pkg.version || manifest.schema_version !== 1) throw new Error('Invalid package version or manifest');
107
+ if (pkg.name !== '@fx-data/ai-diagnostics' || !versionPattern.test(pkg.version) || manifest.version !== pkg.version || manifest.schema_version !== 1) throw new Error('Invalid package name, version or manifest');
84
108
  const content = path.join(root, 'content');
85
109
  const entry = path.join(root, 'bin', 'core-ai.mjs');
86
110
  if (fs.lstatSync(entry).isSymbolicLink() || sha(fs.readFileSync(entry)) !== manifest.cli_sha256) throw new Error('CLI integrity failure');
@@ -138,12 +162,14 @@ function skillText(home) {
138
162
  const powershell = `& ${[process.execPath, script].map(powershellQuote).join(' ')}`;
139
163
  return `---
140
164
  name: fx-data-diagnostics
141
- description: 仅当用户明确要求“用 FX 数据诊断”(也接受“FX数据诊断”)或选中本 Skill 时使用;排查 fx-data 的性能问题、更新链路和引擎 Metric,也支持本工具的更新、回退、卸载和检查。普通对话不主动加载。
165
+ description: 仅当用户明确要求用 FX 数据诊断分析问题,或安装、更新、回退、卸载、检查 FX 数据诊断(也接受“FX数据诊断”),或选中本 Skill 时使用;排查 fx-data 的性能问题、更新链路和引擎 Metric。普通对话不主动加载。
142
166
  ---
143
167
 
144
168
  # FX 数据诊断
145
169
 
146
- 用户明确调用后,先通过稳定 Node 和脚本入口执行 context:
170
+ 先区分管理请求与诊断请求。安装、更新、回退、卸载或检查直接执行下方稳定入口对应命令,不先运行 context,不读取诊断规则;只有开始诊断时才执行 context:
171
+
172
+ 管理请求已有此处的稳定入口,直接调用即可;不再探测全局 CLI、读取源码或客户端缓存。正常结果返回后,用 2–3 句话反馈结果和必要提示即结束,不额外运行 status、ls、doctor 或 context 复查。本工具不要求安装清单或读取、写入 MEMORY.md 等长期记忆;客户端有强制要求时仍遵循。命令失败或结果不完整时才按具体错误排错。
147
173
 
148
174
  macOS / Linux:
149
175
  \`\`\`sh
@@ -163,10 +189,27 @@ context 返回当前版本、内容路径和 agent_rules。读取 agent_rules,
163
189
 
164
190
  用户要求更新时执行稳定入口 update,默认使用 latest;要求回退到上次更新前时执行 rollback,也可用 --version 指定版本。已保留版本校验通过后直接复用。各客户端的新调查共用切换后的版本,进行中调查继续使用原 pinned_cli。更新后按客户端要求刷新技能或新开聊天;安装副本不做 Git 更新。
165
191
 
166
- 用户要求卸载时先执行稳定入口 status,根据 skills 列出已登记客户端,说明整体卸载将移除这些入口及所有已安装版本,进行中诊断将无法继续调用包内脚本。用户已明确要求卸载时,说明影响后执行 uninstall,不重复请求确认;成功后执行 npm uninstall -g @fx-data/ai-diagnostics。诊断材料及独立安装的 MCP 和配置会保留。状态和完整性检查使用 doctor。
192
+ 用户明确要求卸载时,说明会一起移除本账户已登记客户端的入口、所有已安装版本和 npm 包,进行中诊断将无法继续调用包内脚本,然后直接执行稳定入口 uninstall,不重复请求确认,不先运行 status。命令按记录的原 npm 环境完成清理;根据 removed_skills、npm_package 和 complete 报告结果,不再另跑 npm uninstall。安装未记录可用 npm 来源、返回 npm_package.status=unconfirmed 时,只按 fallback 补充清理,不猜测其他 npm 环境。保留的材料是用户提供的日志、Metric 文件和生成报告,以及独立安装的 MCP 和配置;包内规则与知识随包删除,无需扫描或备份其他目录。doctor 仅用于用户要求检查或排错。
167
193
  `;
168
194
  }
169
195
 
196
+ function workbuddySkillsDirectory(agentHome) {
197
+ const candidates = ['.workbuddy/skills', '.workbuddy-ai/skills'].map(name => path.join(agentHome, name));
198
+ const comparable = value => process.platform === 'win32' ? path.resolve(value).toLowerCase() : path.resolve(value);
199
+ // Desktop editions use different user directories. Only recognize the two
200
+ // global roots; plugin/project entries in the cache are not install targets.
201
+ let watched = [];
202
+ try {
203
+ const dirs = json(path.join(agentHome, '.workbuddy', '.skill-list-cache.json')).watch?.dirs;
204
+ if (Array.isArray(dirs)) watched = dirs.filter(value => typeof value === 'string' && path.isAbsolute(value)).map(comparable);
205
+ } catch { /* A missing or stale cache is not required for installation. */ }
206
+ const detected = candidates.find(directory => watched.includes(comparable(directory)));
207
+ if (detected) return detected;
208
+ const existing = candidates.filter(directory => fs.existsSync(directory));
209
+ if (existing.length > 1) throw new Error('Multiple WorkBuddy skill directories found; specify --skills-dir using the current client configuration.');
210
+ return existing[0] || candidates[0];
211
+ }
212
+
170
213
  function targetsFor(options, previous) {
171
214
  if (options['skills-dir']) {
172
215
  if (!options.agent || !agentPaths[options.agent]) throw new Error('--skills-dir requires one --agent workbuddy|codex|pi');
@@ -176,13 +219,14 @@ function targetsFor(options, previous) {
176
219
  const agentHome = normalizedPath(options['agent-home'] || process.env.FX_DATA_DIAGNOSTICS_AGENT_HOME || os.homedir());
177
220
  let agents = options.agent === 'all' ? Object.keys(agentPaths) : options.agent?.split(',');
178
221
  if (!agents) {
179
- agents = Object.keys(agentPaths).filter(name => fs.existsSync(path.join(agentHome, agentPaths[name].split('/')[0])));
222
+ agents = Object.keys(agentPaths).filter(name => fs.existsSync(path.join(agentHome, agentPaths[name].split('/')[0]))
223
+ || (name === 'workbuddy' && fs.existsSync(path.join(agentHome, '.workbuddy-ai'))));
180
224
  if (!agents.length) throw new Error('No Agent detected. Specify --agent workbuddy, codex, or pi.');
181
225
  }
182
226
  return [...new Set(agents)].map(agent => {
183
227
  if (!agentPaths[agent]) throw new Error(`Unsupported Agent: ${agent}`);
184
228
  const registered = !options['agent-home'] && previous?.skills?.find(item => item.agent === agent);
185
- return { agent, directory: registered?.directory || path.join(agentHome, agentPaths[agent], commandName) };
229
+ return { agent, directory: registered?.directory || path.join(agent === 'workbuddy' ? workbuddySkillsDirectory(agentHome) : path.join(agentHome, agentPaths[agent]), commandName) };
186
230
  });
187
231
  }
188
232
 
@@ -205,6 +249,24 @@ function checkSkill(target, home, installId, { required = false } = {}) {
205
249
  if (filesIn(target.directory).some(name => !['SKILL.md', ownerFile].includes(name))) throw new Error(`Skill directory contains user files: ${target.directory}`);
206
250
  }
207
251
 
252
+ function skillChecks(targets, home, installId) {
253
+ const entries = targets.map(target => {
254
+ try { checkSkill(target, home, installId, { required: true }); return { ...target, status: 'ok' }; }
255
+ catch (error) { return { ...target, status: 'error', error: error.message }; }
256
+ });
257
+ // File ownership/integrity proves registration, not that a running client
258
+ // has reloaded its skills. Do not present disk checks as client acceptance.
259
+ return { status: entries.some(entry => entry.status === 'error') ? 'error' : 'ok', entries, client_discovery: 'not_verified' };
260
+ }
261
+
262
+ function contextFor(root, home, pkg, manifest) {
263
+ const content = path.join(root, 'content');
264
+ return { version: pkg.version, source_commit: manifest.source_commit, content_root: content,
265
+ agent_rules: path.join(content, 'AGENTS.md'), skills_root: path.join(content, '.agents', 'skills'),
266
+ pinned_cli: path.join(root, 'bin', 'core-ai.mjs'), node: process.execPath,
267
+ launcher: launchers(home).executable, launcher_script: launchers(home).script };
268
+ }
269
+
208
270
  function clearAbandonedInstallLock(home) {
209
271
  const file = path.join(home, '.install.lock');
210
272
  let info;
@@ -265,7 +327,13 @@ const home=${JSON.stringify(home)};
265
327
  const json=file=>JSON.parse(fs.readFileSync(file,'utf8'));
266
328
  const sha=value=>createHash('sha256').update(value).digest('hex');
267
329
  const versionPattern=${versionPattern};
330
+ const commandName=${JSON.stringify(commandName)};
331
+ const defaultRegistry=${JSON.stringify(defaultRegistry)};
332
+ ${commandHelp.toString()}
268
333
  ${bundleMetadata.toString()}
334
+ const help=commandHelp(process.argv.slice(2));
335
+ if(help)process.stdout.write(JSON.stringify(help)+'\\n');
336
+ else {
269
337
  process.env.FX_DATA_DIAGNOSTICS_HOME=home;
270
338
  let active;
271
339
  let state;
@@ -285,16 +353,18 @@ try {
285
353
  active=null;
286
354
  }
287
355
  if(active)await import(pathToFileURL(path.join(active,'bin','core-ai.mjs')).href);
356
+ }
288
357
  `;
289
358
  }
290
359
 
291
360
  function install(home, options, { sourceRoot = packageRoot, action = 'install' } = {}) {
361
+ const started = performance.now();
292
362
  const { pkg, manifest } = verifyBundle(sourceRoot, { allowMacMetadata: isInstalledBundle(sourceRoot, home) });
293
363
  noSymlinkAncestors(home);
294
364
  const prior = ownedState(home, false);
295
365
  if (!prior && fs.existsSync(home) && fs.readdirSync(home).length) throw new Error('Refusing to take over a nonempty installation directory');
296
366
  const ownership = prior?.ownership || { schema_version: 1, home, install_id: randomUUID() };
297
- const registrationOnly = action === 'install' && prior?.state?.lifecycle_protocol === lifecycleProtocol;
367
+ const registrationOnly = Boolean(action === 'install' && prior?.state?.lifecycle_protocol === lifecycleProtocol);
298
368
  const requestedTargets = targetsFor(options, prior?.state);
299
369
  const targets = (prior?.state?.skills || []).map(({ agent, directory }) => ({ agent, directory }));
300
370
  for (const target of requestedTargets) {
@@ -330,6 +400,7 @@ function install(home, options, { sourceRoot = packageRoot, action = 'install' }
330
400
  verifyBundle(stage);
331
401
  fs.renameSync(stage, release);
332
402
  }
403
+ const contentReady = performance.now();
333
404
  const text = skillText(home);
334
405
  for (const target of writeDirectories) {
335
406
  atomicWrite(path.join(target.directory, 'SKILL.md'), text, 0o644);
@@ -343,14 +414,30 @@ function install(home, options, { sourceRoot = packageRoot, action = 'install' }
343
414
  ]) {
344
415
  if (!registrationOnly || !fs.existsSync(file)) atomicWrite(file, text, 0o755);
345
416
  }
417
+ const registrationReady = performance.now();
418
+ const checks = { integrity: { status: 'ok' }, skills: skillChecks(targets, home, ownership.install_id), context: { status: 'ok' } };
419
+ if (checks.skills.status !== 'ok') throw new Error(checks.skills.entries.filter(entry => entry.status === 'error').map(entry => entry.error).join('; '));
420
+ const context = contextFor(release, home, pkg, manifest);
421
+ fs.accessSync(context.agent_rules, fs.constants.R_OK);
422
+ if (!fs.statSync(context.skills_root).isDirectory()) throw new Error('Diagnostic skills directory is unavailable');
423
+ fs.accessSync(context.skills_root, fs.constants.R_OK);
346
424
  if (!registrationOnly) atomicWrite(path.join(home, homeMarker), JSON.stringify(ownership));
347
425
  const releaseProtocols = { ...prior?.state?.release_protocols };
348
426
  if (prior?.state?.lifecycle_protocol === lifecycleProtocol) releaseProtocols[prior.state.version] = lifecycleProtocol;
349
427
  releaseProtocols[pkg.version] = lifecycleProtocol;
350
- const state = { schema_version: 1, lifecycle_protocol: lifecycleProtocol, release_protocols: releaseProtocols, install_id: ownership.install_id, package: pkg.name, version: pkg.version, previous_version: prior?.state?.version === pkg.version ? prior.state.previous_version : prior?.state?.version || null, registry, skills: targets, source_commit: manifest.source_commit };
428
+ // Capture the npm bootstrap once. Updates run from npm's temporary cache,
429
+ // and registering another Agent must not replace the original prefix.
430
+ const npmInstallation = prior?.state ? prior.state.npm_installation : detectNpmInstallation(sourceRoot);
431
+ const state = { schema_version: 1, lifecycle_protocol: lifecycleProtocol, release_protocols: releaseProtocols, install_id: ownership.install_id, package: pkg.name, version: pkg.version, previous_version: prior?.state?.version === pkg.version ? prior.state.previous_version : prior?.state?.version || null, registry, skills: targets, source_commit: manifest.source_commit,
432
+ ...(npmInstallation ? { npm_installation: npmInstallation } : {}) };
351
433
  if (JSON.stringify(state) !== JSON.stringify(prior?.state)) atomicWrite(path.join(home, 'state.json'), JSON.stringify(state, null, 2));
434
+ const finished = performance.now();
435
+ const elapsed = (from, to) => Math.round((to - from) * 100) / 100;
352
436
  emit({ ok: true, action, ...state, home, launcher: launchers(home).executable, launcher_script: launchers(home).script, node: process.execPath,
353
- ...(action === 'install' ? { next_step: { prompt: '用 FX 数据诊断分析这条 trace 为什么内存超限。', input_hint: '请附上实际发生问题的 traceId 或链接。' } } : {}),
437
+ reused: registrationOnly, checks, context,
438
+ timings_ms: { content: elapsed(started, contentReady), registration: elapsed(contentReady, registrationReady), validation: elapsed(registrationReady, finished), total: elapsed(started, finished) },
439
+ ...(action === 'install' ? { next_step: { prompt: '用 FX 数据诊断分析这条 trace 为什么内存超限。', input_hint: '请附上实际发生问题的 traceId 或链接。' },
440
+ agent_hint: '安装登记与文件检查已完成。按返回结果用 2–3 句话反馈版本、首次安装或复用、需要的刷新及使用提示后结束;不再运行 status/ls/doctor/context 或读取源码、客户端缓存。本工具不要求安装清单或长期记忆,仍遵循客户端强制规则。客户端发现未验证时只提示刷新或新开聊天。' } : {}),
354
441
  refresh: 'Open a new Agent conversation if the skill has not refreshed.' });
355
442
  } catch (error) {
356
443
  for (const item of backup) {
@@ -378,6 +465,82 @@ function npmExecutable() {
378
465
  return file;
379
466
  }
380
467
 
468
+ function npmPackageAt(prefix) {
469
+ return path.join(prefix, ...(process.platform === 'win32' ? [] : ['lib']), 'node_modules', '@fx-data', 'ai-diagnostics');
470
+ }
471
+
472
+ function hasNpmCommand(prefix, root) {
473
+ const command = path.join(prefix, ...(process.platform === 'win32' ? [] : ['bin']), `${commandName}${process.platform === 'win32' ? '.cmd' : ''}`);
474
+ if (!fs.existsSync(command)) return false;
475
+ if (process.platform !== 'win32') return fs.realpathSync(command) === fs.realpathSync(path.join(root, 'bin/core-ai.mjs'));
476
+ // npm's Windows shim addresses the package relative to its own directory.
477
+ return fs.readFileSync(command, 'utf8').replaceAll('\\', '/').includes('node_modules/@fx-data/ai-diagnostics/bin/core-ai.mjs');
478
+ }
479
+
480
+ function detectNpmInstallation(root) {
481
+ const modules = path.dirname(path.dirname(root));
482
+ const prefix = process.platform === 'win32' ? path.dirname(modules) : path.dirname(path.dirname(modules));
483
+ // An extracted tarball or npm exec cache is not a global installation. The
484
+ // conventional layout and npm-created command must both identify this bundle.
485
+ if (npmPackageAt(prefix) !== root || !hasNpmCommand(prefix, root)) return { kind: 'none' };
486
+ // Reusing an existing package only needs Node. A separate Node-only runtime
487
+ // may not expose the npm that installed it; never let optional cleanup
488
+ // metadata block registration or guess another runtime's npm environment.
489
+ let npm;
490
+ try { npm = fs.realpathSync(npmExecutable()); } catch { return undefined; }
491
+ return { kind: 'global', package_root: root, prefix, node: process.execPath, npm_cli: npm };
492
+ }
493
+
494
+ function assertNoNpmResiduals(source, details) {
495
+ const scope = path.dirname(source.package_root);
496
+ noSymlinkAncestors(scope);
497
+ let names;
498
+ try { names = fs.readdirSync(scope); }
499
+ catch (error) { if (error.code === 'ENOENT') return; throw error; }
500
+ // npm can retire an old package under a temporary name and leave it behind
501
+ // after interruption. Names only signal incomplete cleanup, not ownership:
502
+ // never inspect or delete these entries automatically, or scan other scopes.
503
+ const residuals = names.filter(name => name.startsWith('.ai-diagnostics-')).sort().map(name => path.join(scope, name));
504
+ if (!residuals.length) return;
505
+ const error = new Error('npm cleanup is incomplete: possible retired package paths remain. Managed installation was retained; inspect the listed paths before cleaning them and retrying uninstall.');
506
+ error.result = { action: 'uninstall', complete: false, npm_package: { status: 'partial', ...details,
507
+ residual_paths: residuals, fallback: 'Inspect only residual_paths to verify they are retired copies of this npm package. Remove them only after verification, then retry the same uninstall command; do not scan or remove other directories.' } };
508
+ throw error;
509
+ }
510
+
511
+ function removeNpmInstallation(source) {
512
+ if (!source) return { status: 'unconfirmed', fallback: 'This installation did not record a usable npm environment. If a global npm package remains, use its original Node/npm and prefix to run npm uninstall -g @fx-data/ai-diagnostics. Do not remove packages from an unrelated npm environment.' };
513
+ if (source.kind === 'none') return { status: 'not_applicable' };
514
+ if (source.kind !== 'global' || !['prefix', 'package_root', 'node', 'npm_cli'].every(key => typeof source[key] === 'string' && path.isAbsolute(source[key]))
515
+ || npmPackageAt(source.prefix) !== source.package_root) throw new Error('Recorded npm installation is invalid; managed installation was retained');
516
+ noSymlinkAncestors(source.package_root);
517
+ const details = { package_root: source.package_root, prefix: source.prefix };
518
+ assertNoNpmResiduals(source, details);
519
+ if (!fs.existsSync(source.package_root)) return { status: 'already_absent', ...details };
520
+ const pkg = json(path.join(source.package_root, 'package.json'));
521
+ if (pkg.name !== '@fx-data/ai-diagnostics' || pkg.bin?.[commandName] !== 'bin/core-ai.mjs'
522
+ || !hasNpmCommand(source.prefix, source.package_root)) throw new Error('Recorded npm package or command has changed; managed installation was retained');
523
+
524
+ // npm uninstall does not need network access or user credentials. Isolate its
525
+ // configuration so unrelated registry settings cannot delay or block removal.
526
+ const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'fx-npm-uninstall-'));
527
+ try {
528
+ for (const file of ['user.npmrc', 'global.npmrc']) fs.writeFileSync(path.join(temporary, file), '');
529
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !/^npm_config_/i.test(key) && !/^(NPM_TOKEN|NODE_AUTH_TOKEN)$/i.test(key)));
530
+ Object.assign(env, { NPM_CONFIG_USERCONFIG: path.join(temporary, 'user.npmrc'), NPM_CONFIG_GLOBALCONFIG: path.join(temporary, 'global.npmrc'), NPM_CONFIG_CACHE: path.join(temporary, 'cache') });
531
+ const result = spawnSync(source.node, [source.npm_cli, 'uninstall', '-g', '--prefix', source.prefix,
532
+ '--offline', '--ignore-scripts', '--no-audit', '--no-fund', '--update-notifier=false', '@fx-data/ai-diagnostics'],
533
+ { cwd: temporary, env, encoding: 'utf8', timeout: 60_000 });
534
+ assertNoNpmResiduals(source, details);
535
+ if (result.error || result.status !== 0 || fs.existsSync(source.package_root)) {
536
+ const error = new Error(`npm package removal failed (${result.error?.code || `exit ${result.status}`}); managed installation was retained. Fix the recorded npm environment and retry uninstall.`);
537
+ error.result = { action: 'uninstall', complete: false, npm_package: { status: 'error', ...details } };
538
+ throw error;
539
+ }
540
+ return { status: 'removed', ...details };
541
+ } finally { fs.rmSync(temporary, { recursive: true, force: true }); }
542
+ }
543
+
381
544
  function update(home, options, action = 'update') {
382
545
  const { state } = ownedState(home);
383
546
  if (!state) throw new Error('No active version');
@@ -418,14 +581,28 @@ function python() {
418
581
  }
419
582
 
420
583
  function uninstall(home) {
584
+ const started = performance.now();
421
585
  const { ownership, state } = ownedState(home);
422
586
  clearAbandonedInstallLock(home);
423
587
  const directories = skillDirectories(state?.skills || []);
424
588
  for (const target of directories) checkSkill(target, home, ownership.install_id);
589
+ const checked = performance.now();
590
+ // Clear the external bootstrap first. If npm fails, keep the stable entry and
591
+ // recorded source intact so the same command can safely be retried.
592
+ const npmPackage = removeNpmInstallation(state?.npm_installation);
593
+ const npmRemoved = performance.now();
425
594
  for (const target of directories) if (fs.existsSync(target.directory)) fs.rmSync(target.directory, { recursive: true });
426
595
  // Remove only owned program files. Configuration, npm cache and evidence are retained.
427
- for (const name of ['bin', 'releases', 'state.json']) fs.rmSync(path.join(home, name), { recursive: true, force: true });
428
- emit({ ok: true, action: 'uninstall', home, removed_skills: state?.skills || [], retained: fs.readdirSync(home) });
596
+ // Keep the stable command and state until version cleanup completes, so a
597
+ // client-denied recursive deletion can be confirmed and retried normally.
598
+ for (const name of ['releases', 'bin', 'state.json']) fs.rmSync(path.join(home, name), { recursive: true, force: true });
599
+ const finished = performance.now();
600
+ const elapsed = (from, to) => Math.round((to - from) * 100) / 100;
601
+ emit({ ok: true, action: 'uninstall', complete: npmPackage.status !== 'unconfirmed', home,
602
+ removed_skills: state?.skills || [], npm_package: npmPackage, retained: fs.readdirSync(home),
603
+ agent_hint: npmPackage.status === 'unconfirmed' ? '受管入口已移除,但 npm 包清理尚未确认。只按 npm_package.fallback 补充处理,不宣称完整卸载。'
604
+ : '完整卸载已完成。用 2–3 句话反馈移除结果和保留的用户材料、MCP 配置后结束;无需额外 status/ls/npm uninstall,也无需本工具的安装清单或长期记忆,仍遵循客户端强制规则。',
605
+ timings_ms: { validation: elapsed(started, checked), npm: elapsed(checked, npmRemoved), managed: elapsed(npmRemoved, finished), total: elapsed(started, finished) } });
429
606
  }
430
607
 
431
608
  function doctor(home) {
@@ -442,11 +619,7 @@ function doctor(home) {
442
619
  checks.integrity.error = error.message;
443
620
  }
444
621
  if (installed?.state) {
445
- const entries = installed.state.skills.map(target => {
446
- try { checkSkill(target, home, installed.ownership.install_id, { required: true }); return { ...target, status: 'ok' }; }
447
- catch (error) { return { ...target, status: 'error', error: error.message }; }
448
- });
449
- checks.skills = { status: entries.some(entry => entry.status === 'error') ? 'error' : 'ok', entries };
622
+ checks.skills = skillChecks(installed.state.skills, home, installed.ownership.install_id);
450
623
  }
451
624
  const runtime = python();
452
625
  checks.python = { status: runtime.available ? 'ok' : 'unavailable', ...runtime };
@@ -458,6 +631,10 @@ function doctor(home) {
458
631
  }
459
632
 
460
633
  async function main() {
634
+ // Help is resolved before option validation, installation state, routing or
635
+ // environment discovery. Even a broken installation must remain inspectable.
636
+ const help = commandHelp(process.argv.slice(2));
637
+ if (help) return emit(help);
461
638
  const { command, options, rest } = argumentsFor(process.argv.slice(2));
462
639
  const home = normalizedPath(options.home || process.env.FX_DATA_DIAGNOSTICS_HOME || installedBundleHome(packageRoot) || defaultHome());
463
640
  let current;
@@ -497,11 +674,10 @@ async function main() {
497
674
  if (command === 'update') return update(home, options);
498
675
  if (command === 'rollback') return update(home, options, 'rollback');
499
676
  if (command === 'uninstall') return uninstall(home);
500
- if (command === 'help' || command === '--help') return emit({ name: commandName, display_name: 'FX 数据诊断', commands: ['install [--agent workbuddy|codex|pi|all] [--home DIR] [--agent-home DIR] [--skills-dir DIR]', 'update [--version latest|VERSION]', 'rollback [--version VERSION]', 'uninstall', 'status', 'doctor', 'context', 'session', 'metric <existing parser options>', 'python <script> [args...]'], registry: defaultRegistry, note: 'Install adds Agent entries without changing the shared active version. Update follows latest by default. Rollback reuses retained versions offline. Does not modify user npm configuration or business credentials.' });
501
677
  const { pkg, manifest, content } = verifyBundle(packageRoot, { allowMacMetadata: isInstalledBundle(packageRoot, home) });
502
678
  if (command === '--version' || command === 'version') return emit({ version: pkg.version, package: pkg.name });
503
679
  if (command === 'status') return emit({ ok: true, installed: Boolean(current?.state), home, ...current?.state, lifecycle_protocol: current?.state?.lifecycle_protocol ?? null, migration_required: migrationRequired, launcher: current?.state ? launchers(home).executable : null, launcher_script: current?.state ? launchers(home).script : null, node: process.execPath });
504
- if (command === 'context') return emit({ version: pkg.version, source_commit: manifest.source_commit, content_root: content, agent_rules: path.join(content, 'AGENTS.md'), skills_root: path.join(content, '.agents', 'skills'), pinned_cli: path.join(packageRoot, 'bin', 'core-ai.mjs'), node: process.execPath, launcher: launchers(home).executable, launcher_script: launchers(home).script });
680
+ if (command === 'context') return emit(contextFor(packageRoot, home, pkg, manifest));
505
681
  if (command === 'session') return emit({ directory: fs.mkdtempSync(path.join(os.tmpdir(), 'core-ai-')) });
506
682
  if (command === 'metric' || command === 'python') {
507
683
  const runtime = python();
@@ -516,4 +692,4 @@ async function main() {
516
692
  throw new Error(`Unknown command: ${command}`);
517
693
  }
518
694
 
519
- try { await main(); } catch (error) { process.stderr.write(`${JSON.stringify({ ok: false, error: error.message })}\n`); process.exitCode = 1; }
695
+ try { await main(); } catch (error) { process.stderr.write(`${JSON.stringify({ ok: false, error: error.message, ...error.result })}\n`); process.exitCode = 1; }
@@ -19,13 +19,23 @@ description: 当 $diagnose-performance 已将问题路由到仪表板 2.0(FVS
19
19
  2. 外部组件入口必须先读取或复用[来源判定](../../../knowledge/common/trace-routing.md#外部组件取数入口);其他跨场景冲突按同一文件处理。
20
20
  3. 需要生成或排序候选假设时读取 [references/experience.md](references/experience.md) 的相关经验;已有直接证据足够时核对反证和边界后返回。
21
21
 
22
+ ## 组件取数先做对照
23
+
24
+ 识别为仪表板内组件计算后,先自行查找对应组件的单独计算记录,再深入仪表板上下文;不等待用户先说明“单独正常”。已有可用对照直接复用,查找窗口、预算及编辑态属性缺失时的日志回退见[仪表板内外组件对照](../../../knowledge/common/signoz-query-policy.md#仪表板内外组件对照),身份与成功判定见[组件计算对照的证据](../../../knowledge/dashboard/observability.md#组件计算对照的证据)。
25
+
26
+ - 找到可比的单独计算成功记录,且仪表板失败或明显退化:优先核对仪表板额外带入的动态字段、参数、筛选、联动与权限,比较直接读取已有结果和展开上游计算的差异;不能仅凭入口不同认定原因。
27
+ - 两种入口均异常:保留各自执行,先查共同的组件查询、输入数据和计算链;需要进入其他场景时带上证据返回性能入口,不强行归因为仪表板条件。
28
+ - 未找到对照、身份无法核实或记录不足:立即继续分析已有异常 trace 和必要 Metric,交付已确认事实、可能原因及各自缺口,并提示用户检查“单独组件预览时是否也报错或变慢”。不把提供正常 trace 或执行预览作为继续当前诊断的前提,不自行触发计算。
29
+
30
+ 仪表板内的快速成功请求可以补充解释不同计算路径,但必须标明仍是仪表板请求,不能冒充组件单独预览。用户已经反馈单独预览结果时沿用反馈,不重复要求确认;尚未取得该执行材料时明确其证据层次。
31
+
22
32
  ## 排查顺序
23
33
 
24
34
  1. 先区分筛选器自身候选值取数和组件取数。首次加载、筛选值变化、参数变化和联动只是组件取数的触发上下文,不作为并列取数场景;不要因为组件请求携带筛选条件就把它归为筛选器自身取数。
25
35
  外部组件入口先按上述来源判定确认;属于仪表板的组件计算由本 Skill 直接调查,不先经过组件场景重新分流。
26
36
  2. 记录用户感知的开始/完成条件、report/dashboard、控件/组件、环境、时间窗和触发动作。组件取数由一次动作触发多个组件时,以全部受影响组件刷新完成为用户感知的完成;筛选器自身候选值取数仍单独分析。
27
37
  3. 对控件候选值取数,只分析当前控件的候选值计算及其他控件带来的筛选影响。
28
- 4. 对组件取数,先从入口和计算链路的 span attribute 还原当前组件的完整业务计算,不要先根据慢 span 选择 Metric:
38
+ 4. 对组件取数,先按上节完成有界对照查找,再从入口和计算链路的 span attribute 及关联请求日志还原当前组件的完整业务计算,不要先根据慢 span 选择 Metric:
29
39
  - 组件在计算哪些查询,使用哪些字段、聚合、排序、窗口或 TopN;
30
40
  - 根表、绑定表、筛选字段所在表及完整关系路径;
31
41
  - 控件条件、参数、权限、自动联动和组件间自定义联动分别加入了什么计算;
@@ -35,9 +45,9 @@ description: 当 $diagnose-performance 已将问题路由到仪表板 2.0(FVS
35
45
  - dashboard 与控件、组件的资源关系;
36
46
  - DataRequestObservation 入口信息,以及 `filter_conditions.root_table_id`、`node_count`、`max_depth`、`table_paths`、`tree_summary` 描述的最终筛选树;
37
47
  - 组件计算存在多层、多分支过滤树,或结论依赖筛选器所在层级时,必须按[筛选树还原与交付](../../../knowledge/dashboard/observability.md#筛选树还原与交付)校验并输出完整树结构,不能用数量摘要替代;
38
- - 当前组件的查询、绑定表与算子,以及控件条件、参数、权限、自动联动和组件间自定义联动;
48
+ - 当前组件的查询、绑定表与算子,以及控件条件、参数、权限、自动联动和组件间自定义联动;动态字段覆盖的确认及取证边界按[动态字段知识](../../../knowledge/dashboard/observability.md#动态字段覆盖与上游展开)处理;
39
49
  - 还原出的具体计算是否能够解释慢点;耗时集中在查询执行阶段只证明慢发生在哪里,执行事实按第 9 步取得。
40
- 默认详情缺少业务 attribute 时,按[取证例子](../../../knowledge/dashboard/query-examples.md)和当前 MCP 工具声明定向补取。助手按上述筛选树规则还原、核对和展示树,补齐名称,并处理核对失败的情况。
50
+ 默认详情缺少业务 attribute 时,按[取证例子](../../../knowledge/dashboard/query-examples.md)和当前 MCP 工具声明定向补取。助手按上述筛选树规则还原、核对和展示树,补齐名称,并处理核对失败的情况。控件或动态字段只剩 ID 时,按[名称与请求上下文的关联取证](../../../knowledge/dashboard/observability.md#名称与请求上下文的关联取证)查请求及控件关系日志,不能停在默认属性或计数摘要并宣称名称无法取得。
41
51
  能找到同 report、同组件、同查询表、同一可靠短时间窗内未携带目标控件的请求时,优先作为基线,对比耗时和过滤树规模。当前 trace 没有覆盖参数、聚合、排序等完整查询指纹,因此即使身份和时间匹配也只能标为 `strong_correlated_signal`,不能当作严格因果对照;任一身份字段缺失或超出短窗时降为普通相关信号。
42
52
  6. 用户明确是筛选值变化后变慢时,只把具有同一筛选证据或可靠时间边界的组件请求组成受影响集合;观察全部受影响组件,验证筛选绑定字段与表的位置是否合理,并区分影响范围大和单个计算慢。
43
53
  未观察到其他组件请求只证明本次观测范围,不等于配置上绝对没有其他目标;确认影响范围唯一需按[核对配置影响范围](../../../knowledge/dashboard/observability.md#核对配置影响范围)读取 targets 并按画布 instanceId 判断。
@@ -47,7 +57,7 @@ description: 当 $diagnose-performance 已将问题路由到仪表板 2.0(FVS
47
57
 
48
58
  ## 查询选择与预算
49
59
 
50
- 根据当前缺口读取[取证例子](../../../knowledge/dashboard/query-examples.md),复用已有执行和字段。已有 trace 时补本次业务记录;只有资源时先寻找有限候选;已确认控件与交互范围时再核对受影响请求或同窗对照。精确条件未命中时只在同范围降级一次,不能为寻找基线跨天扫描。
60
+ 根据当前缺口读取[取证例子](../../../knowledge/dashboard/query-examples.md),复用已有执行和字段。已有 trace 时补本次业务记录,并按上述对照规则优先查单独计算;只有资源时先寻找有限候选;已确认控件与交互范围时再核对受影响请求或同窗对照。精确条件未命中时只在同范围降级一次;组件单独计算使用公共规则规定的独立基线窗口,不为寻找基线跨天扫描。
51
61
 
52
62
  筛选器自身取数与组件取数按实际入口和属性区分,触发动作另行记录。只有抽象描述时先确认目标,环境与时间明确后再查询。组件资产 ID 与画布实例 ID 仍需证据核对,不能自动互换。
53
63
 
@@ -53,3 +53,11 @@
53
53
  - 参数或筛选绑定在很深或很上游的层级,扩大组件过滤树:读取完整 `root->parent...` 路径、层级和受影响目标。
54
54
 
55
55
  默认值、点击查询或绑定下移是否可用都带业务前提,不能由历史案例替用户决定。
56
+
57
+ ## 单独预览正常,仪表板展开上游后超限
58
+
59
+ 一次已核查案例中,组件可以直接读取少量已保存的汇总行;仪表板失败请求则出现上游动态字段覆盖,并展开多路明细、关联和公共缓存,最终触发查询内存上限。同一仪表板也有快速成功请求,因此“在仪表板内”本身不足以解释差异。
60
+
61
+ 可优先核对是否已有动态字段覆盖,以及覆盖所在表到组件查询表的路径。覆盖确认的证据和补查边界按[动态字段知识](../../../../knowledge/dashboard/observability.md#动态字段覆盖与上游展开),名称恢复按[关联取证](../../../../knowledge/dashboard/observability.md#名称与请求上下文的关联取证),不把控件键的 `parameter:` 前缀当普通参数类型,也不把 `unbound` 当成动态字段未生效。
62
+
63
+ 本案例通过同字段 ID、表、算子和目标组件匹配,从另一次控件关系请求日志补到了字段名。名称映射不能补出失败时的选择值,历史请求不能冒充失败 trace 的属性。需要实际计划和 Metric 区分展开上游、局部放大及缓存压力;只有计数和时间接近时仍保留因果缺口,不能直接认定动态字段是唯一根因。
@@ -28,6 +28,7 @@ description: 当用户要排查 fx-data 端到端慢、超时、内存超限、O
28
28
  - 环境、时间和历史对比的默认值见[公共查询规则](../../../knowledge/common/signoz-query-policy.md)的“默认值速查”,不向用户重复索要已有信息。“之前没超限”“以前正常”“最近变慢”已经是历史对比目标,不等待用户再次说“帮我对比”。
29
29
  - 企业更新或 dagId 是更新调查入口,按用户目标进入更新性能专项;只有企业 ID 而未说明要看什么时,合并询问更新现象和时间,不扩展成企业全部性能调查。
30
30
  - 场景明确时直接选择专项:分析表 `$diagnose-dataset-performance`、组件 `$diagnose-component-performance`、仪表板 `$diagnose-dashboard-performance`、更新 `$diagnose-update-performance`。
31
+ - 仪表板内组件异常先由仪表板专项[有界核对组件单独计算](../diagnose-dashboard-performance/SKILL.md#组件取数先做对照);未找到对照仍继续本次异常分析,不先要求用户提供成功 trace。
31
32
  - 只有 trace、执行链接或场景不明时,按需读取 [trace 路由](../../../knowledge/common/trace-routing.md),用最小摘要选择起点。公共路由只返回场景及依据,不调用 analyzer;识别为更新后仍先进入 `$diagnose-update-performance`。公共知识无法识别时,以实际接口、span 或 attribute 搜索并只读命中场景的 `observability.md`。
32
33
  - 命中共享的外部组件取数入口时,必须先读取或复用[外部组件来源判定](../../../knowledge/common/trace-routing.md#外部组件取数入口),按同一入口父子链确认来源,再进入组件或仪表板专项。不能仅凭最外层 HTTP span 或共享 span 名分流。
33
34
  - 多场景线索从证据最充分的一处开始,保留其他关联;调查起点不等于根因位置,需要跨场景核对时再进入第二个专项。用户明确只要求获取或解读 Metric 时,交给 `$analyze-engine-metric`;端到端性能问题则继续调查业务场景。
package/content/README.md CHANGED
@@ -40,6 +40,7 @@
40
40
  - 查企业更新没给时间时,默认看最近 24 小时。
41
41
  - 给了 dagId 就能直接查更新链路,不需要再提供时间。
42
42
  - 说“之前正常”“以前没超限”,助手会自动找之前的正常记录做对比,不需要先提供成功记录。
43
+ - 仪表板内组件异常时,助手会先查找对应组件的单独计算记录;找不到也会继续分析,并提示检查单独预览是否同样异常。详见[组件取数对照](.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照)。
43
44
  - 缺少关键信息时,助手一次说明需要补充什么、用来判断什么。
44
45
 
45
46
  继续同一问题时,助手沿用已确认的范围和查询结果。简单追问(数值、链接)直接回答,不重新排查。
@@ -40,6 +40,8 @@
40
40
 
41
41
  组件取数慢和分析表更新慢同时出现时,需要确认这次组件请求是否在等待或读取该次更新的结果。共享底层计算只能提供关联线索,还不足以确认原因相同。
42
42
 
43
+ 仪表板内组件异常优先按[组件取数对照](../.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照)核对单独预览,再分析实际条件与计算路径;对照缺失不阻断本次异常分析。
44
+
43
45
  ## 每一步都用来区分原因
44
46
 
45
47
  先看错误前后的时间线、重复出现的异常、耗时变化和上下游记录,从中提出少量候选原因,并按线索强弱安排核对顺序。每个方向说明哪条日志支持它、有什么其他解释,以及再看什么能区分;暂时缺少字段或关联时,将该方向保留为待验证假设,再看其他可用线索。
@@ -11,7 +11,9 @@
11
11
  ## 关键依据
12
12
  ```
13
13
 
14
- 尚有实质缺口时再增加 `## 待确认信息`。不要为了格式完整输出“下一步”,不要为已解释的问题继续制造取证任务。建议存在适用前提或业务取舍时,在结论旁说明;未经验证的配置改动不写成已确认解决方案。
14
+ 尚有实质缺口时再增加 `## 待确认信息`,写清剩余判断及排查方向。不要为了格式完整输出“下一步”,也不要因此省略尚未解释问题的必要排查方向。问题已经解释时不为格式制造任务;原因尚未解释时,必须说明如何继续区分候选原因。建议存在适用前提或业务取舍时,在结论旁说明;未经验证的配置改动不写成已确认解决方案。
15
+
16
+ “看一下这个 trace 的问题”“分析为什么失败”等请求属于原因分析,不能按查数值或链接的简单事实请求压缩交付。先给清楚的结论,再展开支撑判断所需的计算过程、证据和未解决问题;篇幅由问题复杂度决定,不限制为几条摘要。某项信息无需补查,只缩小该项取证范围,不降低其他分析和交付要求。
15
17
 
16
18
  每次交付时,检查本轮发现的场景知识缺口、优化项和文档差异。有证据支持时,按[诊断后的需求反馈](diagnostic-feedback.md)在报告后附上 `## 需求反馈`;没有则省略。合并专项返回的重复反馈,保留依据和适用限制。业务结论仍需单独交付,不为反馈额外取证。
17
19
 
@@ -19,12 +21,16 @@
19
21
 
20
22
  - 当前结论:先说明已观察到的异常和可疑环节,结合已知业务对象、操作或执行解释;对象或执行尚未确认时说明范围。需要解释具体业务计算或步骤及触发条件时再展开,步骤只引用已验证的业务映射。保留已有环境、时间、资源和选中执行形态,区分已确认事实与候选原因;假设的依据和核对方法见[问题定位方法](architecture.md#每一步都用来区分原因)。不把最慢 span、OOM 或“数据量大”单独命名为完整根因。
21
23
  - 单次失败:连接本次输入与数据形成、局部膨胀、下游资源压力和直接失败条件。没有历史基线只限制“相对以前变化”的判断,不阻断本次执行解释。成对调查再补充相对基线的变化与可比范围。
22
- - 关键依据:保留锚定本次执行、支持或反驳判断、量化本次数据范围的少量证据。已经取得 trace 时给出完整 traceId;只有用户或工具返回完整链接时才引用,不按域名和 ID 拼接地址。只有 task/chain 时明确未取得 trace,不把执行 ID 冒充 trace 闭环。
24
+ - 关键依据:保留锚定本次执行、支持或反驳判断、量化本次数据范围的必要证据,并解释每项证据对判断的作用。已经取得 trace 时给出完整 traceId;只有用户或工具返回完整链接时才引用,不按域名和 ID 拼接地址。只有 task/chain 时明确未取得 trace,不把执行 ID 冒充 trace 闭环。
23
25
  - 行数、列数、字节、输入输出、放大倍数、峰值/上限等已取得且影响判断的数据应进入依据。Metric 规模关系逐项使用完整 `metric_report.json` 的 `data_scale_transitions` 和可比较的 `input_output` 机械计算,不由报告作者重新手算跨节点关系。
24
26
  - 其他执行类型只有错误签名相同而未分别取得 Metric 时,只作为重复现象旁证;不得并入同一条算子原因链。保留反证、替代解释、字段缺失和对比范围。
25
- - 待确认信息:只列阻止当前结论成立的最小缺口、可信冲突候选和一项可执行确认动作。指明使用者可补充的材料或需研发验证的命题,不让使用者选择 Skill、内部 schema 或知识包。
27
+ - 待确认信息:只列影响当前原因判断或处置选择的缺口。按证据强弱和区分原因的作用安排排查顺序,不固定动作数量;每项说明检查哪个业务对象或执行环节、依据什么线索、具体核对什么,以及不同结果分别支持或削弱哪个解释。助手仍能在已授权范围内完成的只读取证应先完成,不能作为建议转交用户。确需用户操作或研发验证时,给出其能执行的动作和需要返回的最小结果,不让使用者选择 Skill、内部 schema 或知识包。
28
+
29
+ 原因分析至少把已取得材料串成可阅读的过程:本次业务计算 → 实际输入及处理路径 → 中间结果或状态 → 资源压力 → 直接失败条件。每一段只写已有依据;无法连接的地方明确是哪条关系尚未证明,并保留有依据的候选解释。不能仅并列“大量中间结果”“存在覆盖”“达到上限”,就把这些事实当成完整原因链;也不能因为尚不能确认唯一原因,省略已经能够成立的贡献因素和局部判断。
30
+
31
+ 排查建议必须能改变判断。例如“临时结果为数千万行且保留数十列”时,应结合已有计划定位对应的汇总前结果及其使用分支,核对这些分支需要的明细和列,再判断是否有可减少的中间状态。尚未定位业务步骤时,说明需要建立哪项对应关系,不能只写“检查是否保留过多明细和字段”。优化方向同时保留业务等价前提,不把待验证方向写成确定解决方案。
26
32
 
27
- 复杂筛选树等场景专项要求继续保留;节点名称未知时使用已有 ID 并说明名称缺口。未取得真实步骤映射时,不把物理 Operator 命名为具体业务步骤。
33
+ 复杂筛选树等场景专项要求继续保留;节点名称未知时使用已有 ID 并说明名称缺口。未取得真实步骤映射时,不把物理 Operator 命名为具体业务步骤。附件保存完整明细,正文仍须展示影响判断的路径、覆盖位置及相关数量,解释其与执行的关系;不能以“已整理到附件”代替正文分析。
28
34
 
29
35
  内部调查摘要保留查询范围、已执行动作、选中/排除候选、来源版本、临时产物位置、证据缺口和未完成动作。最终答复不默认展示调用了哪个 Skill、Pod、无关执行 ID、源码行号和只用于搜索的字段。
30
36
 
@@ -32,7 +38,7 @@
32
38
 
33
39
  ## 输出前核对
34
40
 
35
- 确认结论回答了原始现象、执行没有串线、量化证据未丢失、置信层次没有提升、仍需执行的 Metric/专项分析已完成。任一项证据不足时明确阶段性判断;不能用索要成功样本替代尚未完成的失败 Metric 分析。
41
+ 确认结论回答了原始现象、执行没有串线、量化证据未丢失、置信层次没有提升、仍需执行的 Metric/专项分析已完成。原因分析还须核对:读者能否从正文看懂计算怎样形成压力;候选原因是否有依据且保留反证;尚未解释的关系是否对应具体的区分动作;必要解释是否被挪进附件而在正文消失。任一项证据不足时明确阶段性判断;不能用索要成功样本替代尚未完成的失败 Metric 分析。
36
42
 
37
43
  给使用者的说明不得出现产品代码路径、类名、方法位置或要求查代码的动作。日志字段确有必要时保留原名并解释含义;需要研发接手时按下方要求整理材料。
38
44
 
@@ -43,6 +43,11 @@
43
43
  | 环境名称、URL 与后端服务映射 | [环境识别](../knowledge/common/signoz-environments.md) |
44
44
  | 从 trace 判断属于哪个场景 | [场景识别](../knowledge/common/trace-routing.md) |
45
45
  | 共享外部组件入口属于组件还是仪表板 | [场景识别 · 外部组件取数入口](../knowledge/common/trace-routing.md#外部组件取数入口) |
46
+ | 仪表板组件异常时先查单独计算、无对照时如何继续 | [仪表板专项 · 组件取数先做对照](../.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照) |
47
+ | 仪表板内外组件对照的时间窗与查询预算 | [公共查询规则 · 仪表板内外组件对照](../knowledge/common/signoz-query-policy.md#仪表板内外组件对照) |
48
+ | 单独组件与仪表板请求的可比性、成功判定 | [仪表板 · 组件计算对照的证据](../knowledge/dashboard/observability.md#组件计算对照的证据) |
49
+ | 动态字段类型、覆盖确认与取证边界、上游重算 | [仪表板 · 动态字段覆盖与上游展开](../knowledge/dashboard/observability.md#动态字段覆盖与上游展开) |
50
+ | 从控件关系请求日志补字段名称与绑定 | [仪表板 · 名称与请求上下文的关联取证](../knowledge/dashboard/observability.md#名称与请求上下文的关联取证) |
46
51
  | 知识是否适用于本次版本和记录 | [知识适用性](../knowledge/common/knowledge-applicability.md) |
47
52
  | 何时必须取 Metric、何时可跳过;失败分析何时算完成 | [性能诊断 · 失败分析的完成条件](../.agents/skills/diagnose-performance/SKILL.md#失败分析的完成条件) |
48
53
  | 引擎任务定位、MetricOuts 正文获取与复用 | [Metric 获取](../knowledge/engine/metric-retrieval.md) |
@@ -55,7 +60,7 @@
55
60
  | 企业更新与 dagId 的检索窗口、降级和时间扩展 | [更新发现指引 · 检索时间](../.agents/skills/analyze-analysis-table-update-runtime/references/discover-update-executions.md#检索时间) |
56
61
  | 按输入选择更新场景与 recipe | [更新运行证据](../knowledge/update/runtime-evidence.md#选当前需要的知识) |
57
62
  | 更新的身份术语、关联边、计数单位与停止规则 | [更新证据摘要边界](../.agents/skills/analyze-analysis-table-update-runtime/references/update-diagnostic-contract.md) |
58
- | 答复格式、证据展示 | [诊断结果交付](diagnostic-report.md) |
63
+ | 答复格式、原因链展开、正文与附件分工、可执行排查方向 | [诊断结果交付](diagnostic-report.md) |
59
64
  | 交接研发的材料 | [诊断结果交付 · 交接研发](diagnostic-report.md#交接研发) |
60
65
  | 诊断后的需求反馈 | [需求反馈](diagnostic-feedback.md) |
61
66
  | 非性能案例保留什么、反馈后怎样改进 | [非性能案例](diagnostic-feedback.md#非性能案例怎样保留)、[收到改进请求后](diagnostic-feedback.md#收到改进请求后怎样使用) |
@@ -120,13 +120,17 @@ CLI 记录更新前的版本,用户要求回退时,Agent 根据记录确定
120
120
  npm install -g @fx-data/ai-diagnostics
121
121
  ```
122
122
 
123
- npm 安装取得 CLI 和完整诊断内容;随后 Agent 调用 CLI 创建受管副本、登记当前客户端的薄 Skill,并验证入口可用。已有 npm 包也可直接用于初始化,无需重新下载。包内 README 只保留用户说明和 Agent 文档入口;具体检测顺序和执行命令统一维护在随包发布的 `AGENT_SETUP.md`,可在安装前通过版本固定的在线链接读取,也可从对应 npm 包取得。整个过程由 Agent 连续完成,用户在首次安装或切换客户端时都只需说同一句话。
123
+ npm 安装取得 CLI 和完整诊断内容;随后 Agent 只需调用一次 `install --agent <当前客户端>`,由 CLI 完成受管副本初始化、薄 Skill 登记、完整性与内容路径检查,并返回版本、复用结果、后续诊断入口和阶段耗时。已有 npm 包也可直接用于初始化,无需重新下载;成功后不再单独执行 `doctor`、`context` 或预读诊断规则,Python 与 MCP 留到实际诊断需要时再准备。
124
+
125
+ 客户端技能目录由 CLI 识别,兼顾 WorkBuddy 的不同目录布局,避免 Agent 临场研究客户端文件。检查结果区分文件登记和客户端发现;尚未验证发现时只提示刷新或新开聊天,不将文件存在当作已启用。
126
+
127
+ 包内 README 只保留用户说明和 Agent 文档入口;具体检测顺序和执行命令统一维护在随包发布的 `AGENT_SETUP.md`,成功路径与异常处理分开。该文档可在安装前通过版本固定的在线链接读取,也可从对应 npm 包取得。整个过程由 Agent 连续完成,用户在首次安装或切换客户端时都只需说同一句话。
124
128
 
125
129
  对外配置变量统一使用 `FX_DATA_DIAGNOSTICS_*`。安装完成后默认给出针对具体 trace 的内存超限排查示例,提示附上真实 traceId 或链接;不自动运行示例查询。
126
130
 
127
131
  每个客户端只登记一个薄 Skill,完整诊断 Skills 和知识留在共享能力包内。安装复用不会隐式更新版本;内容损坏或安装冲突应明确处理,不通过另装一份来绕过。
128
132
 
129
- 用户明确要求使用 FX 数据诊断,或在客户端中选中该 Skill 后,Agent 按以下链路执行:
133
+ 安装、更新、回退、卸载和检查直接使用 CLI 管理命令,不加载诊断规则。用户明确要求用 FX 数据诊断分析问题,或在客户端中选中该 Skill 并描述问题后,Agent 按以下链路执行:
130
134
 
131
135
  ```text
132
136
  读取薄 Skill
@@ -136,11 +140,11 @@ npm 安装取得 CLI 和完整诊断内容;随后 Agent 调用 CLI 创建受
136
140
  → 分析证据并回答
137
141
  ```
138
142
 
139
- `context` 返回路径,不直接加载全部内容。普通对话只保留简短入口描述,进入诊断后再按需读取,减少上下文占用。薄 Skill 的描述以明确调用 FX 数据诊断的意图为触发范围,不以“慢”“trace”等通用词默认触发;具体技能选择入口由各客户端提供。[Skill 渐进加载](https://agentskills.io/specification#progressive-disclosure)
143
+ `context` 返回路径,不直接加载全部内容。普通对话只保留简短入口描述,进入诊断后再按需读取,减少上下文占用。薄 Skill 在用户明确要求用 FX 数据诊断分析问题,或安装、更新、回退、卸载、检查 FX 数据诊断时触发,不以“慢”“trace”等通用词默认触发;具体技能选择入口由各客户端提供。[Skill 渐进加载](https://agentskills.io/specification#progressive-disclosure)
140
144
 
141
145
  Agent 优先分析已有材料,必要时查询;按需准备 Python、复用或配置 MCP,凭据仅保存在用户本机。具体执行约定见包内 [AGENTS.md](../AGENTS.md#安装副本的助手规则)。
142
146
 
143
- 卸载针对同一系统账户下的整套能力包。Agent 执行前列出已登记的客户端,提示将一起移除这些入口,进行中的诊断也将无法继续调用包内脚本;随后调用 CLI 清理受管副本和全部已登记的薄 Skill,再通过 npm 卸载全局包。诊断材料及独立安装的 MCP 和配置保留。
147
+ 卸载针对同一系统账户下的整套能力包。Agent 说明会一起移除已登记客户端的入口、已安装版本和 npm 包,进行中的诊断也将无法继续调用包内脚本;随后只调用一次 `uninstall`。CLI 按首次安装记录的原 npm 环境清理全局包,再移除受管副本和薄 Skill,并返回完整卸载状态及实际清理结果。npm 清理失败时保留受管入口供重试;安装未记录可用 npm 来源时,Agent 仅按返回指引补充处理,不猜测其他安装位置。用户提供的日志、Metric 文件和生成报告,以及独立安装的 MCP 和配置会保留;包内规则与知识随包删除,无需扫描或备份其他目录。
144
148
 
145
149
  ## 4. 用户安装、使用与更新
146
150
 
@@ -195,4 +199,4 @@ Agent 自动判断首次安装或复用已有安装,用户无需指定安装
195
199
 
196
200
  > 卸载 FX 数据诊断。
197
201
 
198
- Agent 会提示受影响的客户端,卸载会同时移除这些客户端中的 FX 数据诊断入口。诊断材料及独立安装的 MCP 和配置会保留,卸载机制见[安装与加载](#35-安装与加载)。
202
+ Agent 会说明影响并返回卸载结果;完整卸载会移除本账户各客户端中的 FX 数据诊断入口、已安装版本(含规则和知识)和 npm 包。用户提供的日志、Metric 文件和生成报告,以及独立安装的 MCP 和配置会保留,卸载机制见[安装与加载](#35-安装与加载)。
@@ -11,6 +11,7 @@
11
11
  | 更新来源、链路、节点、重试与计数解释 | 有专用证据分析能力 |
12
12
  | 企业最近更新概况、dagId 链路查询 | 支持无时间直接开始 |
13
13
  | 历史变化对比 | 自动查找目标执行之前的可比基线 |
14
+ | 仪表板组件异常对照 | 优先核对单独计算,未找到仍分析异常;按[场景流程](../.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照)处理 |
14
15
  | 报错、结果异常、偶发不可用 | 仅通用方法,没有专项经验 |
15
16
 
16
17
  每条规则只有一个归属文件,统一登记在[文档目录](index.md)。各 Skill 保留触发条件和引用,经验用于提出业务假设,不另设取证门槛。
@@ -37,7 +37,9 @@
37
37
 
38
38
  筛选器自身取数:查看候选值来源表和字段、实际候选值数量,以及其他控件带入的条件。绑定多个字段并不必然导致慢,必须看实际读取范围与执行结果。
39
39
 
40
- 组件取数:先还原组件查询,再检查筛选、参数、权限和联动条件。涉及多层关联表时,画出从组件绑定表到筛选字段所在表的完整关系,标注筛选落在哪一层;记录不完整时只展示能确认的部分。
40
+ 组件取数:先按[组件取数对照](../.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照)核对单独计算,再还原组件查询,检查动态字段、筛选、参数、权限和联动条件。涉及多层关联表时,画出从组件绑定表到筛选字段所在表的完整关系,标注筛选落在哪一层;记录不完整时只展示能确认的部分。
41
+
42
+ 字段只显示 ID 时,按[名称关联取证](../knowledge/dashboard/observability.md#名称与请求上下文的关联取证)核对已有请求和控件关系日志,区分字段名称、当时选择值与默认值,不直接从日期表达式猜名称。
41
43
 
42
44
  共享路径:同一条件对象可能被多条路径访问。分别说明条件对象数和路径数,保留记录中的共享或环标记;不能按表名相同合并对象,也不能仅因路径深或数量多就认定是慢因。
43
45
 
@@ -1,12 +1,12 @@
1
1
  # SigNoZ 原始记录查询例子
2
2
 
3
- 仅在当前工具需要 Query Builder 对象、其说明资源不可读且已有字段知识足够时参考。例子描述 SigNoZ 原始记录结构;执行工具、调用参数和支持的字段以当前客户端声明为准。占位符必须替换,start/end 使用已核对的毫秒整数。
3
+ 仅在当前工具需要 Query Builder 对象、其说明资源不可读且已有字段知识足够时参考。例子描述 SigNoZ 原始记录结构;执行工具、调用参数和支持的字段以当前客户端声明为准。占位符必须替换;以下 `start` / `end` 仅为示例时间,实际使用时替换为已核对的 Unix 毫秒整数。在 Query Builder JSON 中直接传入整数,不加引号;便捷工具的独立时间参数仍按各自声明传入。
4
4
 
5
5
  ```json
6
6
  {
7
7
  "schemaVersion": "v1",
8
- "start": "<start_ms>",
9
- "end": "<end_ms>",
8
+ "start": 1700000000000,
9
+ "end": 1700000060000,
10
10
  "requestType": "raw",
11
11
  "compositeQuery": {
12
12
  "queries": [{
@@ -14,6 +14,7 @@
14
14
  | 只有 dagId | dagId 不先追问时间,由助手管理检索窗口,命中后按实际记录恢复链范围 |
15
15
  | 用户说“之前没超限”“以前正常”“最近变慢” | 已经是历史对比目标;先定位目标执行,再主动查找目标执行前 24 小时的可比基线 |
16
16
  | 只有单次失败 | 单次失败诊断不要求发现或补充成功执行 |
17
+ | 仪表板内组件计算异常 | 优先有界查找组件单独计算记录;未找到仍继续分析本次异常,见[仪表板内外组件对照](#仪表板内外组件对照) |
17
18
 
18
19
  细则见下方各节。
19
20
 
@@ -108,7 +109,7 @@ Trace、业务属性和 Metric 的临时原文及产物在同一调查中保留
108
109
  - 一旦取得 traceId、taskId 或唯一执行,立即冻结该执行的环境、时间和服务;后续只做 trace、关联日志和 Metric 的精确查询,不再从资源 ID 重走发现链路。
109
110
  - taskId 或 chainId 只锚定执行,不等于已经取得 trace。只有这类锚点时,在冻结的环境、服务和执行时间窗内精确反查一次对应 trace;无法取得时保留 task/chain 证据并明确 trace 缺口,不扩大成新的资源发现。
110
111
  - 精确 trace 已确认但 span 明细因保留期或后端差异不可读时,优先在该 trace 的原时间窗和已确认业务服务下按 `trace_id` 精确查询日志。精确 trace URL 没有时间且默认读取为空时,允许按[Metric 获取方法](../engine/metric-retrieval.md)做一次最多 30 天的受控精确 ID 日志回退;URL 携带 `spanId` 时必须同时过滤 focused span。该动作只从同 trace/focused span 日志恢复原时间和 task,不属于资源发现,也不得继承另一条执行的时间窗。日志返回多个 task 时按用户明确的执行形态消歧,仍不唯一则保留歧义,不静默选择最新或最长的一条。
111
- - 单次失败诊断不要求发现或补充成功执行。“之前没超限”“以前正常”“最近变慢”等已经表达相对历史对比目标,不要求用户再次选择对比或先提供成功 trace;按下方“主动对比”建立基线。对比调查的查询预算按执行分别计算。成功基线是用户明确选择的证据目标,不受“不要深挖尚未选中的成功候选”限制;一侧查询和 Metric 产物不能冒充另一侧。
112
+ - 单次失败诊断不要求发现或补充成功执行;仪表板内组件异常按[仪表板内外组件对照](#仪表板内外组件对照)优先查找,但未命中不阻断单次分析。“之前没超限”“以前正常”“最近变慢”等已经表达相对历史对比目标,不要求用户再次选择对比或先提供成功 trace;按下方“主动对比”建立基线。对比调查的查询预算按执行分别计算。成功基线是用户明确选择的证据目标,不受“不要深挖尚未选中的成功候选”限制;一侧查询和 Metric 产物不能冒充另一侧。
112
113
  - Trace 查询只选择识别场景、执行和关联所需字段。已有精确 traceId 时优先使用定向查询;没有必要时不拉取整棵 trace 的全部 span 和 attribute。
113
114
  - 同一完整原始结果已经足以回答数量或状态时,直接按明确单位整理,不再重复查询聚合。结果未覆盖完整、需要去重口径或总体统计时,才补相应聚合;样本不能替代总体。
114
115
  - MetricOuts 正文的获取、去重、复用和生命周期补查按[Metric 获取方法](../engine/metric-retrieval.md)执行。
@@ -116,7 +117,7 @@ Trace、业务属性和 Metric 的临时原文及产物在同一调查中保留
116
117
 
117
118
  ## 主动对比
118
119
 
119
- 先定位目标执行,再在同环境、对象和执行类型下发现正常基线。用户已给正常时间或成功执行时直接复用;只表达“之前正常”时,以目标执行开始时间为右边界,查询此前 24 小时的半开窗口,不用当前时钟或目标发现窗口的起点代替。时间参数按上方“查询约束”处理,两侧边界分别保留。
120
+ 先定位目标执行,再在同环境、对象和执行类型下发现正常基线;仪表板与组件单独计算的跨入口对照按下节处理。用户已给正常时间或成功执行时直接复用;只表达“之前正常”时,以目标执行开始时间为右边界,查询此前 24 小时的半开窗口,不用当前时钟或目标发现窗口的起点代替。时间参数按上方“查询约束”处理,两侧边界分别保留。
120
121
 
121
122
  基线发现同样遵守“精确首查 + 同范围一次降级”,只取有限候选及判断可比性所需字段。按时间由近到远检查同对象、操作、参数和数据范围的成功执行;有明确成功结果且可比时选择最近一条并记录依据,不能仅凭耗时短或没有 ERROR 认定成功。成功节点也不自动代表整条更新链成功。影响可比性的字段缺失或多个不同操作仍可信时,展示差异再作最小消歧;已经明确的数据范围变化可以作为待解释差异,不要求两侧数值完全相同。
122
123
 
@@ -124,6 +125,16 @@ Trace、业务属性和 Metric 的临时原文及产物在同一调查中保留
124
125
 
125
126
  选定后主动取两侧 Trace、task 和 Metric;已有产物复用,每侧范围独立冻结。不得停在“建议再取 Metric”或等待用户说“你帮我取”。对照未找到、不可比或无法取得时,交付本次事实与已查询范围,并请求更早正常时间或最小缺失项,不自动扩大到七天。用户没有相对变化目标时仍按单次诊断推进。
126
127
 
128
+ ### 仪表板内外组件对照
129
+
130
+ 这是仪表板组件异常的默认浅查,不要求用户先提出对比,也不扩大为整张仪表板或企业扫描。先从目标 trace 恢复组件、查询表、时间与业务服务。已有单独计算材料优先复用;没有时间的对照查找,以目标执行开始前 24 小时的半开窗口为限。用户给出更明确的单独预览时间或 trace 时按该侧证据恢复范围,不套用失败侧时间。
131
+
132
+ 首次在同环境、同业务服务和该窗口内按组件身份及单独计算入口取有限候选,按时间由近到远核对。编辑态可能不记录 `component.id` 和 `data.request_context`;首查未命中时,在同窗口与服务内只降级一次,改查编辑态请求日志中的精确组件 ID,或已确认查询表加请求用户、查询字段等识别信息,不要求 `reportId` 同时命中。查询表只能发现候选,身份与可比性按[组件计算对照的证据](../dashboard/observability.md#组件计算对照的证据)核对;不能因同表就认定为同组件。
133
+
134
+ 候选达到 limit 时,不宣称已覆盖所有记录;能唯一核实最近可比执行时选定并冻结,否则保留覆盖缺口。不继续扩大时间、移除服务或换表试探。未找到或不可比就继续目标失败分析,按场景流程提示用户检查单独预览结果。找到可比成功执行时主动取得两侧执行统计,各侧 engine 服务分别确认,不能把失败侧 engine 服务过滤直接套到成功侧。
135
+
136
+ 控件名称与绑定关系是另一项精确元数据取证,不是上述正常执行发现的重复查询。需要跨 trace 查控件关系时,默认复用同一前置窗口、业务服务和精确字段/组件标识;有明确关联请求时间或 trace 时直接精确读取。关联规则见[名称与请求上下文的关联取证](../dashboard/observability.md#名称与请求上下文的关联取证),不得仅凭“附近”不断扩大范围。
137
+
127
138
  ## 场景与服务角色
128
139
 
129
140
  四个业务场景先在业务 server 服务中寻找入口 trace,各场景的 route、span 和 attribute 读取对应 `observability.md`。确定 trace 后再沿实际上下游服务推进,不在定位入口时预先扫描所有服务。MetricOuts 由 engine 服务记录,业务 server 的 `service.name` 不能套用到 engine 日志,细则见[Metric 获取方法](../engine/metric-retrieval.md)。
@@ -69,6 +69,48 @@ dashboard / reportId
69
69
 
70
70
  当前还可记录 `component.linkage.depth`、`control_context_count`、`source_component_ids` 和 `edges` 来描述计算拓扑。这些是观测摘要,不能替代 FVS 画布的完整实例目标配置。目标部署较早或版本未知时,任何字段缺失都不能反证不存在控件、联动或深层路径;应改用请求材料、配置或版本匹配的其他证据。适用条件见[知识使用方法](../common/knowledge-applicability.md)。
71
71
 
72
+ ## 组件计算对照的证据
73
+
74
+ 仪表板内组件可以正常直接读取分析表已有结果,也可能因附加条件展开上游计算;单独预览使用同一查询表,不代表两侧算了同样的内容。调查顺序见[组件取数先做对照](../../.agents/skills/diagnose-dashboard-performance/SKILL.md#组件取数先做对照),本节只规定可比性。
75
+
76
+ - 独立编辑预览可见 `POST /v2/design/widget/data`、`editComponent`;独立外部预览须按[来源判定](../common/trace-routing.md#外部组件取数入口)确认。`/v2/query/data` 只是通用查询,必须核对请求和组件关联。仪表板内的另一次成功请求仍是仪表板请求。
77
+ - 编辑态不保证有外部入口的 `component.id`、`component.query.table_id` 和请求上下文属性。可以从请求日志恢复组件和 `tableBean.id`、查询字段及操作,再关联其 trace/task;不能过滤这些缺失属性后把零结果当成“没有单独计算”。
78
+ - 有相同组件 ID 时仍需核对查询表、字段、聚合、排序、分页、配置版本和数据版本。ID 未记录时,同用户、同查询表与完整查询配置可作为对应组件的候选依据;同表、同用户或时间接近单独不足以确认身份。差异本身可作为调查对象,字段不全时标为相关对照,不强称严格等价。
79
+ - 成功须有业务结果或任务最终完成证据,例如 `result.status=success/empty`、明确成功响应或同 task 的 `FINISHED`,并核对是否真实执行。HTTP 200、耗时短、没有 ERROR 不足以证明成功;`updating`、取消、失败重试或返回旧结果也不能当成已完成同样计算。正常空结果可保留,但不能冒充同数据范围的非空结果。
80
+ - 对照重点包括请求额外条件及实际计划:直接扫描目标表,还是展开来源表、关联、聚合和中间缓存。分别报告两侧输入、输出和失败边界,不将成功返回的汇总行与失败处理中间明细行直接计算放大倍数;Metric 比较口径沿用引擎知识。
81
+
82
+ ## 动态字段覆盖与上游展开
83
+
84
+ 动态字段是一种可选择替代计算字段的业务配置,不等于任意运行时表达式,也不能从“日期”一词猜它的名称。已核对的 FVS 取数路径中,`control.type=73` 是动态字段下拉控件。绑定使用动态字段位置,包含 `id`、`name`、`operatorId`、`tableId`,组件分支还可带 `widgetId`;请求材料中的位置对象可称 `dynamicField`,控件关系请求中称 `fieldPosition`。
85
+
86
+ 有值且能匹配到有效动态字段配置时,所选计算字段与默认计算字段不同会产生动态字段覆盖;相同选择不产生该项覆盖。覆盖或参数作用于上游表时,下游可能不能直接读取原来已经保存的计算结果,而需沿依赖展开上游计算。普通字段过滤、联动及其他执行条件也可能改变计划,不能仅凭“仪表板入口”或一个控件存在就确定重算原因。
87
+
88
+ 核对时区分三层事实:
89
+
90
+ | 记录 | 能确认什么 | 还需核对什么 |
91
+ |---|---|---|
92
+ | `controls.ids`、`controls.effect_type_counts`、有值计数 | 请求带入了哪类控件项 | 最终是否生成覆盖;需要定位时核对绑定与树中位置 |
93
+ | `filter_conditions.dynamic_field_count>0`、节点 `dynamic=N` 且 `N>0` | 最终条件树包含动态字段覆盖;节点摘要还可定位所在表路径 | 实际计划是否展开上游,以及对应计算和资源压力 |
94
+ | 对应执行计划展开来源表,并有任务与 Metric | 本次实际发生了哪些上游计算及资源压力 | 结合覆盖、筛选和其他条件区分触发因素 |
95
+
96
+ 性能诊断中,动态字段这一项默认取证到“是否存在覆盖”,无需具体选择值和默认计算字段。当前执行的最终条件树已有上述正数证据时,直接确认覆盖,继续沿实际计划和 Metric 分析上游计算、中间结果及资源压力;不为再次证明覆盖而补查这两个值,也不因缺少它们列出诊断缺口、阻塞交付或建议新增服务端埋点。只有用户明确询问具体替换内容或选项语义时,才定向补取这些值;没有取得就保持未知。覆盖存在不等于它导致了全部额外计算或内存超限,因果判断仍需同次执行的计算路径和资源证据。原因分析的展开程度及排查方向按[诊断结果交付](../../docs/diagnostic-report.md#内容与证据)处理。
97
+
98
+ `controls.ids` 中的 `parameter:<id>` 可能只是请求 Map 的键,不能据前缀认定普通参数。`controls.has_parameter`、`controls.parameter_*` 只统计普通参数控件,不覆盖动态字段控件;它们为 false/零不能排除动态字段影响。`controls.binding_info` 的 `unbound` 也可能只是普通字段绑定摘要为空,不能反证动态字段没有绑定或生效。控件名称缺失时,名称摘要可能退回到这个键。
99
+
100
+ `filter_conditions.*_count` 的部分字段只在正数时写入;工具投影的零或 false 可能是缺字段默认值。优先用完整 `tree_summary` 中明确的五类计数核对零值,不由投影零值构造“无条件”对照。计数摘要没有选项值和字段名,不意味着请求或关联日志也没有。
101
+
102
+ 本节依据 2026-10-26 对当前组件取数、动态字段覆盖与条件树观测行为的核对,以及线上记录整理。不能把本地行为直接当成目标环境已部署事实;版本未知时保留依赖版本的判断。线上已有计数和计划可以直接说明观测事实,无需使用者准备产品代码。
103
+
104
+ ## 名称与请求上下文的关联取证
105
+
106
+ 默认 trace 详情未展示名称或请求内容时,先定向补属性,再读取当前请求日志;默认详情、索引字段清单和计数摘要都不能代表完整可查询内容。日志先查 `body`,结构化请求字段仅在实际 schema 存在时读取;不要把未索引的 `widgetId` 等请求 JSON 键直接写成顶层查询字段。
107
+
108
+ 控件或动态字段名称可由 `POST /v1/conf/widget/relations` 的已有请求日志补齐。该请求用于分析仪表板控件关系,可记录 `widgets` 和 `controls`,动态字段位置 `controls[].fieldPosition` 包含 `id/name/operatorId/tableId/widgetId`。请求日志可能截断、脱敏或未采集;只提取命中的位置对象和组件标识,不输出完整业务载荷、用户身份或会话信息。
109
+
110
+ 从 `controls.ids` 的键提取候选字段 ID 后,仍按完整 ID 比较。关系请求必须核对当前租户/用户或会话范围、组件列表包含目标组件,以及字段的表、算子、分支标识与本次绑定是否一致;能取得 report 时同时核对。不是同一执行的关系请求只提供名称与绑定元数据,不可冒充失败 trace 的直接属性、当时的选择值或默认值,也不能证明两次请求由同一次交互触发。
111
+
112
+ 查询范围与预算见[仪表板内外组件对照](../common/signoz-query-policy.md#仪表板内外组件对照),取字段与过滤例子见[控件名称与动态字段](query-examples.md#控件名称与动态字段)。一次补齐当前相关 ID,命中后按关联 trace 精确读取;同字段 ID 但表、算子或分支不同,名称冲突、配置可能已变时保留歧义。元数据只能支持“该次关联请求上报的字段名”,不能确认当前权威名称时说明限制;确无可用材料时再报告名称缺口,不能从表达式、截图结论或相似 ID 猜名称。
113
+
72
114
  ## 筛选树还原与交付
73
115
 
74
116
  组件计算存在多层、多分支过滤树,或判断依赖筛选器所在层级时,必须按本节校验并输出完整树结构。小而完整的树可直接解释;节点数、最大深度或筛选器数量不能代替树结构。
@@ -97,7 +139,7 @@ dashboard / reportId
97
139
 
98
140
  ### 名称、绑定与展示
99
141
 
100
- 从同一 dashboard/report 的只读配置或资源元数据中,一次取得所选完整树所需的 tableId/fieldId 名称映射。每个 ID 只查询一次,将名称补入树中,不根据 ID 形态猜名称。
142
+ 从同一 dashboard/report 的只读配置或资源元数据中,一次取得所选完整树所需的 tableId/fieldId 名称映射;只有控件 ID 时按[名称与请求上下文的关联取证](#名称与请求上下文的关联取证)核对已有关系请求。每个 ID 只查询一次,将名称补入树中,不根据 ID 形态猜名称。
101
143
 
102
144
  每个节点标注“表名(tableId)”及字段筛选、组件筛选、参数、动态字段、联动筛选五类 summary 是否存在;明确零条件时写“筛选:无”,缺失字段保持未知。控件字段筛选和绑定显示“字段名(fieldId)”,有依据时补充“绑定候选控件 + 绑定字段名(fieldId)”。名称不足时保留 ID,并列出缺少名称的表 ID 和字段绑定,不为补名称阻塞已成立的性能结论。
103
145
 
@@ -14,13 +14,39 @@ trace_id = '<已确认trace>' AND service.name = '<业务服务>'
14
14
 
15
15
  - 关联和时间:`name`、`trace_id`、`span_id`、`parent_span_id`、`timestamp`、`duration_nano`;
16
16
  - 本次计算:`reportId`、`data.request_context`、`data.endpoint`、`http.route`、`component.id/type/query.table_id`;
17
- - 控件影响:`controls.ids/names/binding_info/raw_binding.table_ids/raw_binding.field_ids`、`controls.effect_count/input_count/effect_type_counts`;
17
+ - 控件影响:`controls.ids/names/binding_info/raw_binding.table_ids/raw_binding.field_ids`、`controls.effect_count/input_count/effect_type_counts/valued_effect_count`,涉及参数时加 `controls.has_parameter/parameter_effect_count/parameter_ids/parameter_names/parameter_bindings`;
18
18
  - 控件自身:`control.id/name/type`、`control.option_source.table_ids/binding_info`、`control.context.ids/binding_info`;
19
- - 筛选树:`filter_conditions.root_table_id/node_count/path_node_count/max_depth/table_paths/tree_summary`,需要环路或共享校验时加 `cycle_path_count/cycle_paths`;
19
+ - 筛选树:`filter_conditions.root_table_id/node_count/path_node_count/max_depth/table_paths/tree_summary`,需要环路或共享校验时加 `cycle_path_count/cycle_paths`,动态字段问题加 `dynamic_field_count`;
20
20
  - 其他计算上下文:只有判断涉及联动、参数或权限时补对应 observation 字段。
21
21
 
22
22
  上面的 `/` 表示同一前缀下多个字段,不是查询字段名。计数与深度按数值字段处理,`calculation_context.has_*` 按布尔字段处理;`fieldDataType` 使用当前工具支持的类型名,例如 `number`/`bool`,不能直接套用底层存储类型。耗时、身份、路径和绑定同样按工具字段声明取得。大量节点不代表过滤条件多,控件 effect 数不能代替最终生效筛选数。
23
23
 
24
+ ## 组件单独计算对照
25
+
26
+ 时间与查询预算只按[仪表板内外组件对照](../common/signoz-query-policy.md#仪表板内外组件对照),候选与成功判定按[组件计算对照的证据](observability.md#组件计算对照的证据)。先确认当前记录形态再构造过滤,不将跨入口对照限制在同 `reportId`。
27
+
28
+ | 当前材料 | 查询选择 | 需要返回的证据 |
29
+ |---|---|---|
30
+ | 已有单独计算 trace | 直接复用其时间、业务服务和入口 | 请求身份、业务结果、task 与 Metric |
31
+ | 组件身份可在 span 属性中识别 | 同服务、组件 ID 与独立预览入口 | `name/trace_id/span_id`、请求上下文、查询表、结果状态及耗时 |
32
+ | 编辑态未记录组件属性,首查为空 | 同窗口、服务与编辑态入口,在 `body` 或已声明的请求属性中精确匹配组件 ID;必要时用查询表和用户/查询配置确认候选 | 从有限日志提取 `trace_id`、入口、请求中的组件/表和查询字段,再按 trace 精确读取 |
33
+
34
+ 请求 JSON 中的 `tableBean.id`、`componentId` 等不是天然可查询的顶层字段。已有日志响应中恢复这些键即可,不猜 `widgetId` attribute;数据表 ID 命中不能直接认定同组件。返回 `result.status`、任务终态或明确业务响应确认成功,不使用 HTTP 状态码代替。
35
+
36
+ ## 控件名称与动态字段
37
+
38
+ 先复用当前 trace 的属性和请求材料;名称尚缺而已有字段 ID 时,按[名称关联规则](observability.md#名称与请求上下文的关联取证)搜索已有控件关系请求。下面是日志正文查询意图,不是新增业务调用:
39
+
40
+ ```text
41
+ service.name = '<业务服务>'
42
+ AND body CONTAINS '/v1/conf/widget/relations'
43
+ AND body CONTAINS '<精确字段ID>'
44
+ ```
45
+
46
+ 在已确认的元数据窗口只取有限 `timestamp/body/trace_id/span_id`;如已给关系请求 trace,直接按该 trace 查,不再做正文发现。命中后从保存结果中提取 `controls[].fieldPosition` 的身份与名称,并核对 `widgets` 包含目标组件。多个字段尽量合并在同次查询;正文或请求属性中没有关联证据时保留缺口,不盲扫所有服务、所有字段或循环试探关键词。
47
+
48
+ 动态字段选项值、默认值和名称是不同信息。关系请求上报的 `name` 只补名称,`controls.names` 也可能只是控件键;不得由二者推断选择值。覆盖确认所需证据及停止补查的边界,以及 `type=73`、`unbound`、普通参数统计和正数才写入的计数字段,统一按[动态字段覆盖与上游展开](observability.md#动态字段覆盖与上游展开)处理。
49
+
24
50
  ## 资源发现与影响范围
25
51
 
26
52
  | 当前缺口 | 查询范围与条件 | 如何继续 |
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "schema_version": 1,
3
- "version": "0.1.20261016035151",
4
- "source_commit": "8ee2555f84da5b1cf4c796cac4fe979bbd298053",
3
+ "version": "0.1.20261026070913",
4
+ "source_commit": "8910a4c7f834258ea712157af524f43a7f8a78e0",
5
5
  "source_dirty": false,
6
- "cli_sha256": "09f0f899da8d0f76a6f1b667a3141ebd0fecde98b56e92201d89b6994bff50e6",
6
+ "cli_sha256": "f444bb65876bdada3c854295d2c476356b9fdfc9107800112275d0a81e6d7f58",
7
7
  "files": {
8
8
  ".agents/skills/analyze-analysis-table-update-runtime/SKILL.md": "6f8b31592d5c4396aaa928c171f24b28ecd675f792c760e2d4aa5749e61a9750",
9
9
  ".agents/skills/analyze-analysis-table-update-runtime/agents/openai.yaml": "b4802f9423cdc96e911c91a6edbc45e268a5c98bb2a723b8d2776c7d2ee385d2",
@@ -18,13 +18,13 @@
18
18
  ".agents/skills/diagnose-component-performance/SKILL.md": "be170dbf8c89c3e16554844c6ac303bdca7964ab63f4613e9e91da8e7c374bda",
19
19
  ".agents/skills/diagnose-component-performance/agents/openai.yaml": "e3ba81e038e36894cf8f5d07f5193833f438e998190013b4d9e888be611c7d95",
20
20
  ".agents/skills/diagnose-component-performance/references/experience.md": "d78a54618041f9b28e4b143edf589dbca2cf5f438a2bc9b2366efd16ac8aae9e",
21
- ".agents/skills/diagnose-dashboard-performance/SKILL.md": "a188cdf0702ed73efa833654649b3a6df8c9cca1c1ad485750f9081889b1c5a2",
21
+ ".agents/skills/diagnose-dashboard-performance/SKILL.md": "b93b13ec77163dffb7a733ada02439dc60133e5bd03304e7be6db46608dd69fa",
22
22
  ".agents/skills/diagnose-dashboard-performance/agents/openai.yaml": "e888f0249327d6f52229880081980a4e5485aac7803e711b3f04b22532e8a0cb",
23
- ".agents/skills/diagnose-dashboard-performance/references/experience.md": "69e0429211133d397d604bb612d882910137d70d282b3d44f6d00751fe79103e",
23
+ ".agents/skills/diagnose-dashboard-performance/references/experience.md": "18586f190e9accc82c781b004ee9ee94ae263f8878aaaccbaaee1502d51ac49e",
24
24
  ".agents/skills/diagnose-dataset-performance/SKILL.md": "795e87c521d56d73965b37a71bee6634942f57fab6b718daf7e51a15e99447b5",
25
25
  ".agents/skills/diagnose-dataset-performance/agents/openai.yaml": "0dde83a46c72dc55531359d3b73b0b9213846c040fcd06551aff3ad76c8b19e3",
26
26
  ".agents/skills/diagnose-dataset-performance/references/experience.md": "fb7453a2bdecae81f8b8a8ab323288f129445a7172b30250bddced907f96d8df",
27
- ".agents/skills/diagnose-performance/SKILL.md": "09916e3a44da3ab6a143adb74ab7a2a74b8c0b686a0bbe9d113436a97aa2525e",
27
+ ".agents/skills/diagnose-performance/SKILL.md": "ad07215deab437cd43b9bbd79a6a6c367bf959b3ad59ac8428aff77167b60802",
28
28
  ".agents/skills/diagnose-performance/agents/openai.yaml": "9f8ac3a5e270c3b943d9d52670303be8cec8abc791cdf1cb4bafe6e98b6d167c",
29
29
  ".agents/skills/diagnose-update-performance/SKILL.md": "6604cf5552e60395277aa2e6943fd1fba35034ac4ae1e608dcdc1fc7176f513b",
30
30
  ".agents/skills/diagnose-update-performance/agents/openai.yaml": "db51224dffca437c6d1ddddb4fd6d756d84bb2ccc91b328a9faa3a0f84fd4b3a",
@@ -36,24 +36,24 @@
36
36
  ".agents/skills/setup-signoz-mcp/scripts/install_signoz_mcp.ps1": "a50ff759df00aa45332aede4ebee55a1778d3ba4d48ed384c8ae08c68e5cd94e",
37
37
  ".agents/skills/setup-signoz-mcp/scripts/install_signoz_mcp.py": "ae54d74787ee80b55a1aaa84cf621857698ecf990dbf6f2b1a41379a9215abf7",
38
38
  "AGENTS.md": "6b00940c88cdf138f22741a6b26c0e211b54e5a76d4f06f07b139c885f7f1d0d",
39
- "README.md": "826eb56755c1814fc27c6afef8183f30cc2ddbba743526407bc142f002db984f",
40
- "docs/architecture.md": "2982a0e177f688609193ec3c8be3ca1676240f913be29128280e5d24a959172c",
39
+ "README.md": "20c5e6506869fd7cf8331eb2cc9423eb6fc64c6650eccc3f02c3a34fc0805921",
40
+ "docs/architecture.md": "d4e8d4366c508b0feababff82cf323cdc9b38a11bdbfb721e96e0cc085d8325e",
41
41
  "docs/diagnostic-feedback.md": "2d117bb4d748a89d057ed0d2ba72c283cdebbd3b734e5e7b57341b9d02aeaaf0",
42
- "docs/diagnostic-report.md": "77b9461b0ac113c31e9f7b57967e5bd6b539e73badb2928e1f45f104285a5661",
43
- "docs/index.md": "ab54e7a87a2c53d9b2bd8a2ebb481892d63919259d19904fda8ea133d497df0e",
44
- "docs/npm-cli.md": "56d025d7eff75c4b8cd950b31474b0e8aaea3d1236fe319f1c8697d5fc532f5b",
45
- "docs/roadmap.md": "d41e07dc7939fda6d81c7f026fa16d41c19f62ef0b3df101ab70f29eeb18ce31",
46
- "docs/scenario-guide.md": "d84d30016c001f6c43c8a8454c2c0b71f513aea1a4f44476d410fb60e31a7649",
42
+ "docs/diagnostic-report.md": "5ec89d3e5fdec81dfec02671875a19058e22ebd34d6d4fb9d5ab240fc459b691",
43
+ "docs/index.md": "ccb2c68cacaa08940d2de5b9a59e6f49eb09f3f4e44423f6c84ed7e1ac23be76",
44
+ "docs/npm-cli.md": "6dcf794ff1500e2bc931f83e3e300d73047035809a7ab391f1120147b3af70b4",
45
+ "docs/roadmap.md": "11ef0894fc3ebc076fd5266364416abbe5a1c37213ffa6336126725209e727ce",
46
+ "docs/scenario-guide.md": "db3174d7d3cb07f4a9302c59f0f238e528cdde7fa96f5ead466b7f8aa3f4418a",
47
47
  "docs/skill-writing-guide.md": "600e1b20e55423fe44a1f66db82fa0b3175e7e5d618590d75678846fc7180e9d",
48
48
  "knowledge/common/knowledge-applicability.md": "f80d8c31a94b9b92f4bc98d6c5042033d63d795c6af4c90ea72d151588a536ae",
49
49
  "knowledge/common/signoz-environments.json": "ce465576bc4ba42996542e16998a0ee49b26b0f6474d02136346c93c278e1573",
50
50
  "knowledge/common/signoz-environments.md": "6d4f72b4d486c7bda2f5046592bd12f961ee7519d9581ef01811103375df3636",
51
- "knowledge/common/signoz-query-example.md": "3ca4598e6fe0ada09c5d03c18f627e01d4fa51f9d5477437e409b5bdbced5094",
52
- "knowledge/common/signoz-query-policy.md": "2de907f754ee32ccbc2fe106c4e881ae929a67aa0f50051b18b66af2c1ed0e8a",
51
+ "knowledge/common/signoz-query-example.md": "7ce0a196fee46393103f7e45798de2ed37276055f657078a1c74cc1212b42aa3",
52
+ "knowledge/common/signoz-query-policy.md": "f18de2ff490c80f1a66754a5d9fbcc124c0b061641ad1c6c2150884d82f933ad",
53
53
  "knowledge/common/trace-routing.md": "c303d63ff7e1eabd5905b5e1b9e11d467f1b8f992d062be43c13e892fec479bf",
54
54
  "knowledge/component/observability.md": "452c20f416e73a44bc0bddc3c63ec2742cf20c8da634694323257f41c0e1813b",
55
- "knowledge/dashboard/observability.md": "4132c8869b17ced9eb470a5a09872a7e82e84eac42f68545b9c03cfd97b43b9a",
56
- "knowledge/dashboard/query-examples.md": "06c32ce7ff740151c86d374bc4a08fe1379fad14ba64abaa36909186c10e86b4",
55
+ "knowledge/dashboard/observability.md": "f9020b251695315a7c77044b6adbe4f3883792e37ea5eb147cf95e09e2edc628",
56
+ "knowledge/dashboard/query-examples.md": "c34b93e7e8c59ca3a8404428d81d83e89e73969f0d0e9a5714331ff6a2c29c94",
57
57
  "knowledge/dataset/observability.md": "2d4601a06ae84837660e6446663c147276be678e694e7ba9209ae66749c044c5",
58
58
  "knowledge/engine/metric-artifacts.md": "b52c3f19bf09b792f10f499d28abae88a09cdc617983b908af938ee609126595",
59
59
  "knowledge/engine/metric-retrieval.md": "1cdc52e61774f9700656e24064cea0d2318fe9e38135162c845445074f9fd23a",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fx-data/ai-diagnostics",
3
- "version": "0.1.20261016035151",
3
+ "version": "0.1.20261026070913",
4
4
  "description": "FX数据诊断:Agent 诊断技能、知识与本地解析工具",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",