@josephyan/qingflow-app-user-mcp 1.0.8 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/npm/bin/qingflow-app-user-mcp.mjs +31 -2
- package/npm/lib/runtime.mjs +43 -2
- package/package.json +1 -1
- package/pyproject.toml +2 -1
- package/skills/qingflow-app-builder/SKILL.md +276 -0
- package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
- package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
- package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
- package/skills/qingflow-app-builder/references/create-app.md +149 -0
- package/skills/qingflow-app-builder/references/environments.md +63 -0
- package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
- package/skills/qingflow-app-builder/references/gotchas.md +108 -0
- package/skills/qingflow-app-builder/references/match-rules.md +114 -0
- package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
- package/skills/qingflow-app-builder/references/solution-playbooks.md +52 -0
- package/skills/qingflow-app-builder/references/tool-selection.md +107 -0
- package/skills/qingflow-app-builder/references/update-flow.md +186 -0
- package/skills/qingflow-app-builder/references/update-layout.md +68 -0
- package/skills/qingflow-app-builder/references/update-schema.md +72 -0
- package/skills/qingflow-app-builder/references/update-views.md +291 -0
- package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
- package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
- package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
- package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
- package/skills/qingflow-app-user/SKILL.md +2 -1
- package/skills/qingflow-app-user/references/data-gotchas.md +5 -3
- package/skills/qingflow-app-user/references/public-surface-sync.md +6 -3
- package/skills/qingflow-app-user/references/record-patterns.md +14 -4
- package/skills/qingflow-mcp-setup/SKILL.md +111 -0
- package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
- package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
- package/skills/qingflow-mcp-setup/references/environments.md +61 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/skills/qingflow-record-analysis/SKILL.md +94 -183
- package/skills/qingflow-record-analysis/agents/openai.yaml +2 -2
- package/skills/qingflow-record-analysis/references/analysis-gotchas.md +54 -111
- package/skills/qingflow-record-analysis/references/analysis-patterns.md +96 -131
- package/skills/qingflow-record-analysis/references/business-context.md +74 -0
- package/skills/qingflow-record-analysis/references/confidence-reporting.md +49 -74
- package/skills/qingflow-record-analysis/references/data-access-playbook.md +106 -0
- package/skills/qingflow-record-analysis/references/pandas-recipes.md +172 -0
- package/skills/qingflow-record-analysis/references/report-format.md +76 -0
- package/skills/qingflow-record-insert/SKILL.md +28 -7
- package/skills/qingflow-record-update/SKILL.md +3 -1
- package/skills/qingflow-workflow-builder/SKILL.md +96 -0
- package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
- package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
- package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
- package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
- package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
- package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
- package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
- package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
- package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/backend_client.py +55 -1
- package/src/qingflow_mcp/builder_facade/models.py +532 -48
- package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
- package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
- package/src/qingflow_mcp/cli/commands/app.py +3 -16
- package/src/qingflow_mcp/cli/commands/builder.py +354 -56
- package/src/qingflow_mcp/cli/commands/record.py +112 -6
- package/src/qingflow_mcp/cli/formatters.py +133 -2
- package/src/qingflow_mcp/cli/main.py +204 -3
- package/src/qingflow_mcp/public_surface.py +12 -9
- package/src/qingflow_mcp/response_trim.py +288 -22
- package/src/qingflow_mcp/server.py +29 -20
- package/src/qingflow_mcp/server_app_builder.py +108 -30
- package/src/qingflow_mcp/server_app_user.py +29 -23
- package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
- package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
- package/src/qingflow_mcp/solution/executor.py +3 -133
- package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
- package/src/qingflow_mcp/tools/app_tools.py +53 -8
- package/src/qingflow_mcp/tools/package_tools.py +16 -2
- package/src/qingflow_mcp/tools/record_tools.py +14451 -9110
- package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
- package/src/qingflow_mcp/tools/solution_tools.py +30 -2
- package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
- package/skills/qingflow-record-analysis/references/dsl-templates.md +0 -93
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Pandas Recipes
|
|
2
|
+
|
|
3
|
+
Use Python to read returned CSV shards. Never paste raw CSV into the model context.
|
|
4
|
+
|
|
5
|
+
## Load All Shards
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
files = [
|
|
11
|
+
"/absolute/path/records-0001.csv",
|
|
12
|
+
# include every record_access.files[].local_path
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
frames = [pd.read_csv(path, dtype=str, keep_default_na=False) for path in files]
|
|
16
|
+
df = pd.concat(frames, ignore_index=True) if frames else pd.DataFrame()
|
|
17
|
+
|
|
18
|
+
# Columns are readable and field-id anchored, e.g. 项目状态__field_343283094.
|
|
19
|
+
fields = [] # optionally paste record_access.fields here if you need field-id metadata
|
|
20
|
+
field_by_id = {int(item["field_id"]): item for item in fields if "field_id" in item}
|
|
21
|
+
title_by_col = {item["column_name"]: item["title"] for item in fields if item.get("column_name")}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Field Quality Profile
|
|
25
|
+
|
|
26
|
+
Run this before choosing final grouping dimensions.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
def field_quality(frame: pd.DataFrame, *, date_col: str | None = None) -> pd.DataFrame:
|
|
30
|
+
rows = []
|
|
31
|
+
for col in frame.columns:
|
|
32
|
+
blank = frame[col].astype(str).eq("")
|
|
33
|
+
item = {
|
|
34
|
+
"column": col,
|
|
35
|
+
"row_count": len(frame),
|
|
36
|
+
"blank_count": int(blank.sum()),
|
|
37
|
+
"blank_rate": float(blank.mean()) if len(frame) else 0.0,
|
|
38
|
+
"distinct_count": int(frame[col].replace("", pd.NA).nunique(dropna=True)),
|
|
39
|
+
}
|
|
40
|
+
rows.append(item)
|
|
41
|
+
quality = pd.DataFrame(rows).sort_values(["blank_rate", "distinct_count"], ascending=[False, False])
|
|
42
|
+
if date_col and date_col in frame.columns:
|
|
43
|
+
tmp = frame.copy()
|
|
44
|
+
tmp["_period"] = pd.to_datetime(tmp[date_col], errors="coerce").dt.to_period("M").astype(str)
|
|
45
|
+
period_quality = []
|
|
46
|
+
for col in frame.columns:
|
|
47
|
+
if col == date_col:
|
|
48
|
+
continue
|
|
49
|
+
by_period = tmp.groupby("_period")[col].apply(lambda s: float(s.astype(str).eq("").mean()))
|
|
50
|
+
period_quality.append({"column": col, "max_period_blank_rate": float(by_period.max()) if len(by_period) else 0.0})
|
|
51
|
+
quality = quality.merge(pd.DataFrame(period_quality), on="column", how="left")
|
|
52
|
+
return quality
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Quality gates:
|
|
56
|
+
|
|
57
|
+
- `blank_rate > 0.4`: do not use as the primary conclusion dimension.
|
|
58
|
+
- `max_period_blank_rate > 0.8`: do not use for period comparison.
|
|
59
|
+
- Very high `distinct_count` fields are usually identifiers or descriptions, not grouping dimensions.
|
|
60
|
+
- High-missing dimensions may still be reported as `已填写样本观察`.
|
|
61
|
+
|
|
62
|
+
## Column Selection
|
|
63
|
+
|
|
64
|
+
Prefer exact readable CSV columns. Use suffix matching only when you need to address a field id programmatically.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
def col_by_field_id(frame, field_id: int) -> str:
|
|
68
|
+
suffix = f"__field_{field_id}"
|
|
69
|
+
matches = [col for col in frame.columns if col.endswith(suffix)]
|
|
70
|
+
if not matches:
|
|
71
|
+
raise KeyError(f"field_id not in CSV: {field_id}")
|
|
72
|
+
return matches[0]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Count Distribution
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
col = "项目状态__field_18"
|
|
79
|
+
quality = field_quality(df)
|
|
80
|
+
blank_rate = quality.loc[quality["column"].eq(col), "blank_rate"].iloc[0]
|
|
81
|
+
if blank_rate > 0.4:
|
|
82
|
+
print(f"Use only as filled-sample observation: {col} blank_rate={blank_rate:.1%}")
|
|
83
|
+
dist = (
|
|
84
|
+
df[col]
|
|
85
|
+
.replace("", pd.NA)
|
|
86
|
+
.fillna("未填写")
|
|
87
|
+
.value_counts(dropna=False)
|
|
88
|
+
.rename_axis("group")
|
|
89
|
+
.reset_index(name="count")
|
|
90
|
+
)
|
|
91
|
+
dist["share"] = dist["count"] / dist["count"].sum()
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Numeric Aggregation
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
group_col = "项目状态__field_18"
|
|
98
|
+
amount_col = "金额__field_25"
|
|
99
|
+
tmp = df.copy()
|
|
100
|
+
tmp[amount_col] = (
|
|
101
|
+
tmp[amount_col]
|
|
102
|
+
.str.replace(",", "", regex=False)
|
|
103
|
+
.str.replace("¥", "", regex=False)
|
|
104
|
+
.pipe(pd.to_numeric, errors="coerce")
|
|
105
|
+
)
|
|
106
|
+
summary = (
|
|
107
|
+
tmp.groupby(group_col, dropna=False)[amount_col]
|
|
108
|
+
.agg(count="count", total="sum", avg="mean")
|
|
109
|
+
.reset_index()
|
|
110
|
+
.sort_values("total", ascending=False)
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Date Trend
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
date_col = "申请时间__field_2"
|
|
118
|
+
tmp = df.copy()
|
|
119
|
+
tmp[date_col] = pd.to_datetime(tmp[date_col], errors="coerce")
|
|
120
|
+
tmp = tmp.dropna(subset=[date_col])
|
|
121
|
+
tmp["month"] = tmp[date_col].dt.to_period("M").astype(str)
|
|
122
|
+
trend = tmp.groupby("month").size().reset_index(name="count")
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Year-Over-Year Month Comparison
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
date_col = "申请时间__field_2"
|
|
129
|
+
tmp = df.copy()
|
|
130
|
+
tmp[date_col] = pd.to_datetime(tmp[date_col], errors="coerce")
|
|
131
|
+
tmp = tmp.dropna(subset=[date_col])
|
|
132
|
+
tmp["year"] = tmp[date_col].dt.year
|
|
133
|
+
tmp["month"] = tmp[date_col].dt.month
|
|
134
|
+
monthly = tmp.groupby(["year", "month"]).size().reset_index(name="count")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Ratio
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
numerator = len(df[df["项目状态__field_18"].eq("已成交")])
|
|
141
|
+
denominator = len(df)
|
|
142
|
+
ratio = numerator / denominator if denominator else None
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Always report numerator and denominator.
|
|
146
|
+
|
|
147
|
+
## Multi-Select Cells
|
|
148
|
+
|
|
149
|
+
If values are serialized with delimiters, inspect samples first. For simple comma-separated values:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
col = "标签__field_30"
|
|
153
|
+
exploded = (
|
|
154
|
+
df.assign(_value=df[col].str.split(","))
|
|
155
|
+
.explode("_value")
|
|
156
|
+
)
|
|
157
|
+
exploded["_value"] = exploded["_value"].str.strip()
|
|
158
|
+
multi_dist = exploded["_value"].value_counts().reset_index(name="count")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Business Mapping
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
mapping = {
|
|
165
|
+
"烈焰组": "北斗部门",
|
|
166
|
+
"飓风组": "北斗部门",
|
|
167
|
+
}
|
|
168
|
+
department_col = "部门__field_40"
|
|
169
|
+
df["department_normalized"] = df[department_col].replace(mapping)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
State the mapping in the final answer.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Report Format
|
|
2
|
+
|
|
3
|
+
Use this for user-facing analysis reports.
|
|
4
|
+
|
|
5
|
+
## Short Answer
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
结论:
|
|
9
|
+
- ...
|
|
10
|
+
|
|
11
|
+
关键数据:
|
|
12
|
+
- 指标 A:...
|
|
13
|
+
- 指标 B:...
|
|
14
|
+
|
|
15
|
+
口径与范围:
|
|
16
|
+
- 应用 / 视图:...
|
|
17
|
+
- 时间范围:...
|
|
18
|
+
- 字段:...
|
|
19
|
+
- 字段质量:...
|
|
20
|
+
- 业务映射:...
|
|
21
|
+
- 数据完整性:...
|
|
22
|
+
|
|
23
|
+
限制:
|
|
24
|
+
- ...
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Detailed Report
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
1. 分析范围
|
|
31
|
+
- app / view
|
|
32
|
+
- time range
|
|
33
|
+
- filters
|
|
34
|
+
- rows analyzed
|
|
35
|
+
|
|
36
|
+
2. 核心结论
|
|
37
|
+
- concrete numbers first
|
|
38
|
+
- no vague adjectives without numbers
|
|
39
|
+
|
|
40
|
+
3. 分项数据
|
|
41
|
+
- distribution / trend / ranking tables
|
|
42
|
+
- percentages with numerator and denominator
|
|
43
|
+
|
|
44
|
+
4. 解释与建议
|
|
45
|
+
- separate facts from hypotheses
|
|
46
|
+
|
|
47
|
+
5. 口径与可信度
|
|
48
|
+
- fields used
|
|
49
|
+
- field-quality gates and downgraded dimensions
|
|
50
|
+
- mapping rules
|
|
51
|
+
- completeness
|
|
52
|
+
- partial or unverified scope warnings
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Wording Rules
|
|
56
|
+
|
|
57
|
+
- Use `全量可信结论` only when the accessed scope is complete and safe.
|
|
58
|
+
- Use `初步观察` for partial or unverified data.
|
|
59
|
+
- Do not say `全部`, `所有`, `整体`, or `完整` when `safe_for_final_conclusion=false`.
|
|
60
|
+
- For ratios, always show `numerator / denominator`.
|
|
61
|
+
- For comparisons, show both periods' absolute values and the delta.
|
|
62
|
+
|
|
63
|
+
## Comparison Template
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
今年5月 vs 去年5月:
|
|
67
|
+
- 记录数:今年 X,去年 Y,变化 +Z(+P%)
|
|
68
|
+
- 金额:今年 X,去年 Y,变化 +Z(+P%)
|
|
69
|
+
- 结构变化:...
|
|
70
|
+
|
|
71
|
+
口径:
|
|
72
|
+
- 时间字段:...
|
|
73
|
+
- 部门字段:...
|
|
74
|
+
- 部门映射:...
|
|
75
|
+
- 数据完整性:...
|
|
76
|
+
```
|
|
@@ -9,7 +9,9 @@ metadata:
|
|
|
9
9
|
|
|
10
10
|
## Default Path
|
|
11
11
|
|
|
12
|
-
`record_insert_schema_get -> (
|
|
12
|
+
`record_insert_schema_get -> record_insert(items) -> optional record_get/readback`
|
|
13
|
+
|
|
14
|
+
Default to batch-shaped insert. A single new record is `items` with one row.
|
|
13
15
|
|
|
14
16
|
## Core Tools
|
|
15
17
|
|
|
@@ -25,19 +27,20 @@ metadata:
|
|
|
25
27
|
2. Read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`
|
|
26
28
|
3. Inside every field bucket, read field-level `linkage` first when present; it is the canonical static hint for linked visibility, reference-driven auto fill, or formula-driven fields
|
|
27
29
|
4. Inside `optional_fields`, pay special attention to any field with `may_become_required=true`; these are writable fields that can become required when linked visibility or option-driven rules activate
|
|
28
|
-
5. Build `
|
|
30
|
+
5. Build `items` as `[{"fields": {...}}]`, where each `fields` map uses field titles from the insert schema
|
|
29
31
|
6. Treat `runtime_linked_required_fields` as required-but-not-directly-writable runtime/upstream dependencies, not as fields to hand-fill blindly
|
|
30
32
|
7. For `linkage.kind=logic_visibility`, read `sources` as upstream trigger fields and treat `role=manual_input_after_activation` as "fill this only after the upstream condition is satisfied"
|
|
31
33
|
8. For `linkage.kind=reference_fill`, prefer filling the source field first; treat target fields with `role=auto_fill_preferred` or `auto_fill_only` as reference-driven outputs rather than blind manual inputs
|
|
32
34
|
9. For `linkage.kind=formula_fill`, treat the field as formula/default-auto-fill driven unless the user explicitly asks to override it and the field is still writable
|
|
33
|
-
10. If insert succeeds and readback
|
|
35
|
+
10. If insert succeeds and single-record detail/readback matters, prefer `record_get`; use `record_list(..., output_profile="normalized")` only for batch row-shaped normalized readback
|
|
34
36
|
11. Keep subtable payloads under the parent field as a row array
|
|
35
|
-
12. Member / department / relation fields may accept natural strings directly
|
|
37
|
+
12. Member / department / relation fields may accept natural strings directly, such as `"张三"`, `"直销部"`, or `"海军军医大学"`; do not pre-query ids by default
|
|
36
38
|
13. If the write returns `status="needs_confirmation"`, stop and surface the candidates
|
|
37
|
-
14. Retry with explicit ids / objects
|
|
39
|
+
14. Retry failed rows only with explicit ids / objects after the user confirms
|
|
38
40
|
15. Keep `verify_write=true` for production inserts
|
|
39
|
-
16. If post-write
|
|
41
|
+
16. If post-write detail context matters, read `record_get.fields[]`, `media_assets.items[].local_path`, `file_assets.items[].local_path`, `file_assets.items[].extraction.text_path`, and `semantic_context`; `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, so prefer local paths over remote URLs and do not expect legacy `data.normalized_record`
|
|
40
42
|
17. Treat nested schema shape as guidance, not a brittle contract; do not hard-code transient implementation details like optional nested `field_id` shape when composing inserts
|
|
43
|
+
18. For `partial_success`, read `created_record_ids`, then repair only the failed `items[].row_number` using `failed_fields`; never retry the whole batch after any row has `write_executed=true`
|
|
41
44
|
|
|
42
45
|
## Field Notes
|
|
43
46
|
|
|
@@ -51,11 +54,29 @@ metadata:
|
|
|
51
54
|
- `linkage.affects_fields` lists downstream field titles that may change when this field changes
|
|
52
55
|
- `linkage.role=auto_fill_only` means "normally do not hand-fill this unless the product explicitly requires it"
|
|
53
56
|
- `requires_upload=true` means upload the file first, then write the returned value
|
|
57
|
+
- `failed_fields[].next_action` tells the next repair step for that row
|
|
58
|
+
|
|
59
|
+
## CLI Pattern
|
|
60
|
+
|
|
61
|
+
Use a JSON array file:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
qingflow record insert --app-key APP_KEY --items-file records.json --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`records.json`:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
[
|
|
71
|
+
{ "fields": { "客户名称": "测试客户", "负责人": "张三" } }
|
|
72
|
+
]
|
|
73
|
+
```
|
|
54
74
|
|
|
55
75
|
## Do Not
|
|
56
76
|
|
|
57
77
|
- Do not skip `record_insert_schema_get`
|
|
58
78
|
- Do not invent missing required fields
|
|
59
79
|
- Do not flatten subtable leaf fields to the top level
|
|
60
|
-
- Do not silently guess member / department / relation ids
|
|
80
|
+
- Do not pre-query or silently guess member / department / relation ids when a natural string is enough
|
|
81
|
+
- Do not retry a whole batch after `created_record_ids` is non-empty
|
|
61
82
|
- Do not bind logic to a transient nested schema serialization detail when the field title and parent table already identify the legal payload shape
|
|
@@ -34,7 +34,9 @@ metadata:
|
|
|
34
34
|
12. Do not assume any arbitrary combination of writable fields will succeed; one single matched accessible view still has to cover the payload
|
|
35
35
|
13. Do not look for any extra context bucket in update schema; lookup behavior stays inline on the field definitions themselves
|
|
36
36
|
14. When update context feels unstable, trust `record_update_schema_get`'s route-aware matched-view result over guessed `view_id` or remembered UI scope
|
|
37
|
-
15. If readback
|
|
37
|
+
15. If single-record detail/readback matters, prefer `record_get` after the write and read top-level `fields[]`, `media_assets.items[].local_path`, `file_assets.items[].local_path`, `file_assets.items[].extraction.text_path`, and `semantic_context`; `record_get` follows the frontend storage cookie redirect path for Qingflow attachments, so prefer local paths over remote URLs; use `record_list(..., output_profile="normalized")` only for batch row-shaped normalized readback
|
|
38
|
+
16. For batch updates, read top-level `mode`, `dry_run`, `total`, `succeeded`, `failed`, `needs_confirmation`, `updated_record_ids`, `write_executed`, `safe_to_retry`, `verification_status`, and `items[].row_number/status/record_id`
|
|
39
|
+
17. If `write_executed=true`, do not blindly retry the whole batch; use `items[]` and `updated_record_ids` to decide whether only failed rows need repair
|
|
38
40
|
|
|
39
41
|
## Do Not
|
|
40
42
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qingflow-workflow-builder
|
|
3
|
+
description: |
|
|
4
|
+
在已有的轻流应用中,根据业务建模使用 qingflow CLI 声明式搭建工作流。
|
|
5
|
+
适用场景:应用已存在、字段已就绪,需要创建或更新审批/填写/网关/自动化/抄送流程。
|
|
6
|
+
不适用场景:应用不存在或字段缺失——应先完成应用搭建(schema apply)再使用本 Skill。
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 轻流工作流搭建 Skill
|
|
10
|
+
|
|
11
|
+
## 快速索引
|
|
12
|
+
|
|
13
|
+
| 主题 | 文件 |
|
|
14
|
+
|------|------|
|
|
15
|
+
| 依赖命令、能力边界 | [references/01-overview.md](references/01-overview.md) |
|
|
16
|
+
| 更新模式与最小修改原则 | [references/02-update-mode.md](references/02-update-mode.md) |
|
|
17
|
+
| 流程模式速查(线性/分支/回退) | [references/03-flow-patterns.md](references/03-flow-patterns.md) |
|
|
18
|
+
| 阶段 1:业务建模与自检 | [references/04-stage1-business-modeling.md](references/04-stage1-business-modeling.md) |
|
|
19
|
+
| 阶段 2:成员/角色搜索 | [references/05-stage2-members-roles.md](references/05-stage2-members-roles.md) |
|
|
20
|
+
| 阶段 3:Schema + 构建 Spec | [references/06-stage3-build-spec.md](references/06-stage3-build-spec.md) |
|
|
21
|
+
| 阶段 4:验证 Spec | [references/07-stage4-validate-spec.md](references/07-stage4-validate-spec.md) |
|
|
22
|
+
| 阶段 5:Apply + 验证循环 | [references/08-stage5-apply-verify.md](references/08-stage5-apply-verify.md) |
|
|
23
|
+
| 阶段 6:总结报告与回退 | [references/09-stage6-summary.md](references/09-stage6-summary.md) |
|
|
24
|
+
| 节点配置参考 | [references/10-node-config-reference.md](references/10-node-config-reference.md) |
|
|
25
|
+
| 常见问题与排障 | [references/11-troubleshooting.md](references/11-troubleshooting.md) |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 一、思考(Thought):何时启用与前置条件
|
|
30
|
+
|
|
31
|
+
### 触发条件
|
|
32
|
+
|
|
33
|
+
当用户要求**在轻流应用中搭建工作流**时启用本 Skill,典型触发语:
|
|
34
|
+
|
|
35
|
+
- "帮我搭建审批流程"
|
|
36
|
+
- "在这个应用里创建工作流"
|
|
37
|
+
- "按照这张流程图实现工作流"
|
|
38
|
+
- "配置流程分支条件"
|
|
39
|
+
- "给这个应用加一个审批节点"
|
|
40
|
+
|
|
41
|
+
### 前提检查(进入行动前必须完成)
|
|
42
|
+
|
|
43
|
+
在开始任何搭建操作前,按顺序确认以下条件:
|
|
44
|
+
|
|
45
|
+
1. **应用存在性**:`qingflow --json app get --app-key <APP_KEY>` 成功返回
|
|
46
|
+
2. **字段就绪**:`qingflow --json builder app get --app-key <APP_KEY> fields` 返回字段列表,确认业务建模所需字段(如单选框、成员、部门字段)均已存在
|
|
47
|
+
3. **未启用已有流程**(新建场景):`qingflow --json builder app get --app-key <APP_KEY> flow` 查看 `enabled` 状态,若已有流程则为**更新模式**
|
|
48
|
+
|
|
49
|
+
**不满足时的处理**:
|
|
50
|
+
- 应用不存在 → 先完成应用搭建和字段配置,再继续工作流搭建
|
|
51
|
+
- 字段缺失 → 先补充缺失的字段定义,再继续工作流搭建
|
|
52
|
+
- 应用已启用流程 → 进入更新模式(读取现有 spec 再修改)
|
|
53
|
+
|
|
54
|
+
### 能力边界
|
|
55
|
+
|
|
56
|
+
| 在范围内 | 超出范围 |
|
|
57
|
+
|----------|----------|
|
|
58
|
+
| 基于已有应用搭建工作流 | 从零创建应用 |
|
|
59
|
+
| 声明式 WorkflowSpec 生成与 apply | 操作复杂命令拼接 |
|
|
60
|
+
| 分支条件(gateway + autoJudges)配置 | 后端不支持的高级特性 |
|
|
61
|
+
| 审批/填写/抄送/自动化节点配置 | 修改字段定义 |
|
|
62
|
+
| 成员/角色搜索用于节点负责人 | 组织架构管理 |
|
|
63
|
+
| 验证 → apply → 校验循环 | 前端 UI 拖拽操作 |
|
|
64
|
+
|
|
65
|
+
完整命令与依赖说明见 [references/01-overview.md](references/01-overview.md)。
|
|
66
|
+
本 Skill 中的 `scripts/...` 路径均以 `qingflow-workflow-builder/` 技能根目录为基准;执行脚本时先解析为该技能目录下的实际文件。
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 二、行动(Action):核心 SOP
|
|
71
|
+
|
|
72
|
+
按顺序执行以下阶段,每个阶段详见对应 reference 文件:
|
|
73
|
+
|
|
74
|
+
| 阶段 | 任务 | 参考文件 |
|
|
75
|
+
|------|------|----------|
|
|
76
|
+
| 阶段 0 | 判断是否为更新模式,确认最小修改原则 | [references/02-update-mode.md](references/02-update-mode.md) |
|
|
77
|
+
| 阶段 1 | 提取业务建模,完成七维度业务自检 | [references/04-stage1-business-modeling.md](references/04-stage1-business-modeling.md) |
|
|
78
|
+
| 阶段 2 | 搜索成员/角色,用于节点负责人配置 | [references/05-stage2-members-roles.md](references/05-stage2-members-roles.md) |
|
|
79
|
+
| 阶段 3 | 获取 Schema、读取现有 Spec、构建新 Spec | [references/06-stage3-build-spec.md](references/06-stage3-build-spec.md) |
|
|
80
|
+
| 阶段 4 | 验证 Spec(JSON Schema + 自定义约束) | [references/07-stage4-validate-spec.md](references/07-stage4-validate-spec.md) |
|
|
81
|
+
| 阶段 5 | Apply 工作流并验证部署结果 | [references/08-stage5-apply-verify.md](references/08-stage5-apply-verify.md) |
|
|
82
|
+
|
|
83
|
+
构建 Spec 前,若对流程模式有疑问,先查阅 [references/03-flow-patterns.md](references/03-flow-patterns.md)。
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 三、反思(Reflection):验证与总结
|
|
88
|
+
|
|
89
|
+
| 阶段 | 任务 | 参考文件 |
|
|
90
|
+
|------|------|----------|
|
|
91
|
+
| 阶段 6 | 输出结构化搭建报告,回顾业务完整性 | [references/09-stage6-summary.md](references/09-stage6-summary.md) |
|
|
92
|
+
|
|
93
|
+
搭建失败时的回退策略、节点配置细节、常见问题排障分别见:
|
|
94
|
+
- [references/09-stage6-summary.md](references/09-stage6-summary.md)
|
|
95
|
+
- [references/10-node-config-reference.md](references/10-node-config-reference.md)
|
|
96
|
+
- [references/11-troubleshooting.md](references/11-troubleshooting.md)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 轻流工作流搭建:概览与依赖
|
|
2
|
+
|
|
3
|
+
## 关联文件
|
|
4
|
+
|
|
5
|
+
| 文件 | 说明 |
|
|
6
|
+
|------|------|
|
|
7
|
+
| `manifest.yaml` | 技能清单 |
|
|
8
|
+
| `SKILL.md` | 主流程入口 |
|
|
9
|
+
| `scripts/validate_flow_spec.py` | 工作流 Spec 验证脚本,基于 JSON Schema + 自定义约束 |
|
|
10
|
+
| `scripts/diff_flow_spec.py` | 新旧 Spec 差异分析脚本,辅助更新模式下最小修改原则判断 |
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 依赖声明
|
|
15
|
+
|
|
16
|
+
本 Skill 关键依赖 **qingflow CLI**(已安装于 PATH,详见 `qingflow_cli` Skill)。以下 qingflow 命令为核心操作:
|
|
17
|
+
|
|
18
|
+
| 命令 | 用途 |
|
|
19
|
+
|------|------|
|
|
20
|
+
| `qingflow builder flow schema --json` | 获取最新 WorkflowSpecDTO JSON Schema |
|
|
21
|
+
| `qingflow builder flow get --app-key <KEY>` | 读取当前工作流 spec |
|
|
22
|
+
| `qingflow builder flow apply --app-key <KEY> --spec-file <FILE>` | 部署/更新工作流 |
|
|
23
|
+
| `qingflow builder member search --query <关键词>` | 搜索成员 |
|
|
24
|
+
| `qingflow builder role search --keyword <关键词>` | 搜索角色 |
|
|
25
|
+
| `qingflow --json app get --app-key <KEY>` | 获取应用信息(字段列表等) |
|
|
26
|
+
| `qingflow --json builder app get --app-key <KEY> fields` | 获取应用可搭建字段详情 |
|
|
27
|
+
| `qingflow --json builder app get --app-key <KEY> flow` | 获取流程摘要(是否启用等) |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 能力边界
|
|
32
|
+
|
|
33
|
+
| 在范围内 | 超出范围 |
|
|
34
|
+
|----------|----------|
|
|
35
|
+
| 基于已有应用搭建工作流 | 从零创建应用 |
|
|
36
|
+
| 声明式 WorkflowSpec 生成与 apply | 操作复杂命令拼接 |
|
|
37
|
+
| 分支条件(gateway + autoJudges)配置 | 后端不支持的高级特性 |
|
|
38
|
+
| 审批/填写/抄送/自动化节点配置 | 修改字段定义 |
|
|
39
|
+
| 成员/角色搜索用于节点负责人 | 组织架构管理 |
|
|
40
|
+
| 验证 → apply → 校验循环 | 前端 UI 拖拽操作 |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
← 返回主流程:[../SKILL.md](../SKILL.md)
|
|
45
|
+
→ 下一步:[02-update-mode.md](02-update-mode.md)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 更新模式与最小修改原则
|
|
2
|
+
|
|
3
|
+
当应用已有工作流时,进入**更新模式**。核心原则是**最小修改**——只改动必要的部分,保持其他结构不变。
|
|
4
|
+
|
|
5
|
+
## 为什么要保持节点 ID 稳定
|
|
6
|
+
|
|
7
|
+
后端可能存储了 spec 中不支持的运行时配置(如审批策略、超时规则、高级权限等),这些配置与节点 ID 绑定。一旦修改节点 ID,后端会将其视为「删除旧节点 + 创建新节点」,导致不支持的配置全部丢失。
|
|
8
|
+
|
|
9
|
+
**因此:修改已有节点时,只改 attrs/name,绝不改 id。**
|
|
10
|
+
|
|
11
|
+
## 最小修改原则速查
|
|
12
|
+
|
|
13
|
+
| 操作 | ✅ 正确做法 | ❌ 错误做法 |
|
|
14
|
+
|------|-----------|-----------|
|
|
15
|
+
| 添加节点 | 使用新 ID,追加到 nodes 末尾 | 重排已有节点 ID |
|
|
16
|
+
| 删除节点 | 删除对应 node 和关联边 | 不删关联边导致孤立边 |
|
|
17
|
+
| 修改节点配置 | 同一 ID 下只改 attrs/name | 改 ID 导致节点重建 |
|
|
18
|
+
| 添加边 | 追加到 edges 数组 | 修改已有边的 from/to |
|
|
19
|
+
| 修改条件 | 同一条边内改 condition | 删除边再新建 |
|
|
20
|
+
| 调整流转 | 修改边的 from/to | 改节点 ID 来适配 |
|
|
21
|
+
|
|
22
|
+
## 使用 diff 脚本辅助判断
|
|
23
|
+
|
|
24
|
+
在构建新 spec 后、apply 前,使用 diff 脚本对比新旧 spec:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
python3 scripts/diff_flow_spec.py tmp/current_flow.json tmp/flow_spec.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
输出会显示删除的节点/边、新增的节点/边、修改的节点/边,并自动评估是否符合最小修改原则。
|
|
31
|
+
|
|
32
|
+
验证脚本也支持在更新模式下进行最小修改原则校验:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
python3 scripts/validate_flow_spec.py \
|
|
36
|
+
tmp/flow_spec.json \
|
|
37
|
+
--schema tmp/flow_schema.json \
|
|
38
|
+
--previous tmp/current_flow.json
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 更新模式 SOP 调整
|
|
42
|
+
|
|
43
|
+
在更新模式下,阶段 3.2 变为**必须执行**(读取现有 spec),阶段 5.2 的对比验证增加最小修改原则检查:
|
|
44
|
+
|
|
45
|
+
- 节点 ID 不应无故变更
|
|
46
|
+
- 删除的节点/边应为业务需要,而非误删
|
|
47
|
+
- 修改范围应仅限于业务建模中变更的部分
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
← 上一步:[01-overview.md](01-overview.md)
|
|
52
|
+
← 返回主流程:[../SKILL.md](../SKILL.md)
|
|
53
|
+
→ 下一步:[03-flow-patterns.md](03-flow-patterns.md)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 流程模式速查
|
|
2
|
+
|
|
3
|
+
在构建 spec 前,先判断当前流程属于哪种模式,避免用错节点类型或边结构。
|
|
4
|
+
|
|
5
|
+
## 线性流程
|
|
6
|
+
|
|
7
|
+
最简单的流程模式:节点首尾相连,无分支、无条件。
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
n1 → n2 → n3 → n4
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- 边不带 `condition`,按顺序流转即可
|
|
14
|
+
- 无需 gateway 节点
|
|
15
|
+
- 适用于:简单的提交→审批→结束
|
|
16
|
+
|
|
17
|
+
## 分支流程
|
|
18
|
+
|
|
19
|
+
轻流的分支采用**并行分支**模型——通过 `gateway` 节点(`mode: parallel`)分发,所有出边指向的分支**都会进入**,通过每条边上的条件(`condition.autoJudges`)控制分支内的节点逻辑是否执行。
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌──[条件A成立]→ n3 → n4
|
|
23
|
+
n1 → n2 → g1 ──┤
|
|
24
|
+
└──[条件B成立]→ n5
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**关键点**:
|
|
28
|
+
- 分支起点必须是 `gateway`(`mode: parallel`),终点必须是 `gateway`(`mode: join`)
|
|
29
|
+
- 每条分支边都需要配置 `condition`,其中至少一条为 `kind: rules` 带 `autoJudges`
|
|
30
|
+
- 没有条件的边使用 `kind: default` 作为兜底
|
|
31
|
+
- 所有分支并行进入,条件只决定分支内逻辑是否执行,不存在"条件匹配就跳过其他分支"的互斥语义
|
|
32
|
+
|
|
33
|
+
## 循环/回退
|
|
34
|
+
|
|
35
|
+
**轻流工作流引擎不支持循环结构**。不要通过 gateway 或其他方式构建回到前序节点的环。
|
|
36
|
+
|
|
37
|
+
如需回退到前序节点重新处理,应使用审批节点的回退开关:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"type": "approval",
|
|
42
|
+
"attrs": {
|
|
43
|
+
"revert": true,
|
|
44
|
+
"revertScope": "all"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- `revert: true` 开启驳回/回退能力
|
|
50
|
+
- `revertScope` 控制回退范围:`all` 可回退到任意前序节点
|
|
51
|
+
- 这是引擎原生支持的回退机制,无需在 spec 中构建回退边
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
← 上一步:[02-update-mode.md](02-update-mode.md)
|
|
56
|
+
← 返回主流程:[../SKILL.md](../SKILL.md)
|
|
57
|
+
→ 下一步:[04-stage1-business-modeling.md](04-stage1-business-modeling.md)
|