@wubaoqi/guancopilot 0.0.0-stage → 0.2.0

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.
Files changed (239) hide show
  1. package/NOTICE.md +17 -0
  2. package/README.md +146 -2
  3. package/assets/coreutils.wasm +0 -0
  4. package/assets/find.wasm +0 -0
  5. package/assets/guanbi.wasm +4 -0
  6. package/assets/guancli.wasm +0 -0
  7. package/assets/guanhelm.wasm +0 -0
  8. package/assets/jq.wasm +0 -0
  9. package/assets/openskills.wasm +0 -0
  10. package/assets/rg.wasm +0 -0
  11. package/assets/sed.wasm +0 -0
  12. package/bin/guancopilot +0 -0
  13. package/bin/guanvm +0 -0
  14. package/bin/platforms.cjs +6 -0
  15. package/bin/run.cjs +39 -0
  16. package/bin/runtime.cjs +44 -0
  17. package/licenses/Go-LICENSE +27 -0
  18. package/licenses/browser-_speed-highlight_core-LICENSE +121 -0
  19. package/licenses/browser-_speed-highlight_core-package.json +14 -0
  20. package/licenses/browser-dompurify-LICENSE +202 -0
  21. package/licenses/browser-dompurify-LICENSE-MPL +373 -0
  22. package/licenses/browser-dompurify-package.json +10 -0
  23. package/licenses/browser-marked-LICENSE +44 -0
  24. package/licenses/browser-marked-package.json +10 -0
  25. package/licenses/coreutils-LICENSE +18 -0
  26. package/licenses/findutils-LICENSE +18 -0
  27. package/licenses/github.com_BurntSushi_toml_v1.6.0-COPYING +21 -0
  28. package/licenses/github.com_dustin_go-humanize_v1.0.1-LICENSE +21 -0
  29. package/licenses/github.com_google_jsonschema-go_v0.4.3-LICENSE +21 -0
  30. package/licenses/github.com_google_uuid_v1.6.0-LICENSE +27 -0
  31. package/licenses/github.com_jackc_pgpassfile_v1.0.0-LICENSE +22 -0
  32. package/licenses/github.com_jackc_pgservicefile_v0.0.0-20240606120523-5a60cdf6a761-LICENSE +22 -0
  33. package/licenses/github.com_jackc_pgx_v5_v5.11.0-LICENSE +22 -0
  34. package/licenses/github.com_jackc_puddle_v2_v2.2.2-LICENSE +22 -0
  35. package/licenses/github.com_mattn_go-isatty_v0.0.20-LICENSE +9 -0
  36. package/licenses/github.com_modelcontextprotocol_go-sdk_v1.8.0-LICENSE +216 -0
  37. package/licenses/github.com_ncruces_go-strftime_v1.0.0-LICENSE +21 -0
  38. package/licenses/github.com_remyoudompheng_bigfft_v0.0.0-20230129092748-24d4a6f8daec-LICENSE +27 -0
  39. package/licenses/github.com_santhosh-tekuri_jsonschema_v6_v6.0.3-LICENSE +175 -0
  40. package/licenses/github.com_segmentio_asm_v1.1.3-LICENSE +21 -0
  41. package/licenses/github.com_segmentio_encoding_v0.5.4-LICENSE +21 -0
  42. package/licenses/github.com_tetratelabs_wazero_v1.12.0-LICENSE +201 -0
  43. package/licenses/github.com_tetratelabs_wazero_v1.12.0-NOTICE +2 -0
  44. package/licenses/github.com_yosida95_uritemplate_v3_v3.0.2-LICENSE +25 -0
  45. package/licenses/golang.org_x_image_v0.44.0-LICENSE +27 -0
  46. package/licenses/golang.org_x_net_v0.58.0-LICENSE +27 -0
  47. package/licenses/golang.org_x_oauth2_v0.36.0-LICENSE +27 -0
  48. package/licenses/golang.org_x_sync_v0.22.0-LICENSE +27 -0
  49. package/licenses/golang.org_x_sys_v0.47.0-LICENSE +27 -0
  50. package/licenses/golang.org_x_text_v0.41.0-LICENSE +27 -0
  51. package/licenses/golang.org_x_time_v0.15.0-LICENSE +27 -0
  52. package/licenses/guanbi-skills-LICENSE +133 -0
  53. package/licenses/guanbi-skills-LICENSE.README.md +28 -0
  54. package/licenses/guanbi-skills-LICENSE.zh-CN +82 -0
  55. package/licenses/jq-COPYING +176 -0
  56. package/licenses/modernc.org_libc_v1.73.4-LICENSE +27 -0
  57. package/licenses/modernc.org_libc_v1.73.4-LICENSE-3RD-PARTY.md +305 -0
  58. package/licenses/modernc.org_mathutil_v1.7.1-LICENSE +27 -0
  59. package/licenses/modernc.org_memory_v1.11.0-LICENSE +27 -0
  60. package/licenses/modernc.org_memory_v1.11.0-LICENSE-GO +27 -0
  61. package/licenses/modernc.org_memory_v1.11.0-LICENSE-LOGO +1 -0
  62. package/licenses/modernc.org_memory_v1.11.0-LICENSE-MMAP-GO +25 -0
  63. package/licenses/modernc.org_sqlite_v1.53.0-LICENSE +26 -0
  64. package/licenses/mvdan.cc_sh_v3_v3.13.1-LICENSE +27 -0
  65. package/licenses/ripgrep-COPYING +3 -0
  66. package/licenses/ripgrep-LICENSE-MIT +21 -0
  67. package/licenses/sed-LICENSE +21 -0
  68. package/manifest.json +972 -0
  69. package/package.json +30 -4
  70. package/plugin/guancopilot.js +193 -0
  71. package/skills-bundle/.guanvm-skills-bundle +0 -0
  72. package/skills-bundle/_recipes/dashboard/gates/page_check.js +16 -0
  73. package/skills-bundle/_recipes/dashboard/gates/report_check.js +4 -0
  74. package/skills-bundle/_recipes/dashboard/knowledge/01.md +1 -0
  75. package/skills-bundle/_recipes/dashboard/knowledge/02.md +1 -0
  76. package/skills-bundle/_recipes/dashboard/knowledge/03.md +1 -0
  77. package/skills-bundle/_recipes/dashboard/knowledge/04.md +1 -0
  78. package/skills-bundle/_recipes/dashboard/knowledge/05.md +1 -0
  79. package/skills-bundle/_recipes/dashboard/knowledge/06.md +1 -0
  80. package/skills-bundle/_recipes/dashboard/template.json +41 -0
  81. package/skills-bundle/_recipes/etl/gates/etl_live_check.js +10 -0
  82. package/skills-bundle/_recipes/etl/gates/etl_report_check.js +3 -0
  83. package/skills-bundle/_recipes/etl/knowledge/01.md +1 -0
  84. package/skills-bundle/_recipes/etl/knowledge/02.md +9 -0
  85. package/skills-bundle/_recipes/etl/knowledge/03.md +1 -0
  86. package/skills-bundle/_recipes/etl/knowledge/04.md +1 -0
  87. package/skills-bundle/_recipes/etl/knowledge/05.md +1 -0
  88. package/skills-bundle/_recipes/etl/template.json +39 -0
  89. package/skills-bundle/_recipes/hints.json +46 -0
  90. package/skills-bundle/guancli/.commandface.json +155 -0
  91. package/skills-bundle/guancli/SKILL.md +342 -0
  92. package/skills-bundle/guancli/evals/evals.json +156 -0
  93. package/skills-bundle/guancli/evals/fixtures/metric-candidate-empty-search.json +13 -0
  94. package/skills-bundle/guancli/evals/fixtures/metric-candidate-prior-request.json +13 -0
  95. package/skills-bundle/guancli/evals/fixtures/metric-consumption-columns.json +46 -0
  96. package/skills-bundle/guancli/evals/fixtures/metric-consumption-metadata.json +36 -0
  97. package/skills-bundle/guancli/evals/fixtures/metric-consumption-query.json +18 -0
  98. package/skills-bundle/guancli/references/analysis-primitives.md +95 -0
  99. package/skills-bundle/guancli/references/auth-and-config.md +131 -0
  100. package/skills-bundle/guancli/references/examples/metric-date-in.json +7 -0
  101. package/skills-bundle/guancli/references/examples/metric-query-rows.py +83 -0
  102. package/skills-bundle/guancli/references/examples/metric-query.sh +6 -0
  103. package/skills-bundle/guancli/references/form-chatbi.md +199 -0
  104. package/skills-bundle/guancli/references/gateway.md +148 -0
  105. package/skills-bundle/guancli/references/metric-batch-query.md +82 -0
  106. package/skills-bundle/guancli/references/metric-commands.md +562 -0
  107. package/skills-bundle/guancli/references/metric-generic-query.md +215 -0
  108. package/skills-bundle/guancli/references/metric-json-migration.md +18 -0
  109. package/skills-bundle/guancli/references/metric-query-recipes.md +358 -0
  110. package/skills-bundle/guancli/references/preview-and-output.md +192 -0
  111. package/skills-bundle/guancli/references/resource-query.md +101 -0
  112. package/skills-bundle/guands/SKILL.md +281 -0
  113. package/skills-bundle/guands/references/calc-fields-boundaries-and-pitfalls.md +160 -0
  114. package/skills-bundle/guands/references/command-reference.md +267 -0
  115. package/skills-bundle/guands/references/incremental-and-schedule.md +157 -0
  116. package/skills-bundle/guands/references/workflows.md +256 -0
  117. package/skills-bundle/guanetl/SKILL.md +391 -0
  118. package/skills-bundle/guanetl/evals/evals.json +41 -0
  119. package/skills-bundle/guanetl/references/ETL_AI_DEVELOP.md +456 -0
  120. package/skills-bundle/guanmetric/SKILL.md +144 -0
  121. package/skills-bundle/guanmetric/evals/evals.json +117 -0
  122. package/skills-bundle/guanmetric/evals/fixtures/atomic-detail-read-model.json +18 -0
  123. package/skills-bundle/guanmetric/evals/fixtures/confirmed-template-summary.json +12 -0
  124. package/skills-bundle/guanmetric/evals/fixtures/create-result-unknown-state.json +13 -0
  125. package/skills-bundle/guanmetric/evals/forward-eval-2026-09-21.json +62 -0
  126. package/skills-bundle/guanmetric/references/metric-api-contract-examples.json +690 -0
  127. package/skills-bundle/guanmetric/references/metric-api-contract.schema.json +349 -0
  128. package/skills-bundle/guanmetric/references/metric-api-contracts.md +436 -0
  129. package/skills-bundle/guanmetric/references/metric-atomic-mutation.md +238 -0
  130. package/skills-bundle/guanmetric/references/metric-batch-status.md +71 -0
  131. package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract-examples.json +53 -0
  132. package/skills-bundle/guanmetric/references/metric-business-dictionary-api-contract.schema.json +26 -0
  133. package/skills-bundle/guanmetric/references/metric-business-dictionary.md +76 -0
  134. package/skills-bundle/guanmetric/references/metric-common-fields.md +246 -0
  135. package/skills-bundle/guanmetric/references/metric-composite-mutation.md +165 -0
  136. package/skills-bundle/guanmetric/references/metric-delete-mutation.md +70 -0
  137. package/skills-bundle/guanmetric/references/metric-derived-mutation.md +453 -0
  138. package/skills-bundle/guanmetric/references/metric-excel-template.md +365 -0
  139. package/skills-bundle/guanmetric/references/metric-expression-rules.md +146 -0
  140. package/skills-bundle/guanmetric/references/metric-flow-generate-example.json +79 -0
  141. package/skills-bundle/guanmetric/references/metric-flow-generate.schema.json +93 -0
  142. package/skills-bundle/guanmetric/references/metric-flow.md +218 -0
  143. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract-examples.json +171 -0
  144. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contract.schema.json +167 -0
  145. package/skills-bundle/guanmetric/references/metric-hierarchy-api-contracts.md +193 -0
  146. package/skills-bundle/guanmetric/references/metric-mutation.md +155 -0
  147. package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract-examples.json +62 -0
  148. package/skills-bundle/guanmetric/references/metric-public-dimension-api-contract.schema.json +47 -0
  149. package/skills-bundle/guanmetric/references/metric-public-dimension.md +155 -0
  150. package/skills-bundle/guanmetric/references/metric-tree-api-contract-examples.json +277 -0
  151. package/skills-bundle/guanmetric/references/metric-tree-api-contract.schema.json +263 -0
  152. package/skills-bundle/guanmetric/references/metric-tree-api-contracts.md +53 -0
  153. package/skills-bundle/guanmetric/references/metric-tree-mutation.md +500 -0
  154. package/skills-bundle/guanmetric/references/metric-validation-ownership.md +74 -0
  155. package/skills-bundle/guanvis/SKILL.md +333 -0
  156. package/skills-bundle/guanvis/evals/china_map/card_01_basic_map.js +13 -0
  157. package/skills-bundle/guanvis/evals/china_map/card_02_world_map.js +10 -0
  158. package/skills-bundle/guanvis/evals/china_map/card_03_point_map.js +12 -0
  159. package/skills-bundle/guanvis/evals/china_map/page.js +10 -0
  160. package/skills-bundle/guanvis/evals/china_map/schema.js +10 -0
  161. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/README.md +30 -0
  162. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/card_01_comparative_mvp.js +72 -0
  163. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/page.js +6 -0
  164. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/schema.js +22 -0
  165. package/skills-bundle/guanvis/evals/comparative_mvp_roundtrip/verify_default_roundtrip.sh +92 -0
  166. package/skills-bundle/guanvis/evals/complex_report_pro/card_01_monthly.js +63 -0
  167. package/skills-bundle/guanvis/evals/complex_report_pro/page.js +6 -0
  168. package/skills-bundle/guanvis/evals/complex_report_pro/schema.js +6 -0
  169. package/skills-bundle/guanvis/evals/custom_chart_echarts/card_01_bar.js +13 -0
  170. package/skills-bundle/guanvis/evals/custom_chart_echarts/card_02_pie.js +13 -0
  171. package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/bar.js +11 -0
  172. package/skills-bundle/guanvis/evals/custom_chart_echarts/charts/pie.js +15 -0
  173. package/skills-bundle/guanvis/evals/custom_chart_echarts/page.js +5 -0
  174. package/skills-bundle/guanvis/evals/custom_chart_echarts/schema.js +9 -0
  175. package/skills-bundle/guanvis/evals/custom_chart_sdk/card_01_level_nodes.js +19 -0
  176. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.css +51 -0
  177. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.html +1 -0
  178. package/skills-bundle/guanvis/evals/custom_chart_sdk/charts/level_nodes.js +28 -0
  179. package/skills-bundle/guanvis/evals/custom_chart_sdk/page.js +5 -0
  180. package/skills-bundle/guanvis/evals/custom_chart_sdk/schema.js +7 -0
  181. package/skills-bundle/guanvis/evals/dynamic_fields/card_01_dynamic_dataset.js +20 -0
  182. package/skills-bundle/guanvis/evals/dynamic_fields/card_02_dynamic_metric_chart.js +16 -0
  183. package/skills-bundle/guanvis/evals/dynamic_fields/metrics.js +19 -0
  184. package/skills-bundle/guanvis/evals/dynamic_fields/page.js +9 -0
  185. package/skills-bundle/guanvis/evals/dynamic_fields/schema.js +7 -0
  186. package/skills-bundle/guanvis/evals/dynamic_fields/verify_default_roundtrip.sh +137 -0
  187. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/README.md +26 -0
  188. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_01_percentage_advcalc.js +44 -0
  189. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/card_02_scroll_percentage.js +12 -0
  190. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/page.js +6 -0
  191. package/skills-bundle/guanvis/evals/percentage_advcalc_roundtrip/schema.js +22 -0
  192. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/README.md +28 -0
  193. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/card_01_rank_advcalc.js +59 -0
  194. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/page.js +6 -0
  195. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/schema.js +22 -0
  196. package/skills-bundle/guanvis/evals/rank_advcalc_roundtrip/verify_default_roundtrip.sh +135 -0
  197. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/README.md +15 -0
  198. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/card_01_running_total_advcalc.js +74 -0
  199. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/page.js +6 -0
  200. package/skills-bundle/guanvis/evals/running_total_advcalc_roundtrip/schema.js +22 -0
  201. package/skills-bundle/guanvis/evals/sales_dashboard/card_01_revenue_column.js +10 -0
  202. package/skills-bundle/guanvis/evals/sales_dashboard/card_02_trend_line.js +9 -0
  203. package/skills-bundle/guanvis/evals/sales_dashboard/card_03_kpi.js +6 -0
  204. package/skills-bundle/guanvis/evals/sales_dashboard/card_04_pie.js +9 -0
  205. package/skills-bundle/guanvis/evals/sales_dashboard/card_05_calc_field_test.js +12 -0
  206. package/skills-bundle/guanvis/evals/sales_dashboard/page.js +11 -0
  207. package/skills-bundle/guanvis/evals/sales_dashboard/schema.js +14 -0
  208. package/skills-bundle/guanvis/evals/sales_dashboard/selector_01_region.js +10 -0
  209. package/skills-bundle/guanvis/evals/split_charts/card_01_column_split.js +10 -0
  210. package/skills-bundle/guanvis/evals/split_charts/card_02_line_split.js +10 -0
  211. package/skills-bundle/guanvis/evals/split_charts/card_03_combo_split.js +11 -0
  212. package/skills-bundle/guanvis/evals/split_charts/page.js +10 -0
  213. package/skills-bundle/guanvis/evals/split_charts/schema.js +14 -0
  214. package/skills-bundle/guanvis/evals/tab_layout/card_00_total_revenue.js +6 -0
  215. package/skills-bundle/guanvis/evals/tab_layout/card_01_revenue.js +10 -0
  216. package/skills-bundle/guanvis/evals/tab_layout/card_02_trend.js +9 -0
  217. package/skills-bundle/guanvis/evals/tab_layout/card_03_story.js +11 -0
  218. package/skills-bundle/guanvis/evals/tab_layout/page.js +23 -0
  219. package/skills-bundle/guanvis/evals/tab_layout/schema.js +11 -0
  220. package/skills-bundle/guanvis/evals/tab_layout/selector_01_region.js +8 -0
  221. package/skills-bundle/guanvis/references/api-reference.md +476 -0
  222. package/skills-bundle/guanvis/references/builder-reference.md +1676 -0
  223. package/skills-bundle/guanvis/references/chart-properties.md +2101 -0
  224. package/skills-bundle/guanvis/references/checkout-editing.md +53 -0
  225. package/skills-bundle/guanvis/references/complex-report-pro-patterns.md +398 -0
  226. package/skills-bundle/guanvis/references/dir-and-page-management.md +48 -0
  227. package/skills-bundle/guanvis/references/metric-chart-reference.md +258 -0
  228. package/skills-bundle/guanvis/references/publish-and-constraints.md +60 -0
  229. package/skills-bundle/guanvis/references/selector-reference.md +431 -0
  230. package/skills-bundle/guanvis/references/tableau-migration.md +73 -0
  231. package/skills-bundle/guanvis/references/theme.md +123 -0
  232. package/skills-bundle/guanvis/references/troubleshooting.md +25 -0
  233. package/skills-bundle/guanvis/references/validation-and-chart-patterns.md +205 -0
  234. package/skills-bundle/guanwf/SKILL.md +543 -0
  235. package/skills-bundle/guanwf/references/ETL_AI_DEVELOP.md +456 -0
  236. package/skills-bundle/guanwf/references/TASK_NODE_CONTRACTS.md +64 -0
  237. package/skills-bundle/guanwf/references/WORKFLOW_DSL.md +536 -0
  238. package/skills-bundle/guanwf/references/WORKFLOW_NODES.md +166 -0
  239. package/skills-bundle/js/SKILL.md +145 -0
@@ -0,0 +1,456 @@
1
+ # GuanETL AI 开发参考
2
+
3
+ 这份参考只保留 AI 真正常用、且最容易写错的部分。
4
+
5
+ ## 先读什么
6
+
7
+ 写 ETL 前,先按这个顺序看:
8
+
9
+ 1. `etl/etl.go`
10
+ 2. `etl/*.sql`
11
+ 3. 必要时 `etl/meta.json`
12
+ 4. 缺上下文时,用 `guancli` 查 ETL / ds / 字段,不要猜
13
+
14
+ ## 不可违反的规则
15
+
16
+ ```go
17
+ import . "guanetl/internal/framework"
18
+
19
+ func DefineETL() []Node
20
+ ```
21
+
22
+ - 不要改点导入和 `DefineETL()` 签名。
23
+ - 返回值必须是叶子节点数组,通常是一个或多个 `OUTPUT_DATASET`。
24
+ - 节点 ID 必须唯一,格式必须是 `id_数字`。
25
+ - 下游节点必须写在上游节点之后。
26
+ - 只改 `etl/` 目录,不要手改 `_base_etl.json`、`_input.json`、`_exported.json`。
27
+ - 对 `edit` 导入出来的 ETL,优先局部修改,尽量保留现有节点顺序和文件命名。
28
+
29
+ ## 节点选择顺序
30
+
31
+ 优先用专用节点,最后才用 SQL:
32
+
33
+ 1. `BasicInputDataset`
34
+ 2. `BasicSelectColumns`
35
+ 3. `BasicFilterRows`
36
+ 4. `BasicJoinData`
37
+ 5. `BasicGroupBy`
38
+ 6. `BasicCalculator`
39
+ 7. `BasicRemoveDuplicates`
40
+ 8. `BasicAppendRows`
41
+ 9. `BasicSqlScript`
42
+
43
+ 特别注意:
44
+
45
+ - 等值 JOIN 不要写成 SQL,优先 `JOIN_DATA`。
46
+ - 只是筛选、选列、聚合、去重时,不要默认上 SQL。
47
+ - SQL 很长或有很多 CTE 时,优先拆成多个节点。
48
+
49
+ ## 输入字段 schema 放哪里
50
+
51
+ 输入字段 schema 以 `BasicInputDataset(..., []Field{...})` 为主。
52
+
53
+ ```go
54
+ orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
55
+ BasicField("user_id", FieldTypeSTRING),
56
+ BasicField("amount", FieldTypeDOUBLE),
57
+ BasicField("order_date", FieldTypeDATE),
58
+ }, Position{X: 100, Y: 100})
59
+ ```
60
+
61
+ - `guanetl export` 会把这里的字段带到最终 JSON。
62
+ - `meta.json` 主要保留 ETL 名称等元信息。
63
+ - 字段相关报错时,优先检查 `[]Field`,不要先怀疑 `meta.json`。
64
+
65
+ ## ODS 展示格式字符串先解析再声明数值
66
+
67
+ `guancli ds get` 只能告诉你字段声明类型;ODS / mock / 平台导出表可能把 UI 展示值原样存成 `STRING`,例如 `"13小时31分"`、`"27.69%"`、`"90"`。写 DWD 前先执行 `guancli ds preview <dsId>` 看样本;如果 preview 提示 STRING 样本像展示格式,不能在 `BasicInputDataset` 里直接把该字段声明成 `DOUBLE` / `LONG` 后聚合。
68
+
69
+ `guanetl export` 会通过数据集详情接口校验输入字段 schema:服务端字段是 `STRING` 时,`BasicInputDataset` 不能把它声明成 `DOUBLE` / `LONG` / `DATE` 等非 STRING 类型。
70
+
71
+ 推荐先用 `BasicCalculator` 或 SQL 显式解析成新的数值列,再让下游聚合引用新列:
72
+
73
+ ```go
74
+ calc := BasicCalculator("id_1002", "解析展示格式度量", inputA, []Formula{
75
+ {
76
+ Name: "下单转化率_数值",
77
+ Type: FieldTypeDOUBLE,
78
+ Expr: "CAST(REPLACE([下单转化率], \"%\", \"\") AS DOUBLE) / 100",
79
+ Key: "formula_order_rate_value",
80
+ },
81
+ {
82
+ Name: "营业时长_分钟",
83
+ Type: FieldTypeDOUBLE,
84
+ Expr: "CAST(IF(REGEXP_EXTRACT([营业时长], \"([0-9.]+)小时\", 1) = \"\", \"0\", REGEXP_EXTRACT([营业时长], \"([0-9.]+)小时\", 1)) AS DOUBLE) * 60 + CAST(IF(REGEXP_EXTRACT([营业时长], \"([0-9.]+)分\", 1) = \"\", \"0\", REGEXP_EXTRACT([营业时长], \"([0-9.]+)分\", 1)) AS DOUBLE)",
85
+ Key: "formula_open_minutes",
86
+ },
87
+ {
88
+ Name: "店铺分_数值",
89
+ Type: FieldTypeDOUBLE,
90
+ Expr: "CAST([店铺分] AS DOUBLE)",
91
+ Key: "formula_store_score_value",
92
+ },
93
+ }, Position{X: 320, Y: 100})
94
+ ```
95
+
96
+ - 百分比字符串先去 `%`,再确认业务语义是 `27.69` 还是 `0.2769`。
97
+ - 中文时长要统一成明确单位,例如分钟或秒。
98
+ - 纯数字字符串可能是编码 / ID;确认不是维度编码后再 cast。
99
+
100
+ ## 字段 name / alias / showName 规则
101
+
102
+ 后端 ETL 的展示名语义主要来自字段 `alias`,有些接口或前端上下文会叫 `displayName` / `showName`。
103
+
104
+ - 新版 ETL 默认使用输入字段别名:如果数据集字段有 `alias`,进入 ETL 算子后的列名通常是 `alias`;没有 `alias` 时才是原始 `name`。
105
+ - 输入字段别名的事实源是数据集字段元数据;可用 `guands dataset fields <dsId>` 回读,用 `guands dataset alias <dsId> --fd-id <fdId> --alias "展示名"` 更新。`BasicInputDataset(..., []Field{...})` 中的字段 schema 主要用于导入/导出和本地 lint,不会把一个未设置过数据集 alias 的字段强制变成展示名列。
106
+ - 本地校验按 `alias -> displayName/showName/title -> name` 解析有效字段名;同一来源下两个字段解析成相同有效名会被视为歧义,建模前应先改名或明确上游输出。
107
+ - 不要把 JOIN 键类型不一致直接断言为零匹配。STRING 与 LONG/DOUBLE 等数值类型 JOIN 时,Spark 可能做隐式数值 coercion:纯数字字符串可能匹配,`"001"` 可能折叠后匹配数值 `1`,非数字值可能无法匹配,超长 ID 可能丢失精度。业务标识键应在 JOIN 前显式统一为 STRING,并用 preview 验证真实匹配率。
108
+ - `preview` / `save` 会从数据集元数据补齐 `fdId -> alias` 的运行时映射,并把 JOIN predicate 中的输入 alias 归一化为 raw name;本地 `BasicInputDataset` 声明与服务端不一致时,以服务端字段身份为准,且不会修改工作区中的原始 `_exported.json`。当前服务端仍不支持把“设置过 alias 的数据集计算字段”直接选作 JOIN 输出列,应输出其原始依赖列或先在 ETL 中生成普通列。
109
+ - aggregation/window 计算字段不是行级字段,不能直接作为 JOIN 键;请先在 ETL 中生成普通计算列。`preview` / `save` 会在提交远端请求前拦截该用法。
110
+ - SQL 节点使用上游表 `input1`、`input2` 的当前列名。字段有中文展示名时,SQL 里应写展示名并用反引号,例如 ``SELECT `门店ID` FROM input1``。
111
+ - `SELECT_COLUMNS`、`FILTER_ROWS`、`REMOVE_DUPLICATES`、`GROUP_BY` 等直接列名算子,也以当前上游列名为准。
112
+ - 底层数据集读取和数据集元数据仍保留原始 `name`,所以 `guancli` / `guands` 输出字段时要同时关注 `name` 和 `alias`。
113
+ - 不确定时先执行 `guanetl lint`;它会提示疑似把 raw name 写进新版 ETL 算子的情况。
114
+
115
+ ## 最常用函数
116
+
117
+ ### 输入 / 输出
118
+
119
+ ```go
120
+ BasicInputDataset(id, name, inputDsID string, fields []Field, position Position)
121
+ BasicOutputDataset(id, name string, source Node, outputDsName string, position Position)
122
+ BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
123
+ ```
124
+
125
+ **新建输出节点必须用 `BasicOutputDatasetInDir`** 并传入 `guanetl create --output-parent-dir` 确认过的 DATA_SET 目录 id;`save` 会拒绝没有目录的新输出(服务端对空目录不报错而是静默落到根目录,不符合目录管理规范)。无目录版本的 `BasicOutputDataset` 只用于 `edit` 回读已物化输出时的往返兼容(目录已固化在服务端 dataSource 中),不要在新增输出时使用。目录 ID 可通过 `guancli ds tree` 获取,不存在时用 `guanetl mkdir --type DATA_SET` 显式创建。
126
+
127
+ 新建 ETL 时有两类目录 id,不能混用:
128
+
129
+ - `guanetl create --parent-dir` 使用 ETL 目录树 id,可通过 `guancli etl tree` 获取。
130
+ - `BasicOutputDatasetInDir(..., parentDirId, ...)` / `guanetl create --output-parent-dir` 使用 DATA_SET 目录树 id,可通过 `guancli ds tree` 获取。
131
+ - 同名目录在两棵树中的 id 通常不同;需要成对创建时优先使用 `guanetl mkdir-pair`。
132
+ - `create` / `save` 默认会校验目录 id 类型,确认 id 正确但账号无法读取目录树时,可加 `--skip-dir-check` 跳过。
133
+
134
+ ### 选列
135
+
136
+ ```go
137
+ BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, position Position)
138
+ ```
139
+
140
+ ```go
141
+ []ColumnSetting{
142
+ BasicColumnSetting("user_id"),
143
+ {Name: "amount", NewName: "pay_amount"},
144
+ }
145
+ ```
146
+
147
+ ### 筛选
148
+
149
+ ```go
150
+ BasicFilterRows(id, name string, source Node, combineType CombineType, conditions []FilterCondition, position Position)
151
+ ```
152
+
153
+ - `combineType` 用常量 `CombineTypeAND` / `CombineTypeOR`
154
+ - 操作符用 `FilterOp*` 常量:`FilterOpEQ` `FilterOpNE` `FilterOpLT` `FilterOpLE` `FilterOpGT` `FilterOpGE` `FilterOpIN` `FilterOpBT` `FilterOpCONTAINS` `FilterOpNOTCONTAINS` `FilterOpSTARTSWITH` `FilterOpNOTSTARTSWITH` `FilterOpENDSWITH` `FilterOpNOTENDSWITH`
155
+ - 当前 `BasicFilterRows` 不支持 `IS_NULL` / `NOT_NULL`;null 过滤请改用 SQL 节点(如 ``WHERE `列名` IS NULL`` / ``IS NOT NULL``)。
156
+
157
+ ### 等值 JOIN
158
+
159
+ ```go
160
+ BasicJoinData(id, name string, leftSource, rightSource Node, joinType JoinType, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
161
+ ```
162
+
163
+ - `joinType` 用常量 `JoinTypeINNER` `JoinTypeLEFTOUTER` `JoinTypeRIGHTOUTER` `JoinTypeFULLOUTER`
164
+
165
+ ### 聚合
166
+
167
+ ```go
168
+ BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
169
+ ```
170
+
171
+ - 聚合类型用常量 `AggrTypeSUM` `AggrTypeCOUNT` `AggrTypeCOUNTDISTINCT` `AggrTypeMIN` `AggrTypeMAX` `AggrTypeAVG` `AggrTypeFIRSTNOTNULL`
172
+
173
+ ### 计算列
174
+
175
+ ```go
176
+ BasicCalculator(id, name string, source Node, formulas []Formula, position Position)
177
+ ```
178
+
179
+ `Formula` 必须写完整四个字段:
180
+
181
+ ```go
182
+ Formula{
183
+ Name: "订单金额等级",
184
+ Type: FieldTypeSTRING,
185
+ Expr: "IF([金额] >= 1000, \"大额\", \"普通\")",
186
+ Key: "formula_amount_level",
187
+ }
188
+ ```
189
+
190
+ - `Name` 是新增列名。
191
+ - `Type` 是新增列类型,用 `FieldType*` 常量:`FieldTypeINT` `FieldTypeDOUBLE` `FieldTypeSTRING` `FieldTypeTIMESTAMP` `FieldTypeLONG` `FieldTypeSHORT` `FieldTypeFLOAT` `FieldTypeDATE` `FieldTypeBOOL` `FieldTypeDECIMAL` `FieldTypeARRAY`。
192
+ - `Expr` 是服务端公式表达式,字段引用必须写成 `[字段名]`。
193
+ - `Key` 在同一个 `CALCULATOR` 节点内必须唯一;建议用稳定字符串,不要留空。
194
+
195
+ `Expr` 规则:
196
+
197
+ - 字段名遵循“字段 name / alias / showName 规则”:上游字段有 `alias` 时,公式里通常写展示名,例如 `[门店ID]`,不要写 raw name `[store_id]`。
198
+ - 字符串字面量用双引号,例如 `"大额"`。
199
+ - 数值、比较、逻辑可以按 Spark SQL 表达式写,例如 `[金额] * 1.13`、`[数量] > 0 AND [金额] > 0`。
200
+ - 空值判断优先用 `IS NULL` / `IS NOT NULL` 或 `COALESCE`,不要写 `= NULL`。
201
+ - 多个公式之间尽量不要互相依赖;如果需要依赖前一个新增列,拆成两个 `BasicCalculator` 节点更稳。
202
+
203
+ 常用函数(按 Spark SQL 表达式执行,函数名大小写不敏感;这里列 AI 写 ETL 时最常用、最稳的集合):
204
+
205
+ | 类别 | 函数 / 写法 | 示例 |
206
+ |---|---|---|
207
+ | 文本截取 | `LEFT` `RIGHT` `SUBSTRING` | `LEFT([门店编码], 2)` |
208
+ | 文本处理 | `CONCAT` `UPPER` `LOWER` `TRIM` `LENGTH` `REPLACE` | `CONCAT([省份], "-", [城市])` |
209
+ | 数值处理 | `ABS` `ROUND` `CEIL` `FLOOR` | `ROUND([金额], 2)` |
210
+ | 空值处理 | `COALESCE` | `COALESCE([金额], 0)` |
211
+ | 条件判断 | `IF`、`CASE WHEN ... THEN ... ELSE ... END` | `IF([金额] > 0, "有效", "无效")` |
212
+ | 日期时间 | `CURRENT_DATE` `CURRENT_TIMESTAMP` `DATE_FORMAT` | `DATE_FORMAT([下单日期], "yyyy-MM")` |
213
+
214
+ 示例:
215
+
216
+ ```go
217
+ calc := BasicCalculator("id_1002", "新增计算列", inputA, []Formula{
218
+ {
219
+ Name: "含税金额",
220
+ Type: FieldTypeDOUBLE,
221
+ Expr: "ROUND([金额] * 1.13, 2)",
222
+ Key: "formula_tax_amount",
223
+ },
224
+ {
225
+ Name: "门店前缀",
226
+ Type: FieldTypeSTRING,
227
+ Expr: "LEFT([门店ID], 2)",
228
+ Key: "formula_store_prefix",
229
+ },
230
+ }, Position{X: 320, Y: 100})
231
+ ```
232
+
233
+ ### SQL
234
+
235
+ ```go
236
+ BasicSqlScript(id, name string, sources []Node, sql string, position Position)
237
+ ```
238
+
239
+ 推荐把 SQL 放到独立文件:
240
+
241
+ ```go
242
+ node := BasicSqlScript(
243
+ "id_1005",
244
+ "复杂转换",
245
+ []Node{inputA, inputB},
246
+ ReadSQLFile("node_1005.sql"),
247
+ Position{X: 900, Y: 100},
248
+ )
249
+ ```
250
+
251
+ SQL 规则:
252
+
253
+ - 上游节点在 SQL 中用 `input1`、`input2` 这类名字引用。
254
+ - 特殊列名、中文列名、带空格列名,以及 `AS` 输出别名中的非 ASCII 标识符,都必须用反引号。
255
+ - 输入数据集字段存在 `alias` / `displayName` / `showName` 时,优先使用展示名而不是 raw name。
256
+ - 新增 SQL 节点时,要同步新增 `.sql` 文件。
257
+
258
+ ## 推荐骨架
259
+
260
+ ```go
261
+ package main
262
+
263
+ import . "guanetl/internal/framework"
264
+
265
+ func DefineETL() []Node {
266
+ inputA := BasicInputDataset("id_1001", "输入A", "ds_a", []Field{
267
+ BasicField("id", FieldTypeSTRING),
268
+ }, Position{X: 100, Y: 100})
269
+
270
+ transformed := BasicFilterRows(
271
+ "id_1002",
272
+ "筛选A",
273
+ inputA,
274
+ CombineTypeAND,
275
+ []FilterCondition{},
276
+ Position{X: 320, Y: 100},
277
+ )
278
+
279
+ output := BasicOutputDataset("id_1003", "输出", transformed, "结果数据集", Position{X: 560, Y: 100})
280
+ return []Node{output}
281
+ }
282
+ ```
283
+
284
+ ### 多输出 ETL
285
+
286
+ `DefineETL()` 可以返回多个叶子输出节点。适合从同一批输入派生多张下游表,例如同时产出明细表和汇总表。
287
+
288
+ ```go
289
+ package main
290
+
291
+ import . "guanetl/internal/framework"
292
+
293
+ func DefineETL() []Node {
294
+ orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
295
+ BasicField("门店", FieldTypeSTRING),
296
+ BasicField("订单号", FieldTypeSTRING),
297
+ BasicField("销售额", FieldTypeDOUBLE),
298
+ }, Position{X: 100, Y: 120})
299
+
300
+ detail := BasicSelectColumns("id_1002", "订单明细", orders, []ColumnSetting{
301
+ {Name: "门店"},
302
+ {Name: "订单号"},
303
+ {Name: "销售额"},
304
+ }, Position{X: 340, Y: 40})
305
+
306
+ summary := BasicGroupBy(
307
+ "id_1003",
308
+ "门店汇总",
309
+ orders,
310
+ []GroupByColumn{
311
+ BasicGroupByColumn("门店", FieldTypeSTRING),
312
+ },
313
+ []AggregationColumn{
314
+ BasicAggregationColumnWithAlias("销售额", FieldTypeDOUBLE, AggrTypeSUM, "销售额合计"),
315
+ },
316
+ Position{X: 340, Y: 200},
317
+ )
318
+
319
+ outDetail := BasicOutputDatasetInDir("id_1004", "输出明细", detail, "订单明细表", "ds_dir_id", Position{X: 600, Y: 40})
320
+ outSummary := BasicOutputDatasetInDir("id_1005", "输出汇总", summary, "门店销售汇总表", "ds_dir_id", Position{X: 600, Y: 200})
321
+
322
+ return []Node{outDetail, outSummary}
323
+ }
324
+ ```
325
+
326
+ - 每个输出都要有独立的 `OUTPUT_DATASET` 节点 id 和独立的 `outputDsName`。
327
+ - 多输出共享同一个 ETL 的 `save`、`run` 和调度配置;任何一个输出分支变更后,都应重新验收本 ETL 的全部输出。
328
+ - 如果多个输出需要独立调度、独立发布、独立回滚,或希望隔离某个输出分支的变更影响,应拆成多个单输出 ETL。
329
+
330
+ ## 每次改完都检查
331
+
332
+ 1. `DefineETL()` 返回的是叶子节点吗?
333
+ 2. 新节点 ID 唯一吗?格式对吗?
334
+ 3. 节点顺序符合依赖吗?
335
+ 4. `JOIN_DATA` / `GROUP_BY` / `FILTER_ROWS` 的枚举值合法吗?
336
+ 5. 新增 SQL 文件了吗?文件名和代码引用一致吗?
337
+ 6. 输入字段 schema 足够支撑后续节点吗?
338
+ 7. `BasicCalculator` 的每个 `Formula` 都显式填写 `Type` 了吗?缺失会在 `export` 后提示 warning,并可能导致下游原生 `GROUP_BY` / `JOIN_DATA` 静态检查出现类型不匹配。
339
+
340
+ ## 验证顺序
341
+
342
+ 始终按这个顺序:
343
+
344
+ 1. `guanetl export --dir <work_dir>`
345
+ 2. 需要看结果时:`guanetl preview <node_id> --dir <work_dir>`;保存前要求非空时追加 `--require-nonempty`,零行会返回错误并阻止命令链继续执行
346
+ 3. 保存前先看影响:`guanetl save --dir <work_dir> --dry-run`
347
+ 4. 确认无误后:`guanetl save --dir <work_dir>`
348
+
349
+ 如果 `export` 没过,不要直接 `save`。
350
+
351
+ ## save 输出数据集冲突
352
+
353
+ `save` 会先拉取服务端当前 ETL,再把本地 `_exported.json` 合并进去。修改已保存 ETL 时,输出节点必须复用服务端已有输出数据集绑定,否则 direct-save 可能把它当成“创建新输出数据集”,触发“输出数据集目录中存在同名文件”。
354
+
355
+ 新建 ETL 首次保存时,服务端 edit API 可能返回 ETL 不存在。若当前本地 base 与工作区 ETL ID 一致且尚无 actions,CLI 会将其识别为正常首次保存回退并输出 `save.first_save_fallback`;其他 edit 故障使用 `save.edit_fallback` warning,不应静默当作首次保存。
356
+
357
+ 当本地输出节点 id 被重新生成,但 `outputDsName + parentDirId` 与一个服务端已绑定输出唯一匹配时,`save` 会在请求副本中自动恢复原节点 id 和 dsId,并在影响报告中输出 `save.output_binding_reconciled`。源 `_exported.json` 不会被改写。跨目录、双方重名歧义、删除旧输出或无法唯一匹配时不会自动协调。
358
+
359
+ - 再次修改已保存 ETL,优先重新执行 `guanetl edit <etl_id> --dir <新目录>`,不要长期复用旧工作目录。
360
+ - 只追加输出列时,可以原地 `save`:新增列,不改名、不删已有列、不改已有列类型,并保持原 `OUTPUT_DATASET` 节点 id 和 `outputDsName`;但这只保证输出身份稳定,不保证旧 DATAFLOW 数据集的字段目录已同步。对新增并透传到输出的 `BasicCalculator` 字段,必须执行 `guanetl run <etlId> --wait`:命令会按输出数据集独立等待本轮物化,并同时验证 metadata 与实际 preview 返回字段;任一目标输出尚未完成或字段缺失都会报错。
361
+ - 修改已有输出 schema 时,保持原 `OUTPUT_DATASET` 节点 id;改列名、删列、改已有列类型、换输入数据集或重接输出链路都可能影响下游绑定,保存前必须重新 `preview` 并评估下游。
362
+ - 如果用 `guands dataset rename` 改过 ETL 输出数据集名称,必须同步 `etl.go` 中 `BasicOutputDataset(..., outputDsName, ...)` 或 `BasicOutputDatasetInDir(..., outputDsName, ...)` 的名称。
363
+ - 不要为了绕过同名错误手动删除旧输出数据集;旧输出通常仍被 ETL 依赖。
364
+ - 如果只是在保留旧输出的同时增加新输出,使用新的节点 id 和不同的 `outputDsName` 或 `parentDirId` 即可。
365
+ - 如果确实要移除已绑定输出并创建新输出数据集,必须使用新的节点 id、`outputDsName` 或 `parentDirId`,并显式加 `--allow-output-replacement`。该选项不会迁移下游 dsId 引用,调用方必须自行完成依赖迁移。
366
+ - `save` 如果在 direct-save 前提示输出绑定风险,先修本地 `etl.go` / `meta.json`,再重新 `export -> preview -> save`。
367
+ - `save --dry-run` 只生成保存影响报告,不调用 direct-save;需要机器可读结果时加 `--format json`。报告里出现阻断级输出绑定风险时,必须先修本地定义,或在确认主动替换且已规划下游迁移后显式使用 `--allow-output-replacement`。
368
+
369
+ ## Appendix: Framework Surface
370
+
371
+ 这部分只列 AI 写 ETL 时最值得记住的公开函数和合法枚举。
372
+ 源码真相在 runner framework 中,但不要把源码实现细节当成日常写法。
373
+
374
+ ### 推荐优先使用的 Basic 函数
375
+
376
+ ```go
377
+ BasicInputDataset(id, name, inputDsID string, fields []Field, position Position)
378
+ BasicOutputDataset(id, name string, source Node, outputDsName string, position Position)
379
+ BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
380
+ BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, position Position)
381
+ BasicFilterRows(id, name string, source Node, combineType CombineType, conditions []FilterCondition, position Position)
382
+ BasicJoinData(id, name string, leftSource, rightSource Node, joinType JoinType, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
383
+ BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
384
+ BasicCalculator(id, name string, source Node, formulas []Formula, position Position)
385
+ BasicRemoveDuplicates(id, name string, source Node, columnNames []string, position Position)
386
+ BasicAppendRows(id, name string, sources []Node, unionType UnionType, schemaSource string, position Position)
387
+ BasicSqlScript(id, name string, sources []Node, sql string, position Position)
388
+ ```
389
+
390
+ ### 只有需要完整配置时再用的 New 函数
391
+
392
+ ```go
393
+ NewInputDataset(id, name string, config InputDatasetConfig)
394
+ NewOutputDataset(id, name string, source Node, config OutputDatasetConfig)
395
+ NewSelectColumns(id, name string, source Node, config SelectColumnsConfig)
396
+ NewFilterRows(id, name string, source Node, config FilterRowsConfig)
397
+ NewJoinData(id, name string, leftSource, rightSource Node, config JoinDataConfig)
398
+ NewGroupBy(id, name string, source Node, config GroupByConfig)
399
+ NewCalculator(id, name string, source Node, config CalculatorConfig)
400
+ NewRemoveDuplicates(id, name string, source Node, config RemoveDuplicatesConfig)
401
+ NewAppendRows(id, name string, sources []Node, config AppendRowsConfig)
402
+ NewSqlScript(id, name string, sources []Node, config SqlScriptConfig)
403
+ ```
404
+
405
+ 默认优先 `Basic*`,除非你明确需要手动控制更底层配置。
406
+
407
+ ### 高频辅助函数
408
+
409
+ ```go
410
+ BasicField(name string, fieldType FieldType)
411
+ BasicColumnSetting(name string)
412
+ BasicJoinColumnPair(leftColumn, rightColumn string)
413
+ BasicJoinOutputFromLeft(columnName string)
414
+ BasicJoinOutputFromRight(columnName string)
415
+ BasicJoinOutputFromLeftWithAlias(columnName, alias string)
416
+ BasicJoinOutputFromRightWithAlias(columnName, alias string)
417
+ BasicGroupByColumn(name string, columnType FieldType)
418
+ BasicGroupByColumnWithAlias(name string, columnType FieldType, newName string)
419
+ BasicAggregationColumn(name string, columnType FieldType, aggregationType AggregationType)
420
+ BasicAggregationColumnWithAlias(name string, columnType FieldType, aggregationType AggregationType, newName string)
421
+ BasicFilterCondition(columnName string, operator FilterOperator, filterValues []FilterValue)
422
+ BasicFilterValue(value string)
423
+ BasicFilterColumnValue(columnName string)
424
+ ReadSQLFile(filename string)
425
+ ```
426
+
427
+ ### Formula 结构
428
+
429
+ ```go
430
+ type Formula struct {
431
+ Name string `json:"name"`
432
+ Type FieldType `json:"type"`
433
+ Expr string `json:"expr"`
434
+ Key string `json:"key"`
435
+ }
436
+ ```
437
+
438
+ ### 枚举常量(优先使用常量)
439
+
440
+ JOIN 类型、筛选组合、操作符、聚合类型、UNION 类型、字段/公式类型都是命名类型,
441
+ 框架提供同名常量,命名规则是 `<类型前缀> + 去掉下划线的值`(`COUNT_DISTINCT` → `AggrTypeCOUNTDISTINCT`)。
442
+ **优先写常量**:常量名拼错会在 `export` 求值脚本时直接报 `undefined`,而字符串字面量拼错要等到图校验阶段才发现。
443
+ 字符串字面量(`"INNER"`)仍然可用(存量脚本无需修改,`guanetl lint` 会提示改用常量);
444
+ 把 `string` 变量传给枚举参数需要显式转换,例如 `JoinType(v)`。
445
+
446
+ | 枚举 | 常量 | 说明 |
447
+ |---|---|---|
448
+ | 筛选组合 `CombineType` | `CombineTypeAND` `CombineTypeOR` | |
449
+ | 筛选操作符 `FilterOperator` | `FilterOpEQ` `FilterOpNE` `FilterOpLT` `FilterOpLE` `FilterOpGT` `FilterOpGE` `FilterOpIN` `FilterOpBT` `FilterOpCONTAINS` `FilterOpNOTCONTAINS` `FilterOpSTARTSWITH` `FilterOpNOTSTARTSWITH` `FilterOpENDSWITH` `FilterOpNOTENDSWITH` | `FilterOpISNULL` / `FilterOpNOTNULL` 当前后端 FILTER_ROWS 不支持,null 过滤请用 `BasicSqlScript` |
450
+ | 筛选值类型 `FilterValueType` | `FilterValueTypeVALUE` `FilterValueTypeCOLUMN` | 通常经 `BasicFilterValue` / `BasicFilterColumnValue` 间接使用 |
451
+ | JOIN 类型 `JoinType` | `JoinTypeINNER` `JoinTypeLEFTOUTER` `JoinTypeRIGHTOUTER` `JoinTypeFULLOUTER` | 服务端把 FULL_OUTER 记为 OUTER,框架自动转换 |
452
+ | 聚合类型 `AggregationType` | `AggrTypeSUM` `AggrTypeCOUNT` `AggrTypeCOUNTDISTINCT` `AggrTypeMIN` `AggrTypeMAX` `AggrTypeAVG` `AggrTypeFIRSTNOTNULL` | 服务端记为 SUM/CNT/CNT_DISTINCT/MIN/MAX/AVG/NUL,框架自动转换 |
453
+ | APPEND_ROWS `UnionType` | `UnionTypeINCLUDESHARED` `UnionTypeINCLUDEALL` `UnionTypeINCLUDEFROM` | |
454
+ | 字段/公式类型 `FieldType` | `FieldTypeINT` `FieldTypeDOUBLE` `FieldTypeSTRING` `FieldTypeTIMESTAMP` `FieldTypeLONG` `FieldTypeSHORT` `FieldTypeFLOAT` `FieldTypeDATE` `FieldTypeBOOL` `FieldTypeDECIMAL` `FieldTypeARRAY` | `Formula.Type` 只能取这些值;`BasicInputDataset` 的字段类型来自服务端,可能超出该列表,按 `guancli ds get` 的结果照写即可 |
455
+
456
+ `BasicAppendRows` 会按列名对齐各输入源。参与拼接的同名列类型必须一致;例如一侧为 `DATE`、另一侧为 `LONG` 会在 `export` 阶段报错。遇到冲突时,先用 `BasicCalculator` / `BasicSelectColumns` 把各源字段统一类型或移除不用的冲突列,再执行行拼接。
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: guanmetric
3
+ description: 当用户要创建、编辑、删除观远 BI 指标,增删改查指标主题/指标目录,创建或配置指标树(含维度拆解、指标拆解公式、复合指标公式展开),批量标准化 Excel 指标模板,管理指标公共维度,或查询目录树及增删改查业务字典时使用本 skill。查询指标详情、搜索指标、指标数据查询仍使用 guancli。
4
+ compatibility: "Requires Node.js 14+. Install via npm link --foreground-scripts (local) or npm install -g --foreground-scripts @guandata/guanmetric (from internal Nexus registry) so the AI skill refresh result is visible. CLI command: guanmetric."
5
+ ---
6
+
7
+ # guanmetric
8
+
9
+ `guanmetric` 负责观远 BI 指标平台写操作。认证复用 `guancli` 当前环境;上游托管 OIDC Token 时,当前进程需同时设置 `GUANCLI_OIDC_BASE_URL` 和 `GUANCLI_OIDC_ACCESS_TOKEN`,无需本地 profile。
10
+
11
+ 常用命令:
12
+
13
+ ```bash
14
+ guanmetric template schema --type atomic
15
+ guanmetric template init --type atomic --out atomic_metrics.xlsx
16
+ guanmetric template init --type composite --out composite_metrics.xlsx
17
+ guanmetric template normalize 客户指标.xlsx --check-only --output json-v1 --skip-biz-attrs --skip-id-resolve
18
+ guanmetric template normalize 客户指标.xlsx --out standard.xlsx --report report.json
19
+ guanmetric template explain-error report.json --json
20
+ guanmetric project create 经营指标 --dry-run
21
+ guanmetric project edit <project_id> --name 经营分析指标 --dry-run
22
+ guanmetric project delete <project_id>
23
+ guanmetric directory create 广告分析 --project-id <project_id> --dry-run
24
+ guanmetric directory edit <project_id> <dir_id> --name 广告经营分析 --dry-run
25
+ guanmetric directory delete <project_id> <dir_id>
26
+ guanmetric create --file atomic_metric.json --dry-run
27
+ guanmetric composite create --file composite_metric.json --dry-run
28
+ guanmetric composite edit <metric_id> --set desc=新口径 --dry-run
29
+ guanmetric derived create --file derived_metric.json --dry-run
30
+ guanmetric derived edit <metric_id> --file patch.json --dry-run
31
+ guanmetric flow plan --file metric_flow.json
32
+ guanmetric flow generate --file standard.xlsx --out metric_flow.json
33
+ guanmetric flow apply --file metric_flow.json --state metric_flow.state.json --confirm
34
+ guanmetric flow verify --state metric_flow.state.json
35
+ guanmetric flow reconcile --file metric_flow.json --state metric_flow.state.json --key <flow_key> --metric-id <metric_id>
36
+ guanmetric flow reconcile --file metric_flow.json --state metric_flow.state.json --key <flow_key> --metric-id <metric_id> --confirm
37
+ guanmetric edit <metric_id> --file patch.json --dry-run
38
+ guanmetric delete <metric_id>
39
+ guanmetric batch online --id <metric_id_1> --id <metric_id_2> --dry-run
40
+ guanmetric batch offline --file metric_ids.json --dry-run
41
+ guanmetric public-dim create --file public_dim.json --dry-run
42
+ guanmetric business-dict list 销售 -f table
43
+ guanmetric business-dict get <business_dict_id>
44
+ guanmetric business-dict create --file business_dict.json --dry-run
45
+ guanmetric business-dict edit <business_dict_id> --set desc=新口径 --dry-run
46
+ guanmetric business-dict delete <business_dict_id>
47
+ guanmetric accelerate create <metric_id> --ds-id <dsId> --fd-id <fdId> --aggr-type SUM --dim 省份 --dry-run
48
+ guanmetric accelerate create <metric_id> --ds-id <dsId> --fd-id <fdId> --aggr-type SUM --filter-file filter.json --dry-run
49
+ guanmetric accelerate edit <accelerate_metric_id> --file accelerate_query.json --dry-run
50
+ guanmetric accelerate edit <accelerate_metric_id> --filter-json '{"type":"condition","not":false,"value":{"dsId":"ds_sales","fdId":"fd_province","name":"省份","fdType":"STRING","metaType":"DIM","filterType":"IN","filterValue":["浙江"]}}' --dry-run
51
+ guanmetric accelerate priority <metric_id> --priority <accelerate_metric_id>=2 --dry-run
52
+ guanmetric accelerate delete <accelerate_metric_id> --confirm
53
+ guanmetric metric-tree create 销售额指标树 --metric-id <metric_id> --parent-dir-id <metric_tree_dir_id> --dry-run
54
+ guanmetric metric-tree update-config <metric_tree_id> --file metric_tree_config.json --dry-run
55
+ guanmetric metric-tree delete <metric_tree_id>
56
+ guanmetric metric-tree directory tree
57
+ guanmetric metric-tree directory create 经营分析 --dry-run
58
+ guanmetric metric-tree directory edit <dir_id> --name 销售经营分析 --dry-run
59
+ guanmetric metric-tree directory delete <dir_id>
60
+ guanmetric fetch POST /api/metric-platform/<unwrapped_endpoint> --file request.json
61
+ ```
62
+
63
+ ## 编辑红线:编辑 ≠ 删除重建
64
+
65
+ 用户要求**编辑/重命名/修改**已有指标时,原子指标必须用 `guanmetric edit <metric_id>`,复合指标用 `guanmetric composite edit <metric_id>`,衍生指标用 `guanmetric derived edit <metric_id>` 原地更新;改名可用 `--set name=新名`。指标主题用 `project edit`,指标目录用 `directory edit`;业务字典用 `business-dict edit`;公共维度用 `public-dim edit`;查询加速用 `accelerate edit <accelerate_metric_id>` 原地更新或 `accelerate priority <metric_id>` 调整顺序;指标树用 `metric-tree rename`、`metric-tree move` 或 `metric-tree update-config` 原地更新。**严禁**用"delete 后重新 create 一个同名资源"来模拟编辑或改名:资源 ID 被其他对象引用,删除重建会导致引用、权限或历史配置丢失。业务字典删除时,服务端会先解除正式指标和草稿的 `bizMetricId`,并把字典的业务口径和业务属性保留到指标,再逻辑删除字典;因此删除前必须先查看关联指标并取得明确确认。指标树删除重建还会改变指标树 ID,导致归因入口、目录引用和外部链接失效。`delete` 只用于用户明确要求删除资源本身。主题和目录删除也必须先展示精确目标,追加 `--confirm` 才提交;目录根节点不能单独删除。无论通过哪个命令或接口,都不得把"编辑"拆成"删除 + 重建"的组合来绕过这条红线。只有用户明确指示"先新建替代、再删除旧的"时才允许该流程,且执行前必须列出会断链和丢失的内容并获得确认。
66
+
67
+ 批量创建指标时,必须先生成固定结构的标准 Excel 返回给用户确认;确认前不生成最终 payload,不提交真实创建。
68
+
69
+ 生成或校验原子/复合指标输入前,先执行 `guanmetric template schema --type atomic|composite` 获取机器契约;没有现成输入时用 `template init --type atomic|composite` 生成带合法示例的最小模板。进入正式 normalize 前优先运行 `template normalize <file> --check-only --output json-v1`:该命令不写 Excel/report,也不创建、修改或上线指标;`valid=false` 时按 diagnostics 中的稳定 code、Sheet、row、column、expected 和 example 修正。code/column 由校验规则直接产生,不从中文 message 推断;修改展示文案不会改变机器诊断。`TEMPLATE_NEEDS_REVIEW` 不属于真实错误,只有 severity=error 才使命令非零退出。已有普通 normalize report 可用 `template explain-error <report.json> --json` 提取同一诊断契约。
70
+
71
+ 如果用户提供的是客户原始 Excel/CSV,而不是已经接近标准模板的文件,agent 必须先自行读取并理解表格语义,产出一个语义映射中间表(建议命名为 `*.ai_mapped.xlsx`),再执行 `guanmetric template normalize` 生成最终标准模板。不要直接把客户原始 Excel 丢给 CLI 后交付结果;CLI 不负责穷举客户列名或业务表述。CLI 继续负责固定模板结构、必填项标记、业务属性配置读取、自动填充说明、行状态、问题说明、高亮样式、列宽自适应、基础校验和 report 输出。
72
+
73
+ 如果用户没有提供 Excel/CSV,而是给出一段文字、需求描述、粘贴的半结构化表格、截图转写内容或其它非文件信息,也必须先由 agent 理解语义并整理成同样的 `*.ai_mapped.xlsx` 中间表,再执行 `guanmetric template normalize` 生成标准模板。信息不足时不要直接放弃;应尽量补出可判断的指标行,并在标准模板的 `问题说明` 中列出缺失的主题、目录、数据集、字段、聚合方式、公式、衍生自、适用维度、时间维度或业务属性,让用户在返回的标准模板中修改后重新上传。
74
+
75
+ 如果 report 的 `notes` 提示“疑似业务语义列被丢弃但复合/衍生 sheet 为空”,说明 agent 跳过了语义映射步骤,必须返回重新读取原始 Excel、拆分原子/复合/衍生后再标准化,不要把全原子模板交给用户。
76
+
77
+ 如果 report 的 `notes` 提示“疑似衍生语义”、某个衍生 sheet 为空,或“所有衍生指标 sheet 均为空”,说明用户输入或业务口径中可能包含同比/环比、最近 N/时间窗口、累计/历史累计、期末值、业务限定等衍生语义,但 agent 没有产出对应衍生模板行。必须回到语义映射步骤重新识别并补充对应类型的衍生指标;不要把衍生指标缺类的模板直接交付给用户。
78
+
79
+ 标准 Excel 第一张固定为 `汇总` sheet,用于给用户总览本次待创建指标总数、按类型计数、指标名称、指标类型、所在模板、关键来源、行状态和问题说明;创建 payload 仍以各指标类型模板 sheet 为事实源,不从 `汇总` sheet 创建。
80
+
81
+ 生成复合指标模板时,`计算公式` 的每个引用都必须解析为已上线或本批先创建的原子指标/复合指标。复合指标不能引用衍生指标或快捷引用指标;在写入 `*.ai_mapped.xlsx` 或标准创建计划 Excel 前,先建立“指标名称 → 指标类型”索引并拒绝这类引用,不能先生成错误 Excel 再留给 Flow/后端报错。`template normalize --check-only` 也会用 `TEMPLATE_INVALID_REFERENCE` 阻断本批复合指标引用衍生指标。引用对象不在模板中时,先判断它应建为原子指标还是复合指标并补充对应行,不要一律补成原子指标。若业务公式确实要组合最近 N、累计、期末值等衍生结果,按 `metric-derived-mutation.md` 评估组合衍生,并向用户说明改模后的业务语义。
82
+
83
+ 生成衍生指标模板时,如果 `衍生自` 指标不是已明确存在的线上指标,而是本批也要创建的指标,必须在标准模板中补充对应的原子指标或复合指标行;信息不足时先补行并在该基础指标行的 `问题说明` 中列出缺失的数据集、来源字段、聚合方式、计算公式、适用维度等。不要等到创建计划阶段才临时新增基础指标。
84
+
85
+ 复合指标和衍生指标的单条创建/编辑必须使用 `composite create|edit`、`derived create|edit` 专用命令。三类编辑命令优先读取草稿;仅当结构化业务状态明确表示草稿不存在时才回退正式详情,然后把读模型转换成对应 Update DTO 白名单再合并 patch。原子 `edit --replace` 已停用。禁止把详情响应直接交给 `fetch` 回写;`fetch` 会先按服务端路由语义解码并规范化 URL,再硬拒绝三类指标已封装的 create/edit 写路由,百分号编码、点段或矩阵参数不能绕过,只用于尚未封装的低层端点或诊断。指标定义写入时,专用命令和其余允许的 `fetch` 请求都会规范化并校验顶层 `filter`:将兼容别名和 `not: true` 转成 Web 编辑器可识别的明确筛选类型,为空值筛选补 `filterValue: []`,并从当前指标或基础指标的 `applicableDims` 补齐字段元数据;无法无损转换时拒绝提交。业务限定衍生指标还会确认每个条件的 `fdId` 都存在于基础指标 `applicableDims`。
86
+
87
+ 构造或解析指标创建、编辑、详情和列表请求时,先读取 `metric-api-contracts.md`。其中包含三类指标的真实脱敏响应、原子字段/表达式两种模式、六种衍生配置、JSON Schema 以及读模型到写模型映射。详情响应是读模型:`fdInfos`、对象数组形式的 `applicableDims`、对象形式的 `timeDim`、`baseMetric` 和用户对象不能原样提交;编辑请求必须按对应 Update DTO 白名单显式构造。列表请求的 `subTypes` 是逗号分隔字符串,不是 JSON 数组。
88
+
89
+ 构造或解析指标主题/指标目录的创建、编辑、详情/列表/目录树和删除请求时,先读取 `metric-hierarchy-api-contracts.md`。主题编辑只提交 `name/icon/iconColor`;目录编辑只提交 `name/parentDirId/dirType`,其中 `dirType` 固定为 `METRIC_PLATFORM`。指标目录没有单条详情接口,`directory get` 是从指定 Project 的目录树按 ID 提取节点。创建和编辑必须写后回读;删除默认只展示目标,必须追加 `--confirm`。本轮不提供指标 move 或指标 owner 转移命令。
90
+
91
+ 用户通过对话提出一批相互依赖的指标创建需求时,优先使用 `guanmetric flow`,不要逐个调用 `create`/`fetch` 后自行判断成功。标准模板经用户确认且所有真实指标行为“可创建”后,必须优先执行 `flow generate --file <standard.xlsx> --out <metric_flow.json>`,不要由 Agent 手写计划。该命令会解析数据集字段、当前责任人/唯一用户、业务属性、已有指标和批内引用,生成稳定 key 与占位符,并立即运行 `flow plan`;无法唯一解析时输出带 Sheet/行/列的稳定诊断且不写计划。同一模板不能混合“上线”和“草稿”:全部上线生成 `usable`,全部草稿生成 `draft`,混合时必须拆分模板。提交真实创建前必须检查生成计划中的取数定义(`fdId+aggrType` 或 `formulaField`)、适用维度、时间维度、业务属性、责任人、格式、复合/衍生引用和 `publish` 均与确认后的标准模板一致;任一关键字段缺失都不得执行 `flow apply --confirm`。生成后仍可单独运行 `flow plan`;用户确认后运行带 `--confirm` 的 `flow apply`,并始终提供 `--state` 状态文件以便失败或审批后续跑。`acceptance.mode=usable` 要求请求 `publish=true`,最终状态为 `PUBLISHED`、当前用户 `canUse=true`,且 `guancli metric query --limit 1 -f json` 成功;组合衍生引用带固定高级计算粒度的指标时,Flow 会读取引用定义并自动追加唯一兼容的 `--dim "<时间维度> toMonth/toWeek/..."`,同时把实际维度、粒度和策略写入 state 的 `query` 证据。引用粒度不一致时返回 `FLOW_QUERY_GRANULARITY_CONFLICT`,不能靠循环尝试不同粒度掩盖定义冲突。`acceptance.mode=draft` 要求 `publish=false`,只验证草稿定义能够回读。审批中的指标返回 `pending`,不能汇报为已可用。创建结果未知时,`flow apply` 会永久保持该条目为 `blocked/reconcile`,绝不重发创建;创建前已将真正提交的 canonical 请求保存到 state 的 `submittedDefinition`,`reconcile` 只能据此核对候选,不能按实时元数据重建请求,而且同一 state 内一个非空 metricId 只能绑定一个 key。Agent 先只读核对线上候选,再执行不带 `--confirm` 的 `flow reconcile --file ... --state ... --key ... --metric-id ...` 预览,确认环境、planHash、类型、候选 ID 未被占用和关键定义完全一致后,才追加 `--confirm` 把真实 ID 写回 state。原生 Unix/Windows 中,confirmed apply 会在读取计划或访问 BI 前取得 state 锁,verify/reconcile 也在读取 state 或访问 BI 前取得锁,三者由跨进程独占锁串行化;apply preview 不读取 state,因此不持锁。遇到“正由另一个操作占用”时等待对方结束后重试,禁止改用另一个 state 绕过。WASI/非原生运行时因无法证明跨 guest 单写者,明确禁用 apply/verify/reconcile,只保留 generate/plan 等只读能力。部分指标已创建后禁止自动删除回滚,修正问题后复用同一计划和状态文件继续验证。完整契约见 `metric-flow.md`。
92
+ `flow verify` 的 `usable` 只证明指标已发布、可使用且查询成功,不证明它与另一个指标数值等价。只有用户明确要求新旧指标或两组指标数值对比时,才使用 `guancli metric batch-query` 执行独立的等价验证;必须固定相同粒度、筛选范围、排序和 raw 数值,按维度键对齐,并把可能截断的结果报告为 `inconclusive`,禁止用无排序的默认前 50 行按位置比较后宣称指标定义错误。详细 recipe 见 guancli skill 的 `references/metric-batch-query.md`。
93
+
94
+ 指标定义是否合法由 BI 后端最终裁决。Flow 的单指标业务校验必须取“当前 Web 创建页已有规则”减去“当前后端创建链路已有规则”的差集:只补前端有而后端没有的规则,不复制后端已有规则,也不擅自新增前后端都没有的规则。当前差集包括草稿名称格式/长度、英文名格式/长度、业务口径最大长度、原子 `fdId+aggrType` 配对完整性、普通衍生 `baseMetricId+advType` 必填、自定义业务属性文本长度以及原子 save-precheck;计划结构、批内依赖、占位符、环境/状态一致性和验收编排属于 Flow 自身契约。创建接口还会执行落库、权限和上线等后置步骤,因此任何未返回可记录 `metricId` 的创建错误都必须保留结构化业务状态并进入 `reconcile`,不能自动重试。校验职责基线见 `metric-validation-ownership.md`。
95
+ 查询加速写操作已封装为 `accelerate` 专用命令;查询加速列表查询使用 `guancli metric accelerate list <metric_id>`。查询加速筛选条件使用和指标定义一致的 `WebFilterData` 结构,可放在完整请求体的 `filter` 字段,也可通过 `--filter-json` 或 `--filter-file` 传入;合法节点仅为 `condition/operator/composition`,禁止使用 `group/combineType/conditions`。查询加速无法从指标适用维度自动补齐另一数据集的字段元数据,因此条件必须显式提供 `dsId/fdId/name/fdType/metaType`;清空筛选条件可传 `--filter-json null`。完整契约和嵌套示例见 `metric-common-fields.md`。
96
+ 指标树及其目录的创建、修改和删除已封装为 `metric-tree` 专用命令,优先使用专用命令,不要再用裸 `fetch` 直接调用写接口。构造或解析指标树及目录的请求时,先读取 `metric-tree-api-contracts.md`;详情/搜索名称字段是 `metricTreeName`,父目录列表及编辑返回对象名称字段是 `name`,目录树用 `id/name`、目录详情用 `dirId/dirName`,读模型不得整体回写。指标树和目录删除默认只展示详情,明确追加 `--confirm` 才提交。指标树目录必须用 `metric-tree directory` 管理并固定 `dirType=METRIC_TREE`;不要把 `METRIC_PLATFORM` 指标目录 ID 当作指标树目录 ID。用户给出指标拆解公式时,按 `metric-tree-mutation.md` 解析成 `metricRelationTree`;遇到复合指标时读取指标详情中的 `formula`,按规则递归展开或在计划中标注不能无损展开的原因。
97
+
98
+ 指标批量上下线使用 `batch online|offline`,输入可重复传 `--id`,或用 `--file` 提供 `{"ids":[...]}`,两者互斥。必须先执行 `--dry-run`。批量上线支持原子、复合和衍生指标:逐项读取 `fromDraft=true` 草稿,原子指标执行 save-precheck,全部类型执行 validate-update-impact,再按类型原地更新;快捷引用指标不支持上线。预检冲突或下游影响默认将该项标记为 `blocked`,只有用户确认后才追加 `--confirm`。批量下线逐项调用权威单指标下线接口,以保留稳定的逐项成功/失败归因。两种操作都是 best-effort:单项失败后继续,已成功项不回滚,最终 JSON 使用 `schemaVersion=1.0` 并在存在 `failed`/`blocked` 时非零退出。服务端可能启用指标审批,因此“上线请求成功”不等同于最终已上线,最终状态必须回读确认。完整契约见 `metric-batch-status.md`。
99
+
100
+ 参考文档见本 skill 的 `references/` 目录:
101
+
102
+ - `metric-excel-template.md`
103
+ - `metric-mutation.md`
104
+ - `metric-common-fields.md`
105
+ - `metric-atomic-mutation.md`
106
+ - `metric-composite-mutation.md`
107
+ - `metric-derived-mutation.md`
108
+ - `metric-public-dimension.md`
109
+ - `metric-public-dimension-api-contract-examples.json`
110
+ - `metric-public-dimension-api-contract.schema.json`
111
+ - `metric-business-dictionary.md`
112
+ - `metric-business-dictionary-api-contract-examples.json`
113
+ - `metric-business-dictionary-api-contract.schema.json`
114
+ - `metric-tree-mutation.md`
115
+ - `metric-tree-api-contracts.md`
116
+ - `metric-tree-api-contract-examples.json`
117
+ - `metric-tree-api-contract.schema.json`
118
+ - `metric-batch-status.md`
119
+ - `metric-flow.md`
120
+ - `metric-flow-generate-example.json`
121
+ - `metric-flow-generate.schema.json`
122
+ - `metric-validation-ownership.md`
123
+ - `metric-api-contracts.md`
124
+ - `metric-api-contract-examples.json`
125
+ - `metric-api-contract.schema.json`
126
+ - `metric-hierarchy-api-contracts.md`
127
+ - `metric-hierarchy-api-contract-examples.json`
128
+ - `metric-hierarchy-api-contract.schema.json`
129
+
130
+
131
+ ---
132
+
133
+ ## VM 环境差异(观远 BI 工作站)
134
+
135
+ 本技能在 MCP 虚拟工作站中运行,与本地 CLI 有以下差异:
136
+
137
+ - 命令名直接可用:在工作站里直接执行 `guancli` / `guanetl` / `guanvis` / `guands` / `guanmetric` / `guanwf`(无需安装)。
138
+ - 认证:BI 凭证由网关注入,**不要**执行 `auth login` 或设置 token,直接运行业务命令即可。
139
+ - 文件路径一律用以 `/` 开头的绝对路径(`/` 即工作区根,跨会话持久)。
140
+ - 本技能的参考文档在 `/skills/<技能名>/references/` 下、示例工程在 `/skills/<技能名>/evals/` 下(均只读),用 read_file 工具或 `cat` 按需阅读;正文引用 `references/xxx` / `evals/xxx` 时按此路径解析。
141
+ - 图片文件(png/jpg/gif/webp)用 read_file 工具读取,**不要**用 `cat`/`head` 读图片。默认部署下图片以图像内容返回、模型可直接查看;若 read_file 返回"当前部署已关闭图像内容返回"的提示(部署方接入的模型不支持图像理解),则只有文本元信息,此时不要做任何依赖看图的验证,改用数据类命令核对。
142
+ - 不支持 `cd`、`$VAR` 展开、命令替换 `$(...)`、glob 展开;需要组合时把中间结果写入文件。
143
+ - 文本搜索用 `rg`(工作站内无独立 grep 二进制,`grep`/`egrep`/`fgrep` 会自动转成 `rg` 执行,无需绕路)。
144
+ - `install-skill` / profile 切换 / npm 相关步骤在工作站内不适用,忽略即可。