Skip to content
Back to skills

Data Query

ASecurity

所有取数类问题的唯一入口:指标/聚合查询(数值、趋势、同比环比、占比、排名、维度拆分、指标定义浏览)与明细/非标查询(逐行明细、原始记录导出、名单、复购/购物篮/漏斗/留存、中位数分位数)。 用户想要任何数据时触发;显式点名 metric_search / metric_dimensions / metric_dimension_resolve / metric_query / dataset_search / dataset_sql_query 时也触发;"取数+做图/报表/Excel"先走本 Skill 取数。 MQL 与 SQL 的路径选择在 Skill 内部基于元数据检索决定——禁止凭措辞预判路径,禁止凭"数据可能不存在"跳过本 Skill 或改走 web_search / 代码 HTTP。 纯概念解释、无查询意图时不触发。

  • 417 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added November 8, 2026
datapythongobashsql

Works with

  • mcp

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned November 8, 2026

npx -y skills add infometa/workbuddyskills --skill data-query --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Data Query?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Data Query
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/infometa-data-query/badge)](https://www.skillsdirectory.com/skills/infometa-data-query)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: data-query
description_zh: 通过 Aloudata Agent 检索指标、查询聚合指标与明细数据,并在存在业务口径歧义时先向用户澄清。
description_en: Search metrics and query aggregated or detailed data with Aloudata Agent, clarifying ambiguous business definitions before querying.
version: 1.0.0
author: 浙江大应科技有限公司
description: |
  所有取数类问题的唯一入口:指标/聚合查询(数值、趋势、同比环比、占比、排名、维度拆分、指标定义浏览)与明细/非标查询(逐行明细、原始记录导出、名单、复购/购物篮/漏斗/留存、中位数分位数)。
  用户想要任何数据时触发;显式点名 metric_search / metric_dimensions / metric_dimension_resolve / metric_query / dataset_search / dataset_sql_query 时也触发;"取数+做图/报表/Excel"先走本 Skill 取数。
  MQL 与 SQL 的路径选择在 Skill 内部基于元数据检索决定——禁止凭措辞预判路径,禁止凭"数据可能不存在"跳过本 Skill 或改走 web_search / 代码 HTTP。
  纯概念解释、无查询意图时不触发。
---

# 数据查询 Skill(统一入口)

## WorkBuddy 执行约定

- 数据查询依赖本连接器提供的六个 MCP 工具:`metric_search`、`metric_dimensions`、`metric_dimension_resolve`、`metric_query`、`dataset_search`、`dataset_sql_query`。以连接后实际发现的工具描述和参数为准;工具不可用时提示用户检查连接。
- 下文 `ask_human` 表示“向用户澄清并等待答复”的动作,不是本连接器提供的 MCP 工具。使用当前 WorkBuddy 实际提供的交互式提问工具;没有专用提问工具时,直接在对话中提出问题并等待用户答复。澄清完成前不得执行依赖该答复的查询。
- 下文 `read_file` 表示“读取技能随附文件”的动作,使用当前 WorkBuddy 实际提供的文件读取能力,并按本技能目录解析相对路径。文件无法读取时先报告缺失,不凭记忆构造查询。
- 本约定优先于下文具体工具实现说明。`ask_human` 调用示例用于说明问题、选项和口径的组织方式,不是可原样提交给 WorkBuddy 的请求;字段名、题目或选项数量上限及校验行为,以当前工具的实际定义为准。业务澄清条件和等待用户确认的规则保持不变。
- 随附参考文件:@references/mql-syntax.md、@references/sql-syntax.md。首次调用对应查询工具前,按 Step 6 读取对应文件。

你通过两条执行路径之一为用户取数,并如实标注结果的真实口径。所有查询统一走指标平台语义层,**不存在"选数据源"这个概念**——任何工具调用都不传数据源参数:

- **MQL 路径** —— 指标平台(语义层)可表达的聚合指标查询:`metric_search` / `metric_dimensions` / `metric_dimension_resolve` → `metric_query`。语法与示例:`references/mql-syntax.md`。
- **SQL 路径** —— 行级明细与非标分析,只读 SQL:`dataset_search` 探查字典 → `dataset_sql_query`。语法:`references/sql-syntax.md`。

> **数据字典** —— 数据模型的唯一来源,由 `dataset_search` 工具提供,分两级:
> - **总览级**:`dataset_search(action="list", keyword="...", page=N)` —— 数据集名称、显示名、描述;支持关键词过滤与翻页(返回末尾 `hasNext=true` 表示还有下一页)。⚠️ keyword 只匹配数据集名/显示名,**不匹配字段名**——关键词未命中先换词,再不行去掉 keyword 翻页浏览全部数据集,然后才能下"数据不覆盖"的结论。
> - **详情级**:`dataset_search(action="info", name="a,b")` —— 物理表名(PHYSICAL_TABLE,SQL 的 FROM/JOIN 用它,见 `sql-syntax.md` §2)、字段名、展示名、类型(MEASURE/DIMENSION)、原始数据类型、关联关系(joinOn);多个数据集逗号并传一次拿全。
>
> 下文一律说"数据字典总览 / 详情",**不出现任何具体表名、字段名、日期**:租户特定的名字不属于本 Skill,也不属于语法 reference——它们承载规则,字典承载事实。
>
> ⚠️ **字典盲区——两个判定要用、但工具当前不返回的事实**。需要时按下法自行确立,严禁假设,严禁跳过:
> - **数据起始(最早数据日期)**:当某解读的可行性取决于数据覆盖时(请求范围伸向较早历史、变更表能否覆盖所问时点、结果意外 0 行),对该数据集的主日期字段发一条 `SELECT MIN(...)`(`visibility="internal"`)确立起点。不涉及覆盖判定的查询不必探。
> - **建模类型**(事务事实表 / 周期快照事实表 / SCD Type 1 / SCD Type 2 / 维度表):先从字典详情**推断**——描述里的粒度语句("一行一交易"→事务事实表;"当前态 / 快照 / 一行一实体"→SCD Type 1 当前态;"一行一变更"→变更事实表)、字段结构(生效/失效日期对→SCD Type 2;每期一份的快照日期→周期快照;只有更新时间的实体宽表→SCD Type 1)。推断不确定、且该判定会改变答案(Step 3 时点属性、Step 4a 解读枚举)→ 用 internal SQL 探查(同一实体是否随日期出现多行、每期是否各有一份快照)。探查仍不能唯一确定 → 把各可能建模类型下的解读**并集**都列入 Step 4a,绝不按猜测的类型静默执行。

主流程——按顺序执行;Step 1–5 两条路径共享,Step 6 起分叉:

```
Step 1  理解意图 & 对齐概念(知识)
Step 2  时间口径判定(只产出期间标签)
Step 3  元数据探查 + 路由(MQL vs SQL)
Step 4  枚举解读 → 逐解读判可行 → 选出口
Step 5  澄清门(槽位状态机)
Step 6  构建查询(分支专属,先读 reference)
Step 7  执行与失败处理
Step 8  回答 + 口径说明
```

**决策层不变式(管辖 Step 4–5)**:复杂度放在**识别**(Step 3–4),因为它的输入是事实——候选个数、元数据标注、探查到的数据起始、某个词在不在。**决策**只有三个出口,没有第四个:**直答 / 反问 / 告知不可行**。不得自创第四个出口,不得权衡某个歧义"重不重要",不得用"查询成本高"当作跳过反问的理由。

---

## Step 1 — 理解意图 & 对齐概念

从用户(通常口语化的)问题中识别:

1. **业务对象与概念** —— 涉及哪些实体、指标、属性。映射到真实元数据放在 Step 3 做,不凭名字字面相似猜。
2. **答案形态** —— 最终答案是**聚合值/聚合表**(数量、金额、排名),是**比率**(任何最终会带 `%` 输出的东西——占比、构成、分布、渗透率、转化率),还是**原始行**(每行 = 一条真实业务事件)?聚合 vs 原始决定 Step 3 的路由。**是比率的,在这里就打上 `needs_denominator` 标记**,该标记会在 Step 5 澄清门被强制检查——看到答案里有百分比的当下就标,不要拖到后面。
3. **筛选条件** —— 时间、渠道、品类、地区、实体值。
4. **排序与行数** —— 排序方式、Top-N、预览还是全量。
5. **对比意图** —— 同比 / 环比 / vs / 前后对比 / 最好最差——这些会扩大 Step 5 必须确认的范围。

**知识对齐——所有概念词全查,不许先判断哪些词"像业务术语"**(知识工具在当前环境不可用时跳过本节;进入查询的业务词无定义照 Step 5 硬触发处理):

事先判断一个词是不是租户黑话是主观判断,而且必然漏。不要做这个判断,改成:

1. **切一次词,两处共用**。把问题切成概念词——实体、指标、属性、人群词、活动词、修饰词。**多切几种取并集**(「客群迁移」同时产出「客群」和「迁移」):粗切和细切命中的候选不同,并集才是本步和 Step 3 `metric_search` 共同消费的输入。切一次同时喂两边——绝不允许知识检索和元数据检索跑在不同的切法上。
2. **所有 token 合并成一次 `search_knowledge` 调用**(逗号分隔)。逐词串行检索是唯一会让这件事变贵的做法;一次批量调用不贵。
3. **命中的条目一律用 `read_knowledge` 读完整全文**,绝不凭清单摘要行事。
4. **命中条目在"它定义了本次查询实际用到的槽位"时才应用**(时间范围、筛选、去重口径、计算逻辑、人群定义)。没有定义任何在用槽位的条目,忽略。
5. **未命中是一个结论,不是"什么都没发生"**。一个 token 查不到知识条目、却又要进入查询——这正是"业务定义缺失"的情况,带进 Step 5(`population` / `entity_filter`),在那里"进入查询的词未命中知识库"是硬触发。

知识清单按关键词匹配、可能不全:先用改写后的关键词再搜一次,再下"未知"结论。

---

## Step 2 — 时间口径判定(只产出标签,不解析日期)

**财年判定**:系统上下文中存在当前财期注入段(形如 "Current fiscal time … YEAR=FY2026, QUARTER=FY2026-Q2 …")→ 本租户启用财年:

- 所有相对的年/季/月/周(今年、上季度、本月、近 3 个月)**一律按财年解读**,即使用户从未说"财"字;按自然日历解读是错误。唯一例外:用户明确说"自然年 / calendar year"——遵从用户,并在 Step 8 口径说明的时间范围里注明。
- 当前财年/财季/财月/财周**直接取注入段的标签**;严禁自行推算,注入段声明 unavailable / lookup failed 时严禁猜测标签。

无注入段 → 按自然日历,本步其余财年内容不适用。

**本步只产出期间标签**(如 FY2026、FY26 Q1、P3、2025-04),不在本步解析为具体日期:

- **MQL 路径**:标签直接进 `timeConstraint`(写法见 `mql-syntax.md` §1.5.1),平台自己知道边界。
- **SQL 路径**:底层表只有物理日期 → 用租户的**日历表**把标签解析为绝对日期区间(先在字典总览里定位日历数据集,其列与类型见字典详情;关联写法见 `sql-syntax.md` §3)。这是全流程中唯一做解析的位置;严禁自行推导财年边界,严禁假设财季与公历季度对齐。
  - **边界来源的优先级**:① MQL 路径分支切换已解析并交接过来的区间——原样使用,禁止重推 → ② 日历表 → ③ 日历表不可用、或没有所问的那个层级 → `ask_human` 要具体起止日期。任何一步都禁止凭猜出来的边界执行。
  - **日历表没有的期间层级**(如没有财周)→ 绝不自己造一个。要么用日历表确实有的层级解析成日期区间,要么按 Step 4 判为该口径不可得。

**通用时间规则**(财年与否都适用):

- **对比措辞解读**:"同比"(无粒度前缀)一律 = 与上一年同期比,**绝不**解读为环比;"月 / 周 / 季同比" = 与上月 / 上周 / 上季同期比;"环比" = 与紧邻上一期比(粒度取当前统计粒度)。
- **"上 X" vs "本 X"**:"上" = 已结束的上一期,"本" = 进行中的当期;"与上周相比"用上周而非本周——混淆会查错时段。
- **"X 期末 / 期初"** = 该期的最后一天 / 第一天(单日),区别于整期。
- **记法**(财年租户):期间记法是**租户约定**——P{N} / W{N} / Q{N}(第 N 财月 / 财周 / 财季)只是常见的一种,也有租户写 M1–M12 或别的。本租户的实际记法从注入的财期段和知识库取;不许假设,也不许造出日历表里根本没有的期间号。
- **未完结当期 + 任何对比** → 两个对比区间必须对齐到相同的"期初 ~ 今天"跨度再比(各分支 reference 有写法)。严禁拿未完结当期与完整上期直接比。
- **对比的两端必须同粒度、同跨度。** 构建任何同比 / 环比 / vs 之前,先检查两个期间标签是否同级——季度只能比季度、月只能比月、财年只能比财年。`FY2026-Q1` vs `FY2025` 是**季度比全年**:无效,而且连正负号都可能反过来(实际增长约 33% 的渠道会算成 −67%)。这是对两个标签粒度的机械检查,不是判断:粒度不同 → 停下,按匹配的粒度重新取基准期,不许输出该对比。同粒度但跨度不等长(未完结当期 vs 完整上期)按上一条处理。
- **一个问题同时要求多个时间粒度**(如"FY22–FY25 全年 + FY26 Q1")→ **每个粒度各自查询**。绝不用一次细粒度查询覆盖再上卷:去重计数类指标在时间上同样不可加(见 Step 8),拿四个季度拼出来的年度数会偏大。
- **只说月份没说年份**(如"2 月的订单")→ 一律并入 Step 5 反问:确认年份,财年租户下还要确认指公历月还是财月 P{N}。不许因"该月今年已过"自行按今年解读,也不许默认去年。
- **完全没给时间条件**(如"拉一下订单明细")→ `time_grain_binding = MISSING` → 强制反问(打包)。严禁默认"最近 7 天"或任何自造区间。
- **表达了 ≠ 确定了**。判据不是"用户提没提时间",而是**"他说的能不能落成一个确定的查询条件"**。提了时间却落不成确定条件的措辞——「最近」「近期」「这段时间」「前段时间」「大促期间」——判 `MISSING`,与什么都没说等同。严禁自行把模糊词变成具体区间。(带名字的活动期还需在字典列出的活动期数据集里解析出来;解析不到判 `NOT_FOUND`。)
- **给了两个端点不等于给了一个区间**。"2 月到 3 月"可以是一个跨度(2.1→3.31)、两个期间对比(2 月 vs 3 月)、也可以是两个时点对比(2.1→3.1,或 2.28→3.31)。到底是哪个取决于统计对象的建模类型(事务事实表 / 周期快照 / SCD Type 1)——在 Step 4 解决,本步不许猜。

---

## Step 3 — 元数据探查 & 路由

**核心心法**:一行 = **原始事件**(订单 / 交易 / 库存流水)→ SQL 路径;一行 = **实体 + 聚合值**(客户 / 门店 / 商品 + 销售额 / 订单数)→ MQL 路径。

**探查顺序(强制——路由是检索出来的事实,不是措辞判断):**

1. **最终答案是聚合值**——数量、金额、比率、同比、占比、排名、"有多少 XX"、人群数量/占比——**必须先探查语义层**:用 **Step 1 已经切好的那套 token** 调用 `metric_search`(不整句搜;也不许在这里重新换一种切法——知识检索和指标检索必须跑在同一套切法上,否则两边的候选空间会悄悄错开)。候选对齐度按**业务含义和口径**判断,不看名字相似度——多搜几个变体成本很低,硬选不对齐指标的代价很高。
   - 有业务对齐的命中 → **MQL 路径**。
   - 无对齐指标、缺必需维度/筛选、或计算模式超出平台表达能力(见 `mql-syntax.md` 可表达性说明)→ 向用户一句话说明切换,进入 **SQL 路径**。
   - ⚠️ **人群圈选是最高频灰区**:只要答案是聚合数,即使圈选条件像跨记录逻辑("只在 A 渠道买过"、"在 ≥2 个渠道买过"),也必须先 `metric_search`——可能存在预定义人群指标/标签。**能否表达是检索出来的事实,绝不凭措辞判断**;进入 SQL 的唯一入口是确认后的分支切换。

     | 人群模式 | 语义层通常 | 先搜什么 | 判定 |
     |---|---|---|---|
     | 在某渠道/某类目买过(单条件) | ✅ 多有 | 该口径的实体计数指标("{渠道}客户数") | 真实指标存在 → MQL |
     | 只在 A、不在 B(排他) | ⚠️ 看情况 | A、B **两个**口径的指标 | 两个都有 → MQL;**缺任一 → SQL** |
     | 在 ≥N 个渠道/类目买过 | ❌ 少见 | "跨渠道"、"渠道数"、"cross channel"、"omni" | 检索无果 → SQL |
     | 比较行为人群间的派生指标(如跨渠道 vs 单渠道客单价) | ❌ 少见 | 人群标签维度("客户类型"、"渠道类型") | 检索无果 → SQL |

     判定为 MQL 后的具体写法见 `mql-syntax.md` §1.2.3;关键约束:条件只能引用**真实指标**,不能用临时指标模拟带筛选口径的指标。
2. **明确要行级原始记录**(明细 / 流水 / 逐条 / 每一笔 / 原始未聚合导出——每行必须是一条真实业务事件)→ **SQL 路径**。弱信号不算数:"明细""列出""拉个表""具体记录"经常指聚合展示——回到核心心法判断。
3. **非标计算、且答案本身就是明细表/名单**(复购对、漏斗分步名单、购物篮组合、逐实体清单)→ **SQL 路径**。
4. **维表静态属性**("{实体} 的 {属性} 是什么",属性与时间窗口无关)→ **SQL 路径**。

**MQL 路径的探查链**(逐级喂给下一步,不许跳级。探查工具的用法只写在本节——`mql-syntax.md` 从"指标/维度/值已确认"开始):

1. **`metric_search`** —— 按关键词搜指标(多词逗号分隔;`action="list"` 翻页浏览、`action="categories"` 看目录树)。返回行首的英文名就是 **metricName**,后续一切调用逐字用它;带 `[DERIVED]` 标记的派生指标必须核对展示名与用户意图语义一致;`(N dims)` 为可用维度数。
2. **`metric_dimensions`** —— 查指标的可用维度。`metrics` 参数**只能逐字传本会话 `metric_search` 返回过的 metricName**。多指标逗号并传自动返回维度交集(COMMON dimensions)——多指标查询的维度只能从交集选。用户提到具体维度/值时先用 `keyword` 过滤,减少返回量。
3. **`metric_dimension_resolve`** —— 用户原话出现**具体值**(商品名、地名、层级、价格带)而不知其维度或精确写法时反查;**多个值合并成一次调用**(逗号分隔),已知维度用 `dim` 收窄。不许拿 `metric_dimensions` 换关键词反复探值——那是 resolve 的职责。

**探查纪律**:

- **反训练记忆**:绝不把用户原词、中文直译或自造英文 code 当 metricName 传给后续工具——租户指标 code 由管理员定义,与字面翻译无关。报 `Metric not found` → 回 `metric_search` 换业务关键词重搜,不许换近似 code 再试。
- **值逐字照抄**:过滤条件里的维度值必须逐字等于工具返回的真实值(大小写/空格/标点)。样例列表是截断的——**样例里没有 ≠ 不存在**,先用 `metric_dimension_resolve` 反查确认。
- **反查无命中的处置**(按用户意图分流,不是一律回退):
  - 用户意图本身就是**包含/模糊语义**("名字带 X 的"、"含 X 关键词的商品")→ `contains()` 就是正确翻译,直接用,并在 Step 8 口径说明里写明按包含匹配。
  - 用户指的是某个**确定实体**(具体渠道/门店/商品/等级)→ `entity_filter = NOT_FOUND` → 走 Step 5 反问:把 resolve 返回的相近候选作为选项供用户选,或请用户给出精确名称。**严禁用 `contains()` 硬匹配代替确认**——包含匹配可能命中错误实体,返回"看似合理但其实错"的数据。
  - **业务定义型实体**(活动/人群/名单/标签)→ 一律反问(Step 5 既有规则)。
- **时点属性取当时的值,不取当前值**。**不许判断某个属性变化快不快**——那是估计。改成确立一个事实:按**字典盲区判定法**(见开头)确定分组/筛选列所属数据集的「建模类型」。
  - **周期快照事实表** → 取所问日期的快照,完事。
  - **SCD Type 2** → 取该时点生效的版本,完事。
  - **SCD Type 1(维度表 / 当前态宽表)** → 新值覆盖旧值,**历史值根本不在这张表里**。到字典总览里找**描述表明它记录同一实体状态变更的事务事实表**(配对的两张表通常共享同一个实体名)。找到 → 用这些变更事件还原当时的值,**并**按字典盲区方法探查该变更表的数据起始、核对覆盖所问时点——**两步都要做,变更表存在但起始太晚仍然是不可行**。找不到 → 交给 Step 4,严禁静默降级用当前值。

  拿 Type 1 表回答历史时点问题 = 把今天的属性套到过去,属于静默改口径,而且用户看不出来。
- **探查上限**:同一类查询 `metric_dimensions` + `metric_dimension_resolve` 合计最多 3 次,防止无休止换词探测。到限后:已确认的槽位直接构建;仍为 `AMBIGUOUS` / `NOT_FOUND` 的槽位**不因到限而获准硬选**——照走 Step 5 反问。**上限约束的是探查次数,不解除澄清门。**(字典盲区的数据起始/建模类型探查各算一次 internal 查询,不计入本上限,但同样不许无休止重复。)
- resolve 命中多个维度时按用户语义选;选不了 → Step 5。
- 泛词(门店 / 品牌 / 渠道 / 地区 / 商品 / 客户)通常命中多个维度:先探查;语义能唯一确定就用,否则进 Step 5。

**SQL 路径的探查**:用 Step 1 切好的 token 调 `dataset_search(action="list", keyword="...")` 从字典总览定位候选数据集(keyword 只匹配数据集名/显示名——未命中先换词,再不行去掉 keyword 翻页浏览全部,然后才能下"数据不覆盖"的结论);写 SQL 前用 `dataset_search(action="info")` 读完将要触碰的每个数据集的详情。字典是唯一数据模型来源,严禁编造它没有列出的数据集或字段。**凭主观判断"数仓不会有这类数据"跳过检索是违规**——哪怕问题看似公开数据/外部信息,也必须先实际检索过才能下"不覆盖"结论。

**严禁编造名字。** 检索/解析未命中要如实转告,不许换自造变体再试。

**分支切换**:问题从一条路径转入另一条时,一句话告知用户,并**复用上游已确认的一切**:已解析的期间标签与日期边界、已确认的指标/维度口径、已完成的反问答复。不重新询问,不重新推导边界。只有新分支特有的新歧义才走 Step 5。

---

## Step 4 — 枚举解读 → 逐解读判可行 → 选出口

可行性**依赖于解读**,所以解读没摆出来之前根本判不了可行性。先判可行性会导致最糟的一种失败:让用户做选择、他答完了,才发现哪个解读都查不出来——白白花掉用户一轮注意力。

### 4a. 枚举解读

在碰可行性**之前**,把问题成立的所有解读列出来。解读的来源,按顺序:

1. **事实表粒度 × 时间措辞形态** —— 把统计对象的建模类型与用户时间措辞的形态相乘。这张表机械地生成解读,是本节唯一**完备**的来源(建模类型按**字典盲区判定法**确立——详情推断,必要时探查;探查后仍不能唯一确定 → 各可能类型下的解读并集都要列出):

   | 用户的时间措辞 | 事务事实表 | 周期快照事实表 | SCD Type 1 维度表 / 当前态宽表 |
   |---|---|---|---|
   | 一个跨度("2 月的") | 一种解读:区间内计数/求和 | **≥2 种**:取哪个快照日——首个 / 末个(/ 区间内快照均值) | ⛔ 不存历史值,只答得了"当前" |
   | 两个端点("2 月到 3 月") | **≥2 种**:整个跨度内累计 / 分期间对比 | **≥3 种**:首对首、末对末、首对末——措辞若含"变化",再叠加快照差分 vs 事件累加 | ⛔ 同上 |
   | 一个时点("截至 3 月底") | **≥2 种**:当日发生 / 截至当日累计 | 一种解读:该日的快照 | ⛔ 除非有变更事实表能重建 |

   ⚠️ **两端点 + "变化" 生成的是两个解读族,不是两个解读**:两个时点状态之差(净变化)与区间内变更事件累加(总流转,含来回摆动互相抵消的部分)。两者数值不同,而措辞对两者都成立。

   ⚠️ **当总览里没有任何周期快照事实表时**,任何需要历史状态的解读就只剩一个可能来源——该实体的变更事实表,且受限于那张表的数据起始。所以只要某实体的当前态表是 Type 1、且其变更事实表的起始(探查确立)晚于所问月份,"该实体在某个历史月份的构成"就**一个可行解读都没有**——下结论前两个条件都要查。
2. **多候选映射** —— `metric_search` / `metric_dimension_resolve` / 字典详情返回的每个候选都是一种解读(对应 Step 5 的概念冲撞计数)。
3. **未定义术语** —— 未解析业务词(Step 1 知识未命中)的每种常见解读各算一种。
4. **未填槽位** —— 必填槽位(Step 5 的分查询类型表)每个未填项的每个取值各算一种。
5. **替代口径** —— 一旦某个解读因数据不可得而失败,**数据实际支持的最邻近口径本身就是一个解读,必须在这里列出来**:用当前值代替历史值、用被覆盖的子区间代替所问的整段、用较粗的粒度代替缺失的细粒度。必须列出的理由恰恰是:它可行而原解读不可行,这个落差正是需要用户来定的事。可行绝不等于用户想要——替代口径只能**通过 ask_human 提议**,绝不自行采用。

枚举的完备性**只有来源 1 有保证**。2–4 的广度来自 Step 1 的多切法和 Step 3 的探查——探查敷衍就会漏解读,而漏掉的解读是**静默失败**:反问照常发出、用户照常作答,他真正想要的那个从头到尾没出现在选项里。

### 4b. 逐解读判可行

**每个**解读独立检验。命中以下任一项即为不可行:

- **覆盖缺口** —— 请求范围(部分)早于该数据集的数据起始。数据起始按**字典盲区判定法**探查确立(`SELECT MIN(主日期字段)`,internal);未探查前不许下"覆盖不了"的结论,也不许当作"肯定覆盖"。
- **时点口径还原不了** —— 该解读需要某 SCD Type 1 属性的历史值,而不存在对应的变更事实表,或该变更表的数据起始(探查确立)晚于所问时点。
- **不覆盖** —— 按 Step 3 两层都真实探查过,语义层和数据集都不覆盖该概念。
- **不兼容** —— 该解读需要的 指标 × 维度 × 筛选 组合被平台拒绝(多指标无公共维度、该指标不支持这个筛选维度)。**这是数据的事实,不是歧义**——用户没法回答"你要不要一个不存在的组合",所以在本步了结,绝不作为问题送进 Step 5。
- **schema 漂移** —— 后端拒绝字典返回的数据集/字段(`Table not found` / 未知列)。如实告知,不猜替代品。

### 4c. 选出口——只有三个

| 情形 | 出口 |
|---|---|
| 只有一种解读,且可行 | **直答** → Step 6 |
| **所有**解读都不可行、**且不存在替代口径** | **告知不可行** —— 首要回复说清查不了及原因;此时确实没什么可问 |
| 其余情况(≥2 种解读,或 1 种可行 + 若干不可行) | **反问** → Step 5 |

- **存在替代口径时,"全不可行"就变成"反问"。** 用户所问的解读跑不了、但某个替代口径能跑(上面来源 5),那就是"1 个可行 + 若干不可行" → **走反问**,题干里明说原口径为什么跑不了,并把替代口径作为选项给出。只有当**任何**解读都跑不了时,才走告知不可行那个出口。明明有可用的替代口径却不提、直接宣告不可行,等于把一个本可以绕过去的"查不了"甩给用户。
- **可行性只过滤选项,不决定出口。** 只剩一种解读**不构成**直接执行它的许可:用户想要的可能正是被过滤掉的那个。这种情况照走 Step 5,问题变成"你要的是 A(查不了,因为……)还是 B(可查)"。
- **不可行的解读必须活着进入反问。** 在题干里写明它们以及**为什么**不可行。静默丢弃 = 在用户不知情的情况下缩小了他的选择范围,正是本步要防的改写意图。
- **不可行这个出口是首要回复,不是脚注。** 明确说清哪个时段/口径不存在及原因。绝不用空结果或截断结果顶替。
- ⛔ **严禁改写意图**:不许把时段分析偷换成"当前快照",不许静默平移/收缩时间窗口,不许用口径不同的替代表/字段。执行替代查询再补"⚠️ 局限性说明"脚注 = 用户问 X、你答 Y。真正有用的替代方案只能**通过反问提议**,用户同意后才执行。

---

## Step 5 — 澄清门(构建任何查询前强制执行)

**为什么**:口语概念("销售额""客户""时间""城市")映射到物理候选时常有多个、口径各异。拿最像的候选写查询,返回"看似合理但其实错"的数据——比语法错误更危险,因为用户看不出来。

### 本门管什么——一条二元判据

> **这个决策会不会进入查询?**
> **改变返回的数据 → 一律反问。只改变这份数据怎么显示 → 用默认,不反问。**

某个东西落不落进 SQL / MQL 是客观事实,所以这条判据没有裁量空间。它是本门**唯一**的豁免——没有严重度分级,没有"次要口径",没有"用户多半是指显而易见的那个"。

| 不进入查询——不要问 | 进入查询——必须问 |
|---|---|
| 金额单位(万/亿)、小数位、千分位 | 时间范围、时点对齐、变化语义 |
| 图表类型、配色、出图还是出表 | 指标口径、维度选择、并列多维度的组合方式 |
| 对已返回结果的行序重排 | 排名的**排序指标**、Top-N 的 N、LIMIT |
| 日期显示格式、把存储值翻译回用户说法 | 人群定义、分母范围、筛选值 |

有两个词横跨两侧——分类的是**具体决策**,不是词本身:*排序*("按什么排名"进入查询;"已返回的行升序还是降序"不进入);*时区*(影响一行归属哪一天 → 进入;只是格式化时间戳 → 不进入)。

**进入查询的默认值可以不问的唯一通道**——两个条件缺一不可:
1. 默认值**及其后果写在结果里**(看得见,不是脚注),且
2. 用户能**一句话改掉并重算**。

例:明细导出可以默认行数上限,但**必须**同时写明"共 47,382 行,当前展示前 1,000 行"并提供全量导出。这个豁免是工具能力换来的,不是靠判断某个歧义不重要——工具做不到这样展示和重算,就得问。

### 槽位

> **一个问题可以带多个互相独立的时间条件——填满一个不等于填满全部。** 常见的几种:统计窗口;状态属性的观察时点;**人群的纳入窗口**;行为定义词(新客 / 复购 / 流失)的判定窗口;对比基准期。**逐个确认**。绝不因为用户说了某个时间就断定"时间已定"——例如"截至 FY25 末各客群有多少人",确定的是观察时点,完全没说算哪些人,而这是两个不同的问题、答案也不同。

> **槽位是按"实例"评估的,不是按"类型";而且每个子问题各自过一遍门。** 一个问题里同一类槽位可以有多个实例:"pioneer 中有多少 new guest" 含**两个** `population` 实例(人群筛选 + 行为定义词),观察时点与纳入窗口是**两个**时间实例。**只要有一个实例未确认,该槽位就是未确认**——绝不因为其中一个实例定了就把整个槽位标成 CONFIRMED。同理,用户一条消息里问了多个子问题("……?其中 X 又……?")时,每个子问题都要完整评估一遍:它们可以复用已确认的口径,但**不继承"已确认"这个状态**——后一个子问题新引入的概念、时间、人群,一律重新从未确认开始。

逐个评估适用槽位。探查工具只能**验证或消歧用户实际表达过的内容**——检索结果永远填不上用户没表达的意图(搜到"增长率指标 X"不等于用户确认了 X)。

| 槽位 | 检查内容 |
|------|----------|
| `measure` | 要查的指标(或 Top-N / 增长 / 占比背后的度量)唯一且业务对齐 |
| `time_grain_binding` | 期间标签能落成唯一窗口:年份已给,财年租户下公历月 vs 财月 P{N} 已定。模糊措辞(「最近」「大促期间」)判 `MISSING`,不算"已表达" |
| `time_anchor` | **状态型**对象(快照 / Type 1 属性):区间坍缩到哪个时点(期初 / 期末 / 两端各取)。**事务事实表**遇时点措辞:当日发生 vs 截至当日累计 |
| `change_semantics` | 状态的"变化 / 迁移 / 增减":两个时点状态之差(净)vs 变更事件累加(总流转)。不得默认 |
| `entity_filter` | 提到的每个业务实体值唯一映射到字段 + 值 |
| `grouping_dimension` | "按 X 拆"映射到唯一维度/层级;多指标时存在公共维度;该维度是分组展示还是只作筛选(合并成一个数)明确;并列多维度的组合方式明确 |
| `population` | 统计对象 / 人群 / 筛选范围明确;实体计数类问题的**纳入条件及其时间窗口**明确——观察时点不构成纳入条件(它说的是*什么时候看*,不是*算哪些人*);每个进入查询的业务词有唯一定义(Step 1 知识命中,或已反问) |
| `sequence_window` | "先 A 后 B"类问题里,锚点事件 A 的窗口与后续行为 B 的窗口**各自**明确 |
| `calculation_semantics` | Top-N / 增长 / 占比 / 渗透 / 转化 / **分布 / 构成**:排序指标、N、分子、分母、范围都明确;交叉维度 ≥2 时**归一化方向**也要明确 |
| `comparison_scope` | 同比 / 环比 / vs / 前后对比的基准明确 |
| `field_mapping`(SQL 路径) | 每个口语字段概念唯一映射到一个口径匹配的列 |

每个槽位标为:`CONFIRMED`(用户明说过,或本会话已确认同一口径)/ `UNIQUE_WITH_COVERAGE`(工具只返回一个候选且明确覆盖用户修饰词)/ `AMBIGUOUS` / `MISSING` / `NOT_FOUND`。

> `INCOMPATIBLE` **不是**槽位状态,兼容性**不是**槽位——指标 × 维度 × 筛选 的不可能组合是数据的事实,在 Step 4b 了结。用户回答不了这种问题。

**必填槽位随查询类型变化**——查这张表,不要凭"感觉都填上了":

| 查询类型 | 在 `measure` + `time_grain_binding` 之外还必填 |
|---|---|
| 单值查询 | — |
| 实体计数("有多少 X") | 纳入条件**及其时间窗口**;分组所用属性的观察时点;每个行为定义词(新客 / 复购 / 流失)各自的判定窗口——逐个单独确认 |
| Top-N / 排名 | 排序指标、N、排名范围(全体内排 vs 各组内排) |
| 占比 / 渗透 / **分布 / 构成** | 分子口径、**分母范围**;一旦按 ≥2 个维度交叉,还要确认**归一化方向** |
| 对比 / 同比环比 | 基准期、**到底要不要算差值 / 倍数 / 增长率** |
| 变化 / 迁移 | `change_semantics`、`time_anchor` |
| 序列 / 行为链 | 锚点窗口与后续窗口,**各自单独确认** |
| 明细导出 | 行数上限、字段范围(行数上限可走上面的可见默认通道) |
| 分组拆分 | `grouping_dimension`,以及多维度如何组合 |

**机械硬触发。** 每一条都落在一个事实上——候选个数、字符串比较、元数据标注、某个词在不在。没有任何一条要求你权衡、排序或估计;如果执行某条时感觉是在做主观判断,说明你用错了。

1. **概念冲撞——数个数,不权衡。** ≥2 个字段/指标的展示名或描述包含 Step 1 为同一概念切出的任一 token → `AMBIGUOUS`。在**所有切法的并集**上数(粗切和细切命中的候选不同,并集才是计数依据)。不权衡哪个"看起来最对",不看位置、名字长短、类型。**不许自行归并候选**("这两个只是同一指标的不同粒度")——归并是判断;分开数,让用户去合并。高频冲撞:同一概念对应多个**时间字段**(下单 / 支付 / 退款时间);**商品名**的多个层级(款式 / SKU / 主款);同名**地理概念**的多口径(交易发生地 vs 门店所在地 vs 客户注册地);**渠道**层级维度 vs 来源渠道。

   **层级型候选还必须确认"拆到哪一层"。** 命中的字段之间存在层级关系(渠道 1–6 级、省/市/区、款式/SKU/主款、大类/子类)而用户没有指定层级 → `AMBIGUOUS`。"按渠道拆"可能是拆到最上层(几行)、拆到最细层(几十行)、或两层嵌套——这是三种不同的交付物。⚠️ 知识库条目**只有在明确给出默认层级或默认布局时**才解除本条(如"两级同时作为行维度放在一张表里")——那种情况按知识库执行,不反问。仅仅把概念映射到一组字段的条目,解决的是"用哪些字段"、不是"拆到哪一层",粒度仍未确认 → 照常反问。
2. **只有一个候选——逐字比对。** 一个候选不自动等于确认。比字符串:用户原词 vs 候选的展示名 / 已登记别名。
   - **逐字相同** → `UNIQUE_WITH_COVERAGE`,直接用。
   - **不相同** → 做语义匹配的是你,而语义匹配有出错空间 → `AMBIGUOUS`,确认一次。(用户说的词 vs 只是"包含"该词的展示名——「X」vs「X 标识」/「X 类型」——不逐字相同 → 确认。)

   本会话已确认过的口径不重复问。真正等价的同义词应当作为**别名**进知识库——这条触发是靠补别名变安静的,不是靠放松判据。
3. **修饰词未覆盖——子串比对。** 用户措辞带了口径修饰词("含税/不含税""周末""工作日""节假日""所在""周边""线上/线下"……),而**没有任何候选的展示名或描述包含该修饰词** → `AMBIGUOUS`。注意:租户配有日历表时,周末/节假日/工作日口径通常已被其标志字段覆盖(字典详情),那算已覆盖。
4. **进入查询的业务词,定义没有落到实处。** 两种情形都判 `AMBIGUOUS`:
   - **(a) 知识库未命中** —— 某个 Step 1 token 查不到知识条目,且作为筛选、人群或计算进入查询。把它的常见解读做成互斥选项。"新客""新到 {某品类}""高价值""流失""活跃""复购"这类词属于本条的实例,但**触发条件是"未命中",不是"在这张词表里"**——不在表里的词行为完全一样。
   - **(b) 知识库命中了,但定义里还带着未绑定的参数** —— 条目说清了*怎么算*,却留了一个空:"新客 = 在**分析期内**首次发生已识别交易"、"流失 = 连续 **N 个月**未购买"。**定义已知不等于槽位已确认**:那个参数本身(哪个分析期、N 是多少)仍判 `MISSING`,照常反问。知识条目回答的是*怎么算*,从来不回答*用什么参数算*——除非它把参数也写死了。

   绝不自选一种解读再在口径说明里备注。
5. **并列多维度没说怎么组合。** 用户一次点名 ≥2 个维度而没说组合方式 → `AMBIGUOUS`。"各维度分别统计" = 每个维度各出一张单维分布;"维度交叉统计" = 组合后的联合分布——两者行数、分母、可读性完全不同,**没有可依的默认值**。
6. **要输出百分比 → 先把分母写下来。这是强制动作,不是判断题。**

   判据**不是**"用户是不是在问分布"——那要你先给问题归类,而归类会漏(「男女各占多少」「渠道结构」「主要来自哪些渠道」都不带"分布"二字)。判据是:**你即将输出的结果里,有没有任何一列是百分比 / 比率 / 占比 / 构成 / 渗透率 / 转化率**(即 Step 1 是否打了 `needs_denominator` 标记)。有 → 在构建查询**之前**,先在推理里逐字写出这两行:

   ```
   分子 = {对什么做计数/求和}
   分母 = {对什么做计数/求和}
   ```

   然后检查分母:
   - 分母唯一,且用户明说过、或客观上只有一种可能 → 继续。
   - **分母有 ≥2 种站得住的写法 → `AMBIGUOUS`,反问。**

   **按 ≥2 个维度交叉时,分母必然 ≥2 种**——组内归一(每组各 100%:"每个组的人群结构长什么样")/ 跨组归一(每个类别各 100%:"这个类别的人来自哪里")/ 全局归一(整张表 100%)。同一个格子在三种口径下可能分别读作 80%、67%、27%,而读者**不自己把表加总就看不出用的是哪种**。所以这种情况**直接判 `AMBIGUOUS`**,不需要再斟酌。

   把那两行写出来本身就是重点:填空是可观测的动作,"意识到有歧义"不是;而且填的过程正是暴露歧义的地方——写下"分母 = 该渠道总人数"时,很难不想到它还可以是别的。

   ⛔ 反模式:"先按最常见的读法算,再在口径说明里写清分母"。分母不是注释,它就是查询本身。只有一个分组维度、且分母已被用户锁定时,不触发。

7. **业务定义型实体 `NOT_FOUND`**(业务定义的期间、活动、人群、名单、标签):不许 `contains()` 硬匹配。请用户给出定义、可匹配字段/值、或时间范围。
8. **两个窗口只给了一个。** "先 A 后 B""在 A 之后又 B""买过 A 的人后来还买没买"含**两个**窗口:锚点事件 A 的、后续行为 B 的。用户通常只给了 A 的 → B 的窗口判 `MISSING`。严禁拿 A 的窗口顶替 B,严禁默认"A 之后不限时"。把 B 的边界做成互斥选项(A 之后不限时 / A 之后到该期结束 / 仅限同一期内)。

**门的决策:**

- 所有适用槽位均为 `CONFIRMED` / `UNIQUE_WITH_COVERAGE` → 进入 Step 6。
- **打了 `needs_denominator` 标记、却没有把分子/分母两行写出来 → 本门未通过**,无论其它槽位状态如何。先按触发 6 写出来,再重新评估。推理里从未写出分母的百分比查询,不许进入 Step 6。
- 任一槽位为 `AMBIGUOUS` / `MISSING` / `NOT_FOUND` → **下一个工具调用只能是 `ask_human`**。不许"先把能跑的跑完再问";用户回复前查询工具一律禁用。**"备注口径" ≠ "澄清"**——基于硬选口径出结果、再在答案末尾补"⚠️ 口径说明"是反模式。
- **每轮最多一次 ask_human——且必须把当前已知的全部开放歧义打包**:用 `questions` 数组,**每个歧义一个独立子问题**(每题恰好一个决策,最多 4 题)。禁止把多个决策揉进一段话或一个混合选项列表;问其中一个、对其余静默硬选同样禁止。选项必须来自真实候选(真实工具返回,严禁编造)。
- **某个槽位要问时,把依赖它的槽位一并拉进同一轮。** 把下面那张依赖表**正着读**:凡是它列为"这个一变就失效"的下游,**现在就问**,不要等答复回来再问第二轮。两个理由:那些下游歧义本轮就已经存在,按上面的打包规则本来就该一起问;而且正因为它们相互关联,用户只有同屏看到才表达得出这种关联——"两个用同一个基准"还是"分别用不同的"。同一张表,两个方向:倒着读决定什么会失效,正着读决定一轮里发出什么。
  示例:状态属性的观察时点要问 → 表里列出依赖它的有"以该状态圈定的下游人群"和"行为定义词的判定窗口" → 于是"客群快照取哪个时点"和"新客按哪段时间判定"**放在同一个 ask_human 里发出**,绝不拆成两轮。
- **收到 `ask_human` 答复后,回到本门重新评估全部槽位**——绝不拿着答案直接进 Step 6。一个答复很少只填上被问的那个槽位:它常常同时改变了别的槽位的前提,而**凡是依赖被改动部分的,一律回到未确认**。采纳替代口径(Step 4a 来源 5)是最典型的情形——把"截至 FY25 末"换成"截至今天",所有锚在原基准上的定义都重新悬空。重新评估后所有槽位仍为 CONFIRMED / UNIQUE_WITH_COVERAGE,才放行。⚠️ 这次重新评估是**给"答复到手后才浮现出来的歧义"兜底**,不是把本轮已经能预见的歧义拆到第二轮去问的许可。凡是可预见的,第一轮就要发出去(见上面的依赖正向打包)。
- **没有反问预算,也没有"问累了打折"。** 第二轮、第三轮反问不降低标准;不会因为"已经问过一次"就有哪一项变得可以跳过。`allow_free_text` 全程开着,宁愿自己描述口径也不想选选项的用户随时可以直接写——那才是泄压阀,放松澄清门不是。

**已确认口径的沿用**——确认过的不重复问;但上游一旦变了,依赖它的一切就不再是"已确认"。用户改动问题时适用,反问答复在流程中途改变了前提时同样适用:

| 变了什么 | 仍然有效 | 失效,需重新评估 |
|---|---|---|
| 时间范围 | 指标口径、维度、人群定义 | `time_anchor`、`comparison_scope` |
| 指标 | 时间、人群 | 维度选择、分母、公共维度是否存在 |
| 人群 / 筛选 | 时间、指标 | 分母范围、占比/渗透的统计范围 |
| 某个维度 | 时间、指标、人群 | 并列多维度的组合方式 |
| 分支(MQL ⇄ SQL) | 期间标签与已解析日期、已确认口径、全部反问答复 | 只有新分支特有的新歧义 |
| **采纳了替代口径**(当前值代替历史值、可得子区间代替所问整段、粗粒度代替细粒度) | 与之无关的指标、维度 | **时间基准,以及一切锚在它上面的定义**——新客/复购/流失的判定窗口、对比基准期、状态属性的观察时点 |
| **状态属性的观察时点** | 指标、无关筛选 | **一切以该状态圈定的下游人群**("其中 {某客群} 的……"——该客群的成员变了,子问题自己的窗口也重新回到未确认) |

**结构纪律**(工具侧强制执行,违反会被拒绝或丢弃):

- 选择题给 2–3 个**互斥、可直接提交**的选项:`label` 是完整答案,`description` 一句话说清口径/后果;存在合理默认时恰好一个选项标 `recommended: true`(时间范围没有合理默认,不标)。
- **相互关联的子问题,要给一个"全部用同一个"的选项。** 同一轮里几个槽位通常取同一个值时(几个时间基准、几个渠道层级),把它做成一个选项,而不是逼用户把同一个答案填三遍——同时保留那些确实需要分别取值的组合各自一个选项。
- **`description` 写后果,不写定义。** "按订单归属门店的城市统计"是定义——用户看不出差别。"线上订单会算到发货门店所在城市,纯电商订单会掉出来"是后果——他立刻知道选哪个。description 里如果没有"会 / 则 / 包含 / 排除"这类描述结果差异的词,多半写成了定义,重写。
- **Step 4b 判定不可行的解读,要写进题干并说明原因**——如"{日期 A} 与 {日期 B} 的状态对比查不了:该实体的变更数据仅从 {数据起始} 起"。选项里仍然只放可行解读,但用户必须看到被剔除了什么、为什么;静默丢弃 = 在用户不知情的情况下缩小了他的选择。
- **会咬人的成本要在选中之前说**——某个选项会全表扫描,或其范围只被部分覆盖,写进该选项的 `description`。答完才发现跑不动,这一轮就白问了。
- **可解答性代入测试(每个选项发出前都做)**:把选项逐字代回原问题——代完你仍然不知道该查什么,这个选项就是无效的,整轮反问白问。⛔ 典型无效选项:「是现有的某一个(请指明)」「按标准口径」「其它情况」。
- **真实候选可枚举时逐个成为选项**:候选 ≤3 → 每个候选单独一个选项(`label` 就是候选名本身)。候选 >3(工具对选项数的硬上限就是 3)→ 取与用户原话**最相关的 3 个**做选项(相关度分不出高下时取离所问时间最近的 3 个),置 `allow_free_text: true`,并把**其余候选一并写进 question 文本**——不写用户就不知道还能填什么,自由输入等于摆设。严禁把多个候选折叠成一个"某一个(请指明)"选项。
- ⛔ 选项里不许出现"请提供 / 请告知 / 占位符"式内容——工具会直接丢弃这类选项;需要自由输入的决策单独开一题 `question_type: "text"`。注意工具只拦"请告知/请提供/请填写/请输入/请说明"和 `{}`/`<>` 占位符,**不拦"请指明/请选择/请确认"**——别指望工具兜底,按上面两条自检。选项被丢到不足 2 个时,整题会静默降级成自由文本。
- `header` ≤12 字;问题文本语言必须与当前 UI 语言一致(工具硬校验)。

```
ask_human({
  questions: [
    // description 写「后果」,不写「定义」
    { id: "city_caliber", header: "城市口径", question: "「城市」按哪个口径统计?",
      question_type: "single_select", allow_free_text: true,
      options: [
        { label: "交易发生城市", description: "线上订单会算到收货地址所在城市" },
        { label: "门店所在城市", description: "线上订单会算到履约门店所在城市,纯电商订单会掉出统计" }
      ] },
    // 状态型(Type 1)对象 + 区间措辞:坍缩方式必须问、不得默认;
    // Step 4b 判不可行的那个解读写在题干里并说明原因
    { id: "change_semantics", header: "变化口径",
      question: "「{用户说的变化词}」有两种口径。(第三种解读——用 {日期 A} 与 {日期 B} 的状态对比——查不了:该实体的变更数据仅从 {数据起始} 起。)",
      question_type: "single_select", allow_free_text: true,
      options: [
        { label: "净变化", description: "期末数减期初数,来回摆动互相抵消后看不见" },
        { label: "总流转", description: "区间内每一笔变更事件都计入,走了又回来会被计两次" }
      ] },
    { id: "time_range", header: "时间范围", question: "统计的时间范围是?",
      question_type: "single_select", allow_free_text: true,
      options: [
        { label: "本财年至今", description: "财年年初到今天" },
        { label: "近 30 天", description: "过去 30 个自然日" }
      ] },
    // 真实候选 5 个 > 选项上限 3:取最相关的 3 个做选项,其余写进题干,开自由输入
    { id: "measure", header: "指标口径",
      question: "检索到 5 个口径不同的「销售额」指标:A、B、C、D、E。你要的是哪一个?也可以直接描述你要的口径。",
      question_type: "single_select", allow_free_text: true,
      options: [
        { label: "指标 A", description: "该指标的业务口径与统计粒度" },
        { label: "指标 B", description: "该指标的业务口径与统计粒度" },
        { label: "指标 C", description: "该指标的业务口径与统计粒度" }
      ] }
  ]
})
```

---

## Step 6 — 构建查询(分支专属)

> ⛔ **阅读闸门——本会话中每个查询工具的第一次调用前,必须先读对应 reference:**
>
> - **`metric_query`**:先读完 `references/mql-syntax.md`。它包含构建原则、请求参数全集(含财年写法)、完整示例、输出前自检清单与排障表。凭记忆写 MQL 会被服务端直接拒绝。
> - **`dataset_sql_query`**:先读完 `references/sql-syntax.md`,**且**对将要在 SQL 中触碰的每个数据集,本会话已用 `dataset_search(action="info")` 读过详情(还没读过的先读)。凭记忆写 SQL 会**静默返回错误数据**(方言限制、物理表名、别名规则、日期类型混杂——细节都在 reference 里)。辅助查询(期间区间解析、财期探测、数据起始探查)同样受此约束。
>
> 本会话还没读过对应文件时,下一个工具调用就是对它的 `read_file`——不是查询工具。

分支专属内容全部在 references 里,不在 references 之外即兴发挥。

---

## Step 7 — 执行与失败处理

- 失败时**先读返回的错误文本**,按错误类型修复——每个 reference 带有其工具的错误对照表。严禁不读错误就凭猜改查询。
- 修复后自动重试一次。重试只允许**语义等价**的修改:修语法、等价改写、执行方式优化。**严禁静默丢弃或收窄任何过滤条件**(尤其时间范围)——那会让答案回答另一个问题。
- 查询成功但**意外返回 0 行** → 先过分支 reference 的检查清单(存储值格式、日历 JOIN 类型对齐、时间范围早于数据起始——数据起始按字典盲区方法探查确认),再下"没有数据"的结论。
- 超时 → 先做**口径不变**的等价优化:时间条件落在主日期字段上、把大区间按时间分段拆成多次查询后合并。等价优化后仍超时 → 如实告知当前范围查不动;**收窄范围或抽样只能通过 ask_human 提议、用户同意后执行**——静默收窄再"说明"正是 Step 4 的改写意图反模式。

---

## Step 8 — 回答 + 口径说明

- 结论先行。所有数字取自工具结果,**严禁手工重算**。
- ⛔ **绝不把结果跨维度值加总上卷——时间也是其中一个维度。** 查询返回的粒度就是你展示的粒度。需要更粗的粒度 → **按更粗的粒度重新查一次**,绝不把细粒度的行相加。这不是呈现偏好,是正确性规则:
  - **去重计数类指标不可加**(客户数、用户数、去重订单数、活跃数)。同一实体会出现在多个维度值下,相加即重复计数:总数被高估、占比的分母被高估、结论可能反转。只有 SUM 型度量(金额、数量)才谈得上相加。
  - **时间是最容易忘掉这一条的维度。** 财年**不等于**四个财季之和,月**不等于**各日之和——只要指标是去重计数。一个客户在 Q1 买过一次、Q3 买过一次,按季度看是 2 行,按年看是 **1 个人**:**每个时间粒度都有它自己的去重范围**。要年度数就按年度粒度查,绝不把季度行相加。(金额、数量在时间上确实可加——这正是这个错误让人放松警惕的原因。)
  - **自检**:把你上卷出来的数与平台在该粗粒度直接返回的数对一下。对不上 → 该指标不可加,你的数是错的。(去重计数的比率同样永远不可加。)
- **查询返回的东西,一样都不许静默丢掉。** 用户点明的范围内,行要么全展示,要么明确说清少了什么。两种静默丢弃都禁止:
  - **查询里带了、但输出没有展开的维度** → 要么展开那一层,要么在口径说明里写明结果已按该层汇总。
  - **因篇幅裁掉部分行**(只展示最近几期、只展示头部分组)→ 只有同时满足两条才允许:① 在正文里写明"共 N 期 × M 个分组,此处展示 X";② 给出完整数据——**用 `visibility="export"` 发布成 CSV**(两条路径都支持;因篇幅裁剪过的表正是需要附完整数据的情形)。
  - **"完整"指每一行**和**每一列**。发布的文件必须让读者能自己复现答案表:占比、同比、变化幅度这些派生列也要在里面,不能只有原始计数。答案里满是百分比、CSV 里只有客户数,读者一个数都核对不了。
  - **绝不因为"最近的更有代表性""表太长"就裁掉用户要求的期间或分组**——缩小交付范围是用户的决定。用户原话点名的范围(如 "same time period")一个都不能少。
- **派生列必须在查询内部算出来,不许查完再算。** 占比、构成、同比、增长率、排名:都写进查询本身(按分支 reference 用临时指标 / indirection 表达)。在查询外算是双重错误——既违反"所有数字取自工具结果",算出来的列也进不了 CSV,导出文件必然残缺。平台确实表达不了某个派生列时,如实说明,不要私下在外面算。
- 展示时按可读性取整、换算单位(金额到万/亿、比率留 1–2 位小数)是正常的,不必特意声明。两条底线:取整后的数**不许再参与计算**(占比、差值、增长率一律用原值算,最后只对结果取整);取整**不许改变结论**(排名反转、"持平"变成"增长"、跨过某个阈值)——会改变结论就多给一位精度。
- **存储值 ≠ 用户说法时,展示前翻译回用户的说法**(如财月存储为 `'03'`、用户说 P3 → 显示 P3)。
- 口径说明是口径披露的**默认**去处:能收进它的就不要散在正文里随手补一句,也不要另起"备注 / 局限性"小节。**例外**:Step 4 要求放在**首要位置**的内容(数据不存在、时点口径还原不了、覆盖缺口)是结论本身,写在正文开头,不许降级成末尾脚注。

### 口径说明(统一格式)

只要本次查询涉及以下任一项,就在回答末尾追加口径说明:聚合或去重口径、占比的分子分母、排名范围、对比对齐方式、时间范围、人群/子集定义、非显然的筛选条件、覆盖范围限制、结果的阅读注意点。

**格式**:Markdown 引用块,首词 `**口径说明**:`。默认连写成段——它是一段供人核对的口径,不是清单。要点多到一段读不动时可以分句分行,但**不许为了迁就格式删内容**:该说的口径少一条,比排版难看严重得多。

```markdown
> **口径说明**:{统计对象/子集定义};{计算口径:分子分母、去重粒度、多值字段的说明}。统计时间范围为 {期间标签}({起始日期} 至 {结束日期}),{非显然的筛选条件}。
```

**要覆盖的四类信息**(不适用的略过,不要硬凑)。上面模板里的先后只是常见顺序,实际按哪种更好读来排:

1. **统计对象 / 子集** —— 谁被算进来了。用「」框出限定条件,如:基于「姓名+性别均已填写」的有效客户子集。
2. **计算口径** —— 分子分母是什么、按什么去重、排名/占比的范围。**多值字段必须提示可加和超 100%**;先后判定、首末笔规则同样在此说明。多维度时写明是各维度分别统计还是交叉统计,并写明查询里带了、但被汇总掉的层级;状态型属性写明取的是哪个时点的值;变化/迁移类数字写明是净变化还是总流转。
   **走了可见默认通道的进入查询的默认值(Step 5),写在正文里,不写在这里**——"共 47,382 行,当前展示前 1,000 行"要出现在用户读结果的地方,并附上怎么取全量。塞进口径说明块就不满足当初让它免于反问的可见性条件了。
3. **时间范围** —— 期间标签与具体日期**都给**:`FY2026 财年(2026-02-02 至 2026-07-31)`。只给"FY2026"用户无法核对,只给日期则丢失口径。未完结当期要写明对齐跨度,如 `FY2026 至今(2026-02-02 至 2026-07-31)vs FY2025 同跨度`。用户原话给的就是精确日期区间时,不必再回述一遍。
4. **筛选条件** —— 用户没在原话里说、但实际生效的条件。**非显然的筛选允许直接给字段条件**(行内代码),如:仅含已识别交易(`{标志字段} = 'yes'`)。这比"仅含有效交易"更可核对;真实列名从字典取。

**边界**:
- **可以**出现:维度/字段的**筛选条件**(行内代码)、指标的业务口径、期间标签与日期。
- **默认不出现**:完整 SQL / MQL、物理表名、临时指标名、工具名——它们对核对口径没帮助,只会挤走真正要核的信息。用户明确要看查询语句时照给,但放在正文,不塞进口径说明块。
- 无实质口径可说(如单一指标、用户已明确全部条件、无任何解读)→ **不写**,不要输出空洞模板。
- 注解永远不能替代澄清门:"按口径 A 查了、脚注里披露了"正是 Step 5 的反模式。

---

## 边界与杂项

- **查询性能**:大事实表查询尽量让**口径中已有的**时间/筛选条件落在主日期字段和常用过滤字段上,避免无谓全表扫描。性能**永远不是**加用户口径之外过滤条件的理由——那是改写口径(Step 4);用户确认的范围就是全历史时照常执行,查不动按 Step 7 处理。LIMIT 与 CSV 发布规则见各分支 reference。
- **对路径诚实**:某条路径的工具在当前环境不可用时直说;严禁用另一条路径静默模拟。
- **禁止绕路**:严禁用 bash / python 发 HTTP(S) 绕过上述通路;只有两条数据路径都真实探查过且确实不覆盖时,才考虑 web 工具获取外部信息。

Files in this skill

  • SKILL.md60 KB
  • references/mql-syntax.md58.2 KB
  • references/sql-syntax.md12.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in data →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.