
Alibabacloud Quickbi Smartq
- 135 installs
- 208 repo stars
- Updated August 4, 2026
- aliyun/alibabacloud-aiops-skills
Ask natural-language questions in QuickBI SmartQ, build dashboards, and turn business metrics into decisions without writing SQL manually.
About
alibabacloud-quickbi-smartq enables Claude Code to leverage Alibaba Cloud QuickBI SmartQ for conversational analytics: phrase business questions, refine visualizations, and interpret dashboard output for stakeholders during the grow phase.
- Natural-language BI queries
- QuickBI SmartQ workflows
- Dashboard and report guidance
- KPI exploration without SQL
- Alibaba Cloud analytics integration
Alibabacloud Quickbi Smartq by the numbers
- 135 all-time installs (skills.sh)
- Ranked #754 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/aliyun/alibabacloud-aiops-skills --skill alibabacloud-quickbi-smartqAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 135 |
|---|---|
| repo stars | ★ 208 |
| Last updated | August 4, 2026 |
| Repository | aliyun/alibabacloud-aiops-skills ↗ |
What it does
Ask natural-language questions in QuickBI SmartQ, build dashboards, and turn business metrics into decisions without writing SQL manually.
Files
Quick BI-SmartQ — QuickBI Data Analysis Assistant
One entry point covering all QuickBI data analysis capabilities. Automatically routes to the corresponding module based on user intent — no manual selection required.
Scope
Does:
- Automatically identify user intent and route to the corresponding data analysis module
- Perform natural-language analysis on user-uploaded Excel/CSV files via the Quick BI API (File Q&A)
- Perform natural-language query analysis on Quick BI platform datasets, with automatic intelligent table selection and matching (Dataset Q&A)
- Parse PDF/Word/Excel/CSV/images, extract text, and support extracting key fields to generate structured Excel (Document Parsing)
- Auto-convert QuickBI dashboards into data query skills (Dashboard Skill Generation)
- Perform deep insight analysis on datasets (Data Insight)
- Auto-generate professional data reports based on analysis results (Data Report)
Does NOT:
- Use pandas/openpyxl/csv or similar libraries to read files locally for analysis in Q&A scenarios
- Require users to manually choose a module or provide internal parameters such as cubeId
- Perform tasks unrelated to QuickBI data analysis
Task Routing
Automatically determine intent based on user input and route to the corresponding module for execution.
Routing Decision Table
| User Intent | Routed Module | Reference Document |
|---|---|---|
| Uploaded Excel/CSV file, wants to query specific metrics or answer specific data questions (e.g. TOP N, comparison, filtering) | File Q&A | module-chat-file.md |
| 上传了 Excel/CSV 文件,要查询具体指标或回答具体数据问题(如 TOP N、对比、筛选) | File Q&A | module-chat-file.md |
| No file uploaded, wants to query/analyze specific metrics in platform datasets | Dataset Q&A | module-chat-dataset.md |
| 未上传文件,要查询/分析平台数据集中的具体指标 | Dataset Q&A | module-chat-dataset.md |
| Uploaded multiple files (PDF/Word/images etc.) or selected a folder, wants to query specific data questions (e.g. TOP N, comparison, filtering) | Document Parsing → File Q&A | module-document-parser.md → module-chat-file.md |
| 上传了多个文件(PDF/word/图片等),或者选择文件夹,要查询具体数据问题(如 TOP N、对比、筛选) | Document Parsing → File Q&A | module-document-parser.md → module-chat-file.md |
| Uploaded PDF/Word/images or other unstructured documents, or selected a folder, wants to parse all file contents or extract fields | Document Parsing | module-document-parser.md |
| 上传了 PDF/Word/图片等非结构化文档,或者选择文件夹,要解析所有文件内容或提取字段 | Document Parsing | module-document-parser.md |
| Provided a QuickBI dashboard URL, wants to generate a query skill | Dashboard Skill Generation | module-dashboard.md |
| 提供了 QuickBI 仪表板 URL,要生成查询技能 | Dashboard Skill Generation | module-dashboard.md |
| Uploaded Excel file, wants deep interpretation/insight/trend analysis of data (not generating a report document) | Data Insight | module-data-insight.md |
| 上传了 Excel 文件,要对数据进行深度解读/洞察/趋势分析(不生成报告文档) | Data Insight | module-data-insight.md |
| Uploaded multiple files (PDF/Word/images etc.) or selected a folder, wants deep interpretation/insight/trend analysis of data (not generating a report document) | Document Parsing → Data Insight | module-document-parser.md → module-data-insight.md |
| 上传了多个文件(PDF/word/图片等),或者选择文件夹,要对数据进行深度解读/洞察/趋势分析(不生成报告文档) | Document Parsing → Data Insight | module-document-parser.md → module-data-insight.md |
| Wants to generate a report/analysis report/review report, regardless of whether files are uploaded | Data Report | module-data-report.md |
| 要生成报告/分析报告/复盘报告,无论是否上传文件 | Data Report | module-data-report.md |
Routing Priority Rules
When user intent may match multiple modules, determine by the following priority:
1. "Report" keyword takes priority (「报告」关键词优先): When user intent contains keywords like "report", "review", "summary report", "analysis report" (报告/复盘/总结报告/分析报告), ALWAYS route to the Data Report module, regardless of whether files are uploaded. Data Report module has higher priority than File Q&A and Data Insight. 2. "Interpret", "insight", "trend" keywords (「解读」「洞察」「趋势」关键词): When user wants to understand data meaning, discover trends, or gain insights (解读/洞察/趋势), route to the Data Insight module. 3. Specific data query (具体数据查询): When user wants to query specific metrics (TOP N, sum, comparison, etc.), route to the Q&A module (choose Dataset Q&A or File Q&A based on whether files are present). 4. Dashboard URL (仪表板 URL): When user provides a dashboard link, route to Dashboard Skill Generation.
Routing Examples
| User Input | Routing Result | Reasoning |
|---|---|---|
| "Help me find the product with the highest sales in this data" + uploaded file | → File Q&A (module-chat-file) | Querying specific metric, has file |
| "帮我查一下这份数据中销售额最高的产品" + 上传文件 | → File Q&A (module-chat-file) | 查具体指标,有文件 |
| "Help me analyze this Excel data, TOP 10 headcount by department" + uploaded file | → File Q&A (module-chat-file) | Querying specific metric (TOP N), has file |
| "帮我分析这份Excel数据,各部门人数分布TOP10" + 上传文件 | → File Q&A (module-chat-file) | 查具体指标(TOP N),有文件 |
| "Top 3 regions with the highest sales" | → Dataset Q&A (module-chat-dataset) | Querying specific metric, no file |
| "销量最高的地区TOP3" | → Dataset Q&A (module-chat-dataset) | 查具体指标,无文件 |
| "Parse these contracts and summarize the information" + folder | → Document Parsing (module-document-parser) | |
| "解析这些合同并汇总信息" + 文件夹 | → Document Parsing (module-document-parser) | |
| "Convert this dashboard into a query skill" + URL | → Dashboard Skill Generation (module-dashboard) | Provided dashboard URL |
| "把这个仪表板转化为查询技能" + URL | → Dashboard Skill Generation (module-dashboard) | 提供了仪表板 URL |
| "Help me interpret the trend in sales data" + uploaded file | → Data Insight (module-data-insight) | Requests interpretation/insight, not a report |
| "帮我解读一下销售数据的趋势" + 上传文件 | → Data Insight (module-data-insight) | 要求解读/洞察,非报告 |
| "Any patterns and insights in this data" + uploaded file | → Data Insight (module-data-insight) | Requests insight analysis |
| "这份数据有什么规律和洞察" + 上传文件 | → Data Insight (module-data-insight) | 要求洞察分析 |
| "Generate a sales data report for this month" | → Data Report (module-data-report) | Contains "report" keyword |
| "生成一份本月销售数据报告" | → Data Report (module-data-report) | 含「报告」关键词 |
| "Help me generate an analysis report based on this Excel" + uploaded file | → Data Report (module-data-report) | Contains "report" keyword, file used as reference |
| "帮我基于这份Excel生成一份分析报告" + 上传文件 | → Data Report (module-data-report) | 含「报告」关键词,文件作为参考资料 |
| "Summarize these data, write a review report" + uploaded files | → Data Report (module-data-report) | Contains "review report" keyword |
| "汇总这几份数据,写一份复盘报告" + 上传文件 | → Data Report (module-data-report) | 含「复盘报告」关键词 |
| "Combine these files to generate a data analysis report" + uploaded files | → Data Report (module-data-report) | Contains "report" keyword |
| "结合这些文件生成数据分析报告" + 上传文件 | → Data Report (module-data-report) | 含「报告」关键词 |
| "Parse these 10 invoice PDFs, extract fields and generate Excel" + multiple files | → Document Parsing (module-document-parser) | Contains "extract fields" related keywords |
| "解析这10个发票PDF,提取字段生成Excel" + 多文件 | → Document Parsing (module-document-parser) | 含"提取字段"等相关关键字 |
| "Help me find the product with the highest sales in this data" + multiple files or folder | → Document Parsing → File Q&A (module-chat-file) | Querying specific metric, has multiple files |
| "帮我查一下这份数据中销售额最高的产品" + 多个文件或者文件夹 | → Document Parsing → File Q&A (module-chat-file) | 查具体指标,有多个文件 |
| "Any patterns and insights in these files" + multiple files or folder | → Document Parsing → Data Insight (module-data-insight) | Requests insight analysis |
| "这些文件中的数据有什么规律和洞察" + 多个文件或者文件夹 | → Document Parsing → Data Insight (module-data-insight) | 要求洞察分析 |
| "Summarize these data, write a review report" + ≤5 files | → Data Report (module-data-report) | Contains "review report" keyword |
| "汇总这几份数据,写一份复盘报告" + 5个以内文件 | → Data Report (module-data-report) | 含「复盘报告」关键词 |
| "Summarize these data, write a review report" + >5 files | → Document Parsing → Data Report (module-data-report) | Contains "review report" keyword |
| "汇总这几份数据,写一份复盘报告" + >5个文件 | → Document Parsing → Data Report (module-data-report) | 含「复盘报告」关键词 |
Fallback Rules
- When intent is unclear, default to Dataset Q&A (module-chat-dataset) (当意图不明确时,默认路由到数据集问数)
- If the user involves multiple modules at the same time (e.g. "analyze data and generate a report" ("分析数据并生成报告")), execute them in sequence
- Special scenario — multi-file preprocessing before Q&A (特殊场景 — 多文件问数前置处理):
- When user uploads ≥5 unstructured documents (PDF/Word/images, etc.) and requests analysis
- MUST execute Document Parsing first (generate structured Excel)
- Then route to the corresponding functional module based on question intent (perform intelligent analysis on the generated Excel)
- Example: "Analyze these invoice data" + 10 PDFs → Document Parsing (generate Excel) → File Q&A (analyze Excel)
- 示例:"分析这些发票数据" + 10个PDF → 文档解析(生成Excel) → 文件问数(分析Excel)
- If routing is incorrect, allow the user to manually specify the module (路由错误时允许用户手动指定模块)
Configuration
This skill uses a layered configuration architecture, separating user configuration from the skill package. Skill package updates will NOT overwrite user configuration.
`<workspace-dir>` convention: In this document,<workspace-dir>refers to the absolute path of the folder the user currently has open in the IDE / file manager. The Agent MUST confirm this path before the first operation by running a Python script withos.getenv('CODE_AGENT_CURRENT_SESSION_WORK_DIR'). If the script returns nothing or empty, use the absolute path of the folder selected by the user. MUST NOT infer using$PWD,$CWD, orPath.cwd()or similar runtime variables.
>
`<skill-package-dir>` convention: In this document,<skill-package-dir>refers to the root directory of this skill after installation (i.e. the directory containing thisSKILL.mdfile). The Agent can infer it from the path of this file.
Configuration Loading Priority (higher overrides lower)
1. Environment variable ACCESS_TOKEN (highest priority, suitable for container deployment) 2. Workspace-level configuration <workspace-dir>/.qbi/smartq-chat/config.yaml 3. QBI global configuration ~/.qbi/config.yaml (shared by all skills) 4. Default configuration default_config.yaml inside the skill package (package defaults, updated with the package)
server_domain, api_key, api_secret, and user_token can be placed in the workspace-level configuration or the global configuration. When both exist, the workspace-level configuration takes priority.
Configuration Item Descriptions
- `server_domain`: Quick BI service domain
- `api_key` / `api_secret`: OpenAPI authentication key pair (if not configured, built-in defaults are used for trial mode)
- `user_token`: Quick BI platform user ID; the Q&A interface requires
userId(if not configured, it is registered automatically and written back)
If use_env_property: true is enabled, the configuration can be overridden through the qbi_api_key, qbi_api_secret, qbi_server_domain, and qbi_user_token fields in the ACCESS_TOKEN environment variable JSON.
Automatic Trial Credential Registration
When neither api_key nor api_secret is configured (regardless of whether user_token exists), the script will: 1. If user_token is also not configured, print a friendly message informing the user that trial credentials will be registered automatically and trial mode will begin 2. Use built-in default credentials to populate api_key and api_secret 3. Automatically register a user based on the device's unique identifier and write the userId to the global configuration ~/.qbi/config.yaml (not affected by skill package updates)
Note:user_tokenexisting alone in the global configuration (from automatic trial registration) will NOT prevent trial credential population. ONLY whenapi_keyorapi_secretexists in an external configuration will the trial flow be skipped.
Trial expiration is controlled by the server-side interface through error code AE0579100004 — no local tracking is required.
Custom Configuration Guidance
When users want to use their own Quick BI account credentials (rather than trial credentials), sign in to the Quick BI console, click the avatar option "Copy skill configuration with one click", as shown below:
Show the configuration screenshot to the user based on current locale:
- zh_CN: !Copy Skill Config
- en_US: !Copy Skill Config
After copying, paste the configuration to the Agent. The Agent will automatically write server_domain, api_key, api_secret, and user_token into the workspace-level configuration <workspace-dir>/.qbi/smartq-chat/config.yaml (and decide whether to sync to the global configuration based on the save_global_property switch).
Agent Configuration Update Rules (Required Reading)
Zero-configuration initialization for new users: If the user says "initialize configuration", "I am a new user", or similar, but has NOT provided any specific configuration values, there is no need to manually write anything to any configuration file. Tell the user to run Q&A directly — the system will automatically complete trial registration (see the Automatic Trial Credential Registration section above).
ONLY apply the following write rules when the user explicitly provides specific configuration values.
Existing configuration protection rule: Before writing, the Agent MUST first check whether the workspace-level configuration file <workspace-dir>/.qbi/smartq-chat/config.yaml already exists and contains valid configuration. If the file already exists and is non-empty, the Agent MUST NOT modify or overwrite any configuration items on its own, unless the user explicitly expresses intent to update (e.g. "update my configuration", "replace with this configuration", "change api_key to xxx", etc.). When existing configuration is found, inform the user that configuration already exists and ask whether to confirm overwriting.
When the user provides any one or more of api_key, api_secret, user_token, or server_domain, and the above protection rule is satisfied, the Agent MUST use a file editing tool to directly modify the corresponding user configuration file and write the provided values into the matching fields.
Write location rules:
server_domain,api_key,api_secret,user_token→ ALWAYS write to the workspace-level configuration<workspace-dir>/.qbi/smartq-chat/config.yaml- Global configuration read/write is controlled by the
save_global_propertyswitch (defaulttrue): - If the switch is
false→ MUST NOT read or write global configuration under any circumstance, skip the global-configuration-related steps below - If the switch is
trueand the global configuration~/.qbi/config.yamlis empty or does not exist → also write to the global configuration - If the switch is
trueand the global configuration already contains content → write ONLY to the workspace-level configuration, then ask the user "Global configuration already exists. Do you want to sync the update?" and decide whether to write based on the user's reply
Procedure: 1. Extract configuration key-value pairs from the user message (support common formats such as key: value, key:value, and key=value) 2. Use a file editing tool (such as search_replace) to write the configuration into the workspace-level configuration file 3. Read the save_global_property value in the configuration; if it is false, skip to step 5 4. Check whether the global configuration ~/.qbi/config.yaml exists and is non-empty:
- If it is empty or does not exist → also write to the global configuration
- If it already contains content → ask the user "Global configuration already exists. Do you want to sync the update?" and decide whether to write based on the user's reply
5. After the update, confirm to the user which configuration items were written and where they were written
Prohibited actions:
- ❌ MUST NOT refuse to modify configuration citing reasons such as "limited permissions" or "unable to modify files inside the skill package"
- ❌ MUST NOT suggest workarounds such as using environment variables or manually copying files
- ❌ MUST NOT only output the configuration content and ask the user to modify it themselves
Prerequisites
- Python dependencies MUST be installed:
pip install requests pyyaml matplotlib numpy - Browser automation capability is required (Dashboard Skill Generation module ONLY)
- Dataset Q&A: user MUST have Q&A permission for the target dataset
- File Q&A: file formats limited to
xls,xlsx,csv; single file size ≤ 10MB - Document Parsing:
- System dependency:
brew install tesseract tesseract-lang(required for local parsing ONLY) - Supported formats: PDF, Word (.doc/.docx), Excel (.xls/.xlsx), CSV, images (.png/.jpg/.jpeg)
- Single file size ≤ 10MB (remote OCR limit)
- Error handling:
- Local parsing failure → automatically falls back to remote OCR
- Remote OCR still fails → classified as "parse failure", retaining original filename and error message
- Unknown document type → extract 5+ generic fields, MUST obtain user confirmation before generating Excel
- Detailed documentation: module-document-parser.md
Script Calling Convention (Required Reading)
When calling any Python script: 1. The script path MUST use the absolute path of the installed skill package directory (i.e. <skill-package-dir>/scripts/...); MUST NOT use relative paths 2. MUST pass the absolute path of <workspace-dir> via the --workspace-dir parameter (see the conventions in the Configuration section above for how to obtain it) 3. Wrap path parameter values in quotes (to prevent shell tokenization issues caused by Chinese characters, spaces, or other special characters) 4. smartq_stream_query.py, file_stream_query.py, q_insights.py, create_chat.py, generate_report.py MUST include the --locale parameter — see User Locale Determination Rules below
Invocation examples:
# File upload
python '<skill-package-dir>/scripts/chat/upload_file.py' '/path/to/data.xlsx' --workspace-dir '<workspace-dir>'
# File Q&A
python '<skill-package-dir>/scripts/chat/file_stream_query.py' <fileId> "各部门人数分布" --locale zh_CN --workspace-dir '<workspace-dir>'
# Dataset Q&A
python '<skill-package-dir>/scripts/chat/smartq_stream_query.py' "TOP 3 regions by sales" --locale zh_CN --workspace-dir '<workspace-dir>'
# Dataset Q&A (with dataset name hint — enables name lookup, exact match skips intelligent table selection)
python '<skill-package-dir>/scripts/chat/smartq_stream_query.py' "Based on 'Order Sales Details', what is the sales share by platform in Q1?" --cube-name 'Order Sales Details' --locale zh_CN --workspace-dir '<workspace-dir>'
# Document Parsing - local
python '<skill-package-dir>/scripts/document/document_local_parse.py' '/path/to/folder/' --json --workspace-dir '<workspace-dir>'
# Document Parsing - remote OCR
python '<skill-package-dir>/scripts/document/document_remote_ocr.py' '/path/to/folder/' --workspace-dir '<workspace-dir>'
# Excel generation
python '<skill-package-dir>/scripts/document/generate_excel.py' '<json-path>' --workspace-dir '<workspace-dir>'
# Data Insight
python '<skill-package-dir>/scripts/insight/q_insights.py' "这个报表有什么异常?" --excel-file '/path/to/data.xlsx' --locale zh_CN --workspace-dir '<workspace-dir>'
# Report generation
python '<skill-package-dir>/scripts/report/generate_report.py' "本月销售分析" --locale zh_CN --workspace-dir '<workspace-dir>'User Locale Determination Rules
Core principle: --locale MUST be determined SOLELY based on the user's input text, NOT influenced by any other source.Valid values: zh_CN or en_US only.
Determination method:
- Examine the user's original input message (the question or instruction the user typed)
- Identify the question/instruction language — the language of the sentence structure, verbs, and functional words (not embedded proper nouns)
- Chinese question language →
zh_CN; English or other question language →en_US
Mixed-language handling (critical):
- When user input contains both Chinese and English, determine locale by the question framing language, NOT by embedded entity names (dataset names, field names, table names, etc.)
- Entity names (dataset names, field names, etc.) embedded in the question are proper nouns / references — they do NOT indicate the user's language preference
- Rule of thumb: strip out quoted names or recognizable entity references, then judge the language of the remaining sentence structure
What counts as "user input text":
- ✅ The text the user typed in the current conversation turn
- ✅ The user's original question when performing follow-up queries in the same session
What MUST NOT influence locale determination:
- ❌ The Agent's own reply language (Agent may reply in a language different from the user's input)
- ❌ API response content or error messages (these are always in a fixed language regardless of the user's language)
- ❌ Script console output text
- ❌ Dataset names, field names, or other metadata — whether returned by the platform OR embedded in the user's question as references
- ❌ System prompt language or Agent configuration language
Example:
- User input: "帮我分析销售数据" →
--locale zh_CN(question language is Chinese) - User input: "Analyze sales data" →
--locale en_US(question language is English) - User input: "Analyze the 销售数据集" →
--locale en_US(question language is English; "销售数据集" is a dataset name reference, not the question language) - User input: "Show me data from 2024年度报表" →
--locale en_US(question language is English; "2024年度报表" is a dataset name) - User input: "帮我查一下 Sales Dataset 的数据" →
--locale zh_CN(question language is Chinese; "Sales Dataset" is a dataset name) - User input: "帮我分析销售数据", but API returns English error message →
--locale zh_CN(locale is determined by user input, NOT by API response) - Previous Agent reply was in English, user then types "查询TOP3" →
--locale zh_CN(locale is determined by user input, NOT by Agent's previous reply)
Prohibited actions:
- ❌ MUST NOT omit the
--workspace-dirparameter when calling scripts - ❌ MUST NOT use relative paths to call scripts (e.g.
python3 scripts/chat/...) - ❌ MUST NOT use hard-coded paths or guessed paths
- ❌ MUST NOT omit the
--localeparameter when calling scripts that require it - ❌ MUST NOT determine
--localebased on Agent's own output language or API/script return content
问数模块 (Chat Module)
配置说明请参见主文件的「配置」章节。
Scope
Does:
- 对 Quick BI 平台已授权数据集进行自然语言查询分析(数据集问数)
- 对用户上传的 Excel/CSV 文件通过 Quick BI API 进行自然语言分析(文件问数)
- 自动智能选表匹配最合适的数据集,无需用户提供 cubeId
- 渲染 matplotlib 图表并输出可视化结果和分析结论
Does NOT:
- 在问数场景下使用 pandas/openpyxl/csv 等库直接读取文件进行本地分析
- 要求用户手动提供 cubeId 或其他内部参数
技能触发与模式选择
模式 A:数据集问数(无文件上传)
- 用户没有上传文件,要查询平台数据集 → 数据集问数
- 触发词示例:"问数""小Q问数""查下xx数据集""数据集提问""自然语言查询"
模式 B:文件问数(有文件上传)
- 用户上传了 Excel/CSV 文件并对数据提问 → 文件问数
- 触发词示例:"帮我分析这份数据""查询xx最多的TOP10""各部门销售额对比""分析下这个文件""文件问数"
- 执行方式:严格按两步脚本执行(upload_file.py → file_stream_query.py),不得用其他方式读取或分析文件
前置条件
- 需安装 Python 依赖:
pip install requests pyyaml matplotlib numpy - 数据集问数:用户需要有目标数据集的问数权限
- 文件问数:文件格式限
xls、xlsx、csv,单文件大小 ≤ 10MB
---
模式 A — 数据集问数
对 Quick BI 平台上已授权的数据集进行自然语言查询。
工作流程
一步式执行,脚本内部自动完成完整的问数 → 取数 → 渲染流程:
flowchart LR
input["用户问题"] --> hasCubeId{"已指定 cubeId?"}
hasCubeId -- 是 --> streamQuery["SSE 流式问数"]
hasCubeId -- 否 --> queryCubes["查询有权限的数据集"]
queryCubes --> tableSearch["智能选表 POST /tableSearch\n(带 cubeIds 参数)"]
tableSearch -- 匹配到 --> streamQuery
tableSearch -- 未匹配 --> relevance["按文本相关性选择最相关数据集"]
relevance --> streamQuery
streamQuery --> parseSSE["实时解析 SSE 事件"]
parseSSE --> reasoning["输出推理过程"]
parseSSE --> olapResult["olapResult 事件\n(取数结果直接内联)"]
olapResult --> chart["matplotlib 图表 或 Markdown 表格"]
parseSSE --> conclusion["输出结论"]等待预期:问数分析通常需要 15~60 秒,复杂查询可能更久。建议在发起问数前告知用户正在分析中。
执行命令
默认用法(自动智能选表,无需提供 cubeId):
python scripts/chat/smartq_stream_query.py "分析销售数据集中销量最高的地区TOP3"cubeId 是可选参数,脚本会自动查询用户有权限的数据集并通过智能选表匹配最合适的数据集,无需用户手动提供。
可选:已知目标数据集 ID 时直接指定(跳过智能选表):
python scripts/chat/smartq_stream_query.py "总销售额是多少" --cube-id "dcbb0f94-4cee-4ba2-9950-927918bdd498"可选:提供候选数据集列表辅助智能选表:
python scripts/chat/smartq_stream_query.py "总销售额是多少" --cube-ids "cubeId1,cubeId2,cubeId3"内部处理流程
1. 智能选表(当未指定 --cube-id 时自动触发):
- 调用
GET /openapi/v2/smartq/query/llmCubeWithThemeList查询用户有权限的数据集列表 - 按用户问题与数据集名称的文本相关性对所有权限数据集预排序
- 使用自适应降级策略调用
POST /openapi/v2/smartq/tableSearch进行智能选表: - 依次尝试批次大小
[30, 10](可配置),取当前批次最相关的 top N 个数据集 - 若接口返回
"cubeIds can not be empty or over limit"错误,自动降级到下一批次 - 传入参数:
userQuestion、userId、llmNameForInference(默认SYSTEM_deepseek-r1-0528)、cubeIds - 任意批次匹配成功即返回第一个 cubeId,不再继续尝试
- 若所有批次均未匹配到结果,则按文本相关性从权限数据集中选取最相关的一个
2. 调用问数流式接口:POST /openapi/v2/smartq/queryByQuestionStream,请求体为 JSON(userQuestion、cubeId、userId 等),响应为 SSE 事件流
3. 实时解析 SSE 事件(事件格式:event:message\ndata:{"data":"xxx","type":"xxx","subType":"xxx"}):
relatedInfo→ 输出关联知识(数据集名称、业务定义等)reasoning→ 输出推理过程(subTypeMODEL_REASONING为模型推理)text/sql→ 输出文本和 SQL 语句olapResult→ 核心步骤,取数结果直接内联在事件流中summary→ 输出数据解读(subTypeMODEL_REASONING为模型推理)conclusion→ 输出分析结论check→ 校验错误信息error→ 异常错误信息finish→ 问数结束
4. olapResult 事件处理 :
- 从事件
data中解析取数结果 JSON,包含values(行数据)、chartType(图表类型枚举)、metaType(字段元信息)、logicSql(查询 SQL) metaType中t字段标识维度(dimension)或度量(measure),type字段标识 row/column,多维度场景下colorLegend标识颜色图例维度chartType枚举:NEW_TABLE(交叉表) /BAR(柱图) /LINE(线图) /PIE(饼图) /SCATTER_NEW(散点图) /INDICATOR_CARD(指标看板) /RANKING_LIST(排行榜) /DETAIL_TABLE(明细表) /MAP_COLOR_NEW(色彩地图) /PROGRESS_NEW(进度条) /FUNNEL_NEW(漏斗图)- 将数据转换为 chart_renderer 格式并使用 matplotlib 渲染图表(输出到
$WORKSPACE_DIR/output/目录) - matplotlib 不可用时回退为 Markdown 表格
输出说明
脚本运行时会实时输出以下内容:
[关联知识]命中的数据集和业务定义[推理过程]AI 的分析推理[SQL]生成的查询 SQL[取数结果]图表类型和取数状态- 图表图片或 Markdown 表格:取决于图表类型和渲染条件(详见下方「展示规则」)
- `[图表数据]`:所有图表的结构化数据(含字段信息、数据行、图表类型等)会保存到 JSON 文件,并在控制台输出文件路径
[结论]最终分析结论[数据解读]对数据的进一步解读分析[Trace]请求追踪 ID(问题反馈时提供此 ID 可加速排查)[完成]问数结束
展示规则
并非所有问数结果都会生成图表图片。脚本会根据图表类型和渲染条件自动选择输出图片或 Markdown 表格,Agent 应根据脚本的实际输出格式进行回复。
何时有图片:脚本输出中包含 [...](...) 时,说明图表已渲染为 PNG。 何时无图片:以下场景脚本只输出 Markdown 表格或纯文字结论,不会有 [...](...) 图片:
- 图表类型为交叉表(
NEW_TABLE)或明细表(DETAIL_TABLE)→ 直接输出 Markdown 表格 - matplotlib 未安装或渲染失败 → 回退为 Markdown 表格
- 取数结果为空(
values无数据)→ 仅输出[结论]和[数据解读] - 查询校验失败或出错 → 仅输出
[校验]或[错误]信息
有图片时(强制)
MUST:脚本输出中包含 [...](...) 图片引用时,Agent 的答复中必须原样包含该 Markdown 图片语法,否则用户无法看到图表。这是硬性要求,不可省略。1. 原样复制  到答复正文中,让用户直接看到可视化结果 2. 紧接图片下方标注图表文件路径,例如:> 图表路径:$WORKSPACE_DIR/output/chart_xxx.png 3. 不要在图表上方添加「饼图如下」「脚本输出路径」之类的机械化引导文字,分析结论自然衔接即可 4. 如果有多张图表,按脚本输出顺序逐一内联展示
无图片时
1. 如果脚本输出了 Markdown 表格,直接展示表格,结合 [结论] 和 [数据解读] 进行总结 2. 如果脚本既无图片也无表格(取数为空、查询失败等),基于 [结论] / [错误] / [校验] 信息向用户说明结果 3. 禁止在没有图片输出时自行编造 [...](...) 图片语法或占位表格
示例 A — 脚本输出包含图片时:
假设脚本输出中包含:
Agent 回复应为:
根据分析结果,销量最高的三个地区如下:

> 图表路径:/path/output/chart_1744123456_1.png
从图表可以看出,华东地区以 XX 万的销量位居第一……示例 B — 脚本输出为 Markdown 表格时(无图片):
假设脚本输出中包含:
[取数结果] 图表类型: table (交叉表), 字段数: 3, 数据行数: 5
| 地区 | 销量 | 占比 |
|------|------|------|
| 华东 | 1200 | 35% |
| 华南 | 980 | 28% |
| 华北 | 750 | 22% |
| 西南 | 320 | 9% |
| 其他 | 210 | 6% |
[结论] 华东地区销量最高,占总销量的 35%
[数据解读] 华东和华南两个地区合计占比超过 60%,是主要销售区域……Agent 回复应为:
根据数据集的查询结果:
| 地区 | 销量 | 占比 |
|------|------|------|
| 华东 | 1200 | 35% |
| 华南 | 980 | 28% |
| 华北 | 750 | 22% |
| 西南 | 320 | 9% |
| 其他 | 210 | 6% |
华东地区销量最高,占总销量的 35%。华东和华南两个地区合计占比超过 60%,是主要销售区域……示例 C — 取数结果为空或查询失败时:
假设脚本输出:
[取数结果] 查询结果(无数据)
[结论] 未查询到符合条件的数据,建议调整查询条件后重试Agent 回复应为:
本次查询未返回数据,可能是筛选条件过于严格或数据集中暂无匹配记录。建议您调整查询条件后重试。---
模式 B — 文件问数
基于用户上传的 Excel/CSV 结构化数据文件,通过流式问数接口进行智能分析。
工作流程
严格按两步执行,每一步独立运行并输出完整结果。步骤 1 的输出(fileId)作为步骤 2 的输入。
错误处理原则:业务逻辑错误(权限不足、试用到期、格式不支持等)必须立即终止整个流程;网络类瞬态错误(超时、连接中断)可重试 1~2 次(建议指数退避),重试仍失败则终止。
flowchart LR
file["用户数据文件\n(xls/xlsx/csv)"] --> autoReg{"user_token\n已配置?"}
autoReg -- 否 --> register["自动注册用户\n回填 ~/.qbi/config.yaml"]
autoReg -- 是 --> upload
register -- 失败 --> abort["终止流程\n告知原因"]
register -- 成功 --> upload["步骤1: upload_file.py\nPOST /copilot/parse"]
upload -- 失败 --> abort
upload -- 成功 --> fileId["输出 fileId\n+ 文件结构详情"]
fileId --> stream["步骤2: file_stream_query.py\nPOST /smartq/queryByQuestionStreamByFile"]
stream -- 失败 --> abort
stream -- 成功 --> parseSSE["实时解析 SSE 事件流"]
parseSSE --> reasoning["输出思考/推理过程"]
parseSSE --> code["code 事件 → 拼接完整 Python 代码"]
parseSSE --> result["result 事件 → 解析结构化数据"]
result --> renderChart["matplotlib 渲染图表 PNG\n保存到 $WORKSPACE_DIR/output/"]
parseSSE --> reporter["reporter 事件 → 分析报告"]
parseSSE --> conclusion["输出结论/数据解读"]
parseSSE --> finish["finish 事件 → 结束"]步骤 1 — 上传文件获取 fileId
python scripts/chat/upload_file.py /path/to/data.xlsx| 项目 | 说明 |
|---|---|
| 接口 | POST /openapi/v2/copilot/parse |
| Content-Type | multipart/form-data |
| 功能 | 上传文件并解析各 Sheet 结构详情 |
请求参数
| 参数 | 类型 | 说明 |
|---|---|---|
file | File | 上传的数据文件(multipart 文件域) |
fileName | String | 文件名(如 sales_data.xlsx) |
tableConfigs[0].tableName | String | 表名(默认取文件名去后缀) |
tableConfigs[0].tableType | String | excel 或 csv |
isSave | String | 固定 false |
fileId | String | 留空(首次上传) |
输出内容
脚本会输出:上传进度提示、fileId、完整的响应 JSON(含文件结构详情、各 Sheet 的列名和类型)。
关键输出:从输出中提取 fileId 值,作为步骤 2 的第一个参数。错误处理
步骤 1 出现以下任一情况时,立即终止整个流程,不得继续执行步骤 2:
- 用户自动注册失败
- 文件上传失败(格式不支持、大小超限、服务端解析错误)
- 脚本以非零退出码结束
等待预期:文件问数通常需要 15~60 秒,复杂分析可能需要数分钟(最长 10 分钟超时)。建议提前告知用户耐心等待。
步骤 2 — 基于 fileId 发起流式问数
python scripts/chat/file_stream_query.py <fileId> "用户的问题"示例:
python scripts/chat/file_stream_query.py "abc123-def456" "各部门的销售额对比"| 项目 | 说明 |
|---|---|
| 接口 | POST /openapi/v2/smartq/queryByQuestionStreamByFile |
| Content-Type | application/json |
| 响应格式 | SSE (Server-Sent Events) 事件流 |
| 超时时间 | 10 分钟(600 秒) |
SSE 核心事件类型
| 事件类型 | 输出标记 | 处理方式 |
|---|---|---|
text | (直接输出) | 实时拼接输出文本内容 |
reasoning | (直接输出) | 实时输出 AI 思考推理过程 |
code | (静默收集) | 静默拼接 → 流结束后保存到 $WORKSPACE_DIR/.qbi/smartq-chat/output/ |
result | [取数结果] | 解析结构化数据 → matplotlib 渲染图表 PNG |
reporter | (直接输出) | 实时拼接分析报告文本 |
html | [HTML 图表] | 仅保存原始 HTML 到 $WORKSPACE_DIR/.qbi/smartq-chat/output/ |
html_result | [图表数据] | 解析结构化数据,渲染图表 |
sql | [SQL] | 输出生成的 SQL 语句 |
conclusion | [结论] | 输出最终分析结论 |
summary | [数据解读] | 输出数据解读分析 |
trace | [Trace] | 请求追踪 ID,问题反馈时提供此 ID 可加速排查 |
finish | [完成] | 标记事件流结束(终止事件) |
error | [错误] | 输出错误信息(终止事件) |
展示规则
并非所有文件问数结果都会生成图表图片。脚本会根据渲染条件自动选择输出图片或 Markdown 表格,Agent 应根据脚本的实际输出格式进行回复。
有图片时(强制)
MUST:脚本输出中包含 [...](...) 图片引用时,Agent 的答复中必须原样包含该 Markdown 图片语法,否则用户无法看到图表。这是硬性要求,不可省略。1. 原样复制  到答复正文中 2. 紧接图片下方标注图表文件路径 3. 不要在图表上方添加机械化引导文字 4. 如果有多张图表,按顺序逐一内联展示
无图片时
1. 如果脚本输出了 Markdown 表格,直接展示表格 2. 如果 matplotlib 不可用,基于 result 事件数据输出 Markdown 表格 3. 如果既无图片也无表格,基于 conclusion / summary / reporter 内容组织回复 4. 禁止在没有图片输出时自行编造 [...](...) 图片语法或占位表格
结果总结要求
Agent 在答复用户时,必须同时满足以下两点:
1. 内联图表(优先):若脚本输出中包含  图片引用,必须先原样复制到答复正文中(参见上方「展示规则 › 有图片时」),确保用户能看到可视化结果 2. 文字总结:基于脚本输出中的 conclusion(结论)和 summary(数据解读)内容,结合 reporter(分析报告)文本,对分析结果进行重新组织和总结
禁止向用户展示分析代码或代码文件路径(详见重要提示第 10 条)。
---
异常处理(必读)
脚本已内置以下三种异常的检测逻辑,会在控制台自动打印对应提示。Agent 应参考 ../common/error_messages.md 中的提示文案向用户传达,可根据上下文适当调整措辞,但核心信息(链接、操作建议)不可省略。检测到任一异常时,立即终止流程。
1. 无数据集权限
触发条件:数据集问数模式下,脚本输出包含「您当前没有可用的问数数据集」 检测位置:scripts/chat/cube_resolver.py 权限查询 处理方式:提示用户没有可用数据集,建议尝试文件问数或开通服务。详细提示文案见 error_messages.md
附加规则:整个回复中只展示一次,不得重复。可自然询问用户是否改用文件问数。
2. 试用到期
触发条件:任何步骤的脚本输出或 API 响应中出现错误码 AE0579100004 检测位置:scripts/common/utils.py 中的 check_trial_expired() 处理方式:告知用户试用已到期,引导开通正式服务。详细提示文案见 error_messages.md
3. 数据文件解析失败
触发条件:文件问数模式下,脚本输出包含「数据文件解析失败」 检测位置:scripts/chat/file_stream_query.py 中的 _on_error 方法 处理方式:提示用户检查文件格式和内容后重试。详细提示文案见 error_messages.md
---
关键接口汇总
| 接口 | 方法 | Content-Type | 模式 | 说明 |
|---|---|---|---|---|
/openapi/v2/smartq/tableSearch | POST | application/json | A | 智能选表,返回匹配的 cubeId 列表 |
/openapi/v2/smartq/query/llmCubeWithThemeList | GET | - | A | 查询用户有权限的问数数据集列表 |
/openapi/v2/smartq/queryByQuestionStream | POST | application/json | A | 数据集问数流式接口,返回 SSE(olapResult 事件直接包含取数结果) |
/openapi/v2/copilot/parse | POST | multipart/form-data | B | 上传文件并解析结构,返回 fileId |
/openapi/v2/smartq/queryByQuestionStreamByFile | POST | application/json | B | 文件问数流式接口(SSE) |
/openapi/v2/organization/user/queryByAccount | GET | - | 通用 | 通过 accountName 查询用户是否在组织中 |
/openapi/v2/organization/user/addSuer | POST | application/json | 通用 | 添加用户到组织 |
---
重要提示
1. 文件问数必须走 API:详见顶部「核心约束」,禁止使用 pandas/openpyxl 等库直接分析用户上传的文件 2. 模式选择:根据用户是否上传了文件自动选择数据集问数或文件问数模式 3. 数据集问数无需 cubeId:用户进行数据集问数时,直接执行脚本,不传 --cube-id,脚本会自动智能选表。禁止要求用户提供 cubeId 或提示 cubeId 为必传参数 4. 文件问数必须分步执行:先执行步骤 1 上传文件获取 fileId,再执行步骤 2 传入 fileId 进行问数,不可跳过或合并 5. 错误处理:业务逻辑错误(权限不足、试用到期等)必须立即终止整个流程;网络类瞬态错误(超时、连接中断)可重试 1~2 次后终止。向用户清晰说明报错原因,并提醒:「如需进一步帮助,请联系 Quick BI 产品服务同学获取支持。」 6. 流式超时:默认超时 10 分钟(600 秒),复杂查询可能需要较长时间 7. 文件格式限制:仅支持 xls、xlsx、csv 格式,单文件不超过 10MB 8. userId 自动处理:user_token 未配置时,脚本启动时即自动基于设备唯一标识生成 accountId,通过组织用户接口检查并注册用户,注册成功后将 userId 回写到全局配置 ~/.qbi/config.yaml,后续调用不再重复注册 9. 图表展示(强制):PNG 文件保存在 $WORKSPACE_DIR/output/ 目录中,脚本会以  格式输出。Agent 必须将脚本输出的 [...](...) 原样复制到答复中,这是用户看到图表的唯一方式,不可省略 10. 禁止展示代码:文件问数中 code 事件的 Python 代码仅静默保存,禁止在答复中向用户展示代码内容或代码文件路径 11. 禁止编造占位表格:Agent 禁止自行构造含「(数据见下方图表)」等占位符的 Markdown 表格或其他入空壳表格
---
Examples
Example 1: 数据集问数(自动智能选表)
Input:
用户: "销量最高的地区TOP3是哪些"Expected:
python scripts/chat/smartq_stream_query.py "销量最高的地区TOP3是哪些"脚本自动智能选表匹配数据集,输出推理过程、图表(或 Markdown 表格)和分析结论。
Agent 回复示例(脚本输出含图片时,必须包含图片 Markdown):
根据销售数据集的分析,销量最高的三个地区为:

> 图表路径:/path/output/chart_1744123456_1.png
从图表可以看出:1. XX地区销量最高……Example 2: 文件问数(上传 Excel 分析)
Input:
用户: 上传了 sales_data.xlsx,提问"各部门的销售额对比"Expected:
# 步骤 1:上传文件获取 fileId
python scripts/chat/upload_file.py /path/to/sales_data.xlsx
# 输出 fileId=abc123-def456
# 步骤 2:基于 fileId 发起问数
python scripts/chat/file_stream_query.py "abc123-def456" "各部门的销售额对比"分两步执行,Agent 基于结论和数据解读总结分析结果。若脚本输出了 [...](...) 图片,必须原样复制到回复中;若输出的是 Markdown 表格则直接展示表格;若无图表数据则基于结论文字回复。不展示代码。
Example 3: 数据集问数(结果为交叉表,无图片)
Input:
用户: "各月份的销售明细数据"Expected:
python scripts/chat/smartq_stream_query.py "各月份的销售明细数据"脚本输出交叉表类型数据时,直接输出 Markdown 表格而非图片。
Agent 回复示例(无图片时基于表格和结论回复):
以下是各月份的销售明细:
| 月份 | 销售额 | 订单数 |
|------|--------|--------|
| 1月 | 150万 | 320 |
| 2月 | 128万 | 280 |
| 3月 | 175万 | 390 |
从数据来看,3月的销售额和订单数均为最高……异常提示文案
以下为各异常场景的用户提示文案。Agent 输出时禁止使用 [text](url) 链接语法,所有 URL 直接以纯文本形式内嵌在文案中。
1. 无数据集权限
您当前没有可用的问数数据集。
>
📂 试试「文件问数」
无需任何权限配置,上传 Excel/CSV 文件即可直接分析。
>
🚀 0 元体验,限时加码
现在上阿里云,将额外赠送 30 天全功能体验,解锁企业级安全管控与深度分析引擎,让 AI 洞察更准、更稳。点击下方链接,领取试用:
https://www.aliyun.com/product/quickbi-smart?utm_content=g_1000411205
>
💬 点击下方链接,进入交流群获取最新资讯:
https://at.umtrack.com/r4Tnme
2. 试用到期
小 Q 超级分析助理已陪伴您一周,我们看到您在通过 AI 寻找数据背后的真相,这很了不起。
>
🕙 试用模式已结束
授权到期后,动态分析将暂告一段落。
>
💡 其实,您可以更轻松
目前的"文件模式"仍需您手动搬运数据。让 AI 直连企业存量数据资产,实现分析结果自动更新?立即体验完整功能。
>
🚀 0 元体验,限时加码
现在上阿里云,将额外赠送 30 天全功能体验,解锁企业级安全管控与深度分析引擎,让 AI 洞察更准、更稳。点击下方链接,领取试用:
https://www.aliyun.com/product/quickbi-smart?utm_content=g_1000411205
>
💬 点击下方链接,进入交流群获取最新资讯:
https://at.umtrack.com/r4Tnme
3. 数据文件解析失败
⚠️ 数据文件解析失败
当前问数的数据文件可能存在格式或内容问题,服务端多次重试执行均未成功。
>
💡 建议排查
请检查文件是否为标准的 Excel/CSV 格式,确认数据内容完整无损后重新上传。
>
💬 如仍无法解决,点击下方链接,进入交流群联系 Quick BI 产品服务同学获取支持:
https://at.umtrack.com/r4Tnme
QuickBI 仪表板技能生成器 - 参考文档
本文档包含 SKILL.md 的详细参考内容,供深入了解使用。
分析框架匹配规则
框架匹配规则表
综合指标语义 + 布局模式 + 联动关系,匹配最适合的分析框架。
| 匹配规则(基于真实字段名称) | 分析框架 | 适用场景 | 核心公式/方法 |
|---|---|---|---|
| 包含"销售额/收入"+"成本"+"利润"+"毛利率/利润率" | 杜邦分析 | 财务指标分解,盈利能力分析 | ROE = 利润率 × 资产周转率 × 权益乘数 |
| 包含"获客/新增"+"激活"+"留存"+"转化"+"收入/付费" | AARRR 海盗模型 | 互联网产品增长漏斗分析 | 各环节转化率优化 |
| 包含"最近购买时间"+"购买频次"+"消费金额" | RFM 客户分析 | 客户价值分群,精准营销 | R×F×M 评分矩阵 |
| 包含"产品/商品"维度 + "市场份额/增长率" | 波士顿矩阵 | 产品组合策略分析 | 明星/现金牛/问题/瘦狗分类 |
| 包含"步骤/阶段/环节"维度 + "转化率/流失率" | 漏斗分析 | 流程优化,定位流失环节 | 各环节转化率 = 下一步/上一步 |
| 包含"目标值/计划值" + "实际值/完成值" | 目标达成分析 | KPI 完成度监控 | 达成率 = 实际值/目标值 × 100% |
| 包含"同期/去年同期" + "当期/本期" | 同环比分析 | 时间对比趋势分析 | 同比 = (本期-同期)/同期 × 100% |
| 包含"预算" + "实际/执行" | 预实对比分析 | 预算执行监控 | 预算执行率 = 实际/预算 × 100% |
| 包含"库存/存货" + "周转/动销" | 库存分析 | 库存健康度监控 | 周转率 = 销售成本/平均库存 |
| 包含"客单价" + "客户数/用户数" + "销售额" | 客户价值分析 | 客户贡献度分析 | 销售额 = 客户数 × 客单价 |
| 包含"曝光/展示" + "点击" + "转化/成交" | 营销漏斗分析 | 广告投放效果分析 | CTR/CVR 等转化指标 |
| 包含"人力/人数" + "产出/效率" | 人效分析 | 人力资源效能分析 | 人均产出 = 总产出/人数 |
| 以上都不匹配 | L1-L4 金字塔 | 通用层级分析框架 | 概览→趋势→分解→明细 |
---
布局模式分析规则
布局模式识别
基于 tileLayout 位置信息推断仪表板的整体分析模式。
| 布局特征 | 布局模式 | 典型特点 | 推断的仪表板类型 |
|---|---|---|---|
| 第一行有多个 indicator-card 类型组件 | 指标矩阵型 | 顶部密集指标卡阵列 | 监控型仪表板(强调 L1 概览) |
| 存在 line/bar 等趋势图表 | 核心图表型 | 有主次之分的焦点布局 | 分析型仪表板(强调 L2/L3) |
| 底部存在 common-table | 明细导向型 | 底部有明细表 | 运营型仪表板(强调 L4 追溯) |
| 同一行有多个相同类型组件 | 对比分析型 | 并列布局便于对比 | 多维对比分析 |
| 组件数量少(≤4) | 聚焦分析型 | 少而精的核心图表 | 专题分析仪表板 |
布局模式与分析框架的关联
| 布局模式 | 倾向的分析框架 | 置信度提升依据 |
|---|---|---|
| 指标矩阵型 | 目标达成分析、同环比分析 | 多指标并列 → 关注指标对比 |
| 核心图表型 | 趋势分析、漏斗分析 | 大图表为主 → 关注过程变化 |
| 明细导向型 | L1-L4 金字塔 | 有明细表 → 需要追溯能力 |
| 对比分析型 | 杜邦分析、客户价值分析 | 并列布局 → 关注维度对比 |
---
层级归类规则
L1-L4 层级判断
| 层级 | 类型特征 | 位置特征 |
|---|---|---|
| L1 | indicator-card/kpi/gauge | y ≤ 20(顶部) |
| L2 | line/area/indicator-trend | 20 < y < 50,含 datetime 维度 |
| L3 | bar/pie/ranking-list | 30 < y < 70,含分类维度 |
| L4 | common-table | y > 50(底部) |
分析主题推断(基于图表类型)
| 图表类型 | 分析主题模式 |
|---|---|
| indicator-card/kpi | "{度量}指标展示" |
| line/area | "{度量}时序趋势" |
| pie | "{维度}分布/占比" |
| bar | "{维度}对比分析" |
| ranking-list | "{维度}排行榜" |
| common-table | "{主题}明细查询" |
---
意图路由规则
用户问法模式匹配
| 用户问法模式 | 提取的意图 | 匹配目标 |
|---|---|---|
| "XX是多少/有多少" | 查询单一指标 | L1 指标卡 |
| "XX趋势/走势/变化" | 趋势分析 | L2 折线图/趋势图 |
| "XX排行/TOP/最高/最低" | 排序分析 | L3 排行榜 |
| "XX分布/占比/构成" | 结构分析 | L3 饼图/柱图 |
| "各XX的YY" | 维度分解 | L3 分组图表 |
| "XX明细/详情/列表" | 明细查询 | L4 明细表 |
| "为什么XX下降/上升" | 归因分析 | L1→L2→L3 联合 |
---
业务逻辑推断规则
指标组合推断公式
| 指标组合 | 推断公式 |
|---|---|
| 销售额 + 成本 + 利润 | 利润 = 销售额 - 成本 |
| 销售额 + 销量 | 客单价 = 销售额 / 销量 |
| 目标值 + 实际值 | 达成率 = 实际 / 目标 × 100% |
| 本期 + 同期 | 同比增长率 = (本期-同期)/同期 × 100% |
| 本期 + 上期 | 环比增长率 = (本期-上期)/上期 × 100% |
---
工具函数说明
quickbi_openapi.py 函数清单
| 函数名 | 用途 | 使用阶段 |
|---|---|---|
load_config(config_path=None) | 加载配置(优先级:环境变量 > 工作目录级 > 全局 > 包内默认) | Step 1.0, Step 2.1 |
is_dataportal_url(url) | 判断是否为数据门户 URL | Step 1.0 |
extract_dataportal_ids(url) | 从数据门户 URL 提取 productId 和 menuId | Step 1.0 |
get_dataportal_page_id(...) | 通过 OpenAPI 获取数据门户关联的仪表板 pageId | Step 1.0 |
extract_page_id(url) | 从仪表板 URL 提取 pageId | Step 1.0 |
validate_and_prepare_dashboard(...) | 仪表板预校验及预处理 | Step 1.0 |
get_dashboard_json(...) | 获取仪表板完整 JSON 数据 | Step 2.1 |
query_openapi(...) | 调用 SmartQ 查询接口 | 生成的 skill 查询阶段 |
get_dashboard_update_time(...) | 查询仪表板更新时间 | 生成的 skill 启动校验 |
get_dashboard_json.js 函数清单
| 函数名 | 用途 |
|---|---|
parseDashboardJson(json) | 解析仪表板原始 JSON,提取组件结构 |
analyzeLayout(charts) | 基于 tileLayout 分析图表布局 |
config_loader.py 函数清单
| 函数名 | 用途 |
|---|---|
load_config() | 四层配置加载(优先级:环境变量 > 工作目录级 > 全局 > 包内默认) |
persist_to_global_config(key, value) | 写入全局配置 ~/.qbi/config.yaml |
---
错误码参考
预校验接口错误码
| 错误码 | 含义 | 处理建议 |
|---|---|---|
AE0510000005 | 用户不在组织中 | 检查 user_token 是否正确 |
AE0510150002 | 没有仪表板访问权限 | 检查用户是否有该仪表板的访问权限 |
AE0510200000 | 没有数据集管理或者授权的权限 | 检查是否有数据集管理和问数配置权限 |
AE0581030022 | 未购买问数功能 | 确认已购买 SmartQ 问数功能 |
OE10010106 | API 未授权 | 检查 api_key/api_secret 配置 |
CONNECTION_ERROR | 网络连接失败 | 检查网络和 server_domain 配置 |
数据门户接口错误码
| 错误码 | 含义 | 处理建议 |
|---|---|---|
NO_PAGE_ID | 数据门户菜单未关联仪表板 | 检查门户菜单是否正确配置了仪表板页面 |
CONNECTION_ERROR | 网络连接失败 | 检查网络和 server_domain 配置 |
---
dashboardData 数据结构
Step 2.2 解析后返回的完整数据结构:
{
success: boolean;
basicInfo: {
name: string; // 仪表板名称
pageId: string; // 页面ID
workspaceId: string; // 工作空间ID
gmtModified: number; // 最后修改时间(毫秒级时间戳),用于 skill_generated_at
};
queryControls: Array<{ // 查询控件列表
componentId: string;
internalId: string;
needManualQuery: boolean;
fields: Array<{
labelName: string;
componentType: string; // datetime / enumSelect
relatedGraphIds: string[];
}>;
}>;
chartComponents: Array<{ // 图表组件列表
componentId: string;
componentName: string;
sourceId: string; // 数据集ID - 问数调用的关键
dimensions: Array<{caption: string; pathId: string}>;
measures: Array<{caption: string; aggregateType: string}>;
drillFields: Array<{caption: string}>;
tabInfo: object | null; // Tab 从属关系
}>;
tabComponents: Array<{ // Tab 组件列表
componentId: string;
tabs: Array<{id: string; title: string}>;
}>;
richTextComponents: Array<{textContent: string}>;
layoutAnalysis: {rows: Array}; // 布局分析
}QuickBI 仪表板技能生成器
通过 OpenAPI 获取 QuickBI 仪表板数据,发现其图表组件、字段配置、查询控件、布局关系,提炼分析思路,生成一份可用于数据查询的 SKILL.md 文件。
Scope
Does:
- 接收 QuickBI 仪表板 URL 或数据门户 URL,解析出 pageId
- 调用 OpenAPI 获取仪表板完整 JSON 结构
- 解析图表组件、查询控件、数据集、字段配置
- 分析布局模式,匹配适用的分析框架(L1-L4 金字塔或专业框架)
- 生成完整的查询技能 SKILL.md 文件并安装到技能中心
Does NOT:
- 不执行实际的数据查询(查询由生成的子 skill 负责)
- 不支持非 QuickBI 平台的仪表板
- 不处理需要特殊权限的仪表板(会在预校验阶段提示错误)
- 不在 `fetch_dashboard_data` 失败时尝试任何替代方案(必须终止流程,禁止绕行)
触发场景
当用户提出以下类型的请求时使用此 Skill:
- "帮我把这个 QuickBI 仪表板转化为一个查询 Skill"
- "把这个看板变成一个可以查询数据的 Skill"
- "生成这个仪表板的查询技能"
- "提取这个仪表板的分析思路,生成 Skill"
- "为这个仪表板生成技能:{URL}"
- 用户提供了一个 类似
https://bi.aliyun.com/dashboard/view/pc.htm?pageId=XXXXXXX格式的仪表板 URL,并希望创建查询能力 - 用户提供了一个数据门户页面 URL(格式如
https://bi.aliyun.com/product/view.htm?module=dashboard&productId=xxx&menuId=yyy),并希望创建查询能力
支持的 URL 格式
| URL 类型 | 路径特征 | 关键参数 | 处理方式 |
|---|---|---|---|
| 仪表板页面 | /dashboard/view/pc.htm | pageId | 直接提取 pageId |
| 数据门户页面 | /product/view.htm | productId, menuId | 通过 OpenAPI 获取关联的 pageId |
前置条件
- 需要有效的 API 凭证(用于调用 OpenAPI 获取仪表板数据)
- 配置说明请参见主文件的「配置」章节
---
Phase 1: 输入收集与验证
Step 1.0: 获取用户输入
从用户消息中提取:
1. 页面地址(必需):QuickBI 仪表板链接或数据门户链接 2. 技能名称(可选):生成的 skill 目录名(kebab-case 格式)
- 如果用户指定了技能名称,直接使用(会覆盖同名技能)
- 如果未指定,将在 Phase 2 发现仪表板标题后自动推导
---
Phase 2: 仪表板数据获取与解析
Step 2.1: 一站式获取仪表板数据
⚠️ 强制约束:必须使用封装脚本,禁止自行拆分执行。
使用 scripts/fetch_dashboard_data.py 一站式完成:配置加载 → URL 解析 → 预校验 → 获取 JSON → 解析结构 → 获取数据集名称。
[强制规则] 失败立即终止,禁止任何绕行:
⛔ 绝对禁止:当 fetch_dashboard_data 返回失败时,禁止尝试任何替代方案,包括但不限于:- ❌ 直接调用底层 API(如 get_dashboard_json)- ❌ 跳过预校验步骤
- ❌ 尝试"其他方法"获取数据
- ❌ 继续执行后续任何步骤
>
唯一正确的行为:输出错误信息 → 终止流程 → 等待用户修正后重新触发
- 如果获取失败(
result["success"] == False),必须立即终止整个流程 - 失败原因已在 `result["error"]` 中说明,直接展示给用户即可
- 不要尝试"智能"地绕过错误——预校验失败说明前置条件不满足,绕行只会导致后续步骤全部失败
from dashboard.fetch_dashboard_data import fetch_dashboard_data
# 一站式获取(自动处理:配置加载、URL解析、预校验、获取JSON、解析、数据集名称)
result = fetch_dashboard_data(user_input_url)
if not result["success"]:
print(f"获取失败: {result['error']}")
# ⛔ 必须立即终止!禁止尝试其他方法,禁止继续执行任何后续步骤
return # 流程到此结束,等待用户修正后重新触发
# 提取结果
dashboardData = result["dashboardData"] # 解析后的仪表板结构
datasetNameMap = result["datasetNameMap"] # cubeId -> cubeName 映射
page_id = result["pageId"] # 仪表板 pageId
dashboard_url = result["dashboardUrl"] # 标准仪表板预览页地址(用于生成 skill)
print(f"获取成功: {dashboardData['basicInfo']['name']}")脚本位置:scripts/fetch_dashboard_data.py
执行后必须输出(确认数据已获取):
---
## Step 2.1 执行结果
**执行状态**:{成功/失败}
**仪表板名称**:{dashboardData.basicInfo.name}
**仪表板URL**:{dashboard_url}(用于仪表板知识库的 URL 字段)
**pageId**:{page_id}
**gmtModified**:{dashboardData.basicInfo.gmtModified}(用于 SKILL_METADATA.skill_generated_at)
**图表组件数**:{dashboardData.chartComponents.length} 个
**查询控件数**:{dashboardData.queryControls.length} 个
**Tab组件数**:{dashboardData.tabComponents.length} 个
> ⚠️ **pageId 校验**:上方 pageId 值来自脚本返回的 `result["pageId"]`。
> 如果用户传入的是数据门户 URL(含 productId),pageId 与 productId **一定不同**。
> 后续 Phase 3 生成技能文件时,所有需要 pageId 的地方**必须使用此值**,禁止使用 URL 中的 productId。
**数据集清单**(去重):
| 数据集名称 | 数据集ID |
|-----------|----------|
| {datasetNameMap[cubeId]} | {cubeId} |
> 数据已存储到 `dashboardData`、`datasetNameMap` 和 `dashboard_url`,继续执行 Step 2.2
---失败处理(⛔ 禁止绕行):
- 如果
result["success"] == False,立即终止整个流程 - 输出错误信息
result["error"],告知用户失败原因 - 禁止尝试直接调用
get_dashboard_json或任何其他方法 - 提示用户检查配置或仪表板权限后重新触发
返回数据结构:dashboardData 包含 basicInfo、queryControls、chartComponents、tabComponents、richTextComponents、layoutAnalysis 等字段,完整定义见 reference.md - dashboardData 数据结构。
Step 2.2: 数据验证与补充
目的:验证解析结果的完整性,必要时补充信息。
2.2.1 Tab 结构验证
如果 dashboardData.tabComponents.length > 0:
1. 列出所有 Tab 及其标题 2. 确认每个 Tab 下包含的图表组件 3. 记录 Tab 与图表的从属关系
2.2.2 图表标题验证
1. 检查 dashboardData.chartComponents[].componentName 是否有意义 2. 如果为空或无意义,根据度量/维度字段推断主题 3. 记录调整后的图表标题
2.2.3 富文本内容提取
1. 从 dashboardData.richTextComponents[].textContent 提取纯文本 2. 用于理解仪表板的业务背景和使用说明
Step 2.3: 提炼分析思路与匹配分析框架
【必须执行步骤】 完成 5 个子步骤 + 强制输出(2.3.2-OUTPUT)。
这是将仪表板数据转化为可用分析框架的核心步骤。即使时间紧迫,也必须完成此步骤的所有子步骤(2.3.1 - 2.3.5)和强制输出(2.3.2-OUTPUT)。
核心原则:所有内容必须基于 dashboardData(Step 2.1 获取的数据)推断,不可臆造。2.3.1 数据提取
从 dashboardData.chartComponents 中提取:
数据集清单:收集所有图表的 sourceId,去重后建立清单:
| 数据集ID | 关联图表 | 可用维度 | 可用度量 |
|---|---|---|---|
| {sourceId} | {图表名列表} | {维度字段} | {度量字段(聚合方式)} |
指标体系:遍历 chartComponents[].measures,按 caption 去重。
维度体系:遍历 chartComponents[].dimensions,按 itemType 分类:
datetime→ 时间维度 |geographic→ 地理维度 |dimension→ 分类维度
2.3.2 分析框架匹配
综合指标语义 + 布局模式 + 联动关系,匹配最适合的分析框架。
框架匹配规则:详见 reference.md - 分析框架匹配规则
常用框架:杜邦分析、AARRR 海盗模型、RFM 客户分析、漏斗分析、目标达成分析、同环比分析等。无法匹配时使用 L1-L4 金字塔(概览→趋势→分解→明细)。
2.3.2-OUTPUT: 【强制】输出分析框架匹配结果
不可跳过:完成 2.3.1-2.3.2 后,必须输出以下格式:
---
## 【分析框架匹配结果】
### 提取到的真实字段
**度量字段**:{measures 列表}
**维度字段**:时间({datetime}) | 地理({geographic}) | 分类({dimension})
### 布局模式
**布局特征**:第一行{N}个{类型}组件,总{M}行,底部{有/无}明细表
**布局模式**:{指标矩阵型/明细导向型/对比分析型/聚焦分析型}
### 框架匹配
**匹配框架**:{框架名称}
**置信度**:{high/medium/default}
**匹配依据**:{指标特征} + {布局特征} + {联动特征}
### 层级预览(仅 L1-L4 框架)
| 层级 | 图表数 | 典型图表 | 归类依据 |
|-----|-------|---------|----------|
| L1 | {N} | {示例} | {类型+位置} |
---2.3.3 布局模式分析与仪表板类型推断
基于 tileLayout 位置信息推断仪表板的整体分析模式。详见 reference.md - 布局模式分析规则
布局模式类型:指标矩阵型、核心图表型、明细导向型、对比分析型、聚焦分析型。
2.3.4 自动匹配分析框架(多维度综合推断)
综合指标语义 + 布局模式 + 联动关系,匹配最适合的分析框架
>
重要:这是一个思考推断步骤,综合多个维度的信息来推断分析框架。
层级归类规则:详见 reference.md - 层级归类规则
- L1(整体监控):indicator-card/kpi/gauge,顶部位置
- L2(趋势分析):line/area/indicator-trend,含 datetime 维度
- L3(维度分解):bar/pie/ranking-list,含分类维度
- L4(明细追踪):common-table,底部位置
匹配思考流程:
1. 列出所有真实指标:从 uniqueMeasures 中列出所有指标名称 2. 列出所有真实维度:从 uniqueDimensions 中列出所有维度名称 3. 识别布局模式:根据布局特征判断仪表板类型 4. 分析联动关系:哪些图表共享筛选器?暗示它们在同一分析路径上 5. 分析下钻方向:drillFields 的维度类型暗示分析深入的方向 6. 综合语义分析:结合以上信息,理解仪表板的整体分析意图 7. 框架匹配:根据综合特征判断最匹配的分析框架 8. 记录匹配依据:说明是基于哪些特征(指标+布局+联动)匹配到该框架
2.3.5 业务逻辑推断
业务逻辑推断(基于指标组合):
| 指标组合 | 推断公式 |
|---|---|
| 销售额 + 成本 + 利润 | 利润 = 销售额 - 成本 |
| 销售额 + 销量 | 客单价 = 销售额 / 销量 |
| 目标值 + 实际值 | 达成率 = 实际 / 目标 × 100% |
更多推断规则详见 reference.md - 业务逻辑推断规则
Step 2.4: 推断意图路由矩阵
核心目标:建立用户问题 → 目标图表 → 数据集ID 的精准映射
意图关键词提取规则:
| 用户问法模式 | 提取的意图 | 匹配目标 |
|---|---|---|
| "XX是多少/有多少" | 查询单一指标 | L1 指标卡 |
| "XX趋势/走势/变化" | 趋势分析 | L2 折线图/趋势图 |
| "XX排行/TOP/最高/最低" | 排序分析 | L3 排行榜 |
| "XX分布/占比/构成" | 结构分析 | L3 饼图/柱图 |
| "各XX的YY" | 维度分解 | L3 分组图表 |
| "XX明细/详情/列表" | 明细查询 | L4 明细表 |
| "为什么XX下降/上升" | 归因分析 | L1→L2→L3 联合 |
更多规则详见 reference.md - 意图路由规则
Step 2.5: 汇总探索结果
【前置检查】确认 Step 2.1 和 Step 2.3.2-OUTPUT 已输出分析框架匹配结果
整理所有探查结果,按以下结构输出(详细格式见 Phase 3.2 模板),必须确保:
- 所有图表组件都被列出(包含数据集ID、字段列表、分析主题、层级归属)
- 分析框架匹配结果基于真实提取的指标和维度
- 业务背景综合仪表板标题、图表标题、字段名称、富文本内容
## 探索结果汇总
├── 基本信息(名称/pageId/URL)
├── 业务背景与统计口径
├── 分析框架匹配结果
├── 核心指标体系
├── 维度体系
├── 业务逻辑推断
├── 层级结构(A/B/C 形式)
├── 数据集清单
├── 查询控件
├── 图表组件完整列表(12列)
├── 意图路由矩阵
├── 联动与下钻路径
├── 下钻字段配置
└── 适用场景与查询路径关键要求:
- 所有图表组件必须完整列出,每个都包含数据集ID、字段列表、分析主题
- 分析框架匹配结果基于真实提取的指标和维度
- 层级归属基于图表类型和布局位置
---
Phase 3: 技能文件生成
根据探索结果,组装并写入 SKILL.md 和 config.yaml 文件。
⚠️ 关键变量确认:生成技能文件前,确认以下值的来源:
- pageId =result["pageId"](Step 2.1 脚本返回值)—— 不是用户输入 URL 中的productId
- dashboard_url = result["dashboardUrl"](Step 2.1 脚本返回值,已包含正确 pageId)- skill_generated_at = dashboardData.basicInfo.gmtModified>
数据门户 URL 中的productId是门户 ID,与仪表板pageId是完全不同的值,禁止混用。
Step 3.1: 确定技能名称
如果用户提供了技能名称,直接使用(会覆盖同名技能)。否则: 1. 取仪表板标题 2. 转换为 kebab-case 格式(中文用拼音或有意义的英文缩写) 3. 添加 qbi- 前缀 4. 追加 pageId 前8位确保唯一性 5. 例如:"好美家零售数据" (pageId: ab12cd34-xxxx) → qbi-retail-query-ab12cd34
Step 3.2: 组装 SKILL.md 内容
按以下模板生成技能文件。{占位符} 表示用探索结果填充的内容。
3.2.1-3.2.7 内容模板
【必须执行】读取模板文件
>
使用read_file工具读取./templates/output_skill_template.md文件,获取以下内容的完整模板:
- YAML Frontmatter + 技能元数据(3.2.1):name/description 格式、SKILL_METADATA 注释块
- 标题和触发场景(3.2.2-3.2.3):5-8 个自然语言查询示例
- 前置条件(3.2.4):配置引导流程(与本 skill 的前置条件逻辑一致)
- 仪表板知识库(3.2.5):基本信息、业务背景、数据集清单、查询控件、图表组件完整列表、意图路由矩阵
- 仪表板分析思路(3.2.6):分析框架匹配、层级结构、核心指标、维度体系、业务逻辑、联动路径
- 工作流程(3.2.7):问题理解→拆解→构建查询→调用 SmartQ→汇总结果→错误处理
>
按模板格式生成对应内容,填充 dashboardData 中提取的真实数据。关键要求:
- description 必须保留
INSTEAD OF generic quickbi-smartq-chat优先级声明 - 所有图表组件必须完整列出,不遗漏
- 指标和维度必须来自
dashboardData,不可臆造
Step 3.3: 生成 config.yaml
复制配置模板,敏感字段置空:
# Quick BI 文件问数配置文件
# QBI 域名
server_domain: https://bi.aliyun.com
# OpenAPI 认证配置
api_key:
api_secret:
# 用户令牌
user_token:
# 是否从环境变量读取认证信息
use_env_property: falseStep 3.4: 写入文件
1. 确定输出目录:与当前技能所在目录保持一致
- 获取当前技能的目录路径(即本 SKILL.md 文件所在的父目录的父目录)
- 在该目录下创建
{skill-name}/子目录 - 例如:如果当前技能在
.qoderwork/skills/quickbi-smartq-chat/,则新技能应在.qoderwork/skills/{skill-name}/
2. 创建目录(如不存在) 3. 写入 SKILL.md 文件 4. 写入 config.yaml 模板 5. 复制脚本文件到生成的 skill 的 scripts 目录(需调整 import):
- 复制
scripts/dashboard/quickbi_openapi.py→{skill-name}/scripts/quickbi_openapi.py - 必须调整 import:移除
sys.path.insert(0, ...)行,将from common.config_loader import load_config as _load_config_from_loader改为from config_loader import load_config as _load_config_from_loader(注意:不要加点号前缀,因为 scripts 目录不是 Python 包,脚本通过sys.path.insert方式加载,只能使用绝对导入) - 复制
scripts/common/config_loader.py→{skill-name}/scripts/config_loader.py - 必须调整:将
DEFAULT_CONFIG_PATH = BASE_DIR.parent.parent / "default_config.yaml"改为DEFAULT_CONFIG_PATH = BASE_DIR.parent / "config.yaml"(扁平结构下BASE_DIR指向scripts/,BASE_DIR.parent即 skill 根目录)
6. 复制 `references/common/copy_skill_config.png` 到生成的 skill 的 example/ 目录,用于首次配置引导 7. 告知用户:
- 技能文件已生成
- 首次使用时会引导配置 API 凭证(config.yaml)
- 生成的技能如何使用
8. 将生成的技能安装到技能中心(必须执行): 执行以下命令将技能注册到技能中心:
skills install local --json '{"sourcePath": "<生成的 skill 目录绝对路径>"}'生成的 Skill 目录结构:
./skills/{skill-name}/
├── SKILL.md # 技能文件
├── config.yaml # API 配置(首次使用时会引导用户配置)
├── example/
│ └── copy_skill_config.png # 首次配置引导图片(来源:common/copy_skill_config.png)
└── scripts/
├── quickbi_openapi.py # OpenAPI 调用工具函数
└── config_loader.py # 配置加载器(全局配置存在即用)---
Examples
Example 1: 从仪表板 URL 生成查询技能
Input:
用户:帮我把这个仪表板转成查询技能
https://bi.aliyun.com/dashboard/view/pc.htm?pageId=ab12cd34-5678-90ef-ghij-klmnopqrstuvExpected Output: 1. 执行预校验,确认用户有访问权限 2. 获取仪表板 JSON 并解析组件结构 3. 输出分析框架匹配结果(如 L1-L4 金字塔) 4. 生成 skills/qbi-xxx-ab12cd34/SKILL.md 5. 自动安装到技能中心
Example 2: 从数据门户 URL 生成查询技能
Input:
用户:这是我们的数据门户,生成一个可以查数据的 skill
https://bi.aliyun.com/product/view.htm?module=dashboard&productId=abc123&menuId=menu456Expected Output: 1. 识别为数据门户 URL,调用 get_dataportal_page_id 获取关联的仪表板 pageId 2. 执行后续标准流程(同 Example 1)
---
重要注意事项
1. API 凭证安全:config.yaml 中的 AccessKey 是敏感信息,提醒用户妥善保管 2. 数据集ID是关键:问数查询依赖正确的 sourceId(数据集ID),必须从 JSON 中准确提取 3. 意图路由准确性:意图路由矩阵决定了用户问题能否正确匹配到数据集,需要仔细推断 4. 分析框架基于真实数据:所有分析框架的指标和维度必须来自 dashboardData(Step 2.1 获取的数据),不可臆造 5. 分析框架必须输出:Step 2.3.2-OUTPUT 是强制步骤,必须在汇总探索结果前输出分析框架匹配结果。如果跳过此步骤,生成的 Skill 将缺少核心分析能力
---
附录 C: 工具函数
目录结构
quickbi-smartq-chat/
├── SKILL.md # 统一入口技能
├── default_config.yaml # 默认配置
├── references/
│ ├── dashboard/
│ │ ├── module-dashboard.md # 本文档
│ │ ├── module-dashboard-reference.md # 详细参考文档
│ │ └── templates/
│ │ └── output_skill_template.md # 生成模板
│ └── common/
│ └── copy_skill_config.png # 配置引导图片
└── scripts/
├── common/
│ └── config_loader.py # 配置加载器(四层配置优先级)
└── dashboard/
├── fetch_dashboard_data.py # 一站式仪表板数据获取
├── get_dashboard_json.js # JSON 解析脚本
└── quickbi_openapi.py # OpenAPI 工具函数核心函数
fetch_dashboard_data.py
| 函数名 | 用途 | 使用阶段 |
|---|---|---|
fetch_dashboard_data(url, config=None) | 一站式获取仪表板数据(配置加载+URL解析+预校验+获取JSON+解析+数据集名称) | Step 2.1 |
quickbi_openapi.py
| 函数名 | 用途 | 使用阶段 |
|---|---|---|
load_config(config_path=None) | 加载配置(优先级:环境变量 > 工作目录级 > 全局 > 包内默认) | Step 1.0 |
is_dataportal_url(url) | 判断是否为数据门户 URL | Step 1.0 |
extract_dataportal_ids(url) | 从数据门户 URL 提取 productId 和 menuId | Step 1.0 |
get_dataportal_page_id(...) | 通过 OpenAPI 获取数据门户关联的仪表板 pageId | Step 1.0 |
extract_page_id(url) | 从仪表板 URL 提取 pageId | Step 1.0 |
validate_and_prepare_dashboard(...) | 仪表板预校验及预处理 | Step 1.0 |
get_dashboard_json(...) | 获取仪表板完整 JSON 数据 | 内部调用 |
batch_get_dataset_schema(...) | 批量获取数据集详情(名称等) | 内部调用 |
query_openapi(...) | 调用 SmartQ 查询接口 | 生成的 skill 查询阶段 |
get_dashboard_update_time(...) | 查询仪表板更新时间 | 生成的 skill 启动校验 |
get_dashboard_json.js
| 函数名 | 用途 |
|---|---|
parseDashboardJson(json) | 解析仪表板原始 JSON,提取组件结构 |
analyzeLayout(charts) | 基于 tileLayout 分析图表布局 |
config_loader.py(scripts/common/)
| 函数名 | 用途 |
|---|---|
load_config() | 四层配置加载(优先级:环境变量 > 工作目录级 > 全局 > 包内默认) |
check_trial_expired(result) | 检查 API 返回结果是否为试用过期错误 |
get_server_domain(config=None) | 获取 server_domain(可选传入已加载的 config) |
persist_to_global_config(key, value) | 写入全局配置 ~/.qbi/config.yaml |
persist_to_skill_config(key, value) | 写入工作目录级配置 $WORKSPACE_DIR/.qbi/smartq-chat/config.yaml |
预校验接口错误码
| 错误码 | 含义 | 处理建议 |
|---|---|---|
AE0510000005 | 用户不在组织中 | 检查 user_token 是否正确 |
AE0510150002 | 没有仪表板访问权限 | 检查用户是否有该仪表板的访问权限 |
AE0510200000 | 没有数据集管理或者授权的权限 | 检查是否有数据集管理和问数配置权限 |
AE0581030022 | 未购买问数功能 | 确认已购买 SmartQ 问数功能 |
OE10010106 | API 未授权 | 检查 api_key/api_secret 配置 |
CONNECTION_ERROR | 网络连接失败 | 检查网络和 server_domain 配置 |
数据门户接口错误码
| 错误码 | 含义 | 处理建议 |
|---|---|---|
NO_PAGE_ID | 数据门户菜单未关联仪表板 | 检查门户菜单是否正确配置了仪表板页面 |
CONNECTION_ERROR | 网络连接失败 | 检查网络和 server_domain 配置 |
输出 SKILL.md 模板
本文件包含生成 SKILL.md 的完整模板,供 Step 3.2 引用。
YAML Frontmatter
---
name: {skill-name}
description: >
**专用查询技能** for "{仪表板标题}" QuickBI dashboard.
Use this skill INSTEAD OF generic quickbi-smartq-chat when user asks about:
{5-8个核心度量字段,逗号分隔}.
Trigger keywords: {仪表板标题/简称}, {3-5个业务主题词}.
---生成要点:
- 只保留高区分度的关键词,不要列出所有字段
- 核心度量:选 5-8 个用户最常问的指标(如销售额、订单数、库存数量)
- 业务主题:选 3-5 个该仪表板独有的主题词(如销售分析、供应链、客户服务)
- INSTEAD OF generic quickbi-smartq-chat 是优先级声明,必须保留技能元数据
在 YAML frontmatter 后面添加元数据块,用于支持自动更新校验:
<!-- SKILL_METADATA
dashboard_page_id: {pageId}
skill_generated_at: {生成时的 gmtModified 值}
dashboard_name: {仪表板标题}
generator_skill: quickbi-smartq-chat
-->字段说明:
- dashboard_page_id:仪表板的 pageId,用于查询更新时间- skill_generated_at:直接取 `dashboardData.basicInfo.gmtModified`(Step 2.1 已返回)- dashboard_name:仪表板名称,用于用户提示- generator_skill:生成此技能的元技能名称,用于触发重新生成标题和描述
# {仪表板标题} - 数据查询
针对「{仪表板标题}」仪表板的专用查询技能,支持自然语言查询仪表板相关的数据。入口检查
【关键】此部分位于 SKILL.md 开头,确保 LLM 在处理任何用户问题前先执行更新检测
## ⚠️ 入口检查(每次查询必须执行)
> **强制约束**:当用户提出任何问题时,**必须首先执行此检查**,然后才能执行后续步骤。
**执行步骤**:
1. 运行以下代码检查仪表板是否有更新:
\`\`\`python
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath('__file__')), 'scripts'))
from config_loader import load_config
from quickbi_openapi import get_dashboard_update_time
config = load_config()
result = get_dashboard_update_time(host=config["server_domain"], access_id=config["api_key"], access_key=config["api_secret"], page_id="{pageId}", user_id=config["user_token"])
has_update = result["success"] and result["data"]["last_modified"] > {skill_generated_at}
print(f"has_update: {has_update}")
\`\`\`
> **注意**:`'__file__'` 占位符在生成时必须替换为生成的 SKILL.md 所在目录的绝对路径(即 `<技能包目录>`)
2. **根据结果处理**:
- `has_update == False` → 静默继续,执行后续查询
- `has_update == True` → **立即终止当前 skill**,**原样输出**以下提示(URL 用反引号包裹避免渲染为链接):
检测到「{仪表板名称}」仪表板已更新,当前技能的数据结构可能已过时。
请按以下步骤重新生成技能:(复制下面内容到对话框执行)
第 1 步:删除当前技能
删除 {skill-name} 技能
第 2 步:重新生成技能
为这个仪表板生成技能:`{仪表板URL}`
> **LLM 执行约束**:输出上述提示后立即终止,**禁止**自动删除技能或自动重新生成,必须等待用户手动执行。
---生成要点:
-{pageId}替换为 Step 2.1 返回的result["pageId"]
- ⛔ 禁止使用用户输入 URL 中的 productId(门户 ID ≠ 仪表板 pageId)- 可通过 Step 2.1 输出的「pageId 校验」区块确认正确值
-{skill_generated_at}替换为dashboardData.basicInfo.gmtModified(数值,不带引号)
-{仪表板名称}替换为dashboardData.basicInfo.name
-{仪表板URL}/{url}替换为dashboard_url(Step 2.1 返回的标准仪表板预览页地址,已包含正确 pageId)
- Python 代码压缩为单行式,避免 LLM "理解"但不"执行"
触发场景
根据仪表板分析思路以及仪表板知识库生成 5-8 个自然语言查询示例:
## 触发场景
当用户提出以下类型的问题时使用此 Skill:
- "查询某时间段的销售额"
- "配送方式分布情况"
- "商品子类别销售排行"
- "销售额和利润的趋势"
- "查看明细数据"前置条件
## 前置条件
- 内置 `scripts/quickbi_openapi.py` 和 `scripts/config_loader.py` 工具函数
- 需要有效的 API 凭证配置(四层配置加载,优先级:环境变量 > 工作目录级 > 全局 > 包内默认)
- 支持仪表板更新自动检测(通过元数据中的 `skill_generated_at` 与当前 `gmtModified` 对比)
### 配置加载优先级
**读取逻辑**:四层配置加载,优先级从高到低:`ACCESS_TOKEN` 环境变量 > 工作目录级 `$WORKSPACE_DIR/.qbi/smartq-chat/config.yaml` > 全局 `~/.qbi/config.yaml` > 包内 `config.yaml`
**写入逻辑**:始终写入工作目录级配置;全局配置写入受 `save_global_property` 开关控制
### 首次使用配置指南
**配置检查流程**(必须按此逻辑执行):
1. **调用 `load_config()` 加载配置**:
\`\`\`python
import sys, os
sys.path.insert(0, os.path.join('<技能包目录>', 'scripts'))
from config_loader import load_config
config = load_config() # 四层配置加载
\`\`\`
> **注意**:`<技能包目录>` 在生成时替换为实际路径
2. **检查配置是否完整**:
- 检查 `config.get("api_key")`、`config.get("api_secret")`、`config.get("user_token")` 三个字段
- **配置完整**(三个字段均非空):**不输出任何提示**,直接继续后续流程
- **配置不完整**(任一字段为空):执行步骤 3 的引导流程
3. **仅当配置不完整时**,执行以下引导步骤:
1. 使用 read_file 工具读取文件:`./example/copy_skill_config.png`,记录读取到的文件路径
> 注:该图片由仪表板技能生成器从父 skill 的 `common/copy_skill_config.png` 复制而来
2. 以 `` 的 Markdown 图片语法将图片展示给用户(路径以实际读取路径为准,不要写死)
3. 在图片下方输出引导语:「请登录 Quick BI 控制台,点击头像处的「**一键复制 skill 配置**」(如上图所示),然后将复制的配置粘贴给我。」
4. 等待用户粘贴配置,解析后写入工作目录级配置;若 `save_global_property` 为 `true`,同时写入全局配置 `~/.qbi/config.yaml`仪表板知识库
【重要】图表组件必须完整列出
>
生成的 Skill 必须包含仪表板中所有图表组件(除容器类组件外),不能只列出"主要"图表。
每个图表必须包含:数据集ID(sourceId)、字段配置、分析主题、分析层级。
这是用户问题路由到正确数据集的关键依据。
## 仪表板知识库
### 基本信息
| 属性 | 值 |
|------|---|
| 名称 | {title} |
| URL | `{url}` |
| pageId | `{pageId}` |
> ⚠️ 上方 pageId 和 URL 均取自 Step 2.1 脚本返回值(`result["pageId"]` / `result["dashboardUrl"]`),非用户输入的原始 URL 参数。
### 业务背景与统计口径
> **【重要】此部分信息在问数查询时必须参考,特别是统计口径和过滤条件**
{综合仪表板标题、图表标题、字段名称、富文本生成的业务背景说明,包含:}
- **业务主题**:{从仪表板标题推断的业务领域,如"销售管理分析"、"财务报表"等}
- **核心指标**:{从图表标题和度量字段提取的关键指标,如"销售额、利润、订单数"等}
- **分析维度**:{从维度字段提取的关键维度,如"时间、区域、产品线"等}
- **统计口径**:{从富文本识别的指标定义,如无则写"无特殊说明"}
- **过滤条件**:{从富文本识别的数据过滤规则,如"不含退货"、"仅已完成订单",如无则写"无"}
### 数据集清单
> 汇总所有图表关联的数据集ID,这是 `query_openapi` 查询的核心入参
| 数据集ID | 关联图表数 | 主要用途 |
|----------|-----------|----------|
| `{sourceId1}` | {N}个 | {销售分析/库存管理等} |
| `{sourceId2}` | {N}个 | {客户分析等} |
### 查询控件 ({N} 个)
> **查询控件影响图表数据的筛选范围,理解控件配置有助于正确解读数据**
| 控件名 | 类型 | 时间粒度 | 默认值 | 需手动触发 | 关联图表 |
|--------|------|---------|--------|-----------|---------|
| {labelName} | {componentType} | {timeGranularity或-} | {defaultValue或无} | {needManualQuery: 是/否} | {关联图表列表} |
**查询控件字段说明**:
- **控件名**:`fieldConfigs[].labelName`
- **类型**:`componentType`(datetime=时间选择器, enumSelect=枚举选择器等)
- **时间粒度**:`config.timeGranularity`(day/week/month/quarter/year),影响时间筛选精度
- **默认值**:`defaultValue`,图表首次加载时的预设筛选值
- **需手动触发**:`needManualQuery`,若为"是"则用户需点击"查询"按钮才生效
- **关联图表**:该筛选器影响哪些图表的数据
### 图表组件(完整列表)
> **必须列出所有图表组件**,每个图表的数据集ID是问数查询的关键
| 图表名 | 组件类型 | 数据集ID | 维度字段 | 度量字段 | 过滤条件 | 下钻字段 | 关联筛选器 | 分析主题 | 分析层级 | 位置 | 所属Tab |
|--------|---------|---------|---------|---------|---------|---------|---------|---------|---------|------|--------|
| {图表名} | {customComponentId} | `{sourceId}` | {维度列表} | {度量(聚合)} | {filters列表或无} | {drillFields或无} | {关联的查询控件或无} | {分析主题} | L1/2/3/4 | ({x},{y}) | {Tab或-} |
**图表组件字段说明**:
- **图表名**:从 `componentName` 或 `attribute.caption` 提取
- **组件类型**:`customComponentId` 的值(如 indicator-card, ranking-list, pie 等)
- **数据集ID**:`queryInput.sourceId`,**必填项**,是 `query_openapi` 查询的核心参数(cube_id)
- **维度字段**:从 `queryInput.area` 中 `itemType` 为 dimension/datetime/geographic 的字段
- **度量字段**:从 `queryInput.area` 中 `itemType` 为 measure 的字段,标注聚合类型
- **过滤条件**:从 `queryInput.area` 中 `id` 为 `filters` 的字段,以及 `defaultFilters` 预设过滤(理解图表数据范围的关键)
- **下钻字段**:从 `queryInput.area` 中 `id` 为 `drill` 的 `columnList` 提取,支持用户下钻分析
- **关联筛选器**:从 `relatedQueryControls` 提取,标注该图表受哪些查询控件影响
- **分析主题**:根据图表类型和字段推断的业务分析场景(如"销售趋势分析"、"区域分布对比"等)
- **分析层级**:L1(整体监控)/L2(趋势分析)/L3(维度分解)/L4(明细追踪)
- **位置**:从 `componentContent.tileLayout` 提取的栅格 x/y 坐标
- **所属Tab**:如果图表属于某个Tab页,标注Tab名称
### 意图路由矩阵
> 用户问题 → 目标图表 → 数据集ID 的映射关系
| 用户意图关键词 | 目标图表 | 组件ID | 数据集ID | 分析主题 | 分析层级 | 路由说明 |
|---------------|---------|--------|---------|---------|---------|----------|
| {关键词1}, {关键词2} | {图表名} | `{componentId}` | `{sourceId}` | {分析主题} | L1/L2/L3/L4 | {为什么匹配到这个图表} |
**路由规则**:
1. 优先匹配度量字段名称(如"销售额"→ 包含销售额度量的图表)
2. 其次匹配维度字段名称(如"按区域"→ 包含区域维度的图表)
3. 再次匹配图表类型特征(如"排行"→ ranking-list 类型图表)
4. 最后匹配分析主题(如"趋势"→ 分析主题包含趋势的图表)
5. 无法匹配时,使用所有数据集ID让 `query_openapi` 自行判断仪表板分析思路
【核心】此部分内容必须完全基于 `dashboardData`(Step 2.2 获取的数据)生成
- 所有指标名称必须来自图表的 measures 字段- 所有维度名称必须来自图表的 dimensions 字段- 分析框架匹配必须基于真实指标组合
- 不可臆造不存在的指标或维度
## 仪表板分析思路
### 一、报表主题
- **报表名称**:{从 basicInfo.name 获取}
- **核心主题**:{基于图表标题和指标推断的业务主题}
- **分析目标**:{解决什么业务问题}
- **目标用户**:{推断的使用者角色,如"运营人员"、"财务分析师"等}
### 二、分析框架匹配
> **基于仪表板真实指标组合自动匹配最适合的分析框架**
常用框架:杜邦分析、AARRR 海盗模型、RFM 客户分析、漏斗分析、目标达成分析、同环比分析等。无法匹配时使用 **L1-L4 金字塔**(概览→趋势→分解→明细)。
#### 本仪表板匹配结果
- **匹配的分析框架**:{根据真实指标匹配的框架名称}
- **匹配置信度**:{high/medium/default}
- **匹配依据**:基于以下真实字段匹配 - {列出匹配到的关键字段}
### 三、层级结构
> **根据仪表板复杂度选择合适的形式**
**形式 A:简单仪表板(1-2 个组件)**
- 直接列出组件即可,不强行分层
- 示例:`组件1: 销售额指标卡, 组件2: 销售趋势图`
**形式 B:标准层级仪表板(3-8 个组件,有明确层级)**
- 使用 L1-L4 金字塔描述:L1 整体监控:{indicator-card/gauge 类型图表} └─ 核心指标:{度量字段}
L2 趋势分析:{line/area/indicator-trend 类型图表} └─ 时间维度 + 关键指标
L3 维度分解:{bar/pie/ranking-list 类型图表} └─ 分析维度 + 分析指标
L4 明细追踪:{common-table 类型图表} └─ 明细字段列表
**形式 C:多主题仪表板(Tab 页签或独立板块)**
- 按主题/Tab 分组描述:主题1: {Tab名称或板块名} └─ 组件列表及层级
主题2: {Tab名称或板块名} └─ 组件列表及层级
### 四、核心指标体系
> **从 `dashboardData.chartComponents[].measures` 提取,不可臆造**
| 指标类型 | 指标名称 | 聚合方式 | 来源图表 | 分析层级 | 分析价值 |
|---------|---------|---------|---------|---------|---------|
| 结果指标 | {measure.caption} | {aggregateType} | {图表名} | L1 | {推断的分析价值,如"核心业绩指标"} |
| 过程指标 | {measure.caption} | {aggregateType} | {图表名} | L2/L3 | {推断的分析价值,如"过程监控指标"} |
| 效率指标 | {measure.caption} | {aggregateType} | {图表名} | L1/L2 | {推断的分析价值,如"效率评估指标"} |
**指标分类说明**:
- **结果指标**:反映最终业务成果,如销售额、利润、订单数等
- **过程指标**:反映业务过程状态,如转化率、完成率、增长率等
- **效率指标**:反映资源利用效率,如人均产出、周转率、客单价等
### 五、维度体系
> **从 `dashboardData.chartComponents[].dimensions` 提取,不可臆造**
| 维度层级 | 维度名称 | 维度类型 | 来源图表 | 分析用途 |
|---------|---------|---------|---------|---------|
| 时间维度 | {dimension.caption} | datetime | {图表名} | 趋势分析、同环比对比 |
| 地理维度 | {dimension.caption} | geographic | {图表名} | 区域分布、地域下钻 |
| 分类维度 | {dimension.caption} | dimension | {图表名} | 结构分析、归因定位 |
**维度层级说明**:
- **时间维度**:用于趋势分析和周期对比
- **地理维度**:用于区域分布和地域下钻
- **分类维度**:用于结构分析和问题归因
### 六、业务逻辑
> **基于真实指标字段名称推断可能的计算关系**
**指标关系**(基于字段名称推断):
- {推断的计算公式1,如:利润 = 销售额 - 成本}
- {推断的计算公式2,如:毛利率 = 利润 / 销售额 × 100%}
- {推断的计算公式3,如:客单价 = 销售额 / 订单数}
**业务逻辑链**:
{指标间的因果关系链,如:}
- 销售额 ← 客户数 × 客单价
- 利润 ← 销售额 - 成本
- 毛利率 ← 利润 / 销售额
**分析路径**:发现问题(L1 指标异常) → 趋势定位(L2 确定异常时间点) → 维度归因(L3 定位问题维度) → 明细追溯(L4 查看具体记录)
### 七、联动与下钻路径
| 源层级 | 源图表 | 联动动作 | 目标层级 | 目标图表 | 分析用途 |
|-------|-------|---------|---------|---------|---------|
| L1 | {指标卡图表名} | 点击/筛选 | L2 | {趋势图表名} | 从总览下钻到趋势,确定异常时间点 |
| L2 | {趋势图表名} | 点击时间点 | L3 | {分布图表名} | 从趋势下钻到维度,定位问题分类 |
| L3 | {分布图表名} | 点击维度值 | L4 | {明细表名} | 从维度下钻到明细,追溯具体记录 |
### 八、适用场景与查询路径
| 业务问题类型 | 典型问题示例 | 分析路径 | 涉及层级 | 涉及指标/维度 |
|-------------|-------------|---------|---------|--------------|
| 现状查询 | "当前{指标名}是多少" | L1 直接查询 | L1 | {指标名} |
| 趋势分析 | "{指标名}趋势如何" | L1→L2 | L1, L2 | {时间维度} + {指标名} |
| 对比分析 | "各{维度}的{指标名}对比" | L1→L3 | L1, L3 | {分类维度} + {指标名} |
| 问题诊断 | "为什么{指标名}下降" | L1→L2→L3 | L1, L2, L3 | {相关指标链} |
| 归因定位 | "哪个{维度}有问题" | L3 深入 | L3 | {分类维度} + {多个指标} |
| 明细追溯 | "查看{主题}明细" | L3→L4 | L3, L4 | {维度} + {明细字段} |
| 排行查询 | "{维度}排行TOP N" | L3 排序 | L3 | {分类维度} + {指标名} |工作流程
## 工作流程
### Step 1: 理解用户问题
参照「仪表板知识库」和「分析思路」以及 「分析路径」理解用户问题,**必须输出理解结果**:
1. **解析用户问题**:提取关键词(指标、维度、时间范围、筛选条件)
2. **参照知识库理解**:
- 对照「图表组件」表格,识别涉及的图表和数据集
- 对照「分析思路」,匹配适用的分析场景
- 对照「业务逻辑」,理解指标间的关联关系
3. **【必须输出】理解结果**:---
问题理解
- 用户意图:{用户想要了解什么}
- 匹配分析场景:{从分析思路中匹配的场景,如"趋势分析"、"对比分析";多 Tab 仪表板需全面考虑各 Tab 的分析场景}
- 涉及指标:{相关的度量字段}
- 涉及维度:{相关的维度字段}
---
### Step 2: 判断问题分类并拆解
根据 Step 1 的理解结果,判断问题类型并输出分析计划:
#### 分类规则
| 类型 | 判断条件 | 处理方式 |
|------|----------|----------|
| **简单问题** | 用户问题与某图表名高度吻合 | 无需拆解,直接查询该图表对应的维度+指标 |
| **复合问题** | 需要多维度分析或归因 | 拆解为 2-3 个子问题 |
#### 子问题拆解规范(仅复合问题需要)
1. **三要素**:每个子问题必须明确 `分析维度` + `待查指标` + `目的`
2. **单表原则**:每个子问题应能让 SmartQ 直接返回一张表,避免多维度交叉
3. **查询顺序**:按 L1→L2→L3 逻辑排列(先总体 → 再维度分解 → 最后细分定位)
4. **动态补充**:根据查询结果动态决定是否需要追加问题(非固定数量)
5. **比率处理**:若问题涉及比率(如增长率、转化率),可先尝试直接查询;如SmartQ无法识别或结果异常,再拆解为基础指标(分子/分母)自行计算
#### 输出格式(此步骤输出给用户查看)
分析计划
用户问题:{原始问题} 分析层级:{L1/L2/L3/L4} - {简要说明,如"基于L1整体销售额,下钻到L2门店维度"}
| # | 子问题 | 维度 | 指标 | 目的 |
|---|---|---|---|---|
| 1 | {问题描述} | {维度} | {指标} | {目的} |
| 2 | {问题描述} | {维度} | {指标} | {目的} |
### Step 3: 构建查询
根据问题分类构建查询:
**简单问题**:- 查询问题:{用户原始问题或转换后的查询语句}
- 数据集ID:{从匹配图表获取的 sourceId}
**复杂问题**(拆解为 2-4 个子问题):---
子问题清单
| 序号 | 子问题 | 数据集ID | 分析目的 |
|---|---|---|---|
| 1 | {子问题1} | {sourceId} | {这个子问题要解答什么} |
| 2 | {子问题2} | {sourceId} | {这个子问题要解答什么} |
| 3 | {子问题3} | {sourceId} | {这个子问题要解答什么} |
---
**兜底处理**:如果无法匹配到具体图表,收集所有图表组件的 `数据集ID`,去重后作为多数据集入参。
### Step 4: 调用 SmartQ
使用 Step 3 构建的查询,调用内置的 `scripts/quickbi_openapi.py` 中的 `query_openapi` 函数:
import sys, os sys.path.insert(0, os.path.join('<技能包目录>', 'scripts')) from quickbi_openapi import query_openapi from config_loader import load_config
加载配置(优先级:环境变量 > 工作目录级 > 全局 > 包内默认)
config = load_config()
简单问题:单次调用
result = query_openapi( endpoint=config["server_domain"], access_key_id=config["api_key"], access_key_secret=config["api_secret"], question=query_question, user_id=config["user_token"], cube_id=dataset_id # 单个数据集ID 或 逗号分隔的多个ID )
复杂问题:循环调用每个子问题
results = [] for sub_question in sub_questions: result = query_openapi( endpoint=config["server_domain"], access_key_id=config["api_key"], access_key_secret=config["api_secret"], question=sub_question["question"], user_id=config["user_token"], cube_id=sub_question["cube_id"] ) results.append({"sub_question": sub_question, "result": result})
根据结果动态决定是否补充查询
如发现异常值,可动态添加第3个问题
**多次调用规则**:
- 每个子问题独立调用一次 `query_openapi`,最多 **3 次**(极简原则)
- 按 Step 1 拆解的顺序逐条执行
- **动态补充**:前一次结果如发现异常(如某维度值过高/过低),可补充下一个问题深入分析
**重要约束**:
- **数据正常返回时不尝试其他方式**:当 API 正常返回数据(包括空数据、全0数据等),直接展示
- **仅异常时降级到浏览器**:只有当 API 返回异常(接口抛错、网络不通、权限错误等)时,才尝试打开仪表板
- **不添加个人判断**:不对数据的合理性进行判断,直接展示 API 返回的原始结果
### Step 5: 汇总结果展示
根据问题分类展示结果:
**简单问题**:查询结果
智能总结: {ConclusionText,如有}
| 列1 | 列2 | ... |
|---|---|---|
| 数据... |
共 N 条数据
**复杂问题**:分析结果
子问题 1: {子问题描述}
{该子问题的查询结果表格}
子问题 2: {子问题描述}
{该子问题的查询结果表格}
...
综合分析
{基于各子问题结果的综合分析,回答用户原始问题}
来源:仪表板名称
**来源链接说明**:
- URL 从「仪表板知识库 - 基本信息 - URL」字段获取
- 如果问题匹配到了具体的图表组件,则在 URL 末尾追加 `&componentId={componentId}&highlight=true`
## 错误处理
| 场景 | 检测方式 | 处理策略 |
|------|---------|----------|
| 问题理解失败 | 无法提取有效关键词 | 询问用户具体想查询什么数据 |
| 意图匹配失败 | 无法匹配到图表组件 | 使用所有数据集ID进行兜底查询 |
| API 调用失败 | 返回错误码 | 根据错误码提示用户(权限、实例过期等)|
| 部分子问题失败 | 部分 API 调用失败 | 展示成功的结果,标注失败的子问题 |
**注意**:API 成功返回的数据(包括空数据、全0数据等)直接展示给用户,不进行二次查询或修正。QuickBI 文档解析工具
核心能力: 1. 📄 文档内容识别: 解析 PDF、Word、Excel、CSV、图片等非结构化文件为可读取的文本内容 2. 📊 字段提取与汇总: 从文档中智能提取核心字段,自动生成带格式的多 Sheet Excel 报表
Scope
Does:
- 识别 PDF、Word(.doc/.docx)、Excel(.xls/.xlsx)、CSV、图片(.png/.jpg/.jpeg) 等文档内容
- 支持单文件、多文件批量处理、文件夹递归扫描
- 优先本地提取文本,失败后自动降级到远程 OCR
- 根据预定义分类体系(10 大分类组、37 个子类型)智能提取核心字段
- 支持未知文档的动态结构化提取(5+ 字段需用户确认)
- 生成带格式的多 Sheet Excel 报表(汇总统计 + 分类数据)
Does NOT:
- 不支持修改原始文档内容
- 不支持在线编辑 Excel
- 不支持非文档类文件(如视频、音频、可执行文件)
- 严禁杜撰或编造任何提取数据
Instructions
本技能提供 2 种使用模式,根据用户意图自动选择:
⚠️ 模式判定规则(重要)
严格按以下规则判断使用哪个模式:
| 用户意图关键词 | 使用模式 | 说明 |
|---|---|---|
| 识别、读取、提取文本、转成文本、查看内容 | 模式 A | 仅需文档内容,不需要结构化 |
| 提取字段、生成 Excel、汇总报表、结构化、分类提取 | 模式 B | 需要字段提取和 Excel 输出 |
| 解析 + 无后续说明 | 模式 A | 默认仅识别内容 |
| 解析 + 明确提到字段/Excel/汇总 | 模式 B | 需要完整流程 |
关键原则:
- 📌 "解析"、"识别"、"读取" 等动词默认指向模式 A
- 📌 只有用户明确要求"提取字段"、"生成 Excel"、"汇总报表"时才使用模式 B
- 📌 不确定时,优先使用模式 A,然后询问用户是否需要生成 Excel
---
模式 A: 文档内容识别
适用场景: 用户仅需读取文档中的文本内容,无需结构化提取
处理流程: Step 1 (文本识别)
示例:
- "帮我读取这个 PDF 的内容"
- "解析这个 PDF 的内容"
- "提取这些 Word 文档的文本"
- "扫描文件夹,把所有文档转成文本"
---
模式 B: 字段提取与 Excel 汇总
适用场景: 用户需要从众多文档中,提取核心字段并生成结构化的Excel
处理流程: Step 1 (文本识别) → Step 2 (字段提取) → Step 3 (生成 Excel)
示例:
- "解析这些发票,提取关键字段并生成 Excel"
- "扫描合同文件夹,汇总所有合同信息到Excel"
- "批量处理文档,按分类提取字段并导出"
---
工作流程概览
用户上传文件/文件夹
↓
┌─────────────────────────────────────┐
│ Step 1: 文本识别 │
│ 本地解析优先 → 失败降级远程 OCR │
│ 输出: JSON (file + parsedText) │
└─────────────────────────────────────┘
↓
[模式 A: 到此结束,返回文本内容]
↓
┌─────────────────────────────────────┐
│ Step 2: 字段提取 │
│ 智能分类 → 提取核心字段 │
│ 输出: JSON (分类 + 字段数据) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Step 3: 生成 Excel 报表 │
│ 多 Sheet + 格式化 + 汇总统计 │
│ 输出: .xlsx 文件 │
└─────────────────────────────────────┘
↓
输出总结 + Excel 交付物Step 1: 文档内容识别
目标: 提取文档中的原始文本内容,生成 JSON 文件
核心能力: 📄 支持 PDF、Word、Excel、CSV、图片等多种格式的智能识别
执行逻辑:
1. 优先调用本地解析 (document_local_parse.py)
# 单文件
python scripts/document/document_local_parse.py <文件路径> --json
# 多文件
python scripts/document/document_local_parse.py <文件1> <文件2> <文件3> --json
# 文件夹(递归扫描)
python scripts/document/document_local_parse.py <文件夹路径> --json2. 如果本地解析失败,尝试远程 OCR (document_remote_ocr.py)
# 文件夹扫描
python scripts/document/document_remote_ocr.py <文件夹路径>
# 多文件
python scripts/document/document_remote_ocr.py --files <文件1> <文件2>3. 输出格式:
[
{
"file": "filename.pdf",
"parsedText": "提取的完整文本内容..."
}
]注意事项:
- 本地解析支持: PDF(PyMuPDF)、Word(python-docx)、Excel(openpyxl)、CSV(pandas)、图片(Tesseract OCR)
- 远程 OCR 支持: PDF、图片、Word、Excel、PPT(通过 QuickBI API)
- 单文件最大 10MB
- 默认输出到
output/目录,带时间戳
Step 2: 字段提取与智能分类
目标: 根据文档分类体系,从原始文本中提取核心字段
核心能力: 📊 智能分类 + 动态提取 + 用户确认机制
执行逻辑:
1. 加载分类体系: 参考 references/document_classification.md
- 10 大分类组: A.财务与税务、B.人力资源、C.供应链与采购、D.行政与法务、E.医疗、F.保险、G.物流、H.技术与运维、I.客服与销售、J.政务与合规
- 37 个子类型: 每个子类型有明确的字段定义和中文表头。
2. 文档分类与字段提取: 按照以下优先级策略处理
第一优先级: 匹配预定义分类体系
- 参考
references/document_classification.md进行分类 - 优先匹配标题/抬头(如"增值税发票"、"银行回单")
- 根据关键字段路由(如含税号→A1,含流水号→A2/A3)
- 匹配成功后,严格按照对应子类型的字段定义提取数据
第二优先级: 动态结构化提取
- 如果无法匹配预定义的 37 个子类型,评估文档是否具备结构化提取价值
- 判断标准: 能否从文本中识别并提取 至少 5 个有效字段
- 如果可以提取 5+ 个字段:
- 智能识别字段名称和对应值
- 必须使用 AskUserQuestion 工具让用户确认字段定义
- 用户确认后,按确认的字段结构进行提取
- 为新类型创建临时 Sheet 名(格式:
自定义_{类型名})
第三优先级: 归入未识别类
- 如果无法匹配预定义分类 且 无法结构化提取 5+ 个字段
- 归入"未识别"类,记录内容预览和疑似类型
3. 字段提取: 严格按照分类体系定义的字段提取
- 字段命名: 英文
snake_case - Excel 表头: 中文名(括号内英文字段名)
- 每个子类型的隐含首列:
filename(源文件名)
4. 组装 JSON:
{
"scan_time": "2026-04-07 15:00:00",
"total_files": 10,
"extraction_data": {
"增值税发票": {
"headers_cn": ["源文件名", "发票类型", "发票代码", "发票号码", "开票日期", "购买方名称", "销售方名称", "价税合计"],
"rows": [
["invoice_001.pdf", "专用", "033002100511", "03933249", "2023-05-14", "购买方公司", "销售方公司", "118.00"]
]
},
"未识别": {
"headers_cn": ["源文件名", "内容预览", "疑似类型", "置信度"],
"rows": [
["unknown.pdf", "这是一段文本...", "合同", "中"]
]
}
}
}⚠️ 核心原则: 严禁杜撰数据
- ✅ 允许: 从
parsedText中提取存在的字段值 - ✅ 允许: 字段缺失时留空(空字符串)
- ❌ 禁止: 编造不存在的字段和字段值,禁止杜撰数据
- ❌ 禁止: 根据上下文推测或补全数据
- ❌ 禁止: 修改原始文本内容
- ❌ 禁止: 填充默认值(除非分类体系明确说明,如"币种默认 CNY")
提取示例:
# ✅ 正确: 从文本中提取
if "发票代码" in text:
invoice_code = extract_value(text, "发票代码") # 提取实际值
else:
invoice_code = "" # 留空,不编造
# ❌ 错误: 杜撰数据
invoice_code = "1234567890" # 文本中没有,禁止编造Step 3: 生成 Excel 汇总报表
目标: 将提取的字段数据生成结构化、带格式的 Excel 报表
核心能力: 📈 多 Sheet 自动化 + 格式化 + 汇总统计
执行命令:
# 默认输出到 output/doc_scan_result_{timestamp}.xlsx
python scripts/document/generate_excel.py <Step2的JSON路径>
# 自定义输出路径
python scripts/document/generate_excel.py <Step2的JSON路径> /path/to/output.xlsxExcel 结构:
- excel名称 :
{category名称}_{timestamp}.xlsx - 汇总 Sheet(首页): 统计各分类组的文件数量和提取字段
- 数据 Sheet(每个子类型一个): 带格式的表格数据
- 蓝色表头(
#4472C4)+ 白色粗体 - 自动筛选 + 冻结首行
- 自动列宽 + 单元格换行
最终交付
在窗口中输出:
1. 处理总结:
文档解析完成
文件总数: 10
成功识别: 9
识别失败: 1
分类统计:
- A.财务与税务: 5 个文件(增值税发票 3, 银行回单 2)
- B.人力资源: 2 个文件(简历 1, 劳动合同 1)
- 未识别: 1 个文件
提取字段: 45 个2. Excel 交付物路径:
✓ Excel 已生成: /path/to/output/invoice_20260407_150000.xlsxExamples
模式 A 示例
Example 1: 解析单个文件内容
Input:
请帮我读取这个 PDF 的内容: /Users/user/document.pdfExpected output:
[Step 1] 本地解析 document.pdf...
[PDF提取] 成功提取 2350 字符
[保存] JSON 结果已保存到: output/extract_results_1775575200.json
文档解析完成
文件总数: 1
成功识别: 1
提取文本: 2350 字符
✓ 文本内容已保存: output/extract_results_1775575200.jsonExample 2: 批量解析文件夹
Input:
扫描并解析 /Users/user/documents/ 下的所有文档,提取文本内容Expected output:
[Step 1] 扫描文件夹...
[扫描] 在 /Users/user/documents/ 中找到 15 个支持的文件
[并行提取] 开始处理 15 个文件 (最大并行数: 10)
...
文档解析完成
文件总数: 15
成功识别: 14
识别失败: 1
总文本量: 45,230 字符
✓ 文本内容已保存: output/extract_results_1775576400.json---
模式 B 示例
Example 3: 解析发票并生成 Excel 表格
Input:
请解析这些发票文件,提取关键字段并生成 Excel 报表: /Users/user/invoices/Expected output:
[Step 1] 本地解析 invoices/ 文件夹...
[扫描] 找到 10 个支持的文件
[并行提取] 开始处理 10 个文件 (最大并行数: 10)
...
[Step 2] 智能分类与字段提取...
- 增值税发票: 5 个文件 (提取 13 个字段/文件)
- 银行回单: 3 个文件 (提取 11 个字段/文件)
- 未识别: 2 个文件
[Step 3] 生成 Excel 汇总报表...
[格式化] 应用蓝色表头 + 自动筛选 + 冻结首行
[保存] ✓ Excel 结果已保存到: output/doc_scan_result_20260407_150000.xlsx
文档解析完成
文件总数: 10
文件总数: 10
成功识别: 8
未识别: 2
分类统计:
- A.财务与税务: 8 个文件 (增值税发票 5, 银行回单 3)
- 未识别: 2 个文件
提取字段: 98 个
✓ Excel 报表已生成: output/doc_scan_result_20260407_150000.xlsxExample 4: 本地解析失败,降级到远程 OCR
Input:
解析这个扫描件 PDF 并提取字段: /Users/user/scanned_invoice.pdfExpected output:
[Step 1] 本地解析 scanned_invoice.pdf...
[PDF提取] 警告: 本地提取文本较少 (12 字符),可能是扫描件,尝试 OCR...
[PDF提取] 降级到 Tesseract OCR 识别...
[OCR降级] OCR 识别质量不佳,尝试远程 OCR...
[远程 OCR] 上传 scanned_invoice.pdf...
[上传] ✓ scanned_invoice.pdf -> taskId: abc123
[轮询] ✓ 任务解析成功 (850 字符)
[Step 2] 智能分类: 增值税发票 (vat-invoice)
提取字段: 发票代码、发票号码、开票日期、购买方名称... (从 OCR 文本中提取)
[Step 3] 生成 Excel 汇总报表...
[保存] ✓ Excel 结果已保存到: output/doc_scan_result_20260407_160000.xlsx
文档解析完成
文件总数: 1
成功识别: 1 (远程 OCR)
文件总数: 1
成功识别: 1 (远程 OCR)
分类统计:
- A.财务与税务: 1 个文件 (增值税发票 1)
提取字段: 13 个
✓ Excel 报表已生成: output/doc_scan_result_20260407_160000.xlsxExample 5: 未知文档动态提取(需用户确认)
Input:
解析这个自定义文档并提取字段: /Users/user/custom_report.pdfExpected output:
[Step 1] 本地解析 custom_report.pdf...
[PDF提取] 成功提取 1580 字符
[Step 2] 智能分类...
⚠️ 无法匹配预定义的 37 个标准分类
🔍 评估文档结构化提取价值...
✓ 识别到 8 个潜在字段: 报告编号、检测日期、样品名称、检测项目、结果值、检测员、审核人、检测机构
[AskUserQuestion] 检测到未知文档类型,确认识别字段:
┌─────────────────────────────────────┐
│ 文档类型: 检测报告 (自定义) │
│ 识别字段: │
│ 1. 报告编号 (report_no) │
│ 2. 检测日期 (test_date) │
│ 3. 样品名称 (sample_name) │
│ 4. 检测项目 (test_items) │
│ 5. 结果值 (results) │
│ 6. 检测员 (inspector) │
│ 7. 审核人 (reviewer) │
│ 8. 检测机构 (testing_org) │
│ │
│ 是否确认按此结构提取? │
└─────────────────────────────────────┘
用户确认: ✓ 是
[Step 2] 按确认结构提取字段...
[提取] 成功提取 8 个字段
[Step 3] 生成 Excel 汇总报表...
[创建 Sheet] 自定义_检测报告
[保存] ✓ Excel 结果已保存到: output/doc_scan_result_20260407_170000.xlsx
============================================================
文档解析完成
============================================================
文件总数: 1
成功识别: 1 (自定义类型)
分类统计:
- 自定义_检测报告: 1 个文件
提取字段: 8 个
============================================================
✓ Excel 报表已生成: output/doc_scan_result_20260407_170000.xlsxAdditional Resources
- 分类体系详细定义: document_classification.md
脚本接口参考
1. 本地解析脚本 (document_local_parse.py)
功能: 纯本地文本提取,支持 PDF/Word/Excel/CSV/图片,不依赖外部 API
支持格式:
- PDF(.pdf)、Word(.doc/.docx)、Excel(.xls/.xlsx)、CSV(.csv)
- 图片(.png/.jpg/.jpeg/.bmp/.tiff/.webp) - 使用 Tesseract OCR
命令行用法:
# 单文件
python scripts/document/document_local_parse.py <文件路径> --json
# 多文件
python scripts/document/document_local_parse.py <文件1> <文件2> <文件3> --json
# 文件夹递归扫描
python scripts/document/document_local_parse.py <文件夹路径> --json
# 自定义输出目录
python scripts/document/document_local_parse.py <路径> --json --output-dir /custom/output/
# 禁用 OCR 降级
python scripts/document/document_local_parse.py <文件路径> --json --no-ocr核心参数:
| 参数 | 说明 | 默认值 |
|---|---|---|
--json | 保存 JSON 结果 | False |
--output-dir | JSON 输出目录 | output/ |
--no-ocr | 禁用 OCR 降级 | False |
输出格式:
[
{"file": "filename.pdf", "parsedText": "提取的文本内容..."}
]系统依赖:
# macOS
brew install tesseract tesseract-lang
# Ubuntu/Debian
sudo apt-get install tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng---
2. 远程 OCR 脚本 (document_remote_ocr.py)
功能: 基于 QuickBI API 的远程 OCR 识别,支持批量并发处理
支持格式:
- PDF、图片(.png/.jpg/.jpeg/.webp/.bmp/.gif/.jp2)
- Word(.doc/.docx)、PPT(.ppt/.pptx)、Excel(.xls/.xlsx/.csv)
- 文件大小限制: 单文件 ≤ 10MB
命令行用法:
# 文件夹扫描(递归)
python scripts/document/document_remote_ocr.py <文件夹路径>
# 多文件
python scripts/document/document_remote_ocr.py --files <文件1> <文件2> <文件3>
# 自定义输出路径
python scripts/document/document_remote_ocr.py <路径> --output /custom/result.json
# JSON 模式(仅输出JSON,无日志)
python scripts/document/document_remote_ocr.py <路径> --json
# 调整并发数
python scripts/document/document_remote_ocr.py <路径> --upload-workers 5 --poll-workers 10核心参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
directory | 可选 | - | 目录路径(递归扫描) |
--files | 可选 | - | 文件列表(与 directory 二选一) |
--upload-workers | int | 5 | 上传并发数(最大10) |
--poll-workers | int | 10 | 轮询并发数(最大10) |
--output | str | - | 输出 JSON 路径 |
--json | flag | false | 仅输出JSON(无日志) |
输出格式:
[
{"file": "filename.pdf", "parsedText": "识别文本..."},
{"file": "error.pdf", "parsedText": null, "error": "错误信息"}
]配置说明:请参见主文件的「配置」章节。
---
3. Excel 生成脚本 (generate_excel.py)
功能: 将分类提取的 JSON 数据转换为多 Sheet Excel 报表
命令行用法:
# 默认输出到 output/doc_scan_result_{timestamp}.xlsx
python scripts/document/generate_excel.py <JSON路径>
# 自定义输出路径
python scripts/document/generate_excel.py <JSON路径> /path/to/output.xlsx输入 JSON 格式:
{
"scan_time": "2026-04-07 15:00:00",
"total_files": 10,
"extraction_data": {
"增值税发票": {
"headers_cn": ["源文件名", "发票类型", "发票代码", "..."],
"rows": [["file.pdf", "专用", "033002100511", "..."]]
},
"未识别": {
"headers_cn": ["源文件名", "内容预览", "疑似类型", "置信度"],
"rows": [["unknown.pdf", "文本...", "合同", "中"]]
}
}
}Excel 结构:
- 汇总 Sheet(首页): 统计各分类组文件数量和提取字段
- 数据 Sheet(每子类型一个): 蓝色表头 + 自动筛选 + 冻结首行 + 自动列宽
依赖安装
# 安装所有 Python 依赖(requirements.txt 位于 scripts 目录下)
pip install -r <项目根目录>/scripts/requirements.txt
# 系统依赖(仅本地解析需要)
# macOS
brew install tesseract tesseract-lang
# Ubuntu/Debian
sudo apt-get install tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng核心 Python 依赖:
- 本地解析:
PyMuPDF,python-docx,openpyxl,xlrd,pandas,pytesseract,Pillow - 远程 OCR:
requests,pyyaml - Excel 生成:
openpyxl>=3.1.0
注意事项
1. 模式判定优先级: 严格按"模式判定规则"表格判断,不确定的话优先使用模式 A,然后询问用户是否需要生成 Excel 2. 数据真实性: Step 2 字段提取严禁杜撰,所有数据必须来源于 Step 1 的 parsedText 3. 字段缺失处理: 如果文本中不存在某字段,留空(""),不要编造 4. 分类容错: 无法匹配预定义分类的文档,优先尝试动态提取(5+ 字段),失败后归入"未识别"类 5. 动态提取确认: 未知文档提取 5+ 字段时,必须使用 AskUserQuestion 让用户确认 6. 输出路径: 所有输出文件默认在 output/ 目录,带时间戳避免覆盖 7. 并发限制: 远程 OCR 最大并发 10 个文件,本地解析最大并行 10 个文件 8. 文件大小: 单文件最大 10MB(远程 OCR 限制) 9. OCR 降级策略: 本地解析 PDF 提取文本 < 50 字符时,自动降级到 Tesseract OCR;仍失败则尝试远程 OCR
requests
pyyaml
matplotlib
numpy
openpyxl
xlrd
PyMuPDF
python-docx
pandas
pytesseract
Pillow