@josephyan/qingflow-app-user-mcp 1.0.9 → 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.
Files changed (88) hide show
  1. package/README.md +6 -2
  2. package/npm/bin/qingflow-app-user-mcp.mjs +31 -2
  3. package/npm/lib/runtime.mjs +43 -2
  4. package/package.json +1 -1
  5. package/pyproject.toml +2 -1
  6. package/skills/qingflow-app-builder/SKILL.md +276 -0
  7. package/skills/qingflow-app-builder/agents/openai.yaml +4 -0
  8. package/skills/qingflow-app-builder/references/build-complete-system.md +428 -0
  9. package/skills/qingflow-app-builder/references/build-single-app.md +530 -0
  10. package/skills/qingflow-app-builder/references/create-app.md +149 -0
  11. package/skills/qingflow-app-builder/references/environments.md +63 -0
  12. package/skills/qingflow-app-builder/references/flow-actors-and-permissions.md +123 -0
  13. package/skills/qingflow-app-builder/references/gotchas.md +108 -0
  14. package/skills/qingflow-app-builder/references/match-rules.md +114 -0
  15. package/skills/qingflow-app-builder/references/public-surface-sync.md +75 -0
  16. package/skills/qingflow-app-builder/references/solution-playbooks.md +52 -0
  17. package/skills/qingflow-app-builder/references/tool-selection.md +107 -0
  18. package/skills/qingflow-app-builder/references/update-flow.md +186 -0
  19. package/skills/qingflow-app-builder/references/update-layout.md +68 -0
  20. package/skills/qingflow-app-builder/references/update-schema.md +72 -0
  21. package/skills/qingflow-app-builder/references/update-views.md +291 -0
  22. package/skills/qingflow-app-builder-code-integrations/SKILL.md +137 -0
  23. package/skills/qingflow-app-builder-code-integrations/agents/openai.yaml +4 -0
  24. package/skills/qingflow-app-builder-code-integrations/references/code-block.md +66 -0
  25. package/skills/qingflow-app-builder-code-integrations/references/q-linker.md +77 -0
  26. package/skills/qingflow-app-user/SKILL.md +2 -1
  27. package/skills/qingflow-app-user/references/data-gotchas.md +5 -3
  28. package/skills/qingflow-app-user/references/public-surface-sync.md +6 -3
  29. package/skills/qingflow-app-user/references/record-patterns.md +14 -4
  30. package/skills/qingflow-mcp-setup/SKILL.md +111 -0
  31. package/skills/qingflow-mcp-setup/agents/openai.yaml +4 -0
  32. package/skills/qingflow-mcp-setup/references/claude-desktop.md +34 -0
  33. package/skills/qingflow-mcp-setup/references/environments.md +61 -0
  34. package/skills/qingflow-mcp-setup/references/generic-stdio.md +31 -0
  35. package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
  36. package/skills/qingflow-record-analysis/SKILL.md +94 -186
  37. package/skills/qingflow-record-analysis/agents/openai.yaml +2 -2
  38. package/skills/qingflow-record-analysis/references/analysis-gotchas.md +54 -113
  39. package/skills/qingflow-record-analysis/references/analysis-patterns.md +95 -134
  40. package/skills/qingflow-record-analysis/references/business-context.md +74 -0
  41. package/skills/qingflow-record-analysis/references/confidence-reporting.md +47 -74
  42. package/skills/qingflow-record-analysis/references/data-access-playbook.md +106 -0
  43. package/skills/qingflow-record-analysis/references/pandas-recipes.md +172 -0
  44. package/skills/qingflow-record-analysis/references/report-format.md +76 -0
  45. package/skills/qingflow-record-insert/SKILL.md +28 -7
  46. package/skills/qingflow-record-update/SKILL.md +3 -1
  47. package/skills/qingflow-workflow-builder/SKILL.md +96 -0
  48. package/skills/qingflow-workflow-builder/manifest.yaml +8 -0
  49. package/skills/qingflow-workflow-builder/references/01-overview.md +45 -0
  50. package/skills/qingflow-workflow-builder/references/02-update-mode.md +53 -0
  51. package/skills/qingflow-workflow-builder/references/03-flow-patterns.md +57 -0
  52. package/skills/qingflow-workflow-builder/references/04-stage1-business-modeling.md +131 -0
  53. package/skills/qingflow-workflow-builder/references/05-stage2-members-roles.md +29 -0
  54. package/skills/qingflow-workflow-builder/references/06-stage3-build-spec.md +165 -0
  55. package/skills/qingflow-workflow-builder/references/07-stage4-validate-spec.md +33 -0
  56. package/skills/qingflow-workflow-builder/references/08-stage5-apply-verify.md +51 -0
  57. package/skills/qingflow-workflow-builder/references/09-stage6-summary.md +88 -0
  58. package/skills/qingflow-workflow-builder/references/10-node-config-reference.md +93 -0
  59. package/skills/qingflow-workflow-builder/references/11-troubleshooting.md +15 -0
  60. package/skills/qingflow-workflow-builder/scripts/diff_flow_spec.py +275 -0
  61. package/skills/qingflow-workflow-builder/scripts/validate_flow_spec.py +605 -0
  62. package/src/qingflow_mcp/__init__.py +1 -1
  63. package/src/qingflow_mcp/backend_client.py +55 -1
  64. package/src/qingflow_mcp/builder_facade/models.py +532 -48
  65. package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
  66. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  67. package/src/qingflow_mcp/cli/commands/app.py +3 -16
  68. package/src/qingflow_mcp/cli/commands/builder.py +354 -56
  69. package/src/qingflow_mcp/cli/commands/record.py +112 -6
  70. package/src/qingflow_mcp/cli/formatters.py +102 -2
  71. package/src/qingflow_mcp/cli/main.py +204 -3
  72. package/src/qingflow_mcp/public_surface.py +12 -9
  73. package/src/qingflow_mcp/response_trim.py +287 -22
  74. package/src/qingflow_mcp/server.py +29 -20
  75. package/src/qingflow_mcp/server_app_builder.py +108 -30
  76. package/src/qingflow_mcp/server_app_user.py +28 -23
  77. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  78. package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
  79. package/src/qingflow_mcp/solution/executor.py +3 -133
  80. package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
  81. package/src/qingflow_mcp/tools/app_tools.py +53 -8
  82. package/src/qingflow_mcp/tools/package_tools.py +16 -2
  83. package/src/qingflow_mcp/tools/record_tools.py +14283 -9162
  84. package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
  85. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  86. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  87. package/skills/qingflow-record-analysis/references/dsl-templates.md +0 -93
  88. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ ROOT="${QINGFLOW_MCP_ROOT:-}"
6
+
7
+ if [[ -z "$ROOT" ]]; then
8
+ if git_root="$(git rev-parse --show-toplevel 2>/dev/null)" && [[ -d "$git_root/qingflow-support/mcp-server" ]]; then
9
+ ROOT="$git_root/qingflow-support/mcp-server"
10
+ elif [[ -d "$PWD/qingflow-support/mcp-server" ]]; then
11
+ ROOT="$PWD/qingflow-support/mcp-server"
12
+ else
13
+ repo_root="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
14
+ if [[ -d "$repo_root/qingflow-support/mcp-server" ]]; then
15
+ ROOT="$repo_root/qingflow-support/mcp-server"
16
+ fi
17
+ fi
18
+ fi
19
+
20
+ if [[ -z "$ROOT" ]]; then
21
+ echo "Unable to locate qingflow-support/mcp-server. Set QINGFLOW_MCP_ROOT to the repo copy of the MCP server." >&2
22
+ exit 1
23
+ fi
24
+
25
+ ENTRY="$ROOT/qingflow-mcp"
26
+ PY="$ROOT/.venv/bin/python"
27
+
28
+ echo "ROOT=$ROOT"
29
+ test -d "$ROOT"
30
+ echo "OK root exists"
31
+
32
+ test -x "$ENTRY"
33
+ echo "OK entrypoint exists and is executable"
34
+
35
+ test -x "$PY"
36
+ echo "OK virtualenv python exists"
37
+
38
+ PYTHONPATH="$ROOT/src" "$PY" -c "import qingflow_mcp.server; print('OK import qingflow_mcp.server')"
@@ -1,68 +1,71 @@
1
1
  ---
2
2
  name: qingflow-record-analysis
3
- description: Analyze Qingflow record data safely after the MCP is already connected and authenticated. Use when the user wants grouped distributions, ratios, averages, rankings, trends, insights, or any final statistical conclusion across an existing app's data. Do not use this skill for schema changes, app design, or ordinary record CRUD unless they are strictly supporting an analysis flow.
3
+ description: Analyze Qingflow record data safely after the MCP is already connected and authenticated. Use when the user wants grouped distributions, ratios, averages, rankings, trends, insights, comparisons, or any final statistical conclusion across an existing app's data. Default to schema-first record_access CSV retrieval plus local Python/pandas; do not use export tools unless the user explicitly asks for a file download.
4
4
  metadata:
5
- short-description: Analyze Qingflow record data with schema-first CSV access and Python
5
+ short-description: Analyze Qingflow record data with record_access CSV and Python
6
6
  ---
7
7
 
8
8
  # Qingflow Record Analysis
9
9
 
10
- This skill is for final statistical conclusions only.
11
- Assumes MCP is connected, authenticated, and on the correct workspace.
12
- Analysis tasks must start with `app_get`, then `record_browse_schema_get(view_id=...)`. `record_browse_schema_get.fields` is the selected Qingflow table view's readable header schema and is the same field source used by `record_access.fields` / `schema.json`. Read top-level `fields` and `suggested_*`, then choose field_id-based columns and filters only.
13
- If `app_get.accessible_views` marks a view with `analysis_supported=false`, do not use that view for `record_access`, `record_list`, or `record_analyze`. `boardView` and `ganttView` are special UI views, not data-access targets.
10
+ Use this skill only for final statistical conclusions: counts, distributions, ratios, averages, rankings, trends, comparisons, and analysis reports.
14
11
 
15
- ## Step 1: `app_get` → Step 2: `record_browse_schema_get(view_id=...)` → Step 3: `record_access` → Step 4: Python
12
+ Default path, every time:
16
13
 
17
- This is the default execution order. Never skip `app_get` when the browse range is unclear. Never call `record_access` without a browse schema.
18
-
19
- Core tools: `app_get`, `record_browse_schema_get`, `record_access`, Python. Use field_id-based DSLs only for columns, filters, sort clauses, and any optional lightweight `record_analyze` helper. Use `record_list`/`record_get` only for browse samples. Use `record_analyze` only as a lightweight non-default statistics helper when a compact grouped result is enough. Task/comment work stays in [$qingflow-task-ops](../qingflow-task-ops/SKILL.md).
20
- For analysis-style tasks, prefer a concrete time range or business filter. If the user did not give one and the data could be large, ask for scope instead of trying an unbounded historical scan.
21
-
22
- ## Execution Modes
23
-
24
- Choose the lightest mode that can still support a trustworthy conclusion:
25
-
26
- 1. `client_python_from_record_access`
27
- - Default and preferred path
28
- - Use `record_access` directly after `app_get -> record_browse_schema_get`
29
- - Read CSV shard files from `files[].local_path` with Python
30
- - Treat `complete=true` and `safe_for_final_conclusion=true` as required before giving a full-scope final conclusion
31
-
32
- 2. `lightweight_record_analyze`
33
- - Optional helper for small grouped summaries or quick sanity checks
34
- - Still requires `app_get -> record_browse_schema_get` first
35
- - Do not make this the default agent path
36
-
37
- 3. `cross_app_manual_reconcile`
38
- - Use when the question depends on joining multiple apps, organization-history alias mapping, or other business logic that current public tools do not express directly
39
- - Be explicit that the conclusion is based on manual reconciliation rules, not one single app's CSV access
40
-
41
- ## Fallback Ladder
42
-
43
- Trigger a fallback when any of these are true:
44
-
45
- - `record_access` returns `complete=false`, `truncated=true`, or `safe_for_final_conclusion=false`
46
- - the target view is unsupported for data access
47
- - field semantics are ambiguous enough that local aggregation would be misleading
48
- - the question requires cross-app reconciliation
49
- - the question depends on organization-tree scope, historical department aliases, or other business rules that are not first-class MCP filters
50
-
51
- When you fall back:
52
-
53
- 1. Keep the standard read order: `app_get -> record_browse_schema_get`
54
- 2. State which fallback mode you chose
55
- 3. State whether the result is still full-scope or only a verified subset
56
- 4. State the time field, organization scope, and any alias mapping used
57
- 5. Prefer concrete numbers plus a conservative conclusion over broad wording
58
-
59
- ---
14
+ ```text
15
+ app_get -> record_browse_schema_get(view_id=...) -> record_access -> Python/pandas -> final answer
16
+ ```
60
17
 
61
- ## DSL Contract
18
+ `record_access` may appear either as an MCP tool or as a CLI subcommand. If the active client does not expose an MCP `record_access` tool, do not switch to aggregate helpers. Use the CLI entry instead:
62
19
 
63
- ### `record_access` Contract
20
+ ```bash
21
+ qingflow record --help
22
+ qingflow record access --help
23
+ qingflow record access --app-key APP_KEY --view-id VIEW_ID --columns-file columns.json --where-file where.json --order-by-file order_by.json --json
24
+ ```
64
25
 
65
- Use `record_access` to fetch detail rows into local CSV shards. It does not analyze, aggregate, or return large `items`.
26
+ The CLI command is under the `record` command group, so the discovery path is: first inspect `qingflow record --help`, then inspect `qingflow record access --help`.
27
+
28
+ ## Hard Rules
29
+
30
+ - Never start analysis from `record_list`, export, QingBI, or guessed field ids.
31
+ - Never conclude `record_access` is unavailable just because it is not visible as a top-level MCP tool; check the CLI path `qingflow record access`.
32
+ - Never call `record_access` before `record_browse_schema_get`.
33
+ - Use only field ids returned by `record_browse_schema_get.fields`.
34
+ - If `app_get.accessible_views[].analysis_supported=false`, do not use that view for `record_access`.
35
+ - Prefer an explicit time range or business filter. If the user gives none and the table may be large, ask for scope or use a clearly provided month/quarter/year.
36
+ - Read every CSV shard in `record_access.files[].local_path` with Python. Do not print raw CSV or load raw rows into model context.
37
+ - Before final analysis, run a field-quality profile in pandas: row count, null rate, distinct count, and period coverage for candidate grouping fields.
38
+ - Do not use a high-missing field as the main conclusion dimension. If a candidate dimension is sparse, downgrade it to an `已填写样本观察` and choose a cleaner semantic fallback when available.
39
+ - Full final conclusions require `record_access.complete=true` and `record_access.safe_for_final_conclusion=true`.
40
+ - `record_list` is only for sample inspection after the aggregate result is understood.
41
+ - `record_get` is only for single-record detail verification, logs, references, images, or readable attachments. Read images from `media_assets.items[].local_path`; read documents/tables from `file_assets.items[].local_path` and `extraction.text_path`.
42
+ - `record_export_direct` is only for explicit export/download/Excel requests.
43
+ - `chart_get` / QingBI is only for user-provided report URLs or chart ids.
44
+
45
+ ## Tool Selection
46
+
47
+ | Need | Tool |
48
+ |---|---|
49
+ | Batch analysis, statistics, comparison, report | `record_access -> Python` |
50
+ | Browse a few example rows | `record_list` |
51
+ | Inspect one record, logs, references, images, attachments | `record_get` |
52
+ | User asks to export/download/Excel | `record_export_direct` |
53
+ | User gives report URL or chart id | `chart_get` |
54
+ | Todo/workflow task actions | `$qingflow-task-ops` |
55
+
56
+ ## Standard Procedure
57
+
58
+ 1. Run `app_get` and choose a table-style `view_id` from `accessible_views`.
59
+ 2. Run `record_browse_schema_get(app_key, view_id)`.
60
+ 3. Decide metric intent before fetching data: `count`, `sum`, `avg`, `distinct_count`, ratio, ranking, trend, or comparison.
61
+ 4. Choose minimal `record_access.columns`, plus `where` and `order_by`.
62
+ 5. Run `record_access` through MCP if visible, otherwise through `qingflow record access`.
63
+ 6. Read all returned CSV files in Python; use `fields[]` only when field-id metadata is needed.
64
+ 7. Run field-quality checks for all candidate dimensions.
65
+ 8. Compute results in pandas.
66
+ 9. Report numbers with scope, field choices, field-quality caveats, completeness, and business assumptions.
67
+
68
+ Use field-id DSLs only:
66
69
 
67
70
  ```json
68
71
  {
@@ -74,156 +77,61 @@ Use `record_access` to fetch detail rows into local CSV shards. It does not anal
74
77
  }
75
78
  ```
76
79
 
77
- Then run Python against every `files[].local_path`. CSV columns are stable: `record_id`, then `field_<field_id>`. Use `fields[]` plus `metadata_files.schema` / `metadata_files.readme` in the same output directory to map titles and types, especially when reusing the CSV later.
78
-
79
- - Never ask for `page`, `page_size`, `limit`, or `max_rows`; `record_access` owns paging internally and follows the backend's native paging capability.
80
- - Never treat backend `searchQueIds` as column selection; it is only a full-text search scope.
81
- - If multiple CSV files are returned, read them all.
82
- - If `status=needs_scope`, no CSV was written; use `scope.suggested_time_fields` / `scope.recommended_where_examples` to ask for or apply a time/business range, then call `record_access` again.
83
- - If `status=partial`, read the returned files only as a limited subset; state the limitation and do not present a full-population final conclusion.
84
- - If `complete=false` or `safe_for_final_conclusion=false`, downgrade the answer and disclose the limitation.
85
- - `record_export_direct` is only for explicit export/download/Excel requests, not default analysis.
86
- - QingBI/report reads are only for user-provided report URLs or `chart_id`; do not create or use reports as the default analysis path.
87
-
88
- ### `record_analyze` Lightweight DSL
80
+ Never pass `page`, `page_size`, `limit`, `max_rows`, `timeout`, or `profile` to `record_access`.
89
81
 
90
- ### DSL FORMAT (CRITICAL read this FIRST)
82
+ CSV columns are already readable and field-id anchored, for example `项目状态__field_343283094`. Do not expect separate `schema.json` or `README.md` files.
91
83
 
92
- ### Correct vs Wrong learn from these before building ANY DSL
84
+ For CLI use, write JSON argument files instead of embedding large JSON in shell text:
93
85
 
94
- **dimension item:**
95
- ```
96
- ✅ CORRECT: { "field_id": 9500572, "alias": "报价类型" }
97
- WRONG: 9500572 ← bare integer, not a dict
98
- WRONG: "报价类型" ← string, not a dict
99
- ❌ WRONG: { "field_id": 9500572, "title": "报价类型" } "title" is forbidden
86
+ ```bash
87
+ qingflow record access \
88
+ --app-key APP_KEY \
89
+ --view-id system:all \
90
+ --columns-file columns.json \
91
+ --where-file where.json \
92
+ --order-by-file order_by.json \
93
+ --json
100
94
  ```
101
95
 
102
- **metric item — the key is `op`, NOT `type`/`agg`/`aggregation`:**
103
- ```
104
- ✅ CORRECT: { "op": "count", "alias": "记录数" }
105
- ✅ CORRECT: { "op": "sum", "field_id": 7, "alias": "总金额" }
106
- ❌ WRONG: { "type": "count" } ← "type" is NOT a valid key
107
- ❌ WRONG: { "agg": "count" } ← "agg" is NOT a valid key
108
- ❌ WRONG: { "aggregation": "count" } ← "aggregation" is NOT a valid key
109
- ```
96
+ ## Status Handling
110
97
 
111
- **filter item the key is `op`, NOT `operator`:**
112
- ```
113
- ✅ CORRECT: { "field_id": 2, "op": "between", "value": ["2024-03-01", "2024-03-31"] }
114
- ✅ CORRECT: { "field_id": 5, "op": "eq", "value": "已完成" }
115
- ❌ WRONG: { "field_id": 2, "operator": "between", "value": [...] } ← "operator" is forbidden
116
- ❌ WRONG: { "field_id": 2, "op": ">=", "value": "2024-03-01" } ← ">=" is not valid, use "gte"
117
- ```
98
+ Read `record_access.status` before reading files or writing conclusions.
118
99
 
119
- **sort item:**
120
- ```
121
- CORRECT: { "by": "记录数", "order": "desc" } ← "by" references an alias
122
- WRONG: { "by": 9500572, "order": "desc" } ← field_id not allowed in sort
123
- ```
100
+ - `status=success`, `complete=true`, `safe_for_final_conclusion=true`: full-scope answer is allowed.
101
+ - `status=needs_scope`: no CSV was written. Ask for a time/business range or retry with a user-provided period using `scope.suggested_time_fields`.
102
+ - `status=partial`: read returned files only as a subset. Do not give a full-population conclusion.
103
+ - `complete=false`, `truncated=true`, or `safe_for_final_conclusion=false`: answer as `初步观察` or ask for a narrower scope.
124
104
 
125
- ### Allowed keys per item (ANY other key = error)
105
+ ## Business Context
126
106
 
127
- | Item | Allowed keys only |
128
- |------|-------------------|
129
- | dimension | `field_id`, `alias`, `bucket` |
130
- | metric | `op`, `field_id`, `alias` |
131
- | filter | `field_id`, `op`, `value` |
132
- | sort | `by`, `order` |
107
+ If the question mentions department, team, region, owner group, stage, product line, or a named business scope, check whether aliases or child scopes matter before concluding. Use explicit mappings provided by the user or local context; otherwise ask a short clarification.
133
108
 
134
- ### `op` values
109
+ Example: if `烈焰组` and `飓风组` are sub-departments of `北斗部门`, apply that mapping in Python and state it in the answer.
135
110
 
136
- - metrics: `count`, `sum`, `avg`, `min`, `max`, `distinct_count`
137
- - filters: `eq`, `neq`, `in`, `not_in`, `gt`, `gte`, `lt`, `lte`, `between`, `contains`, `is_null`, `not_null`
138
- - For `count` metric: do NOT pass `field_id`. For all others: `field_id` is required.
139
- - If `metrics` is omitted or `[]`, defaults to `[{"op":"count","alias":"记录数"}]`.
111
+ ## Output Shape
140
112
 
141
- ---
113
+ Default to:
142
114
 
143
- See [references/dsl-templates.md](references/dsl-templates.md) for complete copy-paste templates.
115
+ 1. `结论`
116
+ 2. `关键数据`
117
+ 3. `口径与范围`
118
+ 4. `可信度 / 限制`
144
119
 
145
- **Typical summary / distribution:**
146
- ```json
147
- {
148
- "dimensions": [{ "field_id": FIELD_ID_FROM_SCHEMA, "alias": "维度名" }],
149
- "metrics": [{ "op": "count", "alias": "记录数" }],
150
- "sort": [{ "by": "记录数", "order": "desc" }],
151
- "limit": 50,
152
- "strict_full": true
153
- }
154
- ```
120
+ Concrete numbers are mandatory. Ratios require both numerator and denominator. Trends require a time field and explicit date range. Rankings must come from a sorted pandas result.
121
+ If a requested dimension has poor quality, say so explicitly and provide the nearest reliable fallback dimension, for example platform or product instead of a mostly empty module field.
155
122
 
156
- **Typical time-filter / trend:**
157
- ```json
158
- {
159
- "dimensions": [{ "field_id": TIME_FIELD_ID, "alias": "月份", "bucket": "month" }],
160
- "metrics": [{ "op": "count", "alias": "记录数" }],
161
- "filters": [{ "field_id": TIME_FIELD_ID, "op": "between", "value": ["2024-03-01", "2024-03-31"] }],
162
- "sort": [{ "by": "月份", "order": "asc" }],
163
- "limit": 24,
164
- "strict_full": true
165
- }
166
- ```
123
+ ## References
167
124
 
168
- Top-level arguments:
125
+ Load only what is needed:
169
126
 
170
- - `app_key`: required.
171
- - `dimensions`: `[]` = whole-table summary; `[{...}]` = grouped.
172
- - `strict_full`: `true` for final conclusions. `false` allows partial results.
173
- - `limit`: limits returned rows only, not scan scope.
174
- - `view_id`: the canonical browse selector. Prefer choosing it from `app_get.accessible_views`.
175
- - Prefer `view_id` entries where `analysis_supported=true`. If a view is `boardView` or `ganttView`, switch to a system or table-style custom view before calling `record_access` or `record_analyze`.
176
- - If a chosen `view_id` is `custom:*`, treat the output as analysis over an unverified saved-filter scope unless `verification.view_filter_verified=true`. For critical conclusions, prefer `system:all` plus explicit filters in the DSL.
177
- - `bucket` in dimensions: only for `suggested_time_fields`. Values: `day`/`week`/`month`/`quarter`/`year`/`null`.
178
-
179
- ---
180
-
181
- ## RULES
182
-
183
- - 渗透率 / 转化率 / 占比类结论必须先定义分子和分母。
184
- - Do not claim a metric you did not query.
185
- - Derived ratios must be computed outside the DSL.
186
- - Before choosing a DSL shape, first decide whether the question needs `count`, `sum`, `avg`, `distinct_count`, `ratio`, or `ranking`.
187
- - Rankings must come from structured sorted results.
188
- - For partial answers, explicitly disclose which parts are complete and which parts remain unresolved.
189
- - Complex answers should default to `先结构、后解读`.
190
- - `between`: pass a two-item array.
191
- - Sort entries must reference an alias already defined in `dimensions` or `metrics`.
192
- - Final wording should stay as close as possible to schema titles.
193
- - Do not pass field titles, aliases, or guessed ids.
194
- - If `completeness.statement_scope=returned_groups_only` or `completeness.rows_truncated=true`, downgrade wording to returned groups only.
195
- - One data-access request per coherent dataset. Multiple small Python computations over the same CSV files are fine.
196
- - `record_list` is not the basis for final statistics. Use it only for browse/sample inspection and disclose that scope if quoted.
197
- - Set `alias` for any metric you will sort by, compare, or quote.
198
-
199
- ---
200
-
201
- ## OUTPUT
202
-
203
- - Final answer must show concrete numbers.
204
- - Final answer must state which execution mode was used whenever the answer is not the default `client_python_from_record_access`
205
- - If `result.rows` exists, list each returned row; if there are more than 20 rows, show Top 20 and say so.
206
- - 占比 = 行指标值 / `result.totals.metric_totals` 总值;如 `metric_totals` 缺失,用各行之和作分母。
207
- - Prefer the structured `ranking` block when it exists.
208
- - `safe_for_final_conclusion=true` → `全量可信结论`
209
- - Otherwise → `初步观察`
210
- - `rows_truncated=true` → 用 `前 N 个分组`, 不用 `全部`/`所有`
211
- - If you used a fallback mode or `record_access.safe_for_final_conclusion=false`, explicitly disclose:
212
- - whether this is full-scope or a manually curated subset
213
- - which time field was used
214
- - which organization scope was used
215
- - whether any historical department aliases or cross-app joins were applied
127
+ - Data access status machine: [references/data-access-playbook.md](references/data-access-playbook.md)
128
+ - Python/pandas templates: [references/pandas-recipes.md](references/pandas-recipes.md)
129
+ - Analysis patterns: [references/analysis-patterns.md](references/analysis-patterns.md)
130
+ - Business mappings and scope: [references/business-context.md](references/business-context.md)
131
+ - Confidence and final wording: [references/confidence-reporting.md](references/confidence-reporting.md)
132
+ - Common mistakes: [references/analysis-gotchas.md](references/analysis-gotchas.md)
133
+ - Report templates: [references/report-format.md](references/report-format.md)
216
134
 
217
135
  ## Feedback Escalation
218
136
 
219
- - If the desired analysis still cannot be completed because of missing capability, unsupported analysis shape, or an obviously awkward workflow after reasonable attempts, summarize the exact gap.
220
- - Ask whether the user wants you to submit product feedback.
221
- - Only after explicit user confirmation, call `feedback_submit`.
222
-
223
- ## Resources
224
-
225
- - Shared public-surface baseline: [public-surface-sync.md](../qingflow-app-user/references/public-surface-sync.md)
226
- - DSL templates: [references/dsl-templates.md](references/dsl-templates.md)
227
- - Analysis patterns: [references/analysis-patterns.md](references/analysis-patterns.md)
228
- - Confidence reporting: [references/confidence-reporting.md](references/confidence-reporting.md)
229
- - Analysis gotchas: [references/analysis-gotchas.md](references/analysis-gotchas.md)
137
+ If the desired analysis cannot be completed because of missing capability, unsupported data shape, or an awkward workflow after reasonable attempts, summarize the exact gap and ask whether to submit product feedback. Only after explicit user confirmation, call `feedback_submit`.
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Qingflow Record Analysis"
3
- short_description: "Analyze Qingflow record data with schema-first DSL execution"
4
- default_prompt: "Use $qingflow-record-analysis for grouped distributions, ratios, rankings, trends, and final statistical conclusions in Qingflow apps. Start with app_get and record_browse_schema_get, run record_access with field_id-based columns/where/order_by, then analyze the returned CSV shard files with Python. Treat record_list as sample-only and record_analyze as a lightweight non-default helper."
3
+ short_description: "Analyze Qingflow record data with record_access CSV and Python"
4
+ default_prompt: "Use $qingflow-record-analysis for grouped distributions, ratios, rankings, trends, and final statistical conclusions in Qingflow apps. Start with app_get and record_browse_schema_get, run record_access with field_id-based columns/where/order_by, then analyze every returned CSV shard with Python. Always run field-quality checks before choosing final grouping dimensions, and downgrade sparse dimensions to filled-sample observations. If record_access is not visible as an MCP tool, discover and use the CLI path qingflow record --help -> qingflow record access --help. Treat record_list as sample-only."
@@ -1,150 +1,91 @@
1
1
  # Analysis Gotchas
2
2
 
3
- ## Do not skip schema
3
+ ## Do Not Skip Schema
4
4
 
5
- If the task is analysis-style and you jump straight to `record_list`, `record_export_direct`, or `record_analyze`, you are already off the stable path.
6
-
7
- Correct recovery:
5
+ Correct path:
8
6
 
9
7
  1. `app_get`
10
8
  2. `record_browse_schema_get`
11
- 3. inspect the schema and choose fields
12
- 4. run `record_access`
13
- 5. use Python over the returned CSV shards
14
-
15
- The schema here is applicant-node visible-only. If a field is absent, treat it as not available to the current user rather than switching to guessed ids or builder-side memory.
16
-
17
- ## Normalize relative time phrases before building the DSL.
18
-
19
- Examples:
20
-
21
- - `最近一个完整自然月` -> convert to an explicit full-month date range
22
- - `上个月` -> convert to a concrete month range
23
- - `最近30天` -> convert to exact start/end dates
24
-
25
- Do not pass vague time phrases or impossible dates into MCP.
26
-
27
- ## Do not treat paged list output as full data
28
-
29
- `record_list` can hit:
30
-
31
- - `row_cap=200`
32
- - `row_cap_hit=true`
33
- - `sample_only=true`
34
-
35
- When this happens, it is sample-only evidence.
36
-
37
- It is not acceptable to use that result alone for:
38
-
39
- - 平均值
40
- - 占比
41
- - 排名
42
- - 趋势
43
- - 地域分布
44
- - “基于全部数据”的 business insight
9
+ 3. `record_access`
10
+ 4. Python
45
11
 
46
- ## Do not mix full analyze totals with sample rows
12
+ `record_browse_schema_get` returns readable fields for the selected view. Missing fields are permission or view-scope boundaries, not invitations to guess hidden ids.
47
13
 
48
- If `record_access` or `record_analyze` gives full-population coverage, but list rows are capped, do not merge them into one final statement.
14
+ ## Do Not Use Export For Analysis
49
15
 
50
- Split them into:
16
+ Export tools are for user-requested files. Analysis uses `record_access` because it returns structured completeness and compact field metadata.
51
17
 
52
- - `全量可信结论`
53
- - `样本观察`
18
+ ## Do Not Treat `record_list` As Full Data
54
19
 
55
- ## Do not present truncated grouped rows as a full grouped list
20
+ `record_list` is sample/browse only. It can be capped and should not justify:
56
21
 
57
- If `completeness.rows_truncated=true` or `completeness.statement_scope=returned_groups_only`:
22
+ - average
23
+ - share
24
+ - ranking
25
+ - trend
26
+ - regional distribution
27
+ - "all data" insights
58
28
 
59
- - do not say `各部门`
60
- - do not say `所有分组`
61
- - do not say `完整名单`
29
+ ## Do Not Control Paging
62
30
 
63
- Correct recovery:
31
+ `record_access` owns paging internally.
64
32
 
65
- - do not describe the answer as complete grouped coverage
66
- - keep the wording inside the returned group scope
33
+ Do not invent:
67
34
 
68
- ## Do not guess fields under ambiguity
35
+ - `page`
36
+ - `page_size`
37
+ - `limit`
38
+ - `requested_pages`
39
+ - `scan_max_pages`
40
+ - `max_rows`
41
+ - `timeout`
69
42
 
70
- If the field is uncertain:
43
+ ## Do Not Print Raw CSV
71
44
 
72
- - do not bounce across tools
73
- - do not guess ids
74
- - do not switch from one read tool to another by trial and error
75
- - do not keep retrying different guessed field names in a loop
45
+ Read CSV files with pandas. Summarize computed results, not raw rows.
76
46
 
77
- Correct recovery:
47
+ ## Do Not Rename Source Files
78
48
 
79
- 1. `record_browse_schema_get`
80
- 2. if several plausible candidates remain, ask the user to confirm from a short list
81
- 3. build the DSL only after the field is clear
49
+ CSV columns are directly readable and field-id anchored: `record_id`, `<字段标题>__field_<id>`. Use those columns directly in pandas.
82
50
 
83
- If the intended field is absent from the schema altogether, stop and explain that it is not visible in the current applicant-node permission scope.
51
+ ## Do Not Trust Sparse Dimensions
84
52
 
85
- Examples of the right recovery question:
53
+ Before final grouping, run a field-quality profile. If the selected field is mostly blank, say so and downgrade the claim.
86
54
 
87
- - “我找到两个可能的字段:`线索来源`、`来源渠道`。你要按哪个字段统计?”
88
- - “目前最像‘来源’的字段有这三个:`来源`、`来源渠道`、`获客来源`。请确认你要按哪个字段分析。”
55
+ Rules of thumb:
89
56
 
90
- ## Do not try to control paging manually
57
+ - Overall blank rate above 40%: not a primary conclusion dimension.
58
+ - Any compared period blank rate above 80%: do not use that field for period comparison.
59
+ - A sparse field can support only `已填写样本观察`.
91
60
 
92
- `record_access` hides paging and follows the backend's native paging capability. `record_analyze` hides paging and scan budget on purpose.
61
+ If the user asks for a semantic field such as `板块`, test nearby candidates like product, platform, module, stage, source, owner, or department before concluding.
93
62
 
94
- - Do not invent `page_size`
95
- - Do not invent `requested_pages`
96
- - Do not invent `scan_max_pages`
97
- - Do not rename CSV columns before analysis; use `metadata_files.schema` / `metadata_files.readme` to interpret `field_<id>` columns.
98
- - Do not invent `auto_expand_pages`
99
- - Do not invent `max_rows`
63
+ ## Do Not Hide Incomplete Access
100
64
 
101
- When the result is incomplete:
65
+ If `needs_scope`, no CSV exists. Ask for a time/business scope.
102
66
 
103
- 1. narrow the scope with views or filters
104
- 2. reduce the analysis problem into smaller field_id-based access requests
105
- 3. keep the answer at `初步观察` or `部分结果` if completeness is still not enough
67
+ If `partial`, use only subset wording and avoid full-population claims.
106
68
 
107
- ## Do not guess metric semantics from loose business wording
69
+ If field meaning is ambiguous, ask the user to confirm from a short list.
108
70
 
109
- Before building the DSL, first decide whether the question needs:
71
+ ## Do Not Guess Metrics
110
72
 
111
- - `count`
112
- - `sum`
113
- - `avg`
114
- - `distinct_count`
115
- - a ratio with numerator + denominator
116
- - a sorted ranking result
73
+ Before fetching data, decide whether the request needs count, sum, average, distinct count, ratio, ranking, trend, or comparison.
117
74
 
118
- Do not jump straight from words like `数量`, `人数`, `单量`, or `金额` to one assumed metric.
75
+ ## Do Not Call A Ratio Without Denominator
119
76
 
120
- ## Do not hide partial completion
77
+ For penetration, conversion, or share:
121
78
 
122
- If the user asked for several outputs and only part of them is stable:
123
-
124
- - say which parts are complete
125
- - say which parts are still unresolved
126
- - do not present the answer as fully finished
127
-
128
- If `record_access.status=needs_scope`, stop and ask for a time/business range; no CSV was written. If `record_access.status=partial`, read the returned CSV only as a partial subset and name the limitation before any numbers.
129
-
130
- ## Do not send unsupported formula or div-style metrics into `record_analyze`.
131
-
132
- Examples to avoid:
133
-
134
- - `{"op":"div", ...}`
135
- - metric items with `formula`, `expr`, `numerator`, or `denominator`
136
-
137
- Correct recovery:
138
-
139
- 1. query the source metrics with separate DSLs
140
- 2. confirm both sides are complete and compatible
141
- 3. compute the derived ratio outside MCP in the reasoning layer
79
+ 1. define numerator
80
+ 2. define denominator
81
+ 3. query compatible source data
82
+ 4. compute in Python
83
+ 5. report numerator and denominator
142
84
 
143
- ## Do not call something a ratio without the denominator
85
+ ## Normalize Relative Dates
144
86
 
145
- If the user asks for penetration / conversion / 占比:
87
+ Convert relative phrases into exact ranges before `record_access`.
146
88
 
147
- 1. define numerator
148
- 2. define denominator
149
- 3. query both sides explicitly
150
- 4. only then compute and report the ratio
89
+ - `今年5月` -> exact May 1 to May 31 in the current year
90
+ - `去年同期` -> same date range in previous year
91
+ - `最近30天` -> exact rolling start/end dates