@josephyan/qingflow-app-user-mcp 0.2.0-beta.16 → 0.2.0-beta.18
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 +2 -2
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/skills/qingflow-app-user/SKILL.md +7 -6
- package/skills/qingflow-app-user/references/data-gotchas.md +5 -6
- package/skills/qingflow-app-user/references/record-patterns.md +11 -12
- package/skills/qingflow-record-analysis/SKILL.md +171 -24
- package/skills/qingflow-record-analysis/agents/openai.yaml +2 -2
- package/skills/qingflow-record-analysis/references/analysis-gotchas.md +29 -15
- package/skills/qingflow-record-analysis/references/analysis-patterns.md +36 -20
- package/skills/qingflow-record-analysis/references/confidence-reporting.md +7 -7
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/server.py +2 -0
- package/src/qingflow_mcp/server_app_user.py +3 -2
- package/src/qingflow_mcp/tools/record_tools.py +863 -171
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
Install:
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
6
|
+
npm install @josephyan/qingflow-app-user-mcp@0.2.0-beta.18
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
Run:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.
|
|
12
|
+
npx -y -p @josephyan/qingflow-app-user-mcp@0.2.0-beta.18 qingflow-app-user-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Environment:
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
|
@@ -21,8 +21,8 @@ When the task is in `prod`, browser parity matters, or the user says "the page h
|
|
|
21
21
|
Primary record and data tools:
|
|
22
22
|
|
|
23
23
|
- `record_query`
|
|
24
|
+
- `record_schema_get`
|
|
24
25
|
- `record_write_plan`
|
|
25
|
-
- `record_field_resolve`
|
|
26
26
|
- `record_create`
|
|
27
27
|
- `record_get`
|
|
28
28
|
- `record_update`
|
|
@@ -90,9 +90,10 @@ Do not use builder-side tools here:
|
|
|
90
90
|
- Use `task_statistics` before `task_list` when the user only needs counts
|
|
91
91
|
- Use `task_list_grouped` when worksheet or group buckets matter
|
|
92
92
|
- Use `task_urge` only when the user clearly wants a reminder sent for a pending task
|
|
93
|
-
- Use `
|
|
93
|
+
- Use `record_schema_get` when field selectors are ambiguous; if the task then turns into analysis, switch to `$qingflow-record-analysis`
|
|
94
94
|
- For precise record lookup, use `record_get` when `apply_id` is known
|
|
95
|
-
- Use `
|
|
95
|
+
- Use `record_schema_get` when the user gives field titles and you are not fully sure about the exact schema; do not guess ambiguous fields silently
|
|
96
|
+
- If the task has already shifted into analysis and `record_schema_get` still leaves multiple plausible fields, stop and ask the user to confirm the intended field instead of continuing to try read tools in a loop
|
|
96
97
|
- Treat field selectors as schema-first and platform-generic. Prefer exact field titles, then neutral aliases such as `创建时间`, `新增时间`, `负责人`, `部门`, `时间`, or `阶段` only when the tool resolves them clearly. Do not assume CRM shorthand like `销售`, `商机阶段`, `客户全称`, or similar domain shortcuts apply across arbitrary Qingflow apps
|
|
97
98
|
- For updates, inspect current data first unless the user already provided the exact target and patch
|
|
98
99
|
- For deletes, confirm the exact record scope and report the deleted ids
|
|
@@ -124,9 +125,9 @@ When the user asks for demo data, seed, smoke data, or mock data:
|
|
|
124
125
|
|
|
125
126
|
## Response Interpretation
|
|
126
127
|
|
|
127
|
-
- `record_query(
|
|
128
|
-
- `record_query(
|
|
129
|
-
- For
|
|
128
|
+
- `record_query(query_mode="list")` is browse/sample output, not a final analysis result
|
|
129
|
+
- If `record_query(query_mode="list")` reports `row_cap_hit`, `sample_only`, or capped rows, do not present it as full data
|
|
130
|
+
- For grouped distributions, trends, or final statistical conclusions, switch to `$qingflow-record-analysis` and use `record_schema_get -> record_analyze`
|
|
130
131
|
- `record_write_plan` is static preflight, not a guarantee that submit will pass runtime linkage or visibility checks
|
|
131
132
|
- `record_create` now returns integer `apply_id`; you can pass that id directly into `record_get`, `record_update`, or `record_delete`
|
|
132
133
|
- `verify_write=true` means the tool read the record back and compared the written fields; if it returns `status=verification_failed` or `ok=false`, do not report the create or update as successful
|
|
@@ -5,14 +5,13 @@ For final statistics, grouped distributions, or insight-style analysis, use [$qi
|
|
|
5
5
|
## Counts
|
|
6
6
|
|
|
7
7
|
- Prefer `effective_count`
|
|
8
|
-
- For
|
|
9
|
-
- If `status
|
|
8
|
+
- For final analysis, inspect `record_analyze.data.completeness` and `safe_for_final_conclusion` before concluding
|
|
9
|
+
- If `record_analyze.status!=success`, treat the result as exploratory unless the user explicitly asked for a partial sample
|
|
10
10
|
- `record_query(list)` is for browsing and sample inspection. If it reports `row_cap_hit`, `sample_only`, or capped `returned_items`, do not present it as full data
|
|
11
11
|
- When coverage matters, surface:
|
|
12
12
|
- `backend_total_count`
|
|
13
13
|
- `scanned_count`
|
|
14
|
-
|
|
15
|
-
- Reuse `suggested_next_call` or `estimate.recommended_arguments` instead of inventing bigger scan settings by hand
|
|
14
|
+
- Use narrower views, filters, or smaller analysis questions instead of inventing manual scan settings by hand
|
|
16
15
|
- If the browser and MCP disagree, compare `request_route.base_url` and `request_route.qf_version` first
|
|
17
16
|
- Do not mix a full aggregate total with sample-only list detail in one sentence like “基于全部数据分析”; split the answer into `全量结论` and `样本观察`
|
|
18
17
|
|
|
@@ -25,8 +24,8 @@ For final statistics, grouped distributions, or insight-style analysis, use [$qi
|
|
|
25
24
|
|
|
26
25
|
- `record_write_plan` is static preflight only; linked visibility and runtime required rules can still reject writes
|
|
27
26
|
- `record_write_plan` now exposes `write_format.support_level`; check `full / restricted / unsupported` before attempting non-trivial writes
|
|
28
|
-
- Use `
|
|
29
|
-
- For analysis tasks, use the fixed
|
|
27
|
+
- Use `record_schema_get` when field titles are uncertain instead of guessing ids
|
|
28
|
+
- For analysis tasks, use the fixed path `record_schema_get -> record_analyze`; do not switch tools blindly after `FIELD_NOT_FOUND` or ambiguity
|
|
30
29
|
- Prefer `strict_full=true` for final statistics or business conclusions
|
|
31
30
|
- `record_create` and `record_update` can do post-write verification with `verify_write=true`; use that for complex, subtable, or production writes
|
|
32
31
|
- `apply_id` is normalized to an integer; pass it directly into later record tools
|
|
@@ -9,9 +9,9 @@ Use `record_query` first when:
|
|
|
9
9
|
- the user only gives a title or business key
|
|
10
10
|
- the target record id is unknown
|
|
11
11
|
- updates or deletes need confirmation
|
|
12
|
-
-
|
|
12
|
+
- ordinary list browsing or spot checks are needed
|
|
13
13
|
|
|
14
|
-
Use
|
|
14
|
+
Use [$qingflow-record-analysis](/Users/yanqidong/Documents/qingflow-next/.codex/skills/qingflow-record-analysis/SKILL.md) when:
|
|
15
15
|
|
|
16
16
|
- field titles may be ambiguous
|
|
17
17
|
- filters are still in natural-language shape
|
|
@@ -22,14 +22,13 @@ Use `record_query_plan` first when:
|
|
|
22
22
|
|
|
23
23
|
## Final analysis pattern
|
|
24
24
|
|
|
25
|
-
1. Run `
|
|
26
|
-
2.
|
|
27
|
-
3. Run `
|
|
28
|
-
4. Run `
|
|
29
|
-
5.
|
|
30
|
-
6.
|
|
31
|
-
7. If
|
|
32
|
-
8. If list rows are sample-only, separate the answer into:
|
|
25
|
+
1. Run `record_schema_get`
|
|
26
|
+
2. Generate one or more field_id-based DSLs
|
|
27
|
+
3. Run `record_analyze(strict_full=true)` for summary/distribution/trend/cross analysis
|
|
28
|
+
4. Run `record_query(query_mode="list")` only if you still need sample rows or examples
|
|
29
|
+
5. Report `backend_total_count`, `scanned_count`, and whether the result is safe for a final conclusion
|
|
30
|
+
6. If `status=error` or `safe_for_final_conclusion=false`, stop at “partial result” instead of presenting a final business conclusion
|
|
31
|
+
7. If list rows are sample-only, separate the answer into:
|
|
33
32
|
- `全量可信结论`
|
|
34
33
|
- `样本观察(不作为最终结论)`
|
|
35
34
|
- optional `待验证假设`
|
|
@@ -42,12 +41,12 @@ Do not do this:
|
|
|
42
41
|
2. Get `200` rows back
|
|
43
42
|
3. Report平均值、占比、地域分布 as if they were based on all records
|
|
44
43
|
|
|
45
|
-
This is not acceptable because the list endpoint can be capped. Use `
|
|
44
|
+
This is not acceptable because the list endpoint can be capped. Use `record_schema_get -> record_analyze` first, then treat list rows as sample-only evidence.
|
|
46
45
|
|
|
47
46
|
## Create pattern
|
|
48
47
|
|
|
49
48
|
1. Confirm target app
|
|
50
|
-
2. Resolve fields with `
|
|
49
|
+
2. Resolve fields with `record_schema_get` if needed. Prefer exact schema titles first; only rely on platform-neutral aliases such as `创建时间`, `负责人`, or `部门` when they resolve cleanly, and do not assume business-domain shorthand like `销售` is portable across apps
|
|
51
50
|
3. Run `record_write_plan` for non-trivial payloads or any `fields`-based write
|
|
52
51
|
4. For relation fields, query the target app first and resolve the referenced record `apply_id`
|
|
53
52
|
5. For attachments, call `file_upload_local` first and reuse the returned `attachment_value`
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: qingflow-record-analysis
|
|
3
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.
|
|
4
4
|
metadata:
|
|
5
|
-
short-description: Analyze Qingflow record data with
|
|
5
|
+
short-description: Analyze Qingflow record data with schema-first DSL execution
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Qingflow Record Analysis
|
|
@@ -19,47 +19,194 @@ Before running analysis in `prod`, confirm the intended environment and compare
|
|
|
19
19
|
|
|
20
20
|
Use these tools as the core analysis surface:
|
|
21
21
|
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
- `record_query`
|
|
25
|
-
- `record_aggregate`
|
|
22
|
+
- `record_schema_get`
|
|
23
|
+
- `record_analyze`
|
|
26
24
|
|
|
27
|
-
Use `
|
|
25
|
+
Use `record_query(query_mode="list")` or `record_get` only when you need sample rows or a specific supporting example after the main analysis path.
|
|
28
26
|
|
|
29
27
|
## Hard Rules
|
|
30
28
|
|
|
31
|
-
- Analysis tasks must start with `
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
- Analysis tasks must start with `record_schema_get`
|
|
30
|
+
- Build one or more small DSLs, then run `record_analyze` separately for each question
|
|
31
|
+
- DSL field references must use `field_id` only
|
|
32
|
+
- If the schema still leaves multiple plausible fields, stop and ask the user to confirm from a short candidate list instead of guessing
|
|
33
|
+
- Do not keep retrying different guessed field names in a loop
|
|
36
34
|
- `record_query(list)` is never the basis for a final statistical conclusion
|
|
37
35
|
- If `record_query(list)` reports `row_cap_hit`, `sample_only`, capped `returned_items`, or compact output, treat it as sample-only evidence
|
|
38
|
-
- Do not mix full totals from `
|
|
39
|
-
-
|
|
40
|
-
- For final conclusions, prefer `strict_full=true`
|
|
36
|
+
- Do not mix full totals from `record_analyze` with sample-only list observations as one combined `全量结论`
|
|
37
|
+
- Do not manually tune paging or scan-budget parameters for analysis; `record_analyze` hides them
|
|
38
|
+
- For final conclusions, prefer `strict_full=true`
|
|
41
39
|
|
|
42
40
|
## Standard Operating Order
|
|
43
41
|
|
|
44
42
|
For analysis:
|
|
45
43
|
|
|
46
44
|
1. Confirm target app and environment
|
|
47
|
-
2.
|
|
48
|
-
3.
|
|
49
|
-
4.
|
|
50
|
-
5. Run `
|
|
51
|
-
6. Run `
|
|
52
|
-
7.
|
|
53
|
-
8. Before answering, separate:
|
|
45
|
+
2. Run `record_schema_get`
|
|
46
|
+
3. Inspect fields, aliases, suggested dimensions, suggested metrics, and suggested time fields
|
|
47
|
+
4. Generate one or more field_id-based DSLs
|
|
48
|
+
5. Run `record_analyze` once per DSL
|
|
49
|
+
6. Run `record_query(query_mode="list")` only if you still need sample rows, examples, or manual inspection
|
|
50
|
+
7. Before answering, separate:
|
|
54
51
|
- `全量可信结论`
|
|
55
52
|
- `样本观察`
|
|
56
53
|
- `待验证假设`
|
|
57
54
|
|
|
55
|
+
## DSL Contract
|
|
56
|
+
|
|
57
|
+
Use `record_schema_get` as the source of truth for every DSL field reference:
|
|
58
|
+
|
|
59
|
+
- Use `fields[].field_id` in `dimensions[].field_id`, `metrics[].field_id`, and `filters[].field_id`
|
|
60
|
+
- Treat `suggested_dimensions`, `suggested_metrics`, and `suggested_time_fields` as hints, not as executable DSL by themselves
|
|
61
|
+
- Do not pass field titles, aliases, or guessed ids where `field_id` is required
|
|
62
|
+
|
|
63
|
+
The `record_analyze` call should be built from this argument shape:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"app_key": "APP_1",
|
|
68
|
+
"dimensions": [],
|
|
69
|
+
"metrics": [],
|
|
70
|
+
"filters": [],
|
|
71
|
+
"sort": [],
|
|
72
|
+
"limit": 50,
|
|
73
|
+
"strict_full": true,
|
|
74
|
+
"view_key": null,
|
|
75
|
+
"view_name": null,
|
|
76
|
+
"output_profile": "normal"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Top-level argument rules:
|
|
81
|
+
|
|
82
|
+
- `app_key`: required. The target Qingflow app.
|
|
83
|
+
- `dimensions`: required list. Use `[]` for whole-table summary. Use one item per grouping dimension for grouped analysis.
|
|
84
|
+
- `metrics`: optional list. If omitted or empty, `record_analyze` defaults to a single `count` metric.
|
|
85
|
+
- `filters`: optional list. Filters restrict the analyzed dataset before results are interpreted.
|
|
86
|
+
- `sort`: optional list. Sorting applies to result rows, not raw source rows.
|
|
87
|
+
- `limit`: positive integer. It only limits returned result rows; it does not reduce the internal scan scope.
|
|
88
|
+
- `strict_full`: boolean. Prefer `true` for final conclusions. If `true`, incomplete scans return an error; if `false`, incomplete scans return partial results.
|
|
89
|
+
- `view_key` / `view_name`: optional. Use a view to narrow scope before analysis. Prefer `view_key` when both are available.
|
|
90
|
+
- `output_profile`: `normal` or `verbose`. Prefer `normal` unless you are debugging completeness or route issues.
|
|
91
|
+
|
|
92
|
+
Item contracts:
|
|
93
|
+
|
|
94
|
+
- `dimensions` item:
|
|
95
|
+
- shape: `{ "field_id": 2, "alias": "状态", "bucket": null }`
|
|
96
|
+
- `field_id`: required integer from `record_schema_get`
|
|
97
|
+
- `alias`: optional but recommended; if omitted, the field title becomes the alias
|
|
98
|
+
- `bucket`: optional; allowed values are `day`, `week`, `month`, `quarter`, `year`, or omitted / `null`
|
|
99
|
+
- `bucket` may only be used on fields from `suggested_time_fields`
|
|
100
|
+
- `metrics` item:
|
|
101
|
+
- shape: `{ "op": "sum", "field_id": 7, "alias": "总金额" }`
|
|
102
|
+
- `op`: one of `count`, `sum`, `avg`, `min`, `max`, `distinct_count`
|
|
103
|
+
- `field_id`: required for `sum`, `avg`, `min`, `max`, `distinct_count`; do not pass it for `count`
|
|
104
|
+
- `alias`: optional but strongly recommended because `sort.by` must reference aliases
|
|
105
|
+
- `filters` item:
|
|
106
|
+
- shape: `{ "field_id": 2, "op": "eq", "value": "进行中" }`
|
|
107
|
+
- `field_id`: required integer from `record_schema_get`
|
|
108
|
+
- `op`: optional; defaults to `eq`
|
|
109
|
+
- supported ops: `eq`, `neq`, `in`, `not_in`, `gt`, `gte`, `lt`, `lte`, `between`, `contains`, `is_null`, `not_null`
|
|
110
|
+
- value rules:
|
|
111
|
+
- `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`: pass a single scalar value
|
|
112
|
+
- `in`, `not_in`: pass an array
|
|
113
|
+
- `between`: pass a two-item array like `[min, max]`
|
|
114
|
+
- `is_null`, `not_null`: omit `value`
|
|
115
|
+
- `sort` item:
|
|
116
|
+
- shape: `{ "by": "记录数", "order": "desc" }`
|
|
117
|
+
- `by`: required and must reference an alias already defined in `dimensions` or `metrics`
|
|
118
|
+
- `order`: optional; use `asc` or `desc`; default is `asc`
|
|
119
|
+
- do not sort by raw field title or `field_id`
|
|
120
|
+
|
|
121
|
+
Practical rules:
|
|
122
|
+
|
|
123
|
+
- Keep one DSL focused on one question. Prefer multiple small DSLs over one overloaded request.
|
|
124
|
+
- Always set explicit aliases for metrics you may sort by, compare, or quote in the final answer.
|
|
125
|
+
- For trend analysis, use one time dimension with `bucket`, then sort by that time alias ascending.
|
|
126
|
+
- For cross analysis, use multiple `dimensions` and a small set of metrics.
|
|
127
|
+
- Do not attempt formulas, joins, having clauses, cohort analysis, or manual paging controls in this DSL.
|
|
128
|
+
|
|
129
|
+
## Minimal DSL Templates
|
|
130
|
+
|
|
131
|
+
Summary:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"dimensions": [],
|
|
136
|
+
"metrics": [
|
|
137
|
+
{ "op": "count", "alias": "记录数" }
|
|
138
|
+
],
|
|
139
|
+
"filters": [],
|
|
140
|
+
"sort": [],
|
|
141
|
+
"limit": 1,
|
|
142
|
+
"strict_full": true
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Single-dimension distribution:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"dimensions": [
|
|
151
|
+
{ "field_id": 2, "alias": "状态" }
|
|
152
|
+
],
|
|
153
|
+
"metrics": [
|
|
154
|
+
{ "op": "count", "alias": "记录数" }
|
|
155
|
+
],
|
|
156
|
+
"filters": [],
|
|
157
|
+
"sort": [
|
|
158
|
+
{ "by": "记录数", "order": "desc" }
|
|
159
|
+
],
|
|
160
|
+
"limit": 50,
|
|
161
|
+
"strict_full": true
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Time trend:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"dimensions": [
|
|
170
|
+
{ "field_id": 3, "alias": "月份", "bucket": "month" }
|
|
171
|
+
],
|
|
172
|
+
"metrics": [
|
|
173
|
+
{ "op": "count", "alias": "记录数" }
|
|
174
|
+
],
|
|
175
|
+
"filters": [],
|
|
176
|
+
"sort": [
|
|
177
|
+
{ "by": "月份", "order": "asc" }
|
|
178
|
+
],
|
|
179
|
+
"limit": 24,
|
|
180
|
+
"strict_full": true
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Two-dimensional cross analysis:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"dimensions": [
|
|
189
|
+
{ "field_id": 2, "alias": "状态" },
|
|
190
|
+
{ "field_id": 5, "alias": "负责人" }
|
|
191
|
+
],
|
|
192
|
+
"metrics": [
|
|
193
|
+
{ "op": "count", "alias": "记录数" },
|
|
194
|
+
{ "op": "sum", "field_id": 7, "alias": "总金额" }
|
|
195
|
+
],
|
|
196
|
+
"filters": [],
|
|
197
|
+
"sort": [
|
|
198
|
+
{ "by": "记录数", "order": "desc" }
|
|
199
|
+
],
|
|
200
|
+
"limit": 100,
|
|
201
|
+
"strict_full": true
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
58
205
|
## Output Gate
|
|
59
206
|
|
|
60
|
-
-
|
|
61
|
-
- If
|
|
62
|
-
- If aggregate
|
|
207
|
+
- Only write `全量可信结论` when the supporting `record_analyze` calls report `completeness.status=complete` and `safe_for_final_conclusion=true`
|
|
208
|
+
- If any key analysis call is incomplete, downgrade the answer to `初步观察` or `部分结果`
|
|
209
|
+
- If aggregate-style output is full but list evidence is sample-only, split the answer into:
|
|
63
210
|
- `全量可信结论`
|
|
64
211
|
- `样本观察(不作为最终结论)`
|
|
65
212
|
- optional `待验证假设`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
interface:
|
|
2
2
|
display_name: "Qingflow Record Analysis"
|
|
3
|
-
short_description: "Analyze Qingflow record data with
|
|
4
|
-
default_prompt: "Use $qingflow-record-analysis for grouped distributions, ratios, rankings, trends, and final statistical conclusions in Qingflow apps. Start with
|
|
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 record_schema_get, build one or more field_id-based DSLs, then run record_analyze. Treat record_query(query_mode=\"list\") as sample-only when capped, and separate full conclusions from sample observations."
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# Analysis Gotchas
|
|
2
2
|
|
|
3
|
-
## Do not skip
|
|
3
|
+
## Do not skip schema
|
|
4
4
|
|
|
5
|
-
If the task is analysis-style and you jump straight to `record_query(list)` or `
|
|
5
|
+
If the task is analysis-style and you jump straight to `record_query(query_mode="list")` or `record_analyze`, you are already off the stable path.
|
|
6
6
|
|
|
7
7
|
Correct recovery:
|
|
8
8
|
|
|
9
|
-
1. `
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
9
|
+
1. `record_schema_get`
|
|
10
|
+
2. inspect the schema and choose fields
|
|
11
|
+
3. build one or more small DSLs
|
|
12
|
+
4. run `record_analyze`
|
|
12
13
|
|
|
13
14
|
## Do not treat 200-row list output as full data
|
|
14
15
|
|
|
15
|
-
`record_query(list)` can hit:
|
|
16
|
+
`record_query(query_mode="list")` can hit:
|
|
16
17
|
|
|
17
18
|
- `row_cap=200`
|
|
18
19
|
- `row_cap_hit=true`
|
|
@@ -29,9 +30,9 @@ It is not acceptable to use that result alone for:
|
|
|
29
30
|
- 地域分布
|
|
30
31
|
- “基于全部数据”的 business insight
|
|
31
32
|
|
|
32
|
-
## Do not mix full
|
|
33
|
+
## Do not mix full analyze totals with sample rows
|
|
33
34
|
|
|
34
|
-
If
|
|
35
|
+
If `record_analyze` gives full-population coverage, but list rows are capped, do not merge them into one final statement.
|
|
35
36
|
|
|
36
37
|
Split them into:
|
|
37
38
|
|
|
@@ -45,17 +46,30 @@ If the field is uncertain:
|
|
|
45
46
|
- do not bounce across tools
|
|
46
47
|
- do not guess ids
|
|
47
48
|
- do not switch from one read tool to another by trial and error
|
|
49
|
+
- do not keep retrying different guessed field names in a loop
|
|
48
50
|
|
|
49
51
|
Correct recovery:
|
|
50
52
|
|
|
51
|
-
1. `
|
|
52
|
-
2.
|
|
53
|
+
1. `record_schema_get`
|
|
54
|
+
2. if several plausible candidates remain, ask the user to confirm from a short list
|
|
55
|
+
3. build the DSL only after the field is clear
|
|
53
56
|
|
|
54
|
-
|
|
57
|
+
Examples of the right recovery question:
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
- “我找到两个可能的字段:`线索来源`、`来源渠道`。你要按哪个字段统计?”
|
|
60
|
+
- “目前最像‘来源’的字段有这三个:`来源`、`来源渠道`、`获客来源`。请确认你要按哪个字段分析。”
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
- `suggested_next_call`
|
|
62
|
+
## Do not try to control paging manually
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
`record_analyze` hides paging and scan budget on purpose.
|
|
65
|
+
|
|
66
|
+
- Do not invent `page_size`
|
|
67
|
+
- Do not invent `requested_pages`
|
|
68
|
+
- Do not invent `scan_max_pages`
|
|
69
|
+
- Do not invent `auto_expand_pages`
|
|
70
|
+
|
|
71
|
+
When the result is incomplete:
|
|
72
|
+
|
|
73
|
+
1. narrow the scope with views or filters
|
|
74
|
+
2. reduce the analysis problem into smaller DSLs
|
|
75
|
+
3. keep the answer at `初步观察` or `部分结果` if completeness is still not enough
|
|
@@ -15,42 +15,47 @@ Use this skill when the user asks for:
|
|
|
15
15
|
|
|
16
16
|
## Canonical analysis sequence
|
|
17
17
|
|
|
18
|
-
1. `
|
|
19
|
-
2.
|
|
20
|
-
3. `
|
|
18
|
+
1. `record_schema_get`
|
|
19
|
+
2. build one or more field_id-based DSLs
|
|
20
|
+
3. `record_analyze`
|
|
21
21
|
4. `record_query(query_mode="list")` only for sample inspection
|
|
22
22
|
|
|
23
23
|
## Distribution / ratio pattern
|
|
24
24
|
|
|
25
|
-
1.
|
|
26
|
-
2.
|
|
27
|
-
3.
|
|
28
|
-
4.
|
|
29
|
-
|
|
25
|
+
1. Run `record_schema_get`
|
|
26
|
+
2. Inspect candidate fields and aliases
|
|
27
|
+
3. If several plausible candidates remain, stop and ask the user to confirm the field from a short list
|
|
28
|
+
4. Build a DSL with:
|
|
29
|
+
- one dimension
|
|
30
|
+
- `count`
|
|
31
|
+
- sort by the count alias
|
|
32
|
+
5. Run `record_analyze`
|
|
33
|
+
6. Report:
|
|
30
34
|
- `backend_total_count`
|
|
31
35
|
- `scanned_count`
|
|
32
36
|
- `safe_for_final_conclusion`
|
|
33
37
|
|
|
34
38
|
## Average / ranking pattern
|
|
35
39
|
|
|
36
|
-
1. Run `
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
1. Run `record_schema_get`
|
|
41
|
+
2. Choose one dimension field and one numeric metric field
|
|
42
|
+
3. Build a DSL with:
|
|
43
|
+
- `dimensions=[...]`
|
|
44
|
+
- `metrics=[count,sum]` or `metrics=[count,avg,min,max]`
|
|
45
|
+
4. Run `record_analyze`
|
|
46
|
+
5. Use list mode only to inspect examples after the aggregate result is understood
|
|
43
47
|
|
|
44
48
|
## Trend pattern
|
|
45
49
|
|
|
46
|
-
1. Run `
|
|
47
|
-
2.
|
|
48
|
-
3.
|
|
49
|
-
4.
|
|
50
|
+
1. Run `record_schema_get`
|
|
51
|
+
2. Choose a date/time field from `suggested_time_fields`
|
|
52
|
+
3. Build a DSL with `bucket=day|week|month|quarter|year`
|
|
53
|
+
4. Run `record_analyze`
|
|
54
|
+
5. Treat the result as final only if `safe_for_final_conclusion=true`
|
|
50
55
|
|
|
51
56
|
## Sample inspection pattern
|
|
52
57
|
|
|
53
|
-
Only use `record_query(list)` after
|
|
58
|
+
Only use `record_query(query_mode="list")` after schema/analyze when you need:
|
|
54
59
|
|
|
55
60
|
- example rows
|
|
56
61
|
- spot checks
|
|
@@ -58,3 +63,14 @@ Only use `record_query(list)` after plan/summary/aggregate when you need:
|
|
|
58
63
|
- manual inspection of records behind an aggregate bucket
|
|
59
64
|
|
|
60
65
|
Never use list mode alone to justify final averages, shares, rankings, or regional distribution claims.
|
|
66
|
+
|
|
67
|
+
## Ambiguous field recovery
|
|
68
|
+
|
|
69
|
+
If the user asks for something like “来源分布” or “类型占比” and the exact field is unclear:
|
|
70
|
+
|
|
71
|
+
1. run `record_schema_get`
|
|
72
|
+
2. inspect titles, aliases, and suggested fields
|
|
73
|
+
3. if one candidate is clearly dominant, proceed
|
|
74
|
+
4. if multiple candidates are still plausible, ask the user to confirm which field they want
|
|
75
|
+
|
|
76
|
+
Do not keep retrying different guessed field names in a loop.
|
|
@@ -12,30 +12,30 @@ When analysis is intended as a final answer, use this order:
|
|
|
12
12
|
|
|
13
13
|
Only write `全量可信结论` when:
|
|
14
14
|
|
|
15
|
-
- `
|
|
16
|
-
- the analysis path used
|
|
17
|
-
- `safe_for_final_conclusion=true`
|
|
15
|
+
- `record_schema_get` was used
|
|
16
|
+
- the analysis path used one or more `record_analyze` calls
|
|
17
|
+
- every key analysis result has `safe_for_final_conclusion=true`
|
|
18
18
|
- the result is not just a capped list sample
|
|
19
19
|
|
|
20
20
|
## Sample observation gate
|
|
21
21
|
|
|
22
22
|
Put evidence into `样本观察` when:
|
|
23
23
|
|
|
24
|
-
- it came from `record_query(list)`
|
|
24
|
+
- it came from `record_query(query_mode="list")`
|
|
25
25
|
- the tool reports `row_cap_hit`
|
|
26
26
|
- the tool reports `sample_only`
|
|
27
27
|
- the result is compact/capped and not complete
|
|
28
28
|
|
|
29
29
|
## Downgrade rule
|
|
30
30
|
|
|
31
|
-
If `
|
|
31
|
+
If `record_schema_get` was not used for an analysis task, downgrade the overall framing to `初步观察` instead of `洞察` or `结论`.
|
|
32
32
|
|
|
33
33
|
## Anti-mixing rule
|
|
34
34
|
|
|
35
35
|
Do not combine:
|
|
36
36
|
|
|
37
|
-
- full totals from `
|
|
38
|
-
- sample-only details from `record_query(list)`
|
|
37
|
+
- full totals from `record_analyze`
|
|
38
|
+
- sample-only details from `record_query(query_mode="list")`
|
|
39
39
|
|
|
40
40
|
into one sentence like “基于全部数据分析...”.
|
|
41
41
|
|
|
@@ -28,6 +28,8 @@ def build_server() -> FastMCP:
|
|
|
28
28
|
instructions=(
|
|
29
29
|
"Use auth_login first, then workspace_list and workspace_select. "
|
|
30
30
|
"All resource tools operate with the logged-in user's Qingflow permissions.\n\n"
|
|
31
|
+
"For analytics, use record_schema_get first, let the model build field_id-based DSL, "
|
|
32
|
+
"then call record_analyze. Use record_query for list/detail browsing only.\n\n"
|
|
31
33
|
"Task Center (待办/已办) handling:\n"
|
|
32
34
|
"- Use task_statistics to get counts of pending tasks (todo_count), timeouts, urged, etc.\n"
|
|
33
35
|
"- Use task_list to query tasks. Type values: 1=todo (待办), 2=initiated (我发起的), 3=cc (抄送), 5=done (已办).\n"
|
|
@@ -18,8 +18,9 @@ def build_user_server() -> FastMCP:
|
|
|
18
18
|
server = FastMCP(
|
|
19
19
|
"Qingflow App User MCP",
|
|
20
20
|
instructions=(
|
|
21
|
-
"Use this server for Qingflow record queries,
|
|
22
|
-
"directory lookups, and approval actions.
|
|
21
|
+
"Use this server for Qingflow record queries, schema-first analytics via record_schema_get and record_analyze, "
|
|
22
|
+
"record writes, task center operations, directory lookups, and approval actions. "
|
|
23
|
+
"Use record_query for list/detail reads only. Avoid builder-side app or schema changes here."
|
|
23
24
|
),
|
|
24
25
|
)
|
|
25
26
|
sessions = SessionStore()
|