@finebi/finebi-cli 1.2.1 → 1.4.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 (67) hide show
  1. package/README.md +63 -39
  2. package/package.json +40 -37
  3. package/skills/finebi-cli-agent/SKILL.md +133 -0
  4. package/skills/finebi-cli-agent/agents/openai.yaml +7 -0
  5. package/skills/finebi-cli-agent/references/analysis-dsl-api.md +322 -0
  6. package/skills/finebi-cli-agent/references/artifact-ordering.md +29 -0
  7. package/skills/finebi-cli-agent/references/command-workflows.md +199 -0
  8. package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +1037 -0
  9. package/skills/finebi-cli-agent/references/dsl-handoff-contract.md +195 -0
  10. package/skills/finebi-cli-agent/references/file-to-dashboard.md +65 -0
  11. package/skills/finebi-cli-agent/references/formula-basics.md +102 -0
  12. package/skills/finebi-cli-agent/references/formula-contexts.md +66 -0
  13. package/skills/finebi-cli-agent/references/formula-functions.md +247 -0
  14. package/skills/finebi-cli-agent/references/formula-generation.md +121 -0
  15. package/skills/finebi-cli-agent/references/formula-glossary.md +17 -0
  16. package/skills/finebi-cli-agent/references/formula-help-boundaries.md +87 -0
  17. package/skills/finebi-cli-agent/references/formula-help-index.md +264 -0
  18. package/skills/finebi-cli-agent/references/formula-parameters-aggregate.md +143 -0
  19. package/skills/finebi-cli-agent/references/formula-parameters-analysis.md +223 -0
  20. package/skills/finebi-cli-agent/references/formula-parameters-date.md +236 -0
  21. package/skills/finebi-cli-agent/references/formula-parameters-math-logic.md +271 -0
  22. package/skills/finebi-cli-agent/references/formula-parameters-text.md +217 -0
  23. package/skills/finebi-cli-agent/references/formula-recipes-analysis.md +124 -0
  24. package/skills/finebi-cli-agent/references/formula-recipes-text-date.md +86 -0
  25. package/skills/finebi-cli-agent/references/formula-semantics.md +39 -0
  26. package/skills/finebi-cli-agent/references/formula-syntax.md +70 -0
  27. package/skills/finebi-cli-agent/references/formula-troubleshooting.md +63 -0
  28. package/skills/finebi-cli-agent/references/formula-validation.md +36 -0
  29. package/skills/finebi-cli-agent/references/formula.md +37 -0
  30. package/skills/finebi-cli-agent/references/handoff-contract.md +210 -0
  31. package/skills/finebi-cli-agent/references/mapping-rules.md +423 -0
  32. package/skills/finebi-cli-agent/references/mutation-confirmation-contract.md +150 -0
  33. package/skills/finebi-cli-agent/references/scenario-workflows.md +75 -0
  34. package/skills/finebi-cli-agent/references/widget-dsl-api-examples.md +457 -0
  35. package/skills/finebi-cli-agent/references/widget-dsl-api.md +1340 -0
  36. package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +11 -0
  37. package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key-contract.js +2 -0
  38. package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key.js +174 -0
  39. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-failure.js +18 -0
  40. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-headers.js +21 -0
  41. package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth.js +116 -0
  42. package/skills/finebi-cli-agent/scripts/finebi-cli/src/branding.js +31 -0
  43. package/skills/finebi-cli-agent/scripts/finebi-cli/src/browser-auth.js +331 -0
  44. package/skills/finebi-cli-agent/scripts/finebi-cli/src/capability.js +122 -0
  45. package/skills/finebi-cli-agent/scripts/finebi-cli/src/config-permissions.js +87 -0
  46. package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +189 -0
  47. package/skills/finebi-cli-agent/scripts/finebi-cli/src/dynamic-command.js +324 -0
  48. package/skills/finebi-cli-agent/scripts/finebi-cli/src/error-catalog.js +482 -0
  49. package/skills/finebi-cli-agent/scripts/finebi-cli/src/errors.js +22 -0
  50. package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +467 -0
  51. package/skills/finebi-cli-agent/scripts/finebi-cli/src/index.js +1351 -0
  52. package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +89 -0
  53. package/skills/finebi-cli-agent/scripts/finebi-cli/src/output-limits.js +188 -0
  54. package/skills/finebi-cli-agent/scripts/finebi-cli/src/output.js +33 -0
  55. package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -0
  56. package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-options.js +265 -0
  57. package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-validator.js +194 -0
  58. package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +94 -0
  59. package/skills/finebi-cli-agent/scripts/finebi-cli-runner-core.mjs +399 -0
  60. package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +29 -0
  61. package/src/config.js +27 -27
  62. package/src/error-catalog.js +12 -0
  63. package/src/help.js +70 -55
  64. package/src/index.js +17 -4
  65. package/src/install-skill.js +89 -0
  66. package/src/profiles.js +158 -158
  67. package/src/skill-files.js +94 -0
@@ -0,0 +1,195 @@
1
+ # 语义交接契约
2
+
3
+ ## 1. 输入边界
4
+
5
+ 只消费 `finebi-cli-agent` 已校验的 JSON 对象。该对象记录 Widget/Dashboard 的业务语义和资源证据,不包含 JS 源码,也不授权执行任何 FineBI 写命令。
6
+
7
+ 顶层结构:
8
+
9
+ ```json
10
+ {
11
+ "contractVersion": "1.0",
12
+ "scenario": "CREATE_DASHBOARD",
13
+ "sourceRequest": "在销售分析主题中创建销售总览仪表板。",
14
+ "subject": {
15
+ "id": "subject_001",
16
+ "name": "销售分析"
17
+ },
18
+ "datasets": [],
19
+ "models": [],
20
+ "relationships": [],
21
+ "existingWidgets": [],
22
+ "requestedWidgets": [],
23
+ "requestedDashboard": null
24
+ }
25
+ ```
26
+
27
+ 必须满足:
28
+
29
+ - `contractVersion` 为 `1.0`。
30
+ - `scenario` 为 `CREATE_WIDGET` 或 `CREATE_DASHBOARD`。
31
+ - `sourceRequest` 保留用户原始请求。
32
+ - `subject` 是已确认存在的主题。
33
+ - `requestedWidgets` 非空,且其中 `key`、`name` 在本次请求内唯一。
34
+ - `CREATE_WIDGET` 的 `requestedDashboard` 为 `null`。
35
+ - `CREATE_DASHBOARD` 的 `requestedDashboard` 非空。
36
+
37
+ ## 2. 字段来源
38
+
39
+ ### 2.1 数据集和字段
40
+
41
+ 数据集结构:
42
+
43
+ ```json
44
+ {
45
+ "key": "dataset:sales_detail",
46
+ "id": "analysis_ds_001",
47
+ "name": "销售明细",
48
+ "sourceTableName": "销售明细",
49
+ "subjectId": "subject_001",
50
+ "usable": true,
51
+ "fields": [
52
+ {
53
+ "id": "field_amount",
54
+ "name": "sales_amount",
55
+ "transferName": "销售额",
56
+ "displayName": "销售额",
57
+ "type": "NUMBER"
58
+ }
59
+ ]
60
+ }
61
+ ```
62
+
63
+ 规则:
64
+
65
+ - `datasets` 只包含已确认存在、属于 `subject.id` 且被本次制品实际使用的分析表。
66
+ - `sourceTableName` 和字段原始 `name` 必须来自已验证的运行时元数据。
67
+ - `fields[].type` 只能是 `STRING`、`NUMBER`、`DATE`、`DATE_TIME` 或 `null`。
68
+ - 字段 ID 和数据集 ID 仅用于身份验证,不能写进 DSL 字段引用。
69
+ - DSL 物理字段引用使用 `from(sourceTableName).dim(fieldName)`、`from(sourceTableName).sum(fieldName)` 等名称形式;已保存计算字段使用 `measureID(measureId).dim()` / `measureID(measureId).sum()`。
70
+
71
+ ### 2.2 模型指标集和字段
72
+
73
+ ```json
74
+ {
75
+ "key": "model:sales_metrics",
76
+ "id": "model_sales",
77
+ "name": "销售指标集",
78
+ "subjectId": "subject_001",
79
+ "evidence": "MODEL_INFO",
80
+ "fields": [
81
+ {
82
+ "id": "metric_sales",
83
+ "name": "销售额",
84
+ "kind": "INDEX",
85
+ "type": "NUMBER"
86
+ },
87
+ {
88
+ "id": "field_order_no",
89
+ "name": "订单编号",
90
+ "kind": "TABLE_FIELD",
91
+ "datasetName": "订单明细",
92
+ "type": "STRING"
93
+ },
94
+ {
95
+ "id": "calc_completion_rate",
96
+ "name": "达成率",
97
+ "kind": "CALCULATED_FIELD",
98
+ "type": "NUMBER"
99
+ }
100
+ ]
101
+ }
102
+ ```
103
+
104
+ 规则:
105
+
106
+ - `models` 只包含属于 `subject.id`、被本次制品实际使用且模型名/字段名已有明确证据的模型指标集。
107
+ - `evidence` 只能是 `MODEL_INFO` 或 `WIDGET_DSL`。`MODEL_INFO` 表示模型及字段已由 `model info` 验证,必须保留返回的模型 ID 和字段 ID;`WIDGET_DSL` 无法提供 ID 时可为 null。不得把数据集搜索结果当作模型证据,也不得伪造模型/字段 ID。
108
+ - `fields[].kind` 只能是 `INDEX`、`TABLE_FIELD` 或 `CALCULATED_FIELD`。`TABLE_FIELD` 来自 `model info` 的 `datasets[].fields[]` 并保留所属 `datasetName`,`CALCULATED_FIELD` 来自 `calculatedFields[]`;模型根级维度和记录数不支持。
109
+ - DSL 的模型级指标使用 `model(modelName).metrics().sum/avg/min/max/measure(fieldName)`;数据集与计算字段必须写完整子命名空间。模型 ID 和复合字段 ID 即使已知也不能写进 DSL。
110
+ - `from("同名")` 与 `model("同名")` 是不同身份;不得因模型名恰好等于某个 `sourceTableName` 而互换。模型表字段必须写数据集限定符,模型计算字段必须写 `calculated()`;同一作用域字段展示名不唯一时返回 `BLOCKED`。
111
+
112
+ ## 3. 组件语义
113
+
114
+ 请求组件结构:
115
+
116
+ ```json
117
+ {
118
+ "key": "widget:sales_by_region",
119
+ "name": "各地区销售额",
120
+ "intent": "比较各地区的销售额",
121
+ "chartType": "CLUSTERED_COLUMN",
122
+ "datasetKeys": ["dataset:sales_detail"],
123
+ "modelKeys": [],
124
+ "encodings": [
125
+ {
126
+ "role": "VALUE",
127
+ "datasetKey": "dataset:sales_detail",
128
+ "fieldId": "field_amount",
129
+ "sourceTableName": "销售明细",
130
+ "fieldName": "sales_amount",
131
+ "aggregation": "SUM",
132
+ "sort": "DESC"
133
+ }
134
+ ],
135
+ "filters": [],
136
+ "presentation": {}
137
+ }
138
+ ```
139
+
140
+ 每个包含字段的编码、过滤或排序项必须恰好使用一种来源:物理字段携带 `datasetKey`、`sourceTableName` 和原始 `fieldName`;模型字段携带 `modelKey`、`modelName`、模型字段 `fieldName` 和字段 `kind`,`TABLE_FIELD` 还携带 `datasetName`。引用必须解析到对应 `datasets` 或 `models` 中唯一字段,且 key 同时出现在组件的 `datasetKeys` 或 `modelKeys`;不能用显示名称反向猜测来源类型。
141
+
142
+ ## 4. 关系
143
+
144
+ 同一组件或仪表板过滤器跨数据集引用字段时,`relationships` 必须包含已确认的关系证据:
145
+
146
+ ```json
147
+ {
148
+ "leftDatasetKey": "dataset:sales_detail",
149
+ "leftTableId": "analysis_ds_001",
150
+ "leftFieldId": "field_customer",
151
+ "rightDatasetKey": "dataset:customer_info",
152
+ "rightTableId": "analysis_ds_002",
153
+ "rightFieldId": "field_customer_id",
154
+ "type": "MANY_TO_ONE",
155
+ "status": "DEFAULT"
156
+ }
157
+ ```
158
+
159
+ - 左右 `datasetKey` 必须匹配 `datasets[].key`。
160
+ - 表和字段 ID 必须匹配已确认的运行时关系。
161
+ - `type` 只能为 `MANY_TO_MANY`、`MANY_TO_ONE`、`ONE_TO_MANY`、`ONE_TO_ONE`。
162
+ - 只消费 `status: DEFAULT` 的关系;其他状态返回 `BLOCKED`。
163
+
164
+ ## 5. 仪表板引用
165
+
166
+ 已有组件记录在 `existingWidgets`,必须包含 `key`、`id`、`name`、`subjectId`。
167
+
168
+ 仪表板中的 `widgetRefs[].source`:
169
+
170
+ - `REQUESTED`:`key` 必须匹配一个 `requestedWidgets[].key`。
171
+ - `EXISTING`:`key` 必须匹配一个带已确认 `id` 的 `existingWidgets[]` 项。
172
+
173
+ 仪表板 DSL 一律通过组件名称 `.reference("组件名")` 引用组件,不内联组件定义,不写入组件 ID。
174
+
175
+ ## 6. 文件映射
176
+
177
+ 第二个输入对象:
178
+
179
+ ```json
180
+ {
181
+ "contractVersion": "1.0",
182
+ "files": {
183
+ "widget:sales_by_region": "C:\\work\\finebi-dsl\\sales-by-region.js",
184
+ "dashboard:sales_overview": "C:\\work\\finebi-dsl\\sales-overview.js"
185
+ }
186
+ }
187
+ ```
188
+
189
+ - `files` 精确覆盖全部 `requestedWidgets[].key`,以及存在时的 `requestedDashboard.key`。
190
+ - 每个值都是绝对 `.js` 路径。
191
+ - 父目录必须已存在。
192
+ - 规范化后的路径必须两两不同。
193
+ - 只允许写入映射声明的文件;不得生成额外报告、模板或说明文件。
194
+
195
+ 任一引用缺失、冲突或无法按文档化规则映射时返回 `BLOCKED`,列出精确 JSON 路径,不使用占位值或猜测替代语义。
@@ -0,0 +1,65 @@
1
+ # 原始文件到仪表板
2
+
3
+ 用于用户要求从 Excel/CSV 完成数据分析、主题、公式、组件和仪表板的连续任务。以下 `resource command` 是业务参数,分发版一律通过 `node "<skill-dir>/scripts/finebi-cli-runner.mjs" run ... --json` 执行;首次使用某命令先 `inspect`。不把示例占位符直接执行。
4
+
5
+ ## 1. 选择数据入口
6
+
7
+ - 用户提供本地 xls/xlsx/csv:使用 `analysis-ds upload-excel`。CSV 是支持的输入,无须默认转 Excel。
8
+ - 仅供当前主题使用:附件直接创建主题分析表,无需先建公共数据集。
9
+ - 需要跨主题共享,或明确要求公共数据集:读取 `public-ds template` 与 source-preview 契约,先创建公共数据集,再引用到主题。
10
+ - 已有数据集:按 `command-workflows.md` 搜索并回查,不重复导入。
11
+
12
+ 文件路径属于执行 Node 的机器。上传前确认 Node 版本、入口存在、文件 realpath/大小/扩展名;必要时在本地读取 sheet 名、字段类型、数据粒度、空值和键唯一性。不要将服务器路径、容器挂载路径和 Windows 路径直接互换。Agent 看不到用户文件时,报告具体不可读路径和执行环境,请求最小的文件传入或挂载信息。
13
+
14
+ ## 2. 分阶段执行
15
+
16
+ 只读取当前阶段需要的 help/schema/参考。每阶段保存 `operation → 实际输入 → 服务端实际 ID → 回查证据`,下一阶段消费这个记录。
17
+
18
+ | 阶段 | 操作与输入 | 交给下一阶段 | 完成条件 |
19
+ |---|---|---|---|
20
+ | 上传 | `analysis-ds upload-excel --file "<原始文件绝对路径>"` | `data.attachId`;不是表 ID | `ok=true` 且 attachId 非空;上传没有独立 info,建表验证其可消费性 |
21
+ | 主题 | 按 [Subject 工作流](command-workflows.md#78-subject) 确认来源兼容性后复用主题,或 `subject add` 显式指定分析类型;参数以实时 help 为准 | 实际 subject ID | `subject info` 回查名称、位置和分析类型;省略 name 使用默认名称;失败但含 data.id 时先检查该资源,不重复创建 |
22
+ | 分析表 | `analysis-ds template` 后生成 JS;`analysis-ds add --subject-id <实际主题ID> --file <table.js>` | 每张表的实际 ID、name 和字段 | `analysis-ds info/preview`,核对字段、类型、行数和样本 |
23
+ | 关联 | `relationship info`;核对两侧键、粒度及唯一性后 `relationship add` | 真实关联及基数 | `relationship info` 与跨表查询验证,防止多对多重复累计 |
24
+ | 公式 | `calculated-field context --subject-id <实际主题ID>`;必须使用 `formula functions --subject-id <实际主题ID>` 查询目标主题面板(见 command-workflows.md §7.2);生成 `CalculatedFieldModel` JS,`validate` 后 `add` | 计算字段配置 ID、依赖与预期值 | info/dsl 回查;表达式通过还需在组件出数中核对结果 |
25
+ | 组件 | 按 widget template/API 生成 JS,再 `widget add`;物理数值字段作维度/分类/粒度时先 `measure copy --as dimension` 注册副本(缺副本时 add 报错并指路该命令) | 实际组件 ID、准确名称 | `widget info/preview`;核对指标、分组、筛选和排序 |
26
+ | 仪表板 | 按 dashboard template/schema/API 生成引用组件的 JS,再 `dashboard add` | 实际仪表板 ID/链接 | `dashboard dsl/preview`;核对组件、布局、筛选及关键数字 |
27
+
28
+ 局部示例:一个附件里的“订单”和“客户”应分别生成文件、分别调用 add:
29
+
30
+ ```javascript
31
+ AnalysisTableDsl.create("订单").fromExcel("实际attachId", "订单").build()
32
+ ```
33
+
34
+ ```javascript
35
+ AnalysisTableDsl.create("客户").fromExcel("实际attachId", "客户").build()
36
+ ```
37
+
38
+ 普通 `fromExcel` 省略 sheet 名仅选首个有效 sheet。异构 sheet 分别建表;同结构 sheet 合并为一张表需显式 `.mergeSheets()`,并确认目标 template/help 支持。不把数组当作 sheet 名参数,多个名字按 DSL 可变参数传入。未发布的步骤链、update/dsl/operator-schema 不可用时,使用原样导入、关系和计算字段完成允许的分析,不尝试不存在的改表命令。
39
+
40
+ 分析表 DSL 的 `.from(...)` 使用来源原始 ID;Widget 的物理字段使用回查确认的表名与字段名。保存后的计算字段用 `measureID("配置ID")`,不混入计算结果 fieldId。Dashboard 用 `.reference("已验证组件名")`。名称/ID 规则详见对应 DSL API,不跨命令套用。
41
+
42
+ 金额、日期或状态等清洗如需本地处理,应保持业务口径,保留原始文件和处理说明;不在 Excel 预填本应由 FineBI 验证的公式结果。分析目标涉及同比、累计、排名、DEF 或窗口函数时,先核对目标引擎与公式上下文,再验证分组、排序和过滤下的实际结果。
43
+
44
+ ## 3. 失败恢复
45
+
46
+ 首先读取 runner 的 `error` 和 `envelope.errors`,记录 `code/path/message`。只有新增证据支持时才重试,不重复同一失败请求。
47
+
48
+ | 失败阶段/证据 | 下一步 |
49
+ |---|---|
50
+ | 本地 ENOENT、EACCES、EPERM,尚未发请求 | 核对实际 Node 环境、路径及权限;不改文件格式或 HTTP 端点 |
51
+ | INVALID_ARGUMENT、Unknown option | inspect 目标命令;使用 `--file <path>`,不使用 `--file=<path>`;按实际 schema 修正 |
52
+ | INVALID_JSON | 核对该命令 fileContentType:JSON 需 JSON,TEXT 是 DSL 文本,BINARY 是原始文件;不把所有 `--file` 视作 JSON |
53
+ | 认证失败或缓存缺失/过期 | 使用内置 auth/config 状态命令;按需登录或刷新 capability,不重新安装 CLI |
54
+ | CLI_INVALID_JSON_OUTPUT | 记录入口、退出码和 stderr,定位进程输出异常;不据此改上传路径或绕过 CLI |
55
+ | NETWORK_ERROR、CLI_TIMEOUT、写响应丢失 | 写入结果未知;先按主题和资源查询回查,确认已创建/未创建后决定继续或重试;不自动重复 add |
56
+ | 平台参数/能力拒绝 | 根据返回错误修正对应输入;缺少目标能力则报告限制,不读取项目源码猜内部 API |
57
+ | UNSUPPORTED_FEATURE 且 `nextActions` 含 `measure copy` | 物理数值字段作维度的缺副本前置:按该命令注册副本(`measure copy --subject-id <S> --field-id <F> --as dimension`)后重跑原组件命令;不改写 DSL 规避 |
58
+
59
+ 上游有效资源可以保留并继续使用。修改失败阶段的输入后更新执行计划和文件摘要,无需重新导入全部数据。只有确需放弃资源才准备清理;包含 delete 时按删除确认契约执行。
60
+
61
+ ## 4. 验收与交付
62
+
63
+ 业务目标明确时继续完成生成、写入和回查,不为内部语义对象或临时文件路径增加审批。需要用户决定的仅是无法发现的输入、业务歧义或适用的删除确认。
64
+
65
+ 交付主题/仪表板实际链接、资源清单、计算字段口径、关键数字验证和未完成项。`preview`/DSL 回读只证明对应 API 结果;没有实际查看态证据时,不声称完成前端展示验收。分发包不依赖源码仓库里的 `tools/finebi-verify`,有可用浏览器工具时按用户验收范围补充查看态验证。
@@ -0,0 +1,102 @@
1
+ ## 二、公式基础语法
2
+
3
+ ### 字段引用语法(重要)
4
+
5
+ FineBI 公式内引用字段/参数 **统一使用 `${...}` 形式**(`$` 紧跟 `{}`,内容写在中间;不要写成 `$字段名` 或 `{字段名}`)。字段的 `${...}` 内部支持两种写法,**系统按是否含下划线 `_` 自动判别**(除字段外,`${...}` 还可用作组件参数引用,见下方"组件参数引用"):
6
+
7
+ | 写法 | 格式 | 出现场景 | 示例 |
8
+ |------|------|---------|------|
9
+ | **字段名形式** | `${字段名}` | 用户手写、AI 生成公式时统一用此形式 | `${销售额}`、`${合同付款类型}` |
10
+ | **字段ID形式** | `${<表ID>_<字段名>}` | 公式被保存时系统使用的形态;用户从界面选择字段时,系统会自动写成此形式 | `${ec032d15ef4a49cd8ac7df27cce5584b_购买付款}` |
11
+
12
+ **字段ID格式详解**:
13
+
14
+ - `<表ID>`:32 位十六进制字符串(数据集/表的 ID),例如 `ec032d15ef4a49cd8ac7df27cce5584b`
15
+ - `_`:分隔符(同时是系统判断"是否为字段ID"的依据——`${...}` 内含 `_` 即视为字段ID)
16
+ - `<字段名>`:原始字段名;中文在存储/序列化时常被 Unicode 转义为 `[xxxx]` 形式
17
+
18
+ > **易踩坑(AI 生成公式时注意)**:正因为系统以「是否含 `_`」判别字段ID形态,**字段名本身含下划线的引用会与这条规则冲突**——例如字段名是 `城市_代码`,写 `${城市_代码}` 会被当作字段ID(`<表ID>_<字段名>`)解析,可能无法命中字段。普通解释场景可提示该歧义;通过 `calculated-field validate/add/update` 校验或写入时,必须使用 `context` 字段目录返回的原始 `fieldName`,并按来源用 `.reference(...)` 或 `.modelReference(...)` 声明,服务端负责解析真实 ID。
19
+
20
+ **Unicode 转义识别**:存储形态下中文字段名常以 `[xxxx]` 出现,例如 `${ec032d15ef4a49cd8ac7df27cce5584b_[8d2d][4e70][6570][91cf]}`,其中 `[8d2d][4e70][6570][91cf]` 解码后即"购买付款"(0x8d2d=购、0x4e70=买、0x6570=付、0x91cf=款)。AI 解析时遇到 `[xxxx]` 序列应识别为 Unicode 转义的中文,按对应字段名解释语义即可。
21
+
22
+ **AI 使用指引**:
23
+ - **解析公式时**:遇到 `${<hex>_<name>}` 或 `${<hex>_[unicode]}` 形式,识别为字段引用,按 `<name>` 部分(必要时先解码 `[xxxx]`)解释字段含义;`<hex>` 是表ID,AI 通常无需关心。
24
+ - **生成公式时**:AI 不掌握具体的表ID,**统一用字段名形式 `${字段名}`**;系统接收后会自动补全为字段ID形态保存。
25
+ - 数组 `[ ]` 内的字段元素同样适用上述两种形式:`[${省份}, ${城市}]` 或 `[..., ${ec032d15..._省份}, ...]`。
26
+
27
+ **组件参数引用**:公式除了引用字段,还可以引用**组件参数**(组件上由参数控件绑定的参数)。引用形式与字段**完全一致**,也写 `${参数名}`,例如 `${最低销售额}`、`${开始日期}`。系统解析 `${...}` 时**先查组件参数表**:命中组件参数名 → 按"参数/常量"处理;未命中 → 再按字段处理(字段名或字段ID)。所以一个 `${...}` 到底是字段还是组件参数,**不看写法,取决于是否命中组件参数表**。
28
+
29
+ - **含义**:引用组件参数 = 把该参数控件**当前选中的值**代入计算;控件值变化时公式结果随之变化。
30
+ - **求值**:公式在组件内计算时,组件参数会用控件当前值替换——文本/数字参数替换为字面量值,日期参数替换为对应日期;未命中参数表的 `${...}` 保持为字段引用、按行取值。
31
+ - **校验**:组件参数在类型/聚合校验中被当作"常量"(见规则 9 常量类),可安全出现在聚合公式或非聚合公式中,如 `SUM_AGG(IF(${销售额} > ${最低销售额}, ${销售额}, 0))`、`CONCATENATE(${省份}, " / ", ${目标城市})`。
32
+ - **AI 生成公式时**:若需求涉及组件参数,用参数名 `${参数名}` 引用即可;**AI 解析时**要注意 `${...}` 也可能是组件参数,可结合公式所在组件的参数来判断。
33
+
34
+ ### CalculatedFieldModel JavaScript references(validate/add/update 必须遵守)
35
+
36
+ 当 Agent 生成 `CalculatedFieldModel` JavaScript 并调用 `calculated-field validate`、`add` 或 `update` 时,公式只写 `${字段名}`。主题物理字段和已保存计算字段用 `.reference(fieldName, position, tableNames)` 声明,BI 业务模型字段用 `.modelReference(fieldName, position, modelName)` 声明。同一份 `.js` 文件先用于 `validate`,通过后再用于 `add` 或 `update`。
37
+
38
+ 生成步骤:
39
+
40
+ 1. 已知 `subjectId` 时,先调用 `finebi-cli calculated-field context --subject-id <subjectId> --json` 获取 JavaScript 骨架、当前可用字段目录和引用规则。
41
+ 2. 只能引用当前主题中已经存在的物理字段、计算字段或 `businessModelFields` 中的 BI 业务模型字段;`fieldName` 和 `modelName` 必须与目录名称完全一致,区分大小写。
42
+ 3. 生成最终公式后,从左到右扫描所有 `${字段名}`,为每次出现分配从 `0` 开始的 `position`。
43
+ 4. 为每个位置按目录来源追加声明:主题字段用 `.reference(fieldName, position, tableNames)`;已有计算字段原样复制 `availableReferences.calculatedFields[]` 对应条目的 `tableNames`;模型字段用 `.modelReference(fieldName, position, modelName)`。
44
+ 5. 调用 `.build()` 前,确保公式中的每个字段出现位置都恰好有一条来源声明,且没有多余声明。
45
+
46
+ ```javascript
47
+ CalculatedFieldModel.builder("销售额差异")
48
+ .formula("${销售额} - ${销售额} - ${目标销售额}")
49
+ .reference("销售额", 0, ["订单表"])
50
+ .reference("销售额", 1, ["退货表"])
51
+ .modelReference("目标销售额", 2, "销售指标集")
52
+ .build();
53
+ ```
54
+
55
+ 将 DSL 保存为文本文件后调用:
56
+
57
+ ```powershell
58
+ finebi-cli calculated-field validate --subject-id <subjectId> --file <calculated-field.js>
59
+ finebi-cli calculated-field add --subject-id <subjectId> --file <calculated-field.js>
60
+ finebi-cli calculated-field update --subject-id <subjectId> --id <configId> --file <calculated-field.js>
61
+ ```
62
+
63
+ `fieldName` 和 `modelName` 不是 Agent 自定义的别名;`.reference(...)` 只填写 `fieldName`、`position`、`tableNames`,`.modelReference(...)` 只填写 `fieldName`、`position`、`modelName`,都不填写表 ID、字段 ID、模型 ID、计算字段配置 ID 或其他平台内部 ID。相同字段名可以重复出现在公式中,但必须通过不同 `position` 和来源身份绑定;如果名称组合仍对应多个现有资源,停止生成并报告歧义。
64
+
65
+ 其他字段写法(兼容但少用):
66
+
67
+ ```
68
+ 字段名裸写 仅当名称仅由字母/数字/下划线/中文组成、且不与函数名/关键字冲突时可用:销售额、单价
69
+ `字段名` 字段名含空格、运算符等特殊字符时用反引号包裹:`客户-名称`、`销售 额`
70
+ ```
71
+
72
+ > 注意:方括号 `[ ]` **不是字段引用**,而是数组字面量(见下)。数组里如果元素是字段,元素本身仍写成 `${字段名}`:`[${省份}, ${城市}]`。
73
+
74
+ ### 字面量
75
+
76
+ ```
77
+ 数字:42、3.14、-10、.5、1e3
78
+ 字符串:"北京"(见规则 6);\ 可转义
79
+ 空值:NULL 或 null
80
+ 布尔:TRUE/FALSE(也可小写 true/false)
81
+ ```
82
+
83
+ ### 运算符(按优先级从高到低)
84
+
85
+ ```
86
+ 算术:一元 - / + → ^(乘方)→ * / / / %(取余)→ + / -
87
+ 比较:= 或 ==(相等)| != 或 <>(不等)| < | <= | > | >= | IS DISTINCT FROM | IS NOT DISTINCT FROM
88
+ 逻辑:!(非)→ && 或 &(且)→ || 或 |(或);括号可改变优先级
89
+ 注意:没有 || 拼接;拼接用 CONCATENATE
90
+ ```
91
+
92
+ ### 函数调用与其他语法
93
+
94
+ ```
95
+ 函数名(参数1, 参数2, ...) 参数用逗号分隔,可写空参(如 TODAY())
96
+ @函数名(...) 自定义函数名可带 @ 前缀
97
+ 数组字面量:[a, b, c] 空数组:[] 或 EMPTYLIST
98
+ LOD 表达式(仅直连标准版):{FIXED|INCLUDE|EXCLUDE 维度(,...): 聚合表达式},详见函数库"分析函数"节
99
+ 内部引用占位符:$<编号>$ 如 $0$、$1$,由系统解析时把 ${...} 替换而成,用户/AI 不直接写
100
+ ```
101
+
102
+ ---
@@ -0,0 +1,66 @@
1
+ ## 六、公式使用场景
2
+
3
+ ### 公式适用的位置
4
+
5
+ | 场景 | 用途 | 示例 |
6
+ |------|------|------|
7
+ | 新增计算字段(计算指标 / 快速计算) | 基于已有字段生成新字段 | `SUM_AGG(${销售额}) / SUM_AGG(${销量})` |
8
+ | 筛选 / 过滤条件 | 数据按公式结果筛选 | `${销售额} > 1000 && ${地区} = "华东"` |
9
+ | 聚合指标 | 新增汇总指标 | `SUM_AGG(${销售额})`、`COUNTD_AGG(${会员ID})` |
10
+ | 分析指标 | def / window / 排名等跨行计算 | `DEF(SUM_AGG(${销售额}), [${省份}], [])` |
11
+
12
+ 字段统一用 `${字段名}`,字符串常量双引号,生成时函数名大写。语法相似不代表各位置支持相同函数:新增公式列、明细表、组件聚合视图与标准主题的限制见 [官方使用边界](formula-help-boundaries.md#1-位置主题和版本)。
13
+
14
+ ### 多指标协作(计算字段引用其他计算字段)
15
+
16
+ 复杂分析常拆成多个计算指标互相引用,逐层组合:
17
+
18
+ - 每个计算指标都是独立字段,下游指标用 `${指标名}` 引用其结果。
19
+ - **引用链深度受系统配置上限约束**(见"组件模型层附加约束"),不要无限套娃;链路过深会触发模型层校验报错。
20
+ - **引用字段的校验层性质由其公式决定**:DEF 家族 / LOD 结果字段是 disAgg,普通聚合指标字段是 agg,常量字段/组件参数是 dual——下游公式据此判断混用合法性(见规则 9)。
21
+ - 例(一组互相引用的指标,最终算"完成利润目标的商品占比"):
22
+ - `目标差额 = DEF_ADD(SUM_AGG(${利润}), []) - DEF_ADD(SUM_AGG(${利润目标}), [])`
23
+ - `完成利润目标的商品数 = DEF(COUNTD_AGG(${产品}), [${省份}], ${目标差额} > 0)`(过滤引用上一行的差额字段,差额字段是 disAgg,合法)
24
+ - `总商品数 = DEF(COUNTD_AGG(${产品}), [${省份}])`
25
+ - `占比 = SUM_AGG(${完成利润目标的商品数}) / SUM_AGG(${总商品数})`
26
+ - **分类标签模式**(RFM、盈亏、等级划分等分桶场景):先用 DEF 家族把粒度固定到明细级(如按天),再用 IF 引用该字段分桶打标签。
27
+ - `每天利润 = DEF(SUM_AGG(${利润}), [${订单日期}])`
28
+ - `盈利标签 = IF(${每天利润} > 2000, "高盈利", IF(${每天利润} < 0, "亏损", "盈利"))`
29
+ - 合法性:DEF 结果字段是 disAgg,IF 与字面量(dual) 同级,dual 可跨界,不触发混用报错(见规则 9);标签字段整体仍是 disAgg,可作为维度拖入组件,再用聚合指标(如 `COUNTD_AGG(${订单日期})`)按标签分桶统计。
30
+
31
+ ### 字段引用
32
+
33
+ - **统一使用 `${...}` 形式引用字段**,区分大小写(`${Sales}` 与 `${sales}` 不同)。详细规则见"字段引用语法"小节。
34
+ - **解析用户公式时**:`${...}` 内若含下划线 `_`,则是字段ID形态 `${<表ID>_<字段名>}`(中文部分可能被 Unicode 转义为 `[xxxx]`),按字段名解释语义即可。`${...}` 也可能是**组件参数**(组件上参数控件绑定的参数),系统先按组件参数表匹配,未命中再按字段处理。
35
+ - **生成公式时**:AI 不掌握具体的表ID,统一用字段名形态 `${字段名}`;系统接收后会自动转为字段ID形态保存。涉及组件参数时用参数名 `${参数名}`,等价于把控件当前选中值代入计算。
36
+ - **生成 CalculatedFieldModel JavaScript 时**:在最终公式确定后,按字段出现顺序和目录来源生成 `.reference(fieldName, position, tableNames)` 或 `.modelReference(fieldName, position, modelName)`,不填写任何 ID;同一份 JavaScript 用于 `calculated-field validate` 和后续的 `add` 或 `update`。
37
+ - 重命名后的字段用新名字引用。
38
+ - 公式里引用字段会覆盖该字段原值:用新计算列名承接结果,避免覆盖原字段。
39
+ - 数组参数(def 的维度/过滤、window 的分组/排序)用 `[元素1, 元素2]` 表达,元素本身是字段时仍写成 `${字段名}`,例如 `[${省份}, ${城市}]`。
40
+
41
+ ### 空值安全
42
+
43
+ - 除法等运算先处理空值,避免结果不确定:
44
+ - `NVL(${成本}, 0)` → 先替换空值再计算
45
+ - `IF(ISNULL(${成本}), 0, ${销售额} / ${成本})` → 显式判空
46
+ - `IFERROR(${销售额} / ${成本}, 0)` → 除零 / 无穷等异常兜底(注意 IFERROR 不兜空值)
47
+ - `ISNULL` 判定的是"所有值全为 NULL 或空字符串"。
48
+
49
+ ### 布尔返回值
50
+
51
+ - 判断结果的类型与数值化写法见 [语法规则 3](formula-syntax.md);需要算术计数时显式使用 `IF(条件,1,0)`。
52
+ - 聚合函数与判断函数的返回值类型差异:`MAX_AGG / MIN_AGG` 等在 `def` 中可用于非数值字段,其余聚合一般要求数值字段。
53
+
54
+ ### 引擎差异速查
55
+
56
+ | 函数 | 可用引擎 |
57
+ |------|---------|
58
+ | `FACT`、`DAYS360`、`LUNAR`、`NUMTO`、`PROPER`、`SPLIT`、`SUBSTITUTE`、`WINDOW_MEDIAN` | 仅 Spider(抽取数据) |
59
+ | `APPROX_COUNTD_AGG`、`PERIOD_TD`、`TOSTRING` | 仅 Direct(直连数据) |
60
+ | `FIXED`、`INCLUDE`、`EXCLUDE`(LOD 表达式) | 仅直连标准版(Direct 引擎的业务细分形态) |
61
+ | `DEF`/`DEF_ADD`/`DEF_SUB`、`EARLIER`/`CLEAN`/`CLEAN_WIDGET`、`WINDOW_*` 系列、`RANK_AGG`、`ADD_DIM`/`SUB_DIM`/`FIELD_IN` | 自助主题按面板与数据库支持选用;标准主题不手动书写。跨粒度可用 LOD,跨行错期不能默认替代 |
62
+ | 其余候选函数 | 两种引擎常有同名函数,但重载、数据库支持和负数行为仍可能不同;见 [官方使用边界](formula-help-boundaries.md) |
63
+
64
+ > 跨引擎迁移不仅检查函数是否存在,也检查参数个数、类型和负数语义;按 [官方使用边界](formula-help-boundaries.md) 核对目标主题,不能把两类主题的函数组合成一个表达式。
65
+
66
+ ---