mcpField
A
analytics-engineer

analytics-engineer

github.com/borghei数据与数据库1.0k201 次安装5 天前更新
代码仓库

关于 analytics-engineer

面向数据团队的分析工程技能,指导 dbt 转换层、维度模型、SQL 测试和语义层指标的设计与维护。还包含用于影响分析、模式比较、文档生成和质量评分的脚本。

主要功能

  1. 01规划 dbt 分层模型
  2. 02设计维度模型与事实表
  3. 03编写 SQL 测试和 YAML 文档
  4. 04定义语义层指标
  5. 05分析模型影响与模式差异
  6. 06生成文档并评估模型质量

适用场景

  1. 01搭建新的 dbt 分析项目
  2. 02为数据集市配置增量模型与测试
  3. 03追踪模型变更对下游的影响
  4. 04比较环境模式并生成模型文档

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/borghei/claude-skills --skill analytics-engineer

安装后在 Claude Code 中输入/analytics-engineer确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

REFERENCE.md
assets/
dbt_project.yml
macros.sql
schema.yml
source.yml
references/
dbt_style_guide.md
modeling_patterns.md
optimization.md
testing_guide.md
scripts/
doc_generator.py
impact_analyzer.py
quality_scorer.py
schema_diff.py

详细介绍

分析工程师

该 Agent 以资深分析工程师的身份工作,构建可扩展的 dbt 转换层、设计维度模型、编写经过测试的 SQL,并管理语义层指标定义。

先澄清

构建模型前,先确定以下输入。如果某项缺失或含糊不清,且会影响结果,请询问;如果不会影响结果,则采用合理的默认值并说明所用默认值。

  • **必需粒度 + 下游使用者:**目标模型的行粒度,以及谁会查询它(仪表板、笔记本、反向 ETL)(决定维度模型和物化方式)。
  • **源表和数据新鲜度:**现有哪些源、它们的键和加载频率(决定暂存模型和增量逻辑)。
  • **数据量 + 刷新 SLA:**表大小及必须重建的频率(决定使用视图、表还是增量物化)。

针对当前请求,询问最重要的两到三个输入,而不是整份清单。如果用户希望立即得到结果,就直接生成结果,并在开头说明假设。

工作流

  1. 了解数据需求 -- 确定业务问题、所需粒度和下游使用者(仪表板、笔记本、反向 ETL)。确认源表存在并检查数据新鲜度。
  2. 设计维度模型 -- 选择星型或雪花型架构。按照正确的粒度,将源实体映射到维度表和事实表。记录粒度、主键和外键。
  3. 构建暂存模型 -- 每个源表对应一个 stg_ 模型。重命名列、转换类型、过滤软删除记录,并添加元数据列。验证:dbt build --select stg_*。
  4. 构建中间模型 -- 在 int_ 模型(例如 int_orders_enriched)中封装可复用的业务逻辑。确保每个 CTE 只负责单一用途。
  5. 构建数据集市模型 -- 创建供使用者查询的 dim_ 和 fct_ 模型。配置物化方式(暂存层使用视图,大型事实表使用增量物化,小型数据集市使用表)。
  6. 添加测试和文档 -- 每个主键都添加 unique + not_null 测试。外键添加 relationships 测试。枚举添加 accepted_values 测试。使用 YAML 编写模型说明。
  7. 定义语义层指标 -- 注册指标(sum、average、count_distinct),并设置时间粒度和维度切片,让 BI 使用者获得唯一可信数据源。
  8. 端到端验证 -- 运行 dbt build,确认测试通过率为 100%,对比源数据行数,并验证仪表板中的数字一致。

dbt 项目结构

analytics/
  dbt_project.yml
  models/
    staging/          # stg_<source>__<table>.sql  (one per source table)
    intermediate/     # int_<entity>_<verb>.sql     (reusable logic)
    marts/
      core/           # dim_*.sql, fct_*.sql        (consumption-ready)
      marketing/
      finance/
  macros/             # Reusable Jinja helpers
  tests/              # Custom generic + singular tests
  seeds/              # Static CSV lookups
  snapshots/          # SCD Type 2 captures

具体示例:客户维度

暂存模型(models/staging/crm/stg_crm__customers.sql):

WITH source AS (
    SELECT * FROM {{ source('crm', 'customers') }}
),

renamed AS (
    SELECT
        id                          AS customer_id,
        TRIM(LOWER(name))           AS customer_name,
        TRIM(LOWER(email))          AS email,
        created_at::timestamp       AS created_at,
        updated_at::timestamp       AS updated_at,
        is_active::boolean          AS is_active,
        _fivetran_synced            AS _loaded_at
    FROM source
    WHERE _fivetran_deleted = false
)

SELECT * FROM renamed

数据集市模型(models/marts/core/dim_customer.sql):

WITH customers AS (
    SELECT * FROM {{ ref('stg_crm__customers') }}
),

customer_orders AS (
    SELECT
        customer_id,
        MIN(order_date)  AS first_order_date,
        MAX(order_date)  AS most_recent_order_date,
        COUNT(*)         AS lifetime_orders,
        SUM(order_amount) AS lifetime_value
    FROM {{ ref('stg_orders__orders') }}
    GROUP BY customer_id
),

final AS (
    SELECT
        c.customer_id,
        c.customer_name,
        c.email,
        c.created_at,
        co.first_order_date,
        co.most_recent_order_date,
        co.lifetime_orders,
        co.lifetime_value,
        CASE
            WHEN co.lifetime_value >= 10000 THEN 'platinum'
            WHEN co.lifetime_value >= 5000  THEN 'gold'
            WHEN co.lifetime_value >= 1000  THEN 'silver'
            ELSE 'bronze'
        END AS customer_tier
    FROM customers c
    LEFT JOIN customer_orders co
        ON c.customer_id = co.customer_id
)

SELECT * FROM final

测试配置(models/marts/core/_core__models.yml):

version: 2
models:
  - name: dim_customer
    description: Customer dimension with lifetime order metrics and tier classification.
    columns:
      - name: customer_id
        tests: [unique, not_null]
      - name: email
        tests: [unique, not_null]
      - name: customer_tier
        tests:
          - accepted_values:
              values: ['platinum', 'gold', 'silver', 'bronze']
      - name: lifetime_value
        tests:
          - dbt_utils.expression_is_true:
              expression: ">= 0"

增量事实表模式

-- models/marts/core/fct_orders.sql
{{
    config(
        materialized='incremental',
        unique_key='order_id',
        partition_by={'field': 'order_date', 'data_type': 'date'},
        cluster_by=['customer_id', 'product_id']
    )
}}

WITH orders AS (
    SELECT * FROM {{ ref('stg_orders__orders') }}
    {% if is_incremental() %}
    WHERE order_date >= (SELECT MAX(order_date) FROM {{ this }})
    {% endif %}
),

order_items AS (
    SELECT * FROM {{ ref('stg_orders__order_items') }}
),

final AS (
    SELECT
        o.order_id,
        o.order_date,
        o.customer_id,
        oi.product_id,
        o.store_id,
        oi.quantity,
        oi.unit_price,
        oi.quantity * oi.unit_price AS line_total,
        o.discount_amount,
        o.tax_amount,
        o.total_amount
    FROM orders o
    INNER JOIN order_items oi ON o.order_id = oi.order_id
)

SELECT * FROM final

物化策略

层物化方式原因
暂存层视图轻量封装;无需存储成本
中间层临时 / 视图业务逻辑;可供多个模型引用
数据集市(小型)表提升 BI 工具的查询性能
数据集市(大型)增量高效追加大型事实表数据

语义层指标定义

# models/marts/core/_core__metrics.yml
metrics:
  - name: revenue
    label: Total Revenue
    model: ref('fct_orders')
    calculation_method: sum
    expression: total_amount
    timestamp: order_date
    time_grains: [day, week, month, quarter, year]
    dimensions: [customer_tier, product_category, store_region]
    filters:
      - field: is_cancelled
        operator: '='
        value: 'false'

  - name: average_order_value
    label: Average Order Value
    model: ref('fct_orders')
    calculation_method: average
    expression: total_amount
    timestamp: order_date
    time_grains: [day, week, month]

实用宏

-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
    ({{ column_name }} / 100.0)::decimal(18,2)
{% endmacro %}

-- macros/get_incremental_filter.sql
{% macro get_incremental_filter(column_name, lookback_days=3) %}
    {% if is_incremental() %}
        WHERE {{ column_name }} >= (
            SELECT DATEADD(day, -{{ lookback_days }}, MAX({{ column_name }}))
            FROM {{ this }}
        )
    {% endif %}
{% endmacro %}

CI/CD:拉取请求的精简 CI

# Only run modified models and their downstream dependents
dbt run  --select state:modified+ --defer --state ./target-base
dbt test --select state:modified+ --defer --state ./target-base

完整的 CI/CD 流水线配置请参阅 REFERENCE.md。

参考资料

  • REFERENCE.md -- 扩展模式:源配置、自定义测试、CI/CD 工作流、数据暴露配置、文档模板
  • references/modeling_patterns.md -- 数据建模最佳实践
  • references/dbt_style_guide.md -- SQL 和 dbt 约定
  • references/testing_guide.md -- 测试策略
  • references/optimization.md -- 性能调优

脚本

python scripts/impact_analyzer.py --model dim_customer
python scripts/schema_diff.py --source prod --target dev
python scripts/doc_generator.py --format markdown
python scripts/quality_scorer.py --model fct_orders

工具参考

工具用途关键标志
impact_analyzer.py通过对 manifest DAG 进行 BFS,追踪 dbt 模型的下游影响--model <name>、--manifest <path>、--json
schema_diff.py比较两个 dbt catalog.json 文件,以检测列的新增、删除和类型变更--source <path>、--target <path>、--json
doc_generator.py为 dbt 模型生成 Markdown 文档(列字典、依赖关系、测试)--model <name>、--manifest <path>、--catalog <path>
quality_scorer.py根据文档、测试和对分层约定的遵循情况,为 dbt 模型评定 0-100 分--model <name>、--manifest <path>、--json

故障排查

问题可能原因解决方法
dbt build 因“relation does not exist”而失败上游模型尚未运行,或物化方式已更改运行 dbt build --select +<model>,构建完整的上游链路
增量模型产生重复数据unique_key 与实际粒度不匹配确认 unique_key 配置与主键列匹配;使用 --full-refresh 执行完整刷新
部署后 not_null 测试失败源数据在原本干净的列中引入了意外的 NULL 值添加暂存层 COALESCE,或在调查上游问题时将测试严重级别调整为 warn
schema_diff.py 检测到架构漂移上游源更改了列类型或删除了列与数据工程团队协调;更新暂存模型的类型转换并重新生成文档
语义层指标值与仪表板不一致仪表板在语义层之外应用了自己的筛选或计算将所有计算逻辑移至语义层;审查仪表板级别的计算字段
大型增量模型的 dbt run 速度缓慢回溯窗口过宽,或未启用分区裁剪缩小增量筛选范围,确认 partition_by 配置,并检查数据仓库查询计划
尽管覆盖率良好,quality_scorer.py 仍报告低分暂存模型包含 JOIN 或 GROUP BY 操作,触发了分层违规扣分将聚合逻辑重构到中间层或数据集市模型中;让暂存模型保持精简的封装形式

成功标准

  • 所有 dbt 模型在合并到生产环境前均通过 dbt build,测试通过率达到 100%。
  • 每个模型都有 YAML 描述,且每个主键至少有一项测试(unique + not_null)。
  • 对于最多包含 100M 行的表,增量模型处理新数据的时间少于 5 分钟。
  • 每次发布前均检测并审查生产环境与开发环境之间的架构漂移。
  • 每个数据集市模型的 quality_scorer.py 得分均达到 >= 80/100。
  • 转换运行完成后,下游仪表板在 SLA 范围内刷新(加载时间 < 5 s)。
  • 语义层指标是唯一事实来源——BI 工具中不存在临时指标计算。

范围与限制

范围内: dbt 项目设计、维度建模(Kimball 方法)、SQL 转换逻辑、数据测试、语义层指标定义、dbt 的 CI/CD,以及数据仓库查询优化。

范围外: 原始数据摄取与提取(Fivetran 或 Airbyte 等 ELT/ETL 编排工具)、数据基础设施配置、语义层集成之外的 BI 工具配置,以及实时流式处理管道。

限制: Python 工具基于 dbt manifest/catalog JSON 工件运行,不会直接查询数据仓库。quality_scorer.py 中的评分启发式规则采用基于规则的扣分方式,可能无法涵盖所有项目约定。所有脚本仅使用 Python 标准库——无需外部依赖。

集成点

  • 数据工程师(engineering/senior-data-engineer):协调源表契约、数据摄取 SLA 和架构变更通知。
  • 商业智能(data-analytics/business-intelligence):使用数据集市模型和语义层指标;仪表板规范会引用模型输出。
  • 数据分析师(data-analytics/data-analyst):针对数据集市模型编写临时查询;将数据质量问题反馈给分析工程师。
  • MLOps 工程师(data-analytics/ml-ops-engineer):特征工程流水线可能依赖中间模型或数据集市模型作为上游输入。
  • CI/CD 工作流(templates/):精简的 CI 模式(state:modified+)可集成到 GitHub Actions 或类似运行器中,以自动验证 PR。

评论

支持 Markdown