update by wensicheng on 0629
This commit is contained in:
+110
-148
@@ -1,178 +1,140 @@
|
||||
---
|
||||
name: sql-context-builder
|
||||
description: 当需要组装 SQL 生成器与评审器共同消费的、唯一可信的 SQL Context 时使用。触发词包括"build SQL context"、"固化 join key"、"table aliases"、"列出来自哪张表"、"准备写 SQL"、"context 已经齐了"。消费已校验的 Logic Plan 与 Field Mapping,产出带有稳定 alias、固化 join key、显式 fact/dimension 标记的标准化 SQL Context。SQL 生成器**不得**再重新推导其中任何一项。
|
||||
description: 当逻辑计划已经完成,需要固定 SQL 生成和 SQL review 共同依赖的 alias、字段来源、join key、指标、维度、过滤、时间窗口、事实表/维表上下文时使用。消费 logic_plan 和 validation_result,不写 SQL,不调用 MCP。
|
||||
---
|
||||
|
||||
# SQL Context Builder(SQL 上下文构建器)
|
||||
# SQL Context Builder(SQL 上下文构建)
|
||||
|
||||
## 概述(Overview)
|
||||
## 目标
|
||||
|
||||
把 SQL 生成器本来要重复发明的所有决策一次性固化下来:alias、join key、字段来源、类型、角色标签(fact / dimension / filter)、时间列,都在这里只此一次地钉死。输出是 `sql-review` 及任何 SQL 输出步骤的**唯一可信输入**。
|
||||
把 SQL 生成时可能被临时猜测的内容提前固定下来。下游只能使用本 skill 输出的 alias、字段、join、指标、过滤、时间窗口和输出列。
|
||||
|
||||
核心原则:**任何不在 SQL Context 中的字段,下游 skill 视同不存在。** 如果下游 skill 还需要新内容,请求必须先回退到 `metadata-validator` 和 `logic-planner`。
|
||||
## 输入
|
||||
|
||||
## 何时使用(When to Use)
|
||||
|
||||
**使用场景:**
|
||||
|
||||
- `logic-planner` 返回状态 `PLANNED`
|
||||
- 用户即将写 Spark SQL,需要一份规范化的 context(alias、join key、字段来源)
|
||||
- 同一份逻辑要产出多版 SQL 草稿 — context 保证它们保持一致
|
||||
|
||||
**不要使用场景:**
|
||||
|
||||
- `logic-plan.status` 为 `NEED_USER_CONFIRMATION` — 回到 `logic-planner`
|
||||
- SQL 极简单(单表无 join) — 仍然建议构建 context,除非用户明确跳过
|
||||
- 任务是把 plan 翻译成 SQL — 那是另一回事,不是本 skill 的职责
|
||||
|
||||
## 输入(Inputs)
|
||||
|
||||
1. 来自 `logic-planner` 的 `logic_plan`(状态 `PLANNED`)
|
||||
2. 来自 `metadata-validator` 的 `field_mapping`(状态 `VALIDATED`)
|
||||
3. 原始 `validation_result`(用于表级元数据:类型、来源)
|
||||
|
||||
## 工作流(Workflow)
|
||||
|
||||
```dot
|
||||
digraph sql_context_builder {
|
||||
"读取 logic_plan + field_mapping + validation_result" [shape=box];
|
||||
"为每张表分配稳定 alias" [shape=box];
|
||||
"固化 join key(left_alias.field = right_alias.field)" [shape=box];
|
||||
"把每个字段解析为 (alias, column, type, source_file)" [shape=box];
|
||||
"为每个字段打标签:dimension | metric | filter | time | join_key" [shape=box];
|
||||
"标记事实表 / 维度表" [shape=box];
|
||||
"校验 Logic Plan 的字段在 context 中全部存在" [shape=box];
|
||||
"所有必需字段都齐全?" [shape=diamond];
|
||||
"输出 SQL Context" [shape=box];
|
||||
"输出 SQL Context + missing_fields" [shape=box];
|
||||
|
||||
"读取 logic_plan + field_mapping + validation_result" -> "为每张表分配稳定 alias";
|
||||
"为每张表分配稳定 alias" -> "固化 join key(left_alias.field = right_alias.field)";
|
||||
"固化 join key(left_alias.field = right_alias.field)" -> "把每个字段解析为 (alias, column, type, source_file)";
|
||||
"把每个字段解析为 (alias, column, type, source_file)" -> "为每个字段打标签:dimension | metric | filter | time | join_key";
|
||||
"为每个字段打标签:dimension | metric | filter | time | join_key" -> "标记事实表 / 维度表";
|
||||
"标记事实表 / 维度表" -> "校验 Logic Plan 的字段在 context 中全部存在";
|
||||
"校验 Logic Plan 的字段在 context 中全部存在" -> "所有必需字段都齐全?";
|
||||
"所有必需字段都齐全?" -> "输出 SQL Context" [label="yes"];
|
||||
"所有必需字段都齐全?" -> "输出 SQL Context + missing_fields" [label="no"];
|
||||
}
|
||||
```yaml
|
||||
logic_plan:
|
||||
status: PLANNED
|
||||
validation_result:
|
||||
status: VALIDATED
|
||||
field_mapping: {}
|
||||
joins: []
|
||||
```
|
||||
|
||||
## 规则(Rules)
|
||||
## 工作顺序
|
||||
|
||||
### Alias 分配
|
||||
1. 给每张表分配唯一、稳定、短小的 alias。
|
||||
2. 标记事实表、维表、码表和目标表。
|
||||
3. 把每个业务字段解析成 `alias.column`。
|
||||
4. 固定 join 谓词、join 类型和基数假设。
|
||||
5. 固定指标表达式、维度字段、时间字段、分区字段和过滤字段。
|
||||
6. 固定时间窗口 SQL 片段,尤其是 T-1 完整日边界。
|
||||
7. 固定输出列顺序和别名。
|
||||
8. 检查 logic plan 中引用的字段都能在 context 中找到。
|
||||
9. 对派生字段记录来源步骤和依赖字段。
|
||||
|
||||
- 一张表一个 alias,绝不重用
|
||||
- 事实表(承载主指标的表)优先拿到短 alias(`o`、`f`、`t1`)
|
||||
- 维度表用有意义的 alias(`c` 代表 customer,`p` 代表 product,`ch` 代表 channel)
|
||||
- alias **在此处固化**,SQL 生成器与评审器不得再发明新 alias
|
||||
## 时间窗口上下文
|
||||
|
||||
### Join Key 固化
|
||||
当存在时间过滤时,必须输出 `time_windows`。对“近 N 天按 T-1 完整日”:
|
||||
|
||||
- 对每条 `logic_plan.steps[kind=join]`,固化精确的谓词:
|
||||
- `left_alias.col1 = right_alias.col2`
|
||||
- 包含 join 类型(inner / left / right / full / semi / anti)
|
||||
- 沿用 Logic Plan 中的 cardinality 假设
|
||||
- 如果两张表有多种可能的 join key,在此处固化为所选的那一个,其他候选不再纳入 SQL 范畴
|
||||
```yaml
|
||||
time_windows:
|
||||
- name: t_minus_1_complete_days
|
||||
event_field: fl.login_time
|
||||
lower_bound_sql: date_sub(current_date(), N)
|
||||
upper_bound_sql: current_date()
|
||||
predicate_sql: fl.login_time >= date_sub(current_date(), N) AND fl.login_time < current_date()
|
||||
boundary: left_closed_right_open
|
||||
```
|
||||
|
||||
### 字段解析(Field Resolution)
|
||||
下游 SQL 必须复用 `predicate_sql`,不得临时改成 `BETWEEN`。
|
||||
|
||||
对 Logic Plan 用到的每个字段,记录:
|
||||
## 转化指标上下文
|
||||
|
||||
- `alias`(字段所在表)
|
||||
- `column`(物理列名)
|
||||
- `type`(取自 `validation_result`)
|
||||
- `source_file`(元数据文件来源 — 用于追溯)
|
||||
- `role`:取自 `dimension` | `metric` | `filter` | `time` | `join_key` | `derived` 之一
|
||||
转化率必须固定分母、分子和顺序约束:
|
||||
|
||||
一个字段可以有多个 role(例如 `customer_id` 同时是 `join_key` 和 `dimension`),全部列出。
|
||||
```yaml
|
||||
metrics:
|
||||
- name: conversion_rate
|
||||
numerator: converted_user_cnt
|
||||
denominator: new_user_cnt
|
||||
order_constraint: pay_time >= register_time
|
||||
null_policy: denominator_zero_returns_0
|
||||
exists_after_anchor_event: true
|
||||
```
|
||||
|
||||
### Fact vs Dimension 标记
|
||||
|
||||
- 承载主要事件/度量的表为 `fact`
|
||||
- 起丰富作用的表(customer、product、channel、city)为 `dimension`
|
||||
- 一条 query 只能有一个 fact。如果 Logic Plan 隐含多张事实表,作为 `pending_question` 抛出 — 那是规划问题,不是 context 问题
|
||||
|
||||
### 覆盖度校验
|
||||
|
||||
- `logic_plan.steps` 中的每一步引用的字段都必须存在于本 context
|
||||
- 每条 `field_mapping` 都必须出现在本 context
|
||||
- 每个 `group_by`、`order_by` 字段都必须有标签
|
||||
- `aggregate` 中的每个指标都必须有 `metric` 标签
|
||||
- `filter` 中的每条谓词都必须引用 `filter` 或 `time` 标签的字段
|
||||
|
||||
## 输出契约(Output Contract)
|
||||
## 输出格式
|
||||
|
||||
```yaml
|
||||
sql_context:
|
||||
status: CONTEXT_READY | NEED_USER_CONFIRMATION
|
||||
aliases:
|
||||
- alias: o
|
||||
table: loan_order
|
||||
role: fact
|
||||
source_file: metadata/loan_order.json
|
||||
- alias: c
|
||||
table: customer_info
|
||||
role: dimension
|
||||
source_file: metadata/customer_info.md
|
||||
joins:
|
||||
- left: { alias: o, column: customer_id }
|
||||
right: { alias: c, column: customer_id }
|
||||
type: left
|
||||
cardinality_assumption: 1:N
|
||||
- alias: fl
|
||||
table: user_login
|
||||
role: fact | dimension | lookup | target
|
||||
source_file: "元数据来源"
|
||||
fields:
|
||||
- alias: o
|
||||
column: customer_id
|
||||
type: string
|
||||
role: [join_key]
|
||||
source_file: metadata/loan_order.json
|
||||
- alias: o
|
||||
column: loan_amount
|
||||
type: decimal(18,2)
|
||||
role: [metric]
|
||||
source_file: metadata/loan_order.json
|
||||
- alias: o
|
||||
column: apply_time
|
||||
type: timestamp
|
||||
role: [time, filter]
|
||||
source_file: metadata/loan_order.json
|
||||
- alias: c
|
||||
column: city
|
||||
type: string
|
||||
role: [dimension]
|
||||
source_file: metadata/customer_info.md
|
||||
metrics:
|
||||
- alias: total_loan
|
||||
expr: "SUM(o.loan_amount)"
|
||||
agg: sum
|
||||
- alias: customer_cnt
|
||||
expr: "COUNT(DISTINCT o.customer_id)"
|
||||
agg: count_distinct
|
||||
dimensions: ["c.city"]
|
||||
time_grain:
|
||||
column: o.apply_time
|
||||
granularity: day
|
||||
filters:
|
||||
- "o.status = 'SUCCESS'"
|
||||
missing_fields: []
|
||||
status: <READY | NEED_USER_CONFIRMATION>
|
||||
- id: fl.user_id
|
||||
alias: fl
|
||||
table: user_login
|
||||
column: user_id
|
||||
type: bigint
|
||||
role: [join_key, metric]
|
||||
nullable: false
|
||||
source_file: "元数据来源"
|
||||
joins:
|
||||
- left: fl.user_id
|
||||
right: ur.user_id
|
||||
type: left
|
||||
cardinality: "N:1"
|
||||
source_step: S3
|
||||
time_windows: []
|
||||
metrics: []
|
||||
dimensions: []
|
||||
filters: []
|
||||
outputs: []
|
||||
write_intent:
|
||||
mode: query_only | insert_into | insert_overwrite
|
||||
target_table: "可为空"
|
||||
overwrite_confirmed: false
|
||||
unresolved: []
|
||||
```
|
||||
|
||||
## 禁止行为(Forbidden Behaviors)
|
||||
## 用户可见摘要
|
||||
|
||||
| 自我说服 | 现实 |
|
||||
|---|---|
|
||||
| "alias 让 SQL 生成器自己选" | alias 就在此处固化。下游重新起 alias 会破坏 `sql-review`。 |
|
||||
| "join key 太显然,跳过固化" | 整个 skill 的意义就在于此 — 把 join key 钉死。 |
|
||||
| "字段类型无所谓,SQL 里都是字符串" | 类型决定了聚合、cast、分区裁剪的策略。 |
|
||||
| "派生列到 SQL 阶段再发明就行" | 每个派生列都必须在 Logic Plan 里就出现。 |
|
||||
| "多张事实表?全 join 上就行" | 多事实是 Logic Plan 的问题,要暴露并停止。 |
|
||||
除了 YAML,必须输出:
|
||||
|
||||
## 完成判定(Completion Criteria)
|
||||
```markdown
|
||||
## SQL 上下文
|
||||
### 表别名
|
||||
| Alias | 表名 | 角色 |
|
||||
|---|---|---|
|
||||
|
||||
仅当**以下全部**成立时,`status: READY`:
|
||||
### 字段来源
|
||||
| 字段 | 来源 | 用途 |
|
||||
|---|---|---|
|
||||
|
||||
- Logic Plan 的每一步在本 context 中都有对应条目
|
||||
- `missing_fields` 为空
|
||||
- 每个指标都有 `agg` 和 `expr`
|
||||
- 每个 join 都有固化的 key 和 type
|
||||
- 时间粒度已设定
|
||||
### Join 关系
|
||||
| 左字段 | 右字段 | 类型 | 基数风险 |
|
||||
|---|---|---|---|
|
||||
|
||||
### 指标公式
|
||||
| 指标 | 分子 | 分母 | 空值策略 |
|
||||
|---|---|---|---|
|
||||
|
||||
### 时间窗口
|
||||
| 字段 | 下界 | 上界 | 边界 |
|
||||
|---|---|---|---|
|
||||
```
|
||||
|
||||
## 规则
|
||||
|
||||
- alias 一旦生成,下游不能重新命名。
|
||||
- context 中不存在的字段,下游视为不存在。
|
||||
- join key 只能来自元数据校验和逻辑计划。
|
||||
- 指标表达式必须能追溯到 logic plan 的 aggregate 步骤。
|
||||
- 输出列必须来自 `fields`、`metrics` 或明确派生字段。
|
||||
- 如果 SQL 生成需要新字段,回到 metadata-validator 或 logic-planner。
|
||||
|
||||
## 停止条件
|
||||
|
||||
只在 alias、字段、join、指标、时间字段、过滤字段、输出列和写入意图都完整时输出 `CONTEXT_READY`。
|
||||
|
||||
Reference in New Issue
Block a user