关于 analytics-engineer
面向数据团队的分析工程技能,指导 dbt 转换层、维度模型、SQL 测试和语义层指标的设计与维护。还包含用于影响分析、模式比较、文档生成和质量评分的脚本。
主要功能
- 01规划 dbt 分层模型
- 02设计维度模型与事实表
- 03编写 SQL 测试和 YAML 文档
- 04定义语义层指标
- 05分析模型影响与模式差异
- 06生成文档并评估模型质量
适用场景
- 01搭建新的 dbt 分析项目
- 02为数据集市配置增量模型与测试
- 03追踪模型变更对下游的影响
- 04比较环境模式并生成模型文档
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ borghei/ claude-skills --skill analytics-engineer安装后在 Claude Code 中输入/analytics-engineer确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
REFERENCE.mdassets/dbt_project.ymlmacros.sqlschema.ymlsource.ymlreferences/dbt_style_guide.mdmodeling_patterns.mdoptimization.mdtesting_guide.mdscripts/doc_generator.pyimpact_analyzer.pyquality_scorer.pyschema_diff.py详细介绍
分析工程师
该 Agent 以资深分析工程师的身份工作,构建可扩展的 dbt 转换层、设计维度模型、编写经过测试的 SQL,并管理语义层指标定义。
先澄清
构建模型前,先确定以下输入。如果某项缺失或含糊不清,且会影响结果,请询问;如果不会影响结果,则采用合理的默认值并说明所用默认值。
- **必需粒度 + 下游使用者:**目标模型的行粒度,以及谁会查询它(仪表板、笔记本、反向 ETL)(决定维度模型和物化方式)。
- **源表和数据新鲜度:**现有哪些源、它们的键和加载频率(决定暂存模型和增量逻辑)。
- **数据量 + 刷新 SLA:**表大小及必须重建的频率(决定使用视图、表还是增量物化)。
针对当前请求,询问最重要的两到三个输入,而不是整份清单。如果用户希望立即得到结果,就直接生成结果,并在开头说明假设。
工作流
- 了解数据需求 -- 确定业务问题、所需粒度和下游使用者(仪表板、笔记本、反向 ETL)。确认源表存在并检查数据新鲜度。
- 设计维度模型 -- 选择星型或雪花型架构。按照正确的粒度,将源实体映射到维度表和事实表。记录粒度、主键和外键。
- 构建暂存模型 -- 每个源表对应一个
stg_模型。重命名列、转换类型、过滤软删除记录,并添加元数据列。验证:dbt build --select stg_*。 - 构建中间模型 -- 在
int_模型(例如int_orders_enriched)中封装可复用的业务逻辑。确保每个 CTE 只负责单一用途。 - 构建数据集市模型 -- 创建供使用者查询的
dim_和fct_模型。配置物化方式(暂存层使用视图,大型事实表使用增量物化,小型数据集市使用表)。 - 添加测试和文档 -- 每个主键都添加
unique+not_null测试。外键添加relationships测试。枚举添加accepted_values测试。使用 YAML 编写模型说明。 - 定义语义层指标 -- 注册指标(sum、average、count_distinct),并设置时间粒度和维度切片,让 BI 使用者获得唯一可信数据源。
- 端到端验证 -- 运行
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。