---
name: logic-planner
description: 当需求和元数据都已经确认，需要把业务逻辑拆成 SQL 生成前的确定性执行步骤时使用。它只规划 source、filter、join、dedupe、transform、window、aggregate、output，不写 SQL，不调用 MCP。
---

# Logic Planner（逻辑规划）

## 目标

把已确认的 DRD 和元数据拆成可执行计算步骤，让后续 SQL 只是翻译计划，而不是重新猜业务逻辑。

## 输入

```yaml
requirements_output:
  status: READY_FOR_METADATA
validation_result:
  status: VALIDATED
  field_mapping: {}
  joins: []
```

如果元数据没有 `VALIDATED`，停止并回到 `metadata-validator`。

## 步骤类型

只允许使用这些 `kind`：

- `source`：读取哪张表、哪些字段。
- `filter`：时间、分区、状态、业务条件过滤。
- `join`：多表关联。
- `dedupe`：去重，通常在 join 或 aggregate 前。
- `transform`：派生字段、分类、类型转换、标志位。
- `window`：首笔、末笔、排名、累计、lag/lead。
- `aggregate`：分组和指标计算。
- `output`：最终列、排序、limit 或写入意图。

## 工作顺序

1. 定义输出粒度：一行代表什么。
2. 选事实表：承载核心事件或分母人群的表。
3. 列出 source：每张表只取必要字段。
4. 尽早放 filter：尤其是时间、分区和状态条件。
5. 固定时间窗口边界：下界包含、上界排除。
6. 规划 join：顺序、key、类型、基数假设。
7. 判断是否需要 dedupe/window：首登、首次注册、首笔成功事件等。
8. 规划 transform：标志位、事件顺序、空值处理。
9. 规划 aggregate：group by、指标公式、别名。
10. 规划 output：输出字段、排序、写入模式。

## 时间窗口硬规则

当需求为“近 N 天按 T-1 完整日”时，必须规划为：

```yaml
time_window:
  lower_bound_sql: date_sub(current_date(), N)
  upper_bound_sql: current_date()
  boundary: left_closed_right_open
  predicate_sql: event_time >= date_sub(current_date(), N) AND event_time < current_date()
```

禁止规划为 `BETWEEN ... AND date_add(current_date(), -1)`，尤其是 timestamp 字段。

## 转化/漏斗硬规则

转化类需求必须维护事件顺序。对“注册后成功付费”，推荐计划：

1. `first_login`：取窗口内每个用户的首次登录，形成分母。
2. `registered`：取用户注册事件，必要时去重到一个注册时间。
3. `qualified_payment`：在关联注册事件后筛选 `pay_status = 'SUCCESS' AND pay_time >= register_time`。
4. `final_agg`：按维度计算分母、分子和转化率。

不要先对全量 `pay_order` 按用户取 `MIN(pay_time)` 再与 `register_time` 比较；这可能漏掉“历史早付费但注册后也付费”的用户。

## 必须显式写出的风险

- 1:N 或 N:N join 是否会放大事实行。
- 去重发生在 join 前还是 join 后。
- 指标是否需要 `count_distinct` 而不是 `count`。
- 时间过滤是否能命中分区字段。
- left join 后维度缺失如何处理。
- 转化事件是否严格满足前后顺序。
- 分母为 0 时比率如何处理。

## 输出格式

```yaml
logic_plan:
  status: PLANNED | NEED_USER_CONFIRMATION
  grain: "结果一行代表什么"
  fact_table: "事实表"
  dimension_tables: []
  time_window:
    lower_bound_sql: "date_sub(current_date(), N)"
    upper_bound_sql: "current_date()"
    predicate_sql: "event_time >= date_sub(current_date(), N) AND event_time < current_date()"
    boundary: left_closed_right_open
  steps:
    - id: S1
      kind: source
      name: first_login
      table: "表名"
      columns: ["字段"]
      reason: "为什么需要"
    - id: S2
      kind: filter
      target: "表名或中间结果"
      predicates: ["业务过滤条件"]
      partition_pruning: true
    - id: S3
      kind: join
      left: "左表或CTE"
      right: "右表或CTE"
      keys: ["左字段 = 右字段"]
      type: left
      cardinality: "1:1 | 1:N | N:1 | N:N | unknown"
      row_growth_risk: low | medium | high
    - id: S4
      kind: aggregate
      group_by: ["维度字段"]
      metrics:
        - name: "指标名"
          agg: count_distinct | sum | avg | min | max | ratio
          expression: "业务表达式"
          alias: "指标别名"
    - id: S5
      kind: output
      columns: ["输出列"]
      order_by: []
      write_intent: query_only | insert_into | insert_overwrite
  assumptions: []
  risks: []
  pending_questions: []
```

## 用户可见摘要

除了 YAML，必须输出这张表：

```markdown
## 逻辑计划
| 步骤 | 中间结果 | 类型 | 说明 | 关键条件 |
|---|---|---|---|---|
```

## 停止条件

如果粒度、指标公式、join 基数、去重策略、空值策略、时间归属或写入模式不明确，输出 `NEED_USER_CONFIRMATION`。
