@josephyan/qingflow-cli 1.1.4 → 1.1.5
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 +7 -3
- package/docs/local-agent-install.md +57 -6
- package/entry_point.py +1 -1
- package/npm/bin/qingflow-skills.mjs +5 -0
- package/npm/bin/qingflow.mjs +1 -34
- package/npm/lib/runtime.mjs +21 -101
- package/npm/scripts/postinstall.mjs +1 -10
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/skills/qingflow-cli/SKILL.md +58 -44
- package/skills/qingflow-cli/manifest.yaml +1 -1
- package/skills/qingflow-cli/reference/00-INDEX.md +35 -0
- package/skills/qingflow-cli/reference/builder/10-build-single-app.md +38 -0
- package/skills/qingflow-cli/reference/builder/20-build-complete-system.md +39 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md → builder/30-schema-fields.md} +52 -10
- package/skills/qingflow-cli/reference/builder/40-layout.md +52 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md → builder/50-views.md} +39 -15
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md → builder/60-charts.md} +36 -13
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md → builder/70-portal.md} +36 -13
- package/skills/qingflow-cli/reference/builder/80-buttons-associated-resources.md +41 -0
- package/skills/qingflow-cli/reference/builder/90-workflow.md +34 -0
- package/skills/qingflow-cli/reference/builder/99-publish-verify.md +46 -0
- package/skills/qingflow-cli/reference/builder/README.md +41 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/README.md +130 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/code-block.md +66 -0
- package/skills/qingflow-cli/reference/builder/code-integrations/q-linker.md +77 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md → builder/reference/app-delivery-sop.md} +26 -16
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/README.md +293 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-complete-system.md +809 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/build-single-app.md +830 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/complete-system-development-guide.md +123 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/create-app.md +182 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/environments.md +63 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/flow-actors-and-permissions.md +142 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/gotchas.md +108 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/match-rules.md +114 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/public-surface-sync.md +75 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/single-app-development-guide.md +58 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/solution-playbooks.md +52 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/tool-selection.md +107 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-flow.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-layout.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-schema.md +7 -0
- package/skills/qingflow-cli/reference/builder/reference/legacy-playbooks/update-views.md +7 -0
- package/skills/qingflow-cli/reference/builder/workflow/01-overview.md +45 -0
- package/skills/qingflow-cli/reference/builder/workflow/02-update-mode.md +53 -0
- package/skills/qingflow-cli/reference/builder/workflow/03-flow-patterns.md +57 -0
- package/skills/qingflow-cli/reference/builder/workflow/04-stage1-business-modeling.md +131 -0
- package/skills/qingflow-cli/reference/builder/workflow/05-stage2-members-roles.md +29 -0
- package/skills/qingflow-cli/reference/builder/workflow/06-stage3-build-spec.md +165 -0
- package/skills/qingflow-cli/reference/builder/workflow/07-stage4-validate-spec.md +33 -0
- package/skills/qingflow-cli/reference/builder/workflow/08-stage5-apply-verify.md +51 -0
- package/skills/qingflow-cli/reference/builder/workflow/09-stage6-summary.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/10-node-config-reference.md +93 -0
- package/skills/qingflow-cli/reference/builder/workflow/11-troubleshooting.md +15 -0
- package/skills/qingflow-cli/reference/builder/workflow/README.md +88 -0
- package/skills/qingflow-cli/reference/builder/workflow/workflow-schema.json +1754 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ADMIN_CHEATSHEET.md → core/QINGFLOW_CLI_ADMIN_CHEATSHEET.md} +3 -3
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md → core/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md} +6 -6
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_EXPLORATION_REPORT.md → core/QINGFLOW_CLI_EXPLORATION_REPORT.md} +2 -2
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_FIELD_DATA_TYPES.md → core/QINGFLOW_CLI_FIELD_DATA_TYPES.md} +11 -11
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_MEMBER_CHEATSHEET.md → core/QINGFLOW_CLI_MEMBER_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md → core/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md} +3 -3
- package/skills/qingflow-cli/reference/record/QINGFLOW_CLI_RECORD_DELETE_WORKFLOW.md +31 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md} +4 -4
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md → record/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md} +7 -7
- package/skills/qingflow-cli/reference/record/analysis/README.md +130 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-gotchas.md +91 -0
- package/skills/qingflow-cli/reference/record/analysis/analysis-patterns.md +112 -0
- package/skills/qingflow-cli/reference/record/analysis/business-context.md +74 -0
- package/skills/qingflow-cli/reference/record/analysis/confidence-reporting.md +69 -0
- package/skills/qingflow-cli/reference/record/analysis/data-access-playbook.md +106 -0
- package/skills/qingflow-cli/reference/record/analysis/pandas-recipes.md +172 -0
- package/skills/qingflow-cli/reference/record/analysis/report-format.md +76 -0
- package/skills/qingflow-cli/reference/record/insert/README.md +75 -0
- package/skills/qingflow-cli/reference/{QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md → task/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md} +5 -5
- package/skills/qingflow-cli/reference/task/ops/README.md +131 -0
- package/skills/qingflow-cli/reference/task/ops/environments.md +43 -0
- package/skills/qingflow-cli/reference/task/ops/workflow-usage.md +26 -0
- package/skills/qingflow-cli/scripts/validate_system_build_summary.py +124 -0
- package/skills/qingflow-cli/scripts/workflow/diff_flow_spec.py +275 -0
- package/skills/qingflow-cli/scripts/workflow/validate_flow_spec.py +605 -0
- package/skills/qingflow-mcp-setup/SKILL.md +115 -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 +62 -0
- package/skills/qingflow-mcp-setup/references/generic-stdio.md +32 -0
- package/skills/qingflow-mcp-setup/scripts/check_local_server.sh +38 -0
- package/src/qingflow_mcp/__init__.py +1 -1
- package/src/qingflow_mcp/__main__.py +6 -2
- package/src/qingflow_mcp/builder_facade/models.py +282 -102
- package/src/qingflow_mcp/builder_facade/service.py +4166 -929
- package/src/qingflow_mcp/cli/commands/builder.py +316 -298
- package/src/qingflow_mcp/cli/commands/chart.py +1 -1
- package/src/qingflow_mcp/cli/commands/common.py +12 -3
- package/src/qingflow_mcp/cli/commands/exports.py +2 -2
- package/src/qingflow_mcp/cli/commands/imports.py +3 -3
- package/src/qingflow_mcp/cli/commands/portal.py +2 -2
- package/src/qingflow_mcp/cli/commands/record.py +101 -27
- package/src/qingflow_mcp/cli/commands/task.py +28 -47
- package/src/qingflow_mcp/cli/commands/view.py +1 -1
- package/src/qingflow_mcp/cli/context.py +0 -3
- package/src/qingflow_mcp/cli/formatters.py +784 -16
- package/src/qingflow_mcp/cli/main.py +117 -33
- package/src/qingflow_mcp/errors.py +43 -2
- package/src/qingflow_mcp/public_surface.py +26 -17
- package/src/qingflow_mcp/response_trim.py +81 -17
- package/src/qingflow_mcp/server.py +14 -12
- package/src/qingflow_mcp/server_app_builder.py +65 -21
- package/src/qingflow_mcp/server_app_user.py +22 -16
- package/src/qingflow_mcp/session_store.py +11 -7
- package/src/qingflow_mcp/solution/compiler/__init__.py +3 -1
- package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +173 -0
- package/src/qingflow_mcp/solution/executor.py +245 -18
- package/src/qingflow_mcp/tools/ai_builder_tools.py +1780 -406
- package/src/qingflow_mcp/tools/app_tools.py +184 -43
- package/src/qingflow_mcp/tools/approval_tools.py +197 -35
- package/src/qingflow_mcp/tools/auth_tools.py +92 -16
- package/src/qingflow_mcp/tools/code_block_tools.py +298 -40
- package/src/qingflow_mcp/tools/custom_button_tools.py +64 -10
- package/src/qingflow_mcp/tools/directory_tools.py +236 -72
- package/src/qingflow_mcp/tools/export_tools.py +244 -34
- package/src/qingflow_mcp/tools/feedback_tools.py +9 -0
- package/src/qingflow_mcp/tools/file_tools.py +9 -3
- package/src/qingflow_mcp/tools/import_tools.py +336 -49
- package/src/qingflow_mcp/tools/navigation_tools.py +91 -12
- package/src/qingflow_mcp/tools/package_tools.py +118 -6
- package/src/qingflow_mcp/tools/portal_tools.py +39 -3
- package/src/qingflow_mcp/tools/qingbi_report_tools.py +116 -7
- package/src/qingflow_mcp/tools/record_tools.py +1141 -356
- package/src/qingflow_mcp/tools/resource_read_tools.py +188 -39
- package/src/qingflow_mcp/tools/role_tools.py +80 -9
- package/src/qingflow_mcp/tools/solution_tools.py +59 -45
- package/src/qingflow_mcp/tools/task_context_tools.py +662 -158
- package/src/qingflow_mcp/tools/task_tools.py +113 -29
- package/src/qingflow_mcp/tools/view_tools.py +106 -3
- package/src/qingflow_mcp/tools/workflow_tools.py +48 -4
- package/src/qingflow_mcp/tools/workspace_tools.py +71 -3
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_MATCH_RULES.md → builder/reference/match-rules.md} +0 -0
- /package/skills/qingflow-cli/reference/{QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md → builder/reference/workspace-icons.md} +0 -0
- /package/skills/qingflow-cli/reference/{charts_remove.example.json → examples/charts/charts_remove.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_reorder.example.json → examples/charts/charts_reorder.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_bar.example.json → examples/charts/charts_upsert_bar.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_dashboard_starter.example.json → examples/charts/charts_upsert_dashboard_starter.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{charts_upsert_minimal.example.json → examples/charts/charts_upsert_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_all_types.example.json → examples/portal/portal_sections_all_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_five_types.example.json → examples/portal/portal_sections_five_types.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{portal_sections_standard_workbench.example.json → examples/portal/portal_sections_standard_workbench.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_complex.json → examples/schema/_batch_schema_complex.json} +0 -0
- /package/skills/qingflow-cli/reference/{_batch_schema_scalar.json → examples/schema/_batch_schema_scalar.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_add_fields_minimal.example.json → examples/schema/schema_add_fields_minimal.example.json} +0 -0
- /package/skills/qingflow-cli/reference/{schema_apply_add_fields_all_types.json → examples/schema/schema_apply_add_fields_all_types.json} +0 -0
- /package/skills/qingflow-cli/reference/{views_upsert_table_minimal.example.json → examples/views/views_upsert_table_minimal.example.json} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Analysis Patterns
|
|
2
|
+
|
|
3
|
+
## Canonical Sequence
|
|
4
|
+
|
|
5
|
+
1. `app_get`
|
|
6
|
+
2. `record_browse_schema_get`
|
|
7
|
+
3. decide metric intent
|
|
8
|
+
4. choose `record_access.columns / where / order_by`
|
|
9
|
+
5. `record_access`
|
|
10
|
+
6. Python over every returned CSV shard
|
|
11
|
+
7. optional `record_list` or `record_get` only for sample/detail verification
|
|
12
|
+
|
|
13
|
+
Metric intent must be one of:
|
|
14
|
+
|
|
15
|
+
- `count`
|
|
16
|
+
- `sum`
|
|
17
|
+
- `avg`
|
|
18
|
+
- `distinct_count`
|
|
19
|
+
- ratio with numerator and denominator
|
|
20
|
+
- sorted ranking
|
|
21
|
+
- time trend
|
|
22
|
+
- period comparison
|
|
23
|
+
|
|
24
|
+
## Distribution
|
|
25
|
+
|
|
26
|
+
1. Fetch grouping field and filter fields.
|
|
27
|
+
2. Run the field-quality profile for the grouping field.
|
|
28
|
+
3. If the field passes quality gates, group by the readable field-id anchored column such as `项目状态__field_343283094`.
|
|
29
|
+
4. Count rows and calculate share from the sum of counts.
|
|
30
|
+
5. Report top groups plus total row count.
|
|
31
|
+
|
|
32
|
+
If the grouping field is ambiguous, ask the user to choose from a short candidate list.
|
|
33
|
+
|
|
34
|
+
## Dimension Selection
|
|
35
|
+
|
|
36
|
+
When the user asks for a semantic bucket such as `板块`, `模块`, `业务线`, or `来源`, inspect candidate fields and choose the most reliable one:
|
|
37
|
+
|
|
38
|
+
1. Match schema titles to the user's wording.
|
|
39
|
+
2. Fetch candidate fields together if they are cheap.
|
|
40
|
+
3. Profile `blank_rate`, period coverage, and distinct count.
|
|
41
|
+
4. Prefer the candidate with clear semantics and usable coverage.
|
|
42
|
+
5. If the literal field is sparse, downgrade it to `已填写样本观察` and use the nearest reliable fallback for the main conclusion.
|
|
43
|
+
|
|
44
|
+
Example: if `缺陷所属模块` is mostly empty but `缺陷所属平台` and `所属产品` are complete, use platform/product for the main conclusion and state that module-level analysis is limited.
|
|
45
|
+
|
|
46
|
+
Quality gates:
|
|
47
|
+
|
|
48
|
+
- Overall `blank_rate > 0.4`: not a primary conclusion dimension.
|
|
49
|
+
- Any compared period `blank_rate > 0.8`: not valid for period comparison.
|
|
50
|
+
- High-cardinality description/id fields are not dimensions unless the user explicitly asks for record-level ranking.
|
|
51
|
+
|
|
52
|
+
## Ratio / Conversion / Penetration
|
|
53
|
+
|
|
54
|
+
1. Define numerator and denominator in plain language.
|
|
55
|
+
2. Fetch both populations with compatible scope.
|
|
56
|
+
3. Compute ratio in Python.
|
|
57
|
+
4. Report `numerator / denominator = percentage`.
|
|
58
|
+
|
|
59
|
+
If denominator is missing or scope differs, do not call the result a rate.
|
|
60
|
+
|
|
61
|
+
## Average / Sum
|
|
62
|
+
|
|
63
|
+
1. Fetch grouping field and numeric metric field.
|
|
64
|
+
2. Convert the metric column with `pd.to_numeric(errors="coerce")`.
|
|
65
|
+
3. Report count, sum, and average together when useful.
|
|
66
|
+
4. State how blanks/non-numeric values were handled if material.
|
|
67
|
+
|
|
68
|
+
## Ranking
|
|
69
|
+
|
|
70
|
+
1. Build the metric in Python.
|
|
71
|
+
2. Sort explicitly.
|
|
72
|
+
3. Report Top N with metric values.
|
|
73
|
+
4. Do not infer ranking from unsorted sample rows.
|
|
74
|
+
|
|
75
|
+
## Trend
|
|
76
|
+
|
|
77
|
+
1. Choose a date/time field from `suggested_time_fields`.
|
|
78
|
+
2. Convert relative phrases into exact date ranges.
|
|
79
|
+
3. Fetch the date field and metrics.
|
|
80
|
+
4. Bucket in pandas by day/week/month/quarter/year.
|
|
81
|
+
5. Report both absolute values and changes.
|
|
82
|
+
|
|
83
|
+
## Same-Period Comparison
|
|
84
|
+
|
|
85
|
+
For `今年5月 vs 去年5月`:
|
|
86
|
+
|
|
87
|
+
1. Use the same date field for both periods.
|
|
88
|
+
2. Fetch the full combined date range or two separate compatible ranges.
|
|
89
|
+
3. Apply identical business filters.
|
|
90
|
+
4. Compute absolute delta and percentage delta.
|
|
91
|
+
5. State both periods explicitly.
|
|
92
|
+
|
|
93
|
+
## Sample Inspection
|
|
94
|
+
|
|
95
|
+
Use `record_list` only after the aggregate result is complete, and only for:
|
|
96
|
+
|
|
97
|
+
- representative examples
|
|
98
|
+
- checking surprising categories
|
|
99
|
+
- manually inspecting records behind a bucket
|
|
100
|
+
|
|
101
|
+
Never use `record_list` alone for final averages, shares, rankings, trends, or distributions.
|
|
102
|
+
|
|
103
|
+
## Ambiguous Field Recovery
|
|
104
|
+
|
|
105
|
+
If the exact field is unclear:
|
|
106
|
+
|
|
107
|
+
1. inspect `record_browse_schema_get.fields`
|
|
108
|
+
2. use titles and suggested fields
|
|
109
|
+
3. if one candidate is clearly dominant, proceed
|
|
110
|
+
4. otherwise ask the user to confirm
|
|
111
|
+
|
|
112
|
+
Do not retry tools with guessed field names.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Business Context
|
|
2
|
+
|
|
3
|
+
Use this when analysis depends on organization, aliases, ownership, stage semantics, or user-provided business definitions.
|
|
4
|
+
|
|
5
|
+
## When To Check Context
|
|
6
|
+
|
|
7
|
+
Check for business context when the request mentions:
|
|
8
|
+
|
|
9
|
+
- department / team / group / region
|
|
10
|
+
- owner / assignee / sales rep / partner
|
|
11
|
+
- stage / status / funnel / conversion
|
|
12
|
+
- product line / business line
|
|
13
|
+
- same-period comparison
|
|
14
|
+
- "北斗部门", "SMB", "伙伴", or any named internal scope
|
|
15
|
+
|
|
16
|
+
## Mapping Rules
|
|
17
|
+
|
|
18
|
+
Use explicit mappings in this order:
|
|
19
|
+
|
|
20
|
+
1. the user's message in the current thread
|
|
21
|
+
2. attached or local business context files
|
|
22
|
+
3. schema-visible fields and sample records
|
|
23
|
+
4. short clarification to the user
|
|
24
|
+
|
|
25
|
+
Do not infer hidden org hierarchy from memory. If the mapping changes the denominator or grouping, state it in the final answer.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
dept_map = {
|
|
31
|
+
"烈焰组": "北斗部门",
|
|
32
|
+
"飓风组": "北斗部门",
|
|
33
|
+
}
|
|
34
|
+
df["部门口径"] = df["field_40"].replace(dept_map)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Final wording:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
部门口径:将「烈焰组」「飓风组」合并计入「北斗部门」。
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Ratio Definitions
|
|
44
|
+
|
|
45
|
+
Before computing rates, define:
|
|
46
|
+
|
|
47
|
+
- numerator
|
|
48
|
+
- denominator
|
|
49
|
+
- time range
|
|
50
|
+
- grouping dimension
|
|
51
|
+
- exclusions
|
|
52
|
+
|
|
53
|
+
If any part is ambiguous, ask. Do not rename a count as a rate.
|
|
54
|
+
|
|
55
|
+
## Time Scope
|
|
56
|
+
|
|
57
|
+
Normalize relative dates to exact dates before calling `record_access`.
|
|
58
|
+
|
|
59
|
+
Examples:
|
|
60
|
+
|
|
61
|
+
- `今年5月` -> `2026-05-01` to `2026-05-31` when current year is 2026
|
|
62
|
+
- `去年同期` -> same month range in the previous year
|
|
63
|
+
- `最近一个完整自然月` -> previous calendar month, not the last 30 days
|
|
64
|
+
|
|
65
|
+
## Cross-App Reconciliation
|
|
66
|
+
|
|
67
|
+
If the analysis needs multiple apps:
|
|
68
|
+
|
|
69
|
+
1. run the standard sequence per app
|
|
70
|
+
2. keep each dataset's scope and completeness separately
|
|
71
|
+
3. join in Python only on explicit ids or trusted business keys
|
|
72
|
+
4. disclose join keys and unmatched records
|
|
73
|
+
|
|
74
|
+
If no reliable key exists, report the gap instead of forcing a join.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Confidence Reporting
|
|
2
|
+
|
|
3
|
+
## Full Conclusion Gate
|
|
4
|
+
|
|
5
|
+
Use `全量可信结论` only when:
|
|
6
|
+
|
|
7
|
+
- `record_browse_schema_get` was used
|
|
8
|
+
- data came from `record_access`
|
|
9
|
+
- all returned CSV shards were read in Python
|
|
10
|
+
- `record_access.complete=true`
|
|
11
|
+
- `record_access.truncated=false`
|
|
12
|
+
- `record_access.safe_for_final_conclusion=true`
|
|
13
|
+
- metric definitions are complete
|
|
14
|
+
- denominator exists for every ratio
|
|
15
|
+
- time fields and date ranges are explicit
|
|
16
|
+
- primary grouping dimensions pass field-quality gates
|
|
17
|
+
|
|
18
|
+
## Initial Observation Gate
|
|
19
|
+
|
|
20
|
+
Use `初步观察` when:
|
|
21
|
+
|
|
22
|
+
- `record_access.status=needs_scope`
|
|
23
|
+
- `record_access.status=partial`
|
|
24
|
+
- `record_access.complete=false`
|
|
25
|
+
- `record_access.truncated=true`
|
|
26
|
+
- `record_access.safe_for_final_conclusion=false`
|
|
27
|
+
- evidence came from `record_list`
|
|
28
|
+
- scope or saved view filter is unverified
|
|
29
|
+
|
|
30
|
+
## Anti-Mixing Rule
|
|
31
|
+
|
|
32
|
+
Do not combine full CSV-derived totals and sample-only rows in one sentence.
|
|
33
|
+
|
|
34
|
+
Correct split:
|
|
35
|
+
|
|
36
|
+
- full totals/distributions: `全量可信结论`
|
|
37
|
+
- illustrative examples: `样本观察`
|
|
38
|
+
|
|
39
|
+
## Semantic Gate
|
|
40
|
+
|
|
41
|
+
Even with `safe_for_final_conclusion=true`, downgrade if:
|
|
42
|
+
|
|
43
|
+
- metric definition is incomplete
|
|
44
|
+
- denominator was not queried
|
|
45
|
+
- conclusion mentions trend but no time field was used
|
|
46
|
+
- conclusion mentions volume but no count was computed
|
|
47
|
+
- grouping depends on unconfirmed business aliases
|
|
48
|
+
- custom view scope is not verified
|
|
49
|
+
- primary grouping field has high missingness or poor period coverage
|
|
50
|
+
|
|
51
|
+
## Partial Disclosure
|
|
52
|
+
|
|
53
|
+
If only part of the user request is complete:
|
|
54
|
+
|
|
55
|
+
- say which parts are complete
|
|
56
|
+
- say which parts are unresolved
|
|
57
|
+
- do not collapse into one all-clear conclusion
|
|
58
|
+
|
|
59
|
+
## Compact Disclosure Template
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
可信度:全量可信 / 初步观察
|
|
63
|
+
数据完整性:complete=..., truncated=..., safe_for_final_conclusion=...
|
|
64
|
+
字段质量:primary dimension blank_rate=..., period coverage=...
|
|
65
|
+
取数字段:...
|
|
66
|
+
时间范围:...
|
|
67
|
+
业务口径:...
|
|
68
|
+
限制:...
|
|
69
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Data Access Playbook
|
|
2
|
+
|
|
3
|
+
This file is the operational state machine for `record_access`.
|
|
4
|
+
|
|
5
|
+
## Required Sequence
|
|
6
|
+
|
|
7
|
+
1. `app_get`
|
|
8
|
+
2. choose `view_id` from `accessible_views`
|
|
9
|
+
3. `record_browse_schema_get(app_key, view_id)`
|
|
10
|
+
4. build `record_access.columns / where / order_by` from field ids
|
|
11
|
+
5. run `record_access`
|
|
12
|
+
6. read every CSV shard with Python
|
|
13
|
+
|
|
14
|
+
Do not call `record_access` with field titles, guessed ids, page controls, row limits, or profile.
|
|
15
|
+
CSV columns are readable and field-id anchored, for example `项目状态__field_343283094`; do not look for extra `schema.json` or README files.
|
|
16
|
+
|
|
17
|
+
## Finding `record_access`
|
|
18
|
+
|
|
19
|
+
`record_access` can be available as an MCP tool or as a CLI subcommand. If the MCP surface does not show a top-level `record_access` tool, look under the Qingflow CLI record group before choosing any other path:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
qingflow record --help
|
|
23
|
+
qingflow record access --help
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The CLI call is:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
qingflow record access \
|
|
30
|
+
--app-key APP_KEY \
|
|
31
|
+
--view-id VIEW_ID \
|
|
32
|
+
--columns-file columns.json \
|
|
33
|
+
--where-file where.json \
|
|
34
|
+
--order-by-file order_by.json \
|
|
35
|
+
--json
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This is the same analysis path: it writes CSV shards and metadata for Python. Do not replace it with list browsing, export, QingBI, or aggregate helpers just because the MCP tool is not visible.
|
|
39
|
+
|
|
40
|
+
## Request Patterns
|
|
41
|
+
|
|
42
|
+
### Count or distribution
|
|
43
|
+
|
|
44
|
+
Fetch the grouping field and any time/business filter field.
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"app_key": "APP_KEY",
|
|
49
|
+
"view_id": "system:all",
|
|
50
|
+
"columns": [{ "field_id": 18 }],
|
|
51
|
+
"where": [{ "field_id": 2, "op": "between", "value": ["2026-05-01", "2026-05-31"] }],
|
|
52
|
+
"order_by": []
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Trend
|
|
57
|
+
|
|
58
|
+
Fetch the date/time field plus metric fields.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"app_key": "APP_KEY",
|
|
63
|
+
"view_id": "system:all",
|
|
64
|
+
"columns": [{ "field_id": 2 }, { "field_id": 18 }],
|
|
65
|
+
"where": [{ "field_id": 2, "op": "between", "value": ["2026-01-01", "2026-12-31"] }],
|
|
66
|
+
"order_by": [{ "field_id": 2, "direction": "asc" }]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Ratio
|
|
71
|
+
|
|
72
|
+
If numerator and denominator use different filters, run separate `record_access` calls. Only compute the ratio after both source datasets are complete and compatible.
|
|
73
|
+
|
|
74
|
+
## Status Decisions
|
|
75
|
+
|
|
76
|
+
| Status | Meaning | Agent action |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `success` + `safe_for_final_conclusion=true` | Full retrieved scope is reliable | Give final conclusion |
|
|
79
|
+
| `needs_scope` | Tool refused large unbounded scan, no CSV | Ask for scope or retry with explicit period/business filter |
|
|
80
|
+
| `partial` | Some CSV files written, but not full data | Give only subset observation |
|
|
81
|
+
| `complete=false` | Not all requested data is available | Do not present full-population conclusion |
|
|
82
|
+
| `truncated=true` | Tool had to stop before full scope | Disclose and narrow scope |
|
|
83
|
+
|
|
84
|
+
## `needs_scope` Recovery
|
|
85
|
+
|
|
86
|
+
Use the returned `scope` object:
|
|
87
|
+
|
|
88
|
+
- `reported_total`: explain why scope is needed
|
|
89
|
+
- `suggested_time_fields`: choose likely date fields
|
|
90
|
+
- `recommended_where_examples`: reuse if they match the user request
|
|
91
|
+
|
|
92
|
+
If the user already provided a concrete month/quarter/year, retry with that period. If no business boundary is available, ask one short clarification.
|
|
93
|
+
|
|
94
|
+
## `partial` Recovery
|
|
95
|
+
|
|
96
|
+
You may read the files, but must label output as partial:
|
|
97
|
+
|
|
98
|
+
- say which files/rows were analyzed
|
|
99
|
+
- do not use `全部`, `所有`, `整体`, or `全量`
|
|
100
|
+
- suggest narrowing time or business scope before final conclusion
|
|
101
|
+
|
|
102
|
+
## View Scope
|
|
103
|
+
|
|
104
|
+
For custom views, the result is scoped to that saved view. If `verification.view_filter_verified=false`, disclose that the saved-filter scope could not be fully verified.
|
|
105
|
+
|
|
106
|
+
For board/gantt views, switch to a table-style view or `system:all` plus explicit filters.
|
|
@@ -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
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Qingflow CLI Record Insert
|
|
2
|
+
|
|
3
|
+
## Default Path
|
|
4
|
+
|
|
5
|
+
`record schema insert -> record insert --items-file -> optional record get/readback`
|
|
6
|
+
|
|
7
|
+
Default to batch-shaped insert. A single new record is `items` with one row.
|
|
8
|
+
|
|
9
|
+
## Core Tools
|
|
10
|
+
|
|
11
|
+
- `qingflow record schema insert`
|
|
12
|
+
- `qingflow record insert --items-file`
|
|
13
|
+
- `qingflow record member-candidates`
|
|
14
|
+
- `qingflow record department-candidates`
|
|
15
|
+
- file upload command when attachments are required
|
|
16
|
+
|
|
17
|
+
## Working Rules
|
|
18
|
+
|
|
19
|
+
1. Start with `record schema insert`
|
|
20
|
+
2. Read `required_fields`, `optional_fields`, `runtime_linked_required_fields`, and `payload_template`
|
|
21
|
+
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
|
|
22
|
+
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
|
|
23
|
+
5. Build `items` as `[{"fields": {...}}]`, where each `fields` map uses field titles from the insert schema
|
|
24
|
+
6. Treat `runtime_linked_required_fields` as required-but-not-directly-writable runtime/upstream dependencies, not as fields to hand-fill blindly
|
|
25
|
+
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"
|
|
26
|
+
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
|
|
27
|
+
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
|
|
28
|
+
10. If insert succeeds and single-record detail/readback matters, prefer `record get`; use `record list` only for batch row-shaped normalized readback
|
|
29
|
+
11. Keep subtable payloads under the parent field as a row array
|
|
30
|
+
12. Member / department / relation fields may accept natural strings directly, such as `"张三"`, `"直销部"`, or `"海军军医大学"`; do not pre-query ids by default
|
|
31
|
+
13. If the write returns `status="needs_confirmation"`, stop and surface the candidates
|
|
32
|
+
14. Retry failed rows only with explicit ids / objects after the user confirms
|
|
33
|
+
15. Keep `verify_write=true` for production inserts
|
|
34
|
+
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`
|
|
35
|
+
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
|
|
36
|
+
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`
|
|
37
|
+
|
|
38
|
+
## Field Notes
|
|
39
|
+
|
|
40
|
+
- `searchable_fields` on relation fields defines the backend-native searchable columns
|
|
41
|
+
- `accepts_natural_input=true` means the field may accept a natural string before explicit id fallback
|
|
42
|
+
- `may_become_required=true` means the field is writable now, but may turn required after linked visibility or option rules activate
|
|
43
|
+
- `linkage.kind=logic_visibility` means the field is statically tied to linked visibility or option-driven rules
|
|
44
|
+
- `linkage.kind=reference_fill` means the field participates in reference-based auto fill or default matching logic
|
|
45
|
+
- `linkage.kind=formula_fill` means the field usually comes from formula/default auto-fill logic
|
|
46
|
+
- `linkage.sources` lists the upstream field titles that influence the current field
|
|
47
|
+
- `linkage.affects_fields` lists downstream field titles that may change when this field changes
|
|
48
|
+
- `linkage.role=auto_fill_only` means "normally do not hand-fill this unless the product explicitly requires it"
|
|
49
|
+
- `requires_upload=true` means upload the file first, then write the returned value
|
|
50
|
+
- `failed_fields[].next_action` tells the next repair step for that row
|
|
51
|
+
|
|
52
|
+
## CLI Pattern
|
|
53
|
+
|
|
54
|
+
Use a JSON array file:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
qingflow record insert --app-key APP_KEY --items-file records.json --json
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`records.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
[
|
|
64
|
+
{ "fields": { "客户名称": "测试客户", "负责人": "张三" } }
|
|
65
|
+
]
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Do Not
|
|
69
|
+
|
|
70
|
+
- Do not skip `record schema insert`
|
|
71
|
+
- Do not invent missing required fields
|
|
72
|
+
- Do not flatten subtable leaf fields to the top level
|
|
73
|
+
- Do not pre-query or silently guess member / department / relation ids when a natural string is enough
|
|
74
|
+
- Do not retry a whole batch after `created_record_ids` is non-empty
|
|
75
|
+
- Do not bind logic to a transient nested schema serialization detail when the field title and parent table already identify the legal payload shape
|