@dazitech/cli 3.0.7 → 3.0.9

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 (70) hide show
  1. package/README.md +1 -1
  2. package/dist/clis/dazi-app.js +1 -1
  3. package/dist/clis/dazi-flow.js +1 -1
  4. package/dist/clis/dazi-onto.js +73 -22
  5. package/dist/clis/dazi.js +266 -171
  6. package/dist/docs/flow/flow-project-guide.md +1 -1
  7. package/dist/docs/guides/quickstart.md +18 -4
  8. package/dist/docs/guides/troubleshooting.md +12 -1
  9. package/dist/docs/guides/workspace-v3.md +43 -23
  10. package/dist/docs/index.json +28 -3
  11. package/dist/docs/onto/action-guide.md +3 -3
  12. package/dist/docs/onto/dazi_script_sdk_reference.md +244 -174
  13. package/dist/docs/onto/dazi_script_seed_data_guide.md +158 -155
  14. package/dist/docs/onto/function-guide.md +82 -27
  15. package/dist/docs/onto/space-management.md +3 -1
  16. package/dist/docs/onto//346/234/254/344/275/223/345/210/206/347/261/273/350/247/204/345/210/222/344/270/216SDK/346/211/251/345/261/225/346/226/271/346/241/210.md +168 -0
  17. package/dist/docs/onto//346/234/254/344/275/223/345/221/275/345/220/215/350/247/204/350/214/203_/347/211/251/347/220/206/350/241/250Cube/344/270/216/345/257/271/350/261/241.md +402 -0
  18. package/dist/docs/onto//346/234/254/344/275/223/350/204/232/346/234/254/347/274/226/345/206/231/346/214/207/345/215/227.md +200 -34
  19. package/dist/docs/onto//346/234/254/344/275/223/350/247/204/345/210/222/346/214/207/345/215/227.md +188 -38
  20. package/dist/docs/onto//350/204/232/346/234/254/350/277/220/350/241/214/347/272/240/351/224/231_/345/225/206/345/212/241/346/210/220/346/234/254/346/226/271/346/241/210/345/274/200/345/217/221/350/277/207/347/250/213.md +213 -0
  21. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/344/272/247/345/223/201/351/224/200/345/224/256/346/234/254/344/275/223/350/247/204/345/210/222/346/226/271/346/241/210.md +620 -0
  22. package/dist/docs/onto//350/247/204/345/210/222/347/244/272/344/276/213_/345/210/251/346/266/246/345/210/206/346/236/220/346/234/254/344/275/223/346/226/271/346/241/210.md +680 -541
  23. package/dist/examples/index.json +208 -22
  24. package/dist/examples/onto/README.md +51 -0
  25. package/dist/examples/onto/_templates/ontology_function_template.py +50 -0
  26. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_account_breakdown.py +62 -0
  27. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_budget_vs_actual.py +69 -0
  28. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_cost_center_profit.py +64 -0
  29. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_get_summary.py +61 -0
  30. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_mom_analysis.py +82 -0
  31. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_top_accounts.py +61 -0
  32. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/profit_fn_yoy_analysis.py +79 -0
  33. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
  34. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.account_breakdown.json +1 -0
  35. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.budget_vs_actual.json +1 -0
  36. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.cost_center_profit.json +1 -0
  37. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.get_summary.json +1 -0
  38. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.mom_analysis.json +1 -0
  39. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.top_accounts.json +1 -0
  40. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/functions/test_arguments/profit.fn.yoy_analysis.json +1 -0
  41. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_ontology_init.py +679 -0
  42. package/dist/examples/onto//345/210/251/346/266/246/347/244/272/344/276/213/setup/profit_seed_data.py +216 -0
  43. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_channel_mix.py +89 -0
  44. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_customer_segmentation.py +121 -0
  45. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_get_summary.py +78 -0
  46. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_mom_analysis.py +89 -0
  47. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_region_breakdown.py +84 -0
  48. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_top_products.py +98 -0
  49. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/sales_fn_yoy_analysis.py +87 -0
  50. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/save_test_arguments.ps1 +38 -0
  51. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.channel_mix.json +5 -0
  52. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.customer_segmentation.json +5 -0
  53. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.get_summary.json +5 -0
  54. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.mom_analysis.json +5 -0
  55. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.region_breakdown.json +5 -0
  56. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.top_products.json +5 -0
  57. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/functions/test_arguments/sales.fn.yoy_analysis.json +5 -0
  58. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_ontology_init.py +539 -0
  59. package/dist/examples/onto//351/224/200/345/224/256/347/244/272/344/276/213/setup/sales_seed_data.py +163 -0
  60. package/dist/prompts/index.json +2 -2
  61. package/dist/prompts/onto/action-design.md +4 -1
  62. package/dist/prompts/onto/function-design.md +46 -19
  63. package/dist/prompts/onto/rule-seed.md +5 -1
  64. package/dist/prompts/onto/script-publish-run.md +87 -25
  65. package/package.json +1 -1
  66. package/dist/examples/onto/function/profit_fn_customer_segmentation.py +0 -117
  67. package/dist/examples/onto/function/profit_fn_mom_analysis.py +0 -89
  68. package/dist/examples/onto/function/profit_fn_top_products.py +0 -89
  69. package/dist/examples/onto/function/profit_fn_yoy_analysis.py +0 -89
  70. package/dist/examples/onto/setup/profit_ontology_init.py +0 -388
@@ -0,0 +1,163 @@
1
+ """产品销售演示数据灌入 — space__misc_01
2
+
3
+ 前置:先执行 sales_ontology_init.py 建表。
4
+ 幂等:fact_sales_order_line 已有数据则跳过。
5
+
6
+ 放置:项目/DAZI_TEST/本体/ontos/销售本体示例/setup/sales_seed_data.py
7
+ 发布:dazi onto script publish 项目/DAZI_TEST/本体/ontos/销售本体示例/setup/sales_seed_data.py --space space__misc_01 --type data
8
+ """
9
+
10
+ import json
11
+ import random
12
+ from datetime import date, datetime, timedelta
13
+
14
+
15
+ _SEED_DT = datetime(2025, 1, 1, 0, 0, 0)
16
+
17
+
18
+ def main():
19
+ space_id = "space__misc_01"
20
+ s = space.get(space_id)
21
+
22
+ output.print("=== 产品销售演示数据灌入 ===")
23
+
24
+ # 检查是否已有数据
25
+ try:
26
+ n = int(s.sql.query_one("SELECT count() FROM fact_sales_order_line") or 0)
27
+ except Exception:
28
+ n = 0
29
+ if n > 0:
30
+ output.print(f"fact_sales_order_line 已有 {n} 行,跳过灌数")
31
+ output.print("__JSON_SUMMARY__" + json.dumps({"ok": True, "skipped": True, "rows": n}, ensure_ascii=False))
32
+ return
33
+
34
+ # 1. 灌入 dim_date
35
+ output.print("\n[1/4] 灌入 dim_date...")
36
+ dim_date_rows = []
37
+ start_date = date(2025, 1, 1)
38
+ end_date = date(2026, 6, 30)
39
+ current = start_date
40
+ while current <= end_date:
41
+ date_key = int(current.strftime("%Y%m%d"))
42
+ year = current.year
43
+ month = current.month
44
+ quarter = (month - 1) // 3 + 1
45
+ week_of_year = current.isocalendar()[1]
46
+ day_of_week = current.weekday()
47
+ is_weekend = 1 if day_of_week >= 5 else 0
48
+ year_month = current.strftime("%Y-%m")
49
+ dim_date_rows.append({
50
+ "date_key": date_key,
51
+ "calendar_date": current,
52
+ "year": year,
53
+ "quarter": quarter,
54
+ "month": month,
55
+ "week_of_year": week_of_year,
56
+ "day_of_week": day_of_week,
57
+ "is_weekend": is_weekend,
58
+ "year_month": year_month,
59
+ })
60
+ current += timedelta(days=1)
61
+ s.sql.insert_rows("dim_date", dim_date_rows)
62
+ output.print(f"OK dim_date 插入 {len(dim_date_rows)} 行")
63
+
64
+ # 2. 灌入维表
65
+ output.print("\n[2/4] 灌入维表...")
66
+
67
+ products = [
68
+ {"product_id": "P001", "product_code": "OIL-92", "product_name": "92号汽油", "product_category": "成品油", "product_subcategory": "汽油", "brand": "潘达", "unit": "吨", "list_price": 8200.0, "cost_price": 7100.0, "status": "在售"},
69
+ {"product_id": "P002", "product_code": "OIL-0", "product_name": "0号柴油", "product_category": "成品油", "product_subcategory": "柴油", "brand": "潘达", "unit": "吨", "list_price": 7500.0, "cost_price": 6500.0, "status": "在售"},
70
+ {"product_id": "P003", "product_code": "CHEM-LLDPE", "product_name": "线性低密度聚乙烯", "product_category": "化工品", "product_subcategory": "聚乙烯", "brand": "潘达", "unit": "吨", "list_price": 9200.0, "cost_price": 8100.0, "status": "在售"},
71
+ {"product_id": "P004", "product_code": "CHEM-PP", "product_name": "聚丙烯PP", "product_category": "化工品", "product_subcategory": "聚丙烯", "brand": "潘达", "unit": "吨", "list_price": 8800.0, "cost_price": 7800.0, "status": "在售"},
72
+ {"product_id": "P005", "product_code": "LUB-BASE", "product_name": "润滑油基础油", "product_category": "润滑油", "product_subcategory": "基础油", "brand": "潘达", "unit": "吨", "list_price": 6800.0, "cost_price": 5900.0, "status": "在售"},
73
+ {"product_id": "P006", "product_code": "COKE-1", "product_name": "石油焦", "product_category": "副产品", "product_subcategory": "石油焦", "brand": "潘达", "unit": "吨", "list_price": 3200.0, "cost_price": 2600.0, "status": "在售"},
74
+ ]
75
+ for p in products:
76
+ p["created_at"] = _SEED_DT
77
+ p["updated_at"] = _SEED_DT
78
+
79
+ customers = [
80
+ {"customer_id": "C001", "customer_code": "CUS-001", "customer_name": "华东油品经销", "customer_region": "华东", "customer_type": "战略", "industry": "经销"},
81
+ {"customer_id": "C002", "customer_code": "CUS-002", "customer_name": "华北炼化贸易", "customer_region": "华北", "customer_type": "VIP", "industry": "贸易"},
82
+ {"customer_id": "C003", "customer_code": "CUS-003", "customer_name": "华南化工采购", "customer_region": "华南", "customer_type": "VIP", "industry": "化工"},
83
+ {"customer_id": "C004", "customer_code": "CUS-004", "customer_name": "西南区域物流", "customer_region": "西南", "customer_type": "普通", "industry": "物流"},
84
+ {"customer_id": "C005", "customer_code": "CUS-005", "customer_name": "华中制造集团", "customer_region": "华中", "customer_type": "普通", "industry": "制造"},
85
+ ]
86
+ for c in customers:
87
+ c["created_at"] = _SEED_DT
88
+
89
+ channels = [
90
+ {"channel_id": "CH01", "channel_code": "DIR", "channel_name": "直销", "channel_type": "直销"},
91
+ {"channel_id": "CH02", "channel_code": "DIST", "channel_name": "经销渠道", "channel_type": "经销"},
92
+ {"channel_id": "CH03", "channel_code": "B2B", "channel_name": "B2B平台", "channel_type": "线上"},
93
+ ]
94
+
95
+ s.sql.insert_rows("dim_product", products)
96
+ s.sql.insert_rows("dim_customer", customers)
97
+ s.sql.insert_rows("dim_channel", channels)
98
+ output.print(f"OK dim_product {len(products)} 行, dim_customer {len(customers)} 行, dim_channel {len(channels)} 行")
99
+
100
+ # 3. 生成销售事实数据
101
+ output.print("\n[3/4] 生成销售事实数据...")
102
+ random.seed(627)
103
+ regions = {c["customer_id"]: c["customer_region"] for c in customers}
104
+ ctypes = {c["customer_id"]: c["customer_type"] for c in customers}
105
+ pcats = {p["product_id"]: p["product_category"] for p in products}
106
+ prices = {p["product_id"]: p["list_price"] for p in products}
107
+
108
+ fact_rows = []
109
+ line_seq = 1
110
+ days = (end_date - start_date).days + 1
111
+
112
+ for d_offset in range(days):
113
+ order_date = start_date + timedelta(days=d_offset)
114
+ if random.random() > 0.35:
115
+ continue
116
+ daily_orders = random.randint(2, 8)
117
+ for _ in range(daily_orders):
118
+ order_id = f"O{order_date.strftime('%Y%m%d')}{random.randint(100, 999)}"
119
+ customer_id = random.choice(list(regions.keys()))
120
+ channel_id = random.choice(["CH01", "CH02", "CH03"])
121
+ lines = random.randint(1, 3)
122
+ for _ in range(lines):
123
+ product_id = random.choice(list(pcats.keys()))
124
+ qty = random.randint(5, 200)
125
+ unit_price = round(prices[product_id] * random.uniform(0.92, 1.0), 2)
126
+ discount = round(unit_price * qty * random.uniform(0, 0.05), 2)
127
+ sales_amount = round(unit_price * qty - discount, 2)
128
+ date_key = int(order_date.strftime("%Y%m%d"))
129
+ fact_rows.append({
130
+ "order_id": order_id,
131
+ "order_line_id": f"L{line_seq:06d}",
132
+ "date_key": date_key,
133
+ "order_date": order_date,
134
+ "product_id": product_id,
135
+ "product_category": pcats[product_id],
136
+ "customer_id": customer_id,
137
+ "customer_region": regions[customer_id],
138
+ "customer_type": ctypes[customer_id],
139
+ "channel_id": channel_id,
140
+ "quantity": qty,
141
+ "unit_price": unit_price,
142
+ "discount_amount": discount,
143
+ "sales_amount": sales_amount,
144
+ "currency": "CNY",
145
+ "order_status": random.choice(["已完成", "已完成", "已发货"]),
146
+ })
147
+ line_seq += 1
148
+
149
+ s.sql.insert_rows("fact_sales_order_line", fact_rows)
150
+ output.print(f"OK fact_sales_order_line 插入 {len(fact_rows)} 行")
151
+
152
+ # 4. 完成
153
+ summary = {
154
+ "ok": True,
155
+ "space_id": space_id,
156
+ "dim_date": len(dim_date_rows),
157
+ "products": len(products),
158
+ "customers": len(customers),
159
+ "channels": len(channels),
160
+ "fact_inserted": len(fact_rows),
161
+ }
162
+ output.success("灌数完成")
163
+ output.print("__JSON_SUMMARY__" + json.dumps(summary, ensure_ascii=False, default=str))
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "3.0.5",
3
- "updatedAt": "2026-05-26T00:00:00.000Z",
2
+ "version": "3.0.9",
3
+ "updatedAt": "2026-06-05T00:00:00.000Z",
4
4
  "prompts": [
5
5
  {
6
6
  "id": "onto/script-publish-run",
@@ -40,10 +40,13 @@ def main(params: dict, context: dict, s=None) -> dict:
40
40
  return {"status": "ok"}
41
41
  ```
42
42
 
43
+ 脚本落盘路径:`项目/<业务名>/本体/ontos/<实现名>/setup/` 或 `functions/`(按脚本类型选择)。
44
+ `space_id` 取自 `ontos/<实现名>/README.md`。
45
+
43
46
  发布命令(v3;**勿用** `dazi-onto`):
44
47
 
45
48
  ```powershell
46
- dazi onto script publish 项目/onto_<名>/脚本/<file>.py --space <space-id> --register-action-id <action_code> --register-action-permission-tag "finance.write"
49
+ dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/<file>.py --space <space-id> --register-action-id <action_code> --register-action-permission-tag "finance.write"
47
50
  ```
48
51
 
49
52
  详见提示词 `onto/script-publish-run`。
@@ -7,40 +7,67 @@
7
7
 
8
8
  你是一名搭子平台本体工程师。请根据以下需求设计一个本体函数(ontology function)。
9
9
 
10
+ **执行脚本前必读**:`资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`(`onto/script-run-troubleshooting`)。
11
+ **必须复制结构**:`资源/examples/onto/_templates/ontology_function_template.py` 或 `销售示例/functions/sales_fn_get_summary.py`。
12
+
10
13
  ## 函数需求
11
14
 
12
15
  {{function_description}}
13
16
 
14
17
  ## 要求
15
18
 
16
- 1. 函数名使用 snake_case,以动词开头(如 `calc_balance`、`get_user_info`)
17
- 2. `main(params: dict) -> dict` 为入口函数
18
- 3. 参数通过 `params` 字典传入,输出通过 return 返回
19
- 4. 包含完整的 docstring(中文),说明:函数目的、参数说明、返回值说明
20
- 5. 包含基本的错误处理(try/except)
21
- 6. 引用的搭子数据空间对象通过 `s` 上下文访问
19
+ 1. 文件名 `snake_case`,如 `cost_fn_get_summary.py`;`function_id` 如 `domain.fn.get_summary`
20
+ 2. **`def main():` 无参** — 禁止 `main(params: dict)`;入参来自 `ctx.params`
21
+ 3. 业务逻辑写在 **`_ontology_fn_body(p)`**;`main()` 末尾 **`return _ontology_fn_body(p)`**
22
+ 4. 输出使用 **`return p.function_result(columns=..., data=..., row_count=...)`** — **禁止** `output.print_json()`、`print()` 作为平台输出
23
+ 5. SQL 用 **`p.sql.query()`** 取多列;聚合勿误用 `query_one().get()`
24
+ 6. 脚本顶部 **`TEST_ARGUMENTS`** 与 `functions/test_arguments/<function_id>.json` 同步
25
+ 7. 中文 docstring:目的、参数、返回值、发布命令
22
26
 
23
- ## 输出格式
27
+ ## 输出格式(必须遵循)
24
28
 
25
29
  ```python
26
- def main(params: dict, s=None) -> dict:
27
- """
28
- [函数说明]
30
+ TEST_ARGUMENTS = {
31
+ "v": 1,
32
+ "arguments": { ... },
33
+ "object_type_code": "<ObjectTypeCode>",
34
+ }
35
+
29
36
 
30
- Args:
31
- params: 包含 xxx(说明)
37
+ def _ontology_fn_body(p):
38
+ params = dict(p.get_params() or {})
39
+ # ... p.sql.query(...)
40
+ return p.function_result(
41
+ columns=[...],
42
+ data=[{...}],
43
+ row_count=1,
44
+ )
32
45
 
33
- Returns:
34
- 包含 xxx(说明)
35
- """
36
- # 实现
46
+
47
+ def main():
48
+ s = space.get(ctx.space_id or "")
49
+ _Ports = type(
50
+ "_Ports",
51
+ (),
52
+ {
53
+ "get_params": lambda self: dict(ctx.params or {}),
54
+ "function_result": lambda self, **kw: onto.function_result(**kw),
55
+ },
56
+ )
57
+ p = _Ports()
58
+ p.sql = s.sql
59
+ return _ontology_fn_body(p)
37
60
  ```
38
61
 
39
- 发布与运行(v3,工作区根目录;**勿用** `dazi-onto`):
62
+ 脚本落盘:`项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py`
63
+
64
+ 发布与运行(dazi-work 根;**勿用** `dazi-onto`):
40
65
 
41
66
  ```powershell
42
- dazi onto script publish 项目/onto_<名>/脚本/functions/<file>.py --space <space-id> --register-function-id <id>
67
+ dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/functions/<file>.py --space <space-id> --register-function-id <id>
43
68
  dazi onto function run <id> --space <space-id>
69
+ dazi onto function save-test-arguments <ofn_internal_id> --space <space-id> `
70
+ --arguments-json-file 项目/<业务名>/本体/ontos/<实现名>/functions/test_arguments/<id>.json
44
71
  ```
45
72
 
46
- 详见提示词 `onto/script-publish-run`(侧栏 帮助 → 提示词)。
73
+ 详见 `onto/script-publish-run`、`onto/function-guide`。
@@ -13,6 +13,9 @@
13
13
 
14
14
  ## 种子脚本模板
15
15
 
16
+ 建议将种子脚本保存到 `项目/<业务名>/本体/ontos/<实现名>/setup/<stem>_seed_rules.py`(与同实现的 init/seed 脚本并列)。
17
+ `space_id` 取自 `ontos/<实现名>/README.md`。
18
+
16
19
  ```python
17
20
  def main(params: dict, s=None) -> dict:
18
21
  """
@@ -34,9 +37,10 @@ def main(params: dict, s=None) -> dict:
34
37
  return {"upserted": len(rules), "result": result}
35
38
  ```
36
39
 
37
- 执行命令(v3;**勿用** `dazi-onto`):
40
+ 发布与执行(v3;**勿用** `dazi-onto`):
38
41
 
39
42
  ```powershell
43
+ dazi onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/<stem>_seed_rules.py --space <space-id> --type data
40
44
  dazi onto rule run-seed --space <space-id> --stem <seed_file_stem>
41
45
  ```
42
46
 
@@ -6,7 +6,17 @@
6
6
 
7
7
  ---
8
8
 
9
- 你是搭子平台 **dazi-vscode v3** 本体实施助手。用户已完成或正在完成 `项目/onto_<名称>/规划/` 中的方案,你的任务是编写 `脚本/` 下 Python,并**用正确命令**发布与运行。
9
+ 你是搭子平台 **dazi-vscode v3** 本体实施助手。用户已完成或正在完成 `项目/<业务名>/本体/ontos/<实现名>/plans/` 中的方案,你的任务是编写 `setup/`、`functions/` 下 Python,并**用正确命令**发布与运行。
10
+
11
+ > **规划阶段提醒**:`plans/` 方案须含 **Cube 层独立章节**(维度/度量/派生度量、`bind_source` 对照)。若规划缺 Cube,**不得**开始 init 脚本;先补规划并对照 `本体规划指南.md` **§规划文档完整性自检清单**。
12
+
13
+ ## 执行前必读(强制)
14
+
15
+ 在终端执行 **`dazi onto script publish` / `script run` / `function run` / `save-test-arguments` 之前**,须阅读同步文档:
16
+
17
+ - **`资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`**(`dazi docs show onto/script-run-troubleshooting`)
18
+
19
+ 重点:setup API 勿猜方法名;多列聚合用 `query()` 勿误用 `query_one()`;**本体函数 `main()` 须 `return _ontology_fn_body(p)`,禁止 `output.print_json()`**;`function run` **不支持** `--arguments-json-file`;`save-test-arguments` 须 **`ofn_xxx` 内部 id**。
10
20
 
11
21
  ## 强制规则(违反会导致 CommandNotFound 或 404)
12
22
 
@@ -17,20 +27,35 @@
17
27
  - **仅开发仓库**:`node bundled/clis/dazi.js onto ...`(维护方打包用;客户环境用 `dazi.ps1`)
18
28
  - **完整路径**:`node <搭子仓库>/dazi/dazi-vscode/bundled/clis/dazi.js onto <子命令> ...`,并设置环境变量 `DAZI_BUNDLED_DIR=<同目录 bundled/clis>`
19
29
  4. **工作目录**必须是搭子工作区根(含 `项目/` 目录),例如 `dazi-work`。
20
- 5. **`space_id`** 只从 `项目/onto_<名称>/README.md` 的「数据空间 ID」读取,禁止猜测。
30
+ 5. **`space_id`** 只从 `项目/<业务名>/本体/ontos/<实现名>/README.md` 的「数据空间 ID」读取,禁止猜测。
21
31
 
22
32
  ## 工作区目录(v3)
23
33
 
24
34
  ```text
25
35
  <工作区根>/
26
- 项目/onto_<名称>/
27
- README.md space_id(权威)
28
- 规划/ ← 规划 Markdown(本阶段不 publish)
29
- 脚本/
30
- setup/ 初始化、灌数(先 init 后 seed)
31
- functions/ 本体函数
32
- 资源/docs/ ← dazi docs sync 后的指南
33
- scripts/ dazi.ps1 / dazi.cmd 包装(客户工作区模板应包含)
36
+ 项目/<业务名>/
37
+ README.md 业务项目元信息
38
+ 本体/
39
+ README.md ← 容器说明(onto-assets + ontos 索引)
40
+ onto-assets/ 平台元数据缓存(业务项目级,侧栏懒拉取)
41
+ objects/ functions/ actions/ rules/
42
+ ontos/
43
+ <实现名>/ 本体实现工作单元
44
+ README.md ← space_id(权威)
45
+ 快速启动_<实现名>.md ← 动态生成,含本实现命令模板
46
+ plans/ ← 规划 Markdown(本阶段不 publish)
47
+ setup/ ← 初始化、灌数(先 init 后 seed)
48
+ functions/ ← 本体函数
49
+ test_arguments/ ← 各 function_id 的默认测试入参 JSON
50
+ save_test_arguments.ps1 ← 可选:批量 save-test-arguments
51
+ 流程/
52
+ plans/
53
+ flows/<流程名>/...
54
+ 应用/
55
+ plans/
56
+ apps/<组件名>/...
57
+ 资源/docs/ ← dazi docs sync 后的指南
58
+ scripts/ ← dazi.ps1 / dazi.cmd 包装(客户工作区模板应包含)
34
59
  ```
35
60
 
36
61
  本地开发路径 **不是** `onto/<space_id>/editorial/`;发布时 CLI 自动映射为平台路径
@@ -49,24 +74,25 @@ dazi auth login
49
74
 
50
75
  ## 标准发布与运行顺序
51
76
 
52
- `<空间>`、`<项目路径>` 替换为实际值,例如 `space__0519`、`项目/onto_本体项目01`。
77
+ `<业务名>`、`<实现名>`、`<空间>` 替换为实际值,例如 `潘达石化`、`本体01`、`space__0519`。
78
+ 脚本路径前缀记为 `<项目路径>` = `项目/<业务名>/本体/ontos/<实现名>`。
53
79
 
54
80
  ### 1. 预检(可选)
55
81
 
56
82
  ```powershell
57
- dazi onto script publish-preview <项目路径>/脚本/setup/xxx_ontology_init.py --space <空间> --type setup
83
+ dazi onto script publish-preview <项目路径>/setup/xxx_ontology_init.py --space <空间> --type setup
58
84
  ```
59
85
 
60
86
  ### 2. 发布初始化脚本(setup)
61
87
 
62
88
  ```powershell
63
- dazi onto script publish <项目路径>/脚本/setup/xxx_ontology_init.py --space <空间> --type setup
89
+ dazi onto script publish <项目路径>/setup/xxx_ontology_init.py --space <空间> --type setup
64
90
  ```
65
91
 
66
92
  ### 3. 发布灌数脚本(data)
67
93
 
68
94
  ```powershell
69
- dazi onto script publish <项目路径>/脚本/setup/xxx_seed_data.py --space <空间> --type data
95
+ dazi onto script publish <项目路径>/setup/xxx_seed_data.py --space <空间> --type data
70
96
  ```
71
97
 
72
98
  ### 4. 发布本体函数(**必须** `--register-function-id`,否则函数列表看不到)
@@ -76,7 +102,7 @@ dazi onto script publish <项目路径>/脚本/setup/xxx_seed_data.py --space <
76
102
  发布后必须确认输出含 `functionId` / `function_registration.ok`,并执行 `function list` 能看到该 id。
77
103
 
78
104
  ```powershell
79
- dazi onto script publish <项目路径>/脚本/functions/xxx_fn_yyy.py --space <空间> --register-function-id <domain>.fn.<name>
105
+ dazi onto script publish <项目路径>/functions/xxx_fn_yyy.py --space <空间> --register-function-id <domain>.fn.<name>
80
106
  dazi onto function list --space <空间>
81
107
  ```
82
108
 
@@ -84,8 +110,8 @@ dazi onto function list --space <空间>
84
110
 
85
111
  ```powershell
86
112
  # 按 publish 返回的 scriptId,或:
87
- dazi onto script run --file <项目路径>/脚本/setup/xxx_ontology_init.py --space <空间>
88
- dazi onto script run --file <项目路径>/脚本/setup/xxx_seed_data.py --space <空间>
113
+ dazi onto script run --file <项目路径>/setup/xxx_ontology_init.py --space <空间>
114
+ dazi onto script run --file <项目路径>/setup/xxx_seed_data.py --space <空间>
89
115
  ```
90
116
 
91
117
  **顺序**:先 run init,再 run seed,再发布/运行函数。
@@ -96,6 +122,8 @@ dazi onto script run --file <项目路径>/脚本/setup/xxx_seed_data.py --space
96
122
  dazi onto function run <function_id> --space <空间>
97
123
  ```
98
124
 
125
+ > **`function run` 不支持 `--arguments-json-file`**。须先用 §7 `save-test-arguments` 写入默认参数,再不带 JSON 文件运行。误用示例见 `资源/docs/onto/脚本运行纠错_商务成本方案开发过程.md`(`onto/script-run-troubleshooting`)。
126
+
99
127
  PowerShell 传 JSON 参数易丢引号;可省略 `--params` 使用函数默认,或:
100
128
 
101
129
  ```powershell
@@ -103,26 +131,58 @@ $env:DAZI_PARAMS='{"start_date":"2025-01-01","end_date":"2026-12-31"}'
103
131
  dazi onto function run <function_id> --space <空间> --params $env:DAZI_PARAMS
104
132
  ```
105
133
 
134
+ ### 7. 保存测试参数(test_arguments,**发布后必做**)
135
+
136
+ `publish` + `function run` **不会**自动写入测试参数。侧栏 **Onto → 运行函数** 依赖函数定义上的 **`test_arguments`** 预填表单;未保存时侧栏参数为空。
137
+
138
+ **本地约定**(与 `plans/` 规划文档一致):
139
+
140
+ 1. 每个函数一份 JSON:`<项目路径>/functions/test_arguments/<function_id>.json`
141
+ 2. 脚本内常量 `TEST_ARGUMENTS` 与 JSON **保持同步**
142
+ 3. JSON 格式:`{"v":1,"arguments":{...},"object_type_code":"<ObjectTypeCode>"}`
143
+
144
+ **保存到平台**(`function run` 验证通过后):
145
+
146
+ ```powershell
147
+ # 推荐:批量脚本(解析 function list,用内部 id ofn_xxx 调用 PATCH)
148
+ .\<项目路径>\functions\save_test_arguments.ps1
149
+ ```
150
+
151
+ 单条保存(**须用平台内部 id `ofn_xxx`**,先 `dazi onto function list --space <空间>` 查看;直接传 `sales.fn.xxx` 等 function_id 字符串可能 **404**):
152
+
153
+ ```powershell
154
+ dazi onto function save-test-arguments <ofn_internal_id> --space <空间> `
155
+ --arguments-json-file <项目路径>/functions/test_arguments/<function_id>.json
156
+ ```
157
+
158
+ 验收:
159
+
160
+ ```powershell
161
+ dazi onto function get <function_id> --space <空间>
162
+ # 确认 test_arguments 非 null,且 arguments 与 JSON 一致
163
+ ```
164
+
106
165
  ## 脚本类型与路径推断
107
166
 
108
- | 本地路径特征 | `--type` | 平台目录 |
109
- | ---------------------- | -------------------------- | --------------------- |
110
- | `脚本/setup/*init*.py` | `setup` | `setup/` |
111
- | `脚本/setup/*seed*.py` | `data` | `data/` |
112
- | `脚本/functions/*.py` | (默认 ontology_function) | `ontology_functions/` |
167
+ | 本地路径特征 | `--type` | 平台目录 |
168
+ | ------------------------- | -------------------------- | --------------------- |
169
+ | `.../setup/*init*.py` | `setup` | `setup/` |
170
+ | `.../setup/*seed*.py` | `data` | `data/` |
171
+ | `.../functions/*.py` | (默认 ontology_function) | `ontology_functions/` |
113
172
 
114
173
  ## 编写脚本时注意
115
174
 
175
+ - **本体函数**:复制 `资源/examples/onto/_templates/ontology_function_template.py`;`def main():` 无参,末尾 **`return _ontology_fn_body(p)`**;结果用 **`p.function_result(columns=..., data=..., row_count=...)`**。**禁止** `output.print_json()`、`main(params: dict)`。详见 `function-guide.md`、`onto/script-run-troubleshooting` §3。
116
176
  - 多列 SQL 聚合用 `s.sql.query()` 取 `rows[0]`,**勿**用 `query_one()`(只返回首列标量)。
117
177
  - ClickHouse:`DateTime` 用 `datetime` 对象,`Date` 用 `date` 对象;见同步文档 `dazi_script_seed_data_guide.md`。
118
- - 入口为 `main()`;函数脚本通过 `ctx.space_id`、`ctx.params` 与 `onto.function_result` 返回。
178
+ - setup/seed 入口为 `main()`;函数脚本通过 `ctx.space_id`、`ctx.params` 与 `onto.function_result` 返回。
119
179
 
120
180
  ## 无 `scripts/dazi.ps1` 时的等价命令
121
181
 
122
182
  ```powershell
123
183
  $env:DAZI_BUNDLED_DIR = "<绝对路径>/dazi/dazi-vscode/bundled/clis"
124
184
  cd <工作区根>
125
- node "<绝对路径>/dazi/dazi-vscode/bundled/clis/dazi.js" onto script publish <相对路径> --space <空间> --type setup
185
+ node "<绝对路径>/dazi/dazi-vscode/bundled/clis/dazi.js" onto script publish 项目/<业务名>/本体/ontos/<实现名>/setup/xxx_ontology_init.py --space <空间> --type setup
126
186
  ```
127
187
 
128
188
  VS Code 用户也可在侧栏 **搭子 → Onto 本体** 使用「发布脚本」「运行函数」,无需终端。
@@ -136,11 +196,13 @@ VS Code 用户也可在侧栏 **搭子 → Onto 本体** 使用「发布脚本
136
196
  - `本体规划指南.md`、`本体脚本编写指南.md`
137
197
  - `dazi_script_sdk_reference.md`、`dazi_script_seed_data_guide.md`
138
198
  - `function-guide.md`
199
+ - `脚本运行纠错_商务成本方案开发过程.md`(API / CLI 踩坑实录)
139
200
 
140
201
  ---
141
202
 
142
203
  **用户补充上下文(可选)**:
143
204
 
144
- - 项目名称:{{project_name}}
205
+ - 业务项目:{{business_name}}
206
+ - 本体实现:{{item_name}}
145
207
  - 数据空间 ID:{{space_id}}
146
208
  - 当前任务:{{task}}(如:发布 training_ontology_init 并执行)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dazitech/cli",
3
- "version": "3.0.7",
3
+ "version": "3.0.9",
4
4
  "description": "搭子 v3 命令行工具(Onto / Flow / App),与 dazi-vscode 同版本",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -1,117 +0,0 @@
1
- """Customer Segmentation Function
2
-
3
- 功能:基于利润贡献对客户进行分层
4
- 参数:metric, method, start_date, end_date
5
- 返回:客户分层数据(VIP/High/Medium/Low)
6
-
7
- 放置位置:spaces/space__profit0520/editorial/scripts/ontology_functions/profit_fn_customer_segmentation.py
8
- 检索关键字:profit customer_segmentation
9
- """
10
-
11
-
12
- def _ontology_fn_body(p):
13
- params = dict(p.get_params() or {})
14
-
15
- metric = params.get("metric", "profit")
16
- method = params.get("method", "quartile")
17
- start_date = params.get("start_date", "")
18
- end_date = params.get("end_date", "")
19
-
20
- where_clause = ""
21
- if start_date and end_date:
22
- where_clause = f"WHERE order_date >= '{start_date}' AND order_date <= '{end_date}'"
23
-
24
- sql = f"""
25
- SELECT
26
- customer_id,
27
- customer_region,
28
- sum(revenue) as revenue,
29
- sum(cost) as cost,
30
- sum(revenue) - sum(cost) as profit
31
- FROM profit_analysis_fact
32
- {where_clause}
33
- GROUP BY customer_id, customer_region
34
- ORDER BY profit DESC
35
- """
36
-
37
- result = p.sql.query(sql)
38
-
39
- if not result:
40
- return p.function_result(
41
- columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
42
- data=[],
43
- row_count=0
44
- )
45
-
46
- profits = [float(row.get("profit", 0) or 0) for row in result]
47
- total = len(profits)
48
-
49
- if total == 0:
50
- return p.function_result(
51
- columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
52
- data=[],
53
- row_count=0
54
- )
55
-
56
- if method == "percentile":
57
- sorted_profits = sorted(profits, reverse=True)
58
- p80_idx = min(int(total * 0.8), total - 1)
59
- p50_idx = min(int(total * 0.5), total - 1)
60
- p20_idx = min(int(total * 0.2), total - 1)
61
- p80 = sorted_profits[p80_idx]
62
- p50 = sorted_profits[p50_idx]
63
- p20 = sorted_profits[p20_idx]
64
- else:
65
- sorted_profits = sorted(profits, reverse=True)
66
- q1_idx = min(int(total * 0.25), total - 1)
67
- q2_idx = min(int(total * 0.5), total - 1)
68
- q3_idx = min(int(total * 0.75), total - 1)
69
- p80 = sorted_profits[q1_idx]
70
- p50 = sorted_profits[q2_idx]
71
- p20 = sorted_profits[q3_idx]
72
-
73
- def get_segment(profit_val):
74
- if profit_val >= p80:
75
- return "VIP"
76
- elif profit_val >= p50:
77
- return "High"
78
- elif profit_val >= p20:
79
- return "Medium"
80
- else:
81
- return "Low"
82
-
83
- data = []
84
- for row in result:
85
- profit = float(row.get("profit", 0) or 0)
86
- data.append({
87
- "customer_id": str(row.get("customer_id", "")),
88
- "customer_region": str(row.get("customer_region", "")),
89
- "revenue": round(float(row.get("revenue", 0) or 0), 2),
90
- "cost": round(float(row.get("cost", 0) or 0), 2),
91
- "profit": round(profit, 2),
92
- "segment": get_segment(profit),
93
- })
94
-
95
- return p.function_result(
96
- columns=["customer_id", "customer_region", "revenue", "cost", "profit", "segment"],
97
- data=data,
98
- row_count=len(data)
99
- )
100
-
101
-
102
- def main():
103
- s = space.get(ctx.space_id or "")
104
-
105
- _Ports = type(
106
- "_Ports",
107
- (),
108
- {
109
- "get_params": lambda self: dict(ctx.params or {}),
110
- "function_result": lambda self, **kw: onto.function_result(**kw),
111
- },
112
- )
113
- p = _Ports()
114
- p.space_id = str(ctx.space_id or "")
115
- p.sql = s.sql
116
-
117
- return _ontology_fn_body(p)