--- 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`。