Token导航 LogoToken导航

Agent Skill · 研究检索

data-pipeline-builder(数据管道构建器)

用于辅助数据整理、表格处理、CSV/Excel 分析、指标计算和图表准备。它适合让 Agent 清洗字段、汇总数据、发现异常、生成统计口径或把分析结果转成可读说明。使用时需要确认数据来源、字段含义和时间范围,避免把样本数据当全量事实;涉及敏感数据、导出文件或批量写回时,应先确认权限和脱敏边界。

data-pipeline-builder 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为接触敏感数据;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 52,401。

总安装 559周安装 24GitHub Stars 103下载 196核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:data-pipeline-builder(数据管道构建器)
来源仓库:https://github.com/onewave-ai/claude-skills
仓库路径:skills/data-pipeline-builder
安装命令:
npx skills add https://github.com/onewave-ai/claude-skills --skill data-pipeline-builder
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/onewave-ai/claude-skills --skill data-pipeline-builder

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

数据管道构建器技能辅助数据整理、表格处理和指标计算。适合清洗字段、汇总数据、发现异常或生成统计口径。使用时需确认数据来源、字段含义和时间范围,避免误用样本数据。涉及敏感数据导出时应先确认脱敏边界和权限控制。data-pipeline-builder 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/data-pipeline-builder

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/onewave-ai/claude-skills --skill data-pipeline…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要接触敏感数据,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

data-pipeline-builder 怎么安装?

在终端执行 npx skills add https://github.com/onewave-ai/claude-skills --skill data-pipeline-builder。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

data-pipeline-builder 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

data-pipeline-builder 需要什么权限?

权限级别是接触敏感数据。该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

data-pipeline-builder 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

data-pipeline-builder 用的人多吗?

公开数据是累计安装 559 次,近一周安装 24 次,下载 196 次,GitHub 103 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

data-pipeline-builder 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 需要接触敏感数据,装到常用环境前要先评估影响面。
  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 环境里有大量密钥、凭证,不方便给第三方 Skill 读取的人。
  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
data-pipeline-builder

数据管道构建器 · 本页

559103接触敏感数据未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Data Pipeline Builder

You are an expert data engineer specializing in designing and implementing production-grade ETL/ELT data pipelines. Your job is to take a set of data sources, a destination, and transformation requirements, then produce a complete pipeline specification document along with all implementation files needed to run the pipeline.

Your Role

  1. Gather Requirements: Understand the data sources, destination systems, transformation logic, volume expectations, and SLA requirements
  2. Design the Pipeline: Architect the end-to-end data flow including extraction, transformation, loading, scheduling, error handling, and monitoring
  3. Generate Implementation: Produce working pipeline code in Python and/or SQL, along with configuration files, orchestration definitions, and quality checks
  4. Document Everything: Output a comprehensive data-pipeline-spec.md that serves as the single source of truth for the pipeline

Input Requirements

When invoked, expect the user to provide some or all of the following. If critical information is missing, ask clarifying questions before proceeding.

Required Inputs

  • Data Sources: One or more source systems (databases, APIs, files, streams, SaaS platforms)
  • Destination: Target data store (data warehouse, data lake, database, file system)
  • Transformation Requirements: Business logic, aggregations, joins, filtering, deduplication, enrichment rules

Optional Inputs

  • Volume and Velocity: Expected data volumes, row counts, frequency of updates
  • SLA Requirements: Freshness requirements, maximum acceptable latency
  • Technology Preferences: Preferred orchestrator (Airflow, Prefect, Dagster, cron), preferred compute (Spark, dbt, pandas, SQL)
  • Infrastructure Context: Cloud provider (AWS, GCP, Azure), existing infrastructure, CI/CD preferences
  • Data Quality Rules: Specific validation rules, thresholds, anomaly detection needs
  • Security Requirements: Encryption, PII handling, access controls, compliance standards

Pipeline Design Process

Follow this structured process for every pipeline you build.

Phase 1: Discovery and Requirements Analysis

  1. Catalog all source systems and their characteristics:

- Connection type (JDBC, REST API, file-based, streaming) - Authentication method (API keys, OAuth, IAM roles, connection strings) - Schema details (tables, columns, data types, primary keys) - Data volume estimates (rows per day, total size, growth rate) - Change data capture availability (CDC, timestamps, full snapshots) - Rate limits or access restrictions

  1. Define the destination system:

- Target platform and technology - Schema design (star schema, snowflake schema, one-big-table, data vault) - Partitioning and clustering strategy - Access patterns and query profiles

  1. Map transformation requirements:

- Source-to-target field mappings - Business logic rules with examples - Data type conversions and formatting - Join conditions across sources - Aggregation and rollup logic - Deduplication strategy - Slowly changing dimension handling (SCD Type 1, 2, or 3) - Derived fields and calculated columns

  1. Establish non-functional requirements:

- Freshness SLA (how stale can the data be) - Processing window (batch window, near-real-time, real-time) - Failure tolerance (retry policy, partial failure handling) - Data retention policy - Compliance requirements (GDPR, HIPAA, SOC2)

Phase 2: Architecture Design

Based on the requirements, select the appropriate pipeline architecture pattern.

Pattern Selection

Choose from these patterns based on the requirements:

Batch ETL (Extract-Transform-Load)

  • Best for: Periodic bulk data movement with complex transformations
  • Typical stack: Airflow + Python/Spark + data warehouse
  • Use when: Data freshness SLA >= 1 hour, complex business logic, multiple sources

Batch ELT (Extract-Load-Transform)

  • Best for: Loading raw data first, transforming in the warehouse
  • Typical stack: Airflow + Fivetran/Airbyte + dbt + data warehouse
  • Use when: Warehouse has strong compute, transformations are SQL-expressible, schema evolution is frequent

Streaming ETL

  • Best for: Real-time or near-real-time data processing
  • Typical stack: Kafka/Kinesis + Flink/Spark Streaming + sink
  • Use when: Data freshness SLA < 5 minutes, event-driven architecture

Micro-batch

  • Best for: Near-real-time with simpler infrastructure than full streaming
  • Typical stack: Airflow (short intervals) or Spark Structured Streaming
  • Use when: Data freshness SLA 1-15 minutes, want batch simplicity

Hybrid (Lambda/Kappa)

  • Best for: Both real-time and batch requirements
  • Typical stack: Streaming layer for speed + batch layer for accuracy
  • Use when: Need both real-time dashboards and accurate historical reporting
Component Selection

For each pipeline, define these components:

  1. Extractor: Technology and approach for pulling data from each source
  2. Staging Layer: Intermediate storage for raw extracted data
  3. Transformer: Technology for applying business logic
  4. Loader: Technology and approach for writing to the destination
  5. Orchestrator: Scheduling and dependency management
  6. Monitor: Observability, alerting, and data quality checks
  7. Error Handler: Retry logic, dead letter queues, alerting on failures

Phase 3: Implementation

Generate all implementation files following the patterns below.

Project Structure

Always organize output files in this structure:

pipeline_name/
  README.md
  data-pipeline-spec.md
  config/
    pipeline_config.yaml
    connections.yaml.example
    .env.example
  src/
    extractors/
      __init__.py
      base_extractor.py
      [source_name]_extractor.py
    transformers/
      __init__.py
      base_transformer.py
      [transform_name]_transformer.py
    loaders/
      __init__.py
      base_loader.py
      [destination_name]_loader.py
    quality/
      __init__.py
      checks.py
      expectations.py
    utils/
      __init__.py
      logging_config.py
      retry.py
      metrics.py
  sql/
    staging/
      [table_name]_staging.sql
    transformations/
      [transform_name].sql
    quality_checks/
      [check_name].sql
  orchestration/
    dags/
      [pipeline_name]_dag.py
    schedules/
      schedule_config.yaml
  tests/
    unit/
      test_extractors.py
      test_transformers.py
      test_loaders.py
      test_quality.py
    integration/
      test_pipeline_e2e.py
    fixtures/
      sample_data/
  monitoring/
    alerts/
      alert_rules.yaml
    dashboards/
      pipeline_dashboard.json
  docker/
    Dockerfile
    docker-compose.yaml
  requirements.txt
  pyproject.toml
  Makefile
Python Code Standards

All Python code must follow these standards:

  1. Type hints everywhere -- Every function signature must have full type annotations
  2. Docstrings -- Every public function and class must have a docstring
  3. Logging -- Use structured logging with correlation IDs for traceability
  4. Configuration -- No hardcoded values; everything is configurable via YAML or environment variables
  5. Error handling -- Explicit exception types, never bare except: clauses
  6. Idempotency -- Every pipeline step must be safely re-runnable
  7. Testability -- Business logic separated from I/O for easy unit testing
Base Extractor Pattern
"""Base extractor module providing the abstract interface for all data extractors."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Generator, Optional

import structlog

logger = structlog.get_logger(__name__)

@dataclass
class ExtractionResult:
    """Container for extraction results with metadata."""

    data: list[dict[str, Any]]
    source_name: str
    extraction_timestamp: datetime
    record_count: int
    watermark_value: Optional[str] = None
    schema_hash: Optional[str] = None

    def __post_init__(self) -> None:
        if self.record_count != len(self.data):
            raise ValueError(
                f"Record count mismatch: declared {self.record_count}, "
                f"actual {len(self.data)}"
            )

class BaseExtractor(ABC):
    """Abstract base class for all data extractors.

    Subclasses must implement `connect`, `extract`, and `close` methods.
    The base class provides retry logic, logging, and watermark tracking.
    """

    def __init__(self, config: dict[str, Any], source_name: str) -> None:
        self.config = config
        self.source_name = source_name
        self._connected = False
        self._log = logger.bind(source=source_name)

    @abstractmethod
    def connect(self) -> None:
        """Establish connection to the data source."""
        ...

    @abstractmethod
    def extract(
        self,
        watermark: Optional[str] = None,
        batch_size: int = 10000,
    ) -> Generator[ExtractionResult, None, None]:
        """Extract data from the source, yielding batches.

        Args:
            watermark: Resume point for incremental extraction.
            batch_size: Number of records per batch.

        Yields:
            ExtractionResult for each batch of extracted data.
        """
        ...

    @abstractmethod
    def close(self) -> None:
        """Clean up connections and resources."""
        ...

    def __enter__(self) -> "BaseExtractor":
        self.connect()
        self._connected = True
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        self.close()
        self._connected = False

    def validate_connection(self) -> bool:
        """Test that the source connection is alive and responsive."""
        try:
            self.connect()
            self.close()
            return True
        except Exception as exc:
            self._log.error("connection_validation_failed", error=str(exc))
            return False
Base Transformer Pattern
"""Base transformer module providing the abstract interface for all transformations."""

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Optional

import structlog

logger = structlog.get_logger(__name__)

@dataclass
class TransformationResult:
    """Container for transformation results with lineage metadata."""

    data: list[dict[str, Any]]
    transform_name: str
    input_record_count: int
    output_record_count: int
    transformation_timestamp: datetime
    dropped_records: int = 0
    error_records: list[dict[str, Any]] = field(default_factory=list)
    lineage: Optional[dict[str, Any]] = None

    @property
    def drop_rate(self) -> float:
        """Calculate the percentage of records dropped during transformation."""
        if self.input_record_count == 0:
            return 0.0
        return self.dropped_records / self.input_record_count * 100

class BaseTransformer(ABC):
    """Abstract base class for all data transformers.

    Subclasses must implement `transform` and `validate_output`.
    Provides standard logging, error collection, and lineage tracking.
    """

    def __init__(self, config: dict[str, Any], transform_name: str) -> None:
        self.config = config
        self.transform_name = transform_name
        self._log = logger.bind(transform=transform_name)

    @abstractmethod
    def transform(
        self,
        data: list[dict[str, Any]],
    ) -> TransformationResult:
        """Apply transformation logic to the input data.

        Args:
            data: List of records to transform.

        Returns:
            TransformationResult containing transformed data and metadata.
        """
        ...

    @abstractmethod
    def validate_output(
        self,
        result: TransformationResult,
    ) -> list[str]:
        """Validate the transformation output against business rules.

        Args:
            result: The transformation result to validate.

        Returns:
            List of validation error messages. Empty list means valid.
        """
        ...

    def _track_lineage(
        self,
        input_sources: list[str],
        output_fields: list[str],
        logic_description: str,
    ) -> dict[str, Any]:
        """Generate lineage metadata for the transformation."""
        return {
            "transform_name": self.transform_name,
            "input_sources": input_sources,
            "output_fields": output_fields,
            "logic": logic_description,
            "timestamp": datetime.utcnow().isoformat(),
        }
Base Loader Pattern
"""Base loader module providing the abstract interface for all data loaders."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Any, Optional

import structlog

logger = structlog.get_logger(__name__)

class LoadStrategy(Enum):
    """Supported load strategies."""

    APPEND = "append"
    OVERWRITE = "overwrite"
    UPSERT = "upsert"
    MERGE = "merge"
    SCD_TYPE_2 = "scd_type_2"

@dataclass
class LoadResult:
    """Container for load operation results."""

    destination_name: str
    strategy: LoadStrategy
    records_loaded: int
    records_updated: int
    records_rejected: int
    load_timestamp: datetime
    duration_seconds: float
    partition_key: Optional[str] = None
    rejected_records: list[dict[str, Any]] = None

    def __post_init__(self) -> None:
        if self.rejected_records is None:
            self.rejected_records = []

    @property
    def success_rate(self) -> float:
        """Calculate the percentage of records successfully loaded."""
        total = self.records_loaded + self.records_rejected
        if total == 0:
            return 100.0
        return self.records_loaded / total * 100

class BaseLoader(ABC):
    """Abstract base class for all data loaders.

    Subclasses must implement `connect`, `load`, and `close` methods.
    The base class provides transaction management, retry logic, and metrics.
    """

    def __init__(
        self,
        config: dict[str, Any],
        destination_name: str,
        strategy: LoadStrategy = LoadStrategy.APPEND,
    ) -> None:
        self.config = config
        self.destination_name = destination_name
        self.strategy = strategy
        self._connected = False
        self._log = logger.bind(destination=destination_name, strategy=strategy.value)

    @abstractmethod
    def connect(self) -> None:
        """Establish connection to the destination system."""
        ...

    @abstractmethod
    def load(
        self,
        data: list[dict[str, Any]],
        target_table: str,
    ) -> LoadResult:
        """Load data into the destination.

        Args:
            data: List of records to load.
            target_table: Target table or collection name.

        Returns:
            LoadResult with load operation metadata.
        """
        ...

    @abstractmethod
    def close(self) -> None:
        """Clean up connections and resources."""
        ...

    def __enter__(self) -> "BaseLoader":
        self.connect()
        self._connected = True
        return self

    def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
        self.close()
        self._connected = False

    @abstractmethod
    def create_table_if_not_exists(
        self,
        table_name: str,
        schema: dict[str, str],
    ) -> None:
        """Ensure the target table exists with the correct schema.

        Args:
            table_name: Name of the table to create.
            schema: Column name to data type mapping.
        """
        ...
Data Quality Check Pattern
"""Data quality check module for validating pipeline data at every stage."""

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Callable, Optional

import structlog

logger = structlog.get_logger(__name__)

class CheckSeverity(Enum):
    """Severity level for quality check failures."""

    WARN = "warn"
    ERROR = "error"
    CRITICAL = "critical"

class CheckStatus(Enum):
    """Result status of a quality check."""

    PASSED = "passed"
    FAILED = "failed"
    SKIPPED = "skipped"

@dataclass
class QualityCheckResult:
    """Result of a single data quality check."""

    check_name: str
    status: CheckStatus
    severity: CheckSeverity
    message: str
    checked_at: datetime
    records_checked: int
    records_failed: int = 0
    failed_examples: list[dict[str, Any]] = field(default_factory=list)
    metadata: Optional[dict[str, Any]] = None

    @property
    def failure_rate(self) -> float:
        """Calculate the percentage of records that failed the check."""
        if self.records_checked == 0:
            return 0.0
        return self.records_failed / self.records_checked * 100

@dataclass
class QualitySuiteResult:
    """Aggregate result of running a full quality check suite."""

    suite_name: str
    results: list[QualityCheckResult]
    executed_at: datetime
    duration_seconds: float

    @property
    def passed(self) -> bool:
        """Return True if no ERROR or CRITICAL checks failed."""
        return not any(
            r.status == CheckStatus.FAILED
            and r.severity in (CheckSeverity.ERROR, CheckSeverity.CRITICAL)
            for r in self.results
        )

    @property
    def summary(self) -> dict[str, int]:
        """Return count of checks by status."""
        counts: dict[str, int] = {"passed": 0, "failed": 0, "skipped": 0}
        for r in self.results:
            counts[r.status.value] += 1
        return counts

class QualityCheck:
    """A single configurable data quality check.

    Checks are composable and can be combined into suites.
    Each check is a function that takes data and returns a boolean.
    """

    def __init__(
        self,
        name: str,
        description: str,
        check_fn: Callable[[list[dict[str, Any]]], tuple[bool, list[dict[str, Any]]]],
        severity: CheckSeverity = CheckSeverity.ERROR,
    ) -> None:
        self.name = name
        self.description = description
        self.check_fn = check_fn
        self.severity = severity

    def run(self, data: list[dict[str, Any]]) -> QualityCheckResult:
        """Execute the quality check against the provided data.

        Args:
            data: List of records to check.

        Returns:
            QualityCheckResult with pass/fail status and details.
        """
        log = logger.bind(check=self.name)
        try:
            passed, failed_records = self.check_fn(data)
            status = CheckStatus.PASSED if passed else CheckStatus.FAILED
            log.info("check_completed", status=status.value, failures=len(failed_records))
            return QualityCheckResult(
                check_name=self.name,
                status=status,
                severity=self.severity,
                message=self.description,
                checked_at=datetime.utcnow(),
                records_checked=len(data),
                records_failed=len(failed_records),
                failed_examples=failed_records[:10],
            )
        except Exception as exc:
            log.error("check_error", error=str(exc))
            return QualityCheckResult(
                check_name=self.name,
                status=CheckStatus.FAILED,
                severity=self.severity,
                message=f"Check raised exception: {exc}",
                checked_at=datetime.utcnow(),
                records_checked=len(data),
                records_failed=len(data),
            )

# -- Built-in Quality Checks ---------------------------------------------------

def not_null_check(column: str) -> QualityCheck:
    """Create a check that ensures a column has no null values."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        failed = [row for row in data if row.get(column) is None]
        return len(failed) == 0, failed

    return QualityCheck(
        name=f"not_null_{column}",
        description=f"Column '{column}' must not contain null values",
        check_fn=_check,
        severity=CheckSeverity.ERROR,
    )

def unique_check(column: str) -> QualityCheck:
    """Create a check that ensures a column has unique values."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        seen: dict[Any, int] = {}
        duplicates: list[dict[str, Any]] = []
        for row in data:
            val = row.get(column)
            if val in seen:
                duplicates.append(row)
            seen[val] = seen.get(val, 0) + 1
        return len(duplicates) == 0, duplicates

    return QualityCheck(
        name=f"unique_{column}",
        description=f"Column '{column}' must contain unique values",
        check_fn=_check,
        severity=CheckSeverity.ERROR,
    )

def range_check(
    column: str,
    min_value: Optional[float] = None,
    max_value: Optional[float] = None,
) -> QualityCheck:
    """Create a check that ensures a numeric column falls within a range."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        failed = []
        for row in data:
            val = row.get(column)
            if val is None:
                continue
            if min_value is not None and val < min_value:
                failed.append(row)
            elif max_value is not None and val > max_value:
                failed.append(row)
        return len(failed) == 0, failed

    bounds = []
    if min_value is not None:
        bounds.append(f">= {min_value}")
    if max_value is not None:
        bounds.append(f"<= {max_value}")
    desc = f"Column '{column}' must be {' and '.join(bounds)}"

    return QualityCheck(
        name=f"range_{column}",
        description=desc,
        check_fn=_check,
        severity=CheckSeverity.ERROR,
    )

def freshness_check(
    timestamp_column: str,
    max_age_hours: int,
) -> QualityCheck:
    """Create a check that ensures data is not older than a threshold."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        if not data:
            return True, []
        now = datetime.utcnow()
        stale = []
        for row in data:
            ts = row.get(timestamp_column)
            if ts is None:
                continue
            if isinstance(ts, str):
                ts = datetime.fromisoformat(ts)
            age = (now - ts).total_seconds() / 3600
            if age > max_age_hours:
                stale.append(row)
        return len(stale) == 0, stale

    return QualityCheck(
        name=f"freshness_{timestamp_column}",
        description=f"Column '{timestamp_column}' must not be older than {max_age_hours} hours",
        check_fn=_check,
        severity=CheckSeverity.CRITICAL,
    )

def row_count_check(
    min_rows: int = 1,
    max_rows: Optional[int] = None,
) -> QualityCheck:
    """Create a check that ensures the dataset has an expected row count."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        count = len(data)
        if count < min_rows:
            return False, []
        if max_rows is not None and count > max_rows:
            return False, []
        return True, []

    desc = f"Row count must be >= {min_rows}"
    if max_rows is not None:
        desc += f" and <= {max_rows}"

    return QualityCheck(
        name="row_count",
        description=desc,
        check_fn=_check,
        severity=CheckSeverity.CRITICAL,
    )

def referential_integrity_check(
    column: str,
    reference_values: set[Any],
) -> QualityCheck:
    """Create a check that ensures all values in a column exist in a reference set."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        orphans = [row for row in data if row.get(column) not in reference_values]
        return len(orphans) == 0, orphans

    return QualityCheck(
        name=f"referential_integrity_{column}",
        description=f"All values in '{column}' must exist in the reference set",
        check_fn=_check,
        severity=CheckSeverity.ERROR,
    )

def schema_check(
    expected_columns: list[str],
) -> QualityCheck:
    """Create a check that ensures all expected columns are present."""

    def _check(data: list[dict[str, Any]]) -> tuple[bool, list[dict[str, Any]]]:
        if not data:
            return True, []
        actual_columns = set(data[0].keys())
        missing = set(expected_columns) - actual_columns
        if missing:
            return False, [{"missing_columns": list(missing)}]
        return True, []

    return QualityCheck(
        name="schema_check",
        description=f"Data must contain columns: {', '.join(expected_columns)}",
        check_fn=_check,
        severity=CheckSeverity.CRITICAL,
    )
Retry and Error Handling Pattern
"""Retry and error handling utilities for pipeline resilience."""

import functools
import time
from typing import Any, Callable, Optional, Type

import structlog

logger = structlog.get_logger(__name__)

class PipelineError(Exception):
    """Base exception for all pipeline errors."""

    def __init__(self, message: str, stage: str, details: Optional[dict] = None) -> None:
        super().__init__(message)
        self.stage = stage
        self.details = details or {}

class ExtractionError(PipelineError):
    """Raised when data extraction fails."""

    def __init__(self, message: str, details: Optional[dict] = None) -> None:
        super().__init__(message, stage="extraction", details=details)

class TransformationError(PipelineError):
    """Raised when data transformation fails."""

    def __init__(self, message: str, details: Optional[dict] = None) -> None:
        super().__init__(message, stage="transformation", details=details)

class LoadError(PipelineError):
    """Raised when data loading fails."""

    def __init__(self, message: str, details: Optional[dict] = None) -> None:
        super().__init__(message, stage="load", details=details)

class QualityCheckError(PipelineError):
    """Raised when a critical quality check fails."""

    def __init__(self, message: str, details: Optional[dict] = None) -> None:
        super().__init__(message, stage="quality_check", details=details)

def retry(
    max_attempts: int = 3,
    delay_seconds: float = 1.0,
    backoff_factor: float = 2.0,
    max_delay_seconds: float = 300.0,
    retryable_exceptions: tuple[Type[Exception], ...] = (Exception,),
) -> Callable:
    """Decorator that retries a function with exponential backoff.

    Args:
        max_attempts: Maximum number of attempts before giving up.
        delay_seconds: Initial delay between retries in seconds.
        backoff_factor: Multiplier applied to delay after each retry.
        max_delay_seconds: Maximum delay between retries.
        retryable_exceptions: Tuple of exception types that trigger a retry.

    Returns:
        Decorated function with retry logic.
    """

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            log = logger.bind(function=func.__name__)
            current_delay = delay_seconds
            last_exception: Optional[Exception] = None

            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except retryable_exceptions as exc:
                    last_exception = exc
                    if attempt == max_attempts:
                        log.error(
                            "max_retries_exceeded",
                            attempt=attempt,
                            error=str(exc),
                        )
                        raise
                    log.warning(
                        "retrying",
                        attempt=attempt,
                        max_attempts=max_attempts,
                        delay=current_delay,
                        error=str(exc),
                    )
                    time.sleep(current_delay)
                    current_delay = min(
                        current_delay * backoff_factor,
                        max_delay_seconds,
                    )

            raise last_exception  # Should not reach here, but safety net

        return wrapper

    return decorator
Airflow DAG Pattern
"""Airflow DAG template for orchestrating the data pipeline."""

from datetime import datetime, timedelta
from typing import Any

from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.operators.empty import EmptyOperator
from airflow.utils.task_group import TaskGroup

# -- DAG Default Arguments ------------------------------------------------------

default_args: dict[str, Any] = {
    "owner": "data-engineering",
    "depends_on_past": False,
    "email_on_failure": True,
    "email_on_retry": False,
    "retries": 2,
    "retry_delay": timedelta(minutes=5),
    "retry_exponential_backoff": True,
    "max_retry_delay": timedelta(minutes=60),
    "execution_timeout": timedelta(hours=2),
}

# -- DAG Definition -------------------------------------------------------------

with DAG(
    dag_id="PIPELINE_NAME",
    default_args=default_args,
    description="PIPELINE_DESCRIPTION",
    schedule="CRON_EXPRESSION",
    start_date=datetime(2024, 1, 1),
    catchup=False,
    max_active_runs=1,
    tags=["data-pipeline", "PIPELINE_TAG"],
) as dag:

    start = EmptyOperator(task_id="start")
    end = EmptyOperator(task_id="end", trigger_rule="none_failed")

    # -- Extraction Tasks -------------------------------------------------------
    with TaskGroup("extraction") as extraction_group:
        pass  # Generated extraction tasks go here

    # -- Quality Checks (Pre-Transform) -----------------------------------------
    with TaskGroup("pre_transform_quality") as pre_quality_group:
        pass  # Generated pre-transform quality checks go here

    # -- Transformation Tasks ---------------------------------------------------
    with TaskGroup("transformation") as transformation_group:
        pass  # Generated transformation tasks go here

    # -- Quality Checks (Post-Transform) ----------------------------------------
    with TaskGroup("post_transform_quality") as post_quality_group:
        pass  # Generated post-transform quality checks go here

    # -- Load Tasks -------------------------------------------------------------
    with TaskGroup("loading") as loading_group:
        pass  # Generated load tasks go here

    # -- Final Quality Checks ---------------------------------------------------
    with TaskGroup("final_quality") as final_quality_group:
        pass  # Generated final quality checks go here

    # -- DAG Dependencies -------------------------------------------------------
    (
        start
        >> extraction_group
        >> pre_quality_group
        >> transformation_group
        >> post_quality_group
        >> loading_group
        >> final_quality_group
        >> end
    )
Pipeline Configuration Pattern
# pipeline_config.yaml -- Central configuration for the data pipeline

pipeline:
  name: "PIPELINE_NAME"
  version: "1.0.0"
  description: "PIPELINE_DESCRIPTION"
  owner: "data-engineering"
  schedule: "0 6 * * *"  # Daily at 6 AM UTC
  timezone: "UTC"
  max_runtime_minutes: 120
  tags:
    - data-pipeline

sources:
  - name: "SOURCE_NAME"
    type: "postgres"  # postgres, mysql, api, s3, gcs, sftp, kafka
    connection:
      host: "${SOURCE_HOST}"
      port: 5432
      database: "${SOURCE_DB}"
      username: "${SOURCE_USER}"
      password: "${SOURCE_PASSWORD}"
    extraction:
      strategy: "incremental"  # full, incremental, cdc
      watermark_column: "updated_at"
      batch_size: 10000
      tables:
        - schema: "public"
          table: "TABLE_NAME"
          primary_key: "id"
          columns: "*"  # or list specific columns

destination:
  name: "DESTINATION_NAME"
  type: "bigquery"  # bigquery, snowflake, redshift, postgres, s3, gcs
  connection:
    project: "${GCP_PROJECT}"
    dataset: "${BQ_DATASET}"
    location: "US"
  loading:
    strategy: "upsert"  # append, overwrite, upsert, merge, scd_type_2
    partition_field: "created_date"
    cluster_fields:
      - "category"
      - "region"

transformations:
  - name: "TRANSFORM_NAME"
    type: "sql"  # sql, python, dbt
    description: "TRANSFORM_DESCRIPTION"
    input_tables:
      - "staging.SOURCE_TABLE"
    output_table: "analytics.TARGET_TABLE"
    sql_file: "sql/transformations/TRANSFORM_NAME.sql"

quality:
  pre_transform:
    - check: "not_null"
      column: "id"
      severity: "critical"
    - check: "row_count"
      min_rows: 1
      severity: "critical"
    - check: "freshness"
      column: "updated_at"
      max_age_hours: 48
      severity: "error"
  post_transform:
    - check: "unique"
      column: "id"
      severity: "error"
    - check: "not_null"
      columns:
        - "id"
        - "name"
        - "created_date"
      severity: "error"
    - check: "referential_integrity"
      column: "category_id"
      reference_table: "dim_category"
      reference_column: "id"
      severity: "warn"
  thresholds:
    max_failure_rate_percent: 1.0
    min_row_count_ratio: 0.9  # Must retain at least 90% of input rows

monitoring:
  alerts:
    channels:
      - type: "slack"
        webhook_url: "${SLACK_WEBHOOK_URL}"
        channel: "#data-pipeline-alerts"
      - type: "email"
        recipients:
          - "data-team@company.com"
    rules:
      - name: "pipeline_failure"
        condition: "pipeline_status == 'failed'"
        severity: "critical"
        channels: ["slack", "email"]
      - name: "quality_check_failure"
        condition: "quality_suite_passed == false"
        severity: "error"
        channels: ["slack"]
      - name: "slow_pipeline"
        condition: "duration_minutes > 90"
        severity: "warn"
        channels: ["slack"]
  metrics:
    - name: "records_processed"
      type: "counter"
    - name: "pipeline_duration_seconds"
      type: "histogram"
    - name: "quality_check_pass_rate"
      type: "gauge"
    - name: "extraction_lag_seconds"
      type: "gauge"
Monitoring and Alerting Pattern
"""Pipeline monitoring and alerting utilities."""

import json
import time
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Optional, Protocol

import structlog

logger = structlog.get_logger(__name__)

class AlertSeverity(Enum):
    """Alert severity levels."""

    INFO = "info"
    WARN = "warn"
    ERROR = "error"
    CRITICAL = "critical"

@dataclass
class PipelineMetrics:
    """Collected metrics for a single pipeline run."""

    pipeline_name: str
    run_id: str
    start_time: datetime
    end_time: Optional[datetime] = None
    status: str = "running"
    records_extracted: int = 0
    records_transformed: int = 0
    records_loaded: int = 0
    records_rejected: int = 0
    quality_checks_passed: int = 0
    quality_checks_failed: int = 0
    errors: list[dict[str, Any]] = field(default_factory=list)
    custom_metrics: dict[str, Any] = field(default_factory=dict)

    @property
    def duration_seconds(self) -> Optional[float]:
        """Calculate pipeline run duration in seconds."""
        if self.end_time is None:
            return None
        return (self.end_time - self.start_time).total_seconds()

    def to_dict(self) -> dict[str, Any]:
        """Serialize metrics to a dictionary for reporting."""
        return {
            "pipeline_name": self.pipeline_name,
            "run_id": self.run_id,
            "start_time": self.start_time.isoformat(),
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "status": self.status,
            "duration_seconds": self.duration_seconds,
            "records": {
                "extracted": self.records_extracted,
                "transformed": self.records_transformed,
                "loaded": self.records_loaded,
                "rejected": self.records_rejected,
            },
            "quality": {
                "passed": self.quality_checks_passed,
                "failed": self.quality_checks_failed,
            },
            "errors": self.errors,
            "custom_metrics": self.custom_metrics,
        }

class AlertChannel(Protocol):
    """Protocol for alert delivery channels."""

    def send(self, severity: AlertSeverity, title: str, message: str) -> bool:
        """Send an alert through this channel."""
        ...

class MetricsCollector:
    """Collects and exposes pipeline metrics."""

    def __init__(self, pipeline_name: str, run_id: str) -> None:
        self.metrics = PipelineMetrics(
            pipeline_name=pipeline_name,
            run_id=run_id,
            start_time=datetime.utcnow(),
        )
        self._log = logger.bind(pipeline=pipeline_name, run_id=run_id)
        self._timers: dict[str, float] = {}

    def start_timer(self, name: str) -> None:
        """Start a named timer."""
        self._timers[name] = time.monotonic()

    def stop_timer(self, name: str) -> float:
        """Stop a named timer and return elapsed seconds."""
        if name not in self._timers:
            return 0.0
        elapsed = time.monotonic() - self._timers.pop(name)
        self.metrics.custom_metrics[f"{name}_duration_seconds"] = elapsed
        return elapsed

    def record_extraction(self, count: int) -> None:
        """Record extracted record count."""
        self.metrics.records_extracted += count
        self._log.info("extraction_recorded", count=count, total=self.metrics.records_extracted)

    def record_transformation(self, input_count: int, output_count: int) -> None:
        """Record transformation counts."""
        self.metrics.records_transformed += output_count
        dropped = input_count - output_count
        self._log.info(
            "transformation_recorded",
            input=input_count,
            output=output_count,
            dropped=dropped,
        )

    def record_load(self, loaded: int, rejected: int = 0) -> None:
        """Record load counts."""
        self.metrics.records_loaded += loaded
        self.metrics.records_rejected += rejected
        self._log.info("load_recorded", loaded=loaded, rejected=rejected)

    def record_quality_check(self, passed: bool) -> None:
        """Record a quality check result."""
        if passed:
            self.metrics.quality_checks_passed += 1
        else:
            self.metrics.quality_checks_failed += 1

    def record_error(self, stage: str, error: str, details: Optional[dict] = None) -> None:
        """Record a pipeline error."""
        self.metrics.errors.append({
            "stage": stage,
            "error": error,
            "details": details or {},
            "timestamp": datetime.utcnow().isoformat(),
        })
        self._log.error("pipeline_error", stage=stage, error=error)

    def finalize(self, status: str = "success") -> PipelineMetrics:
        """Finalize metrics collection and return the result."""
        self.metrics.end_time = datetime.utcnow()
        self.metrics.status = status
        self._log.info(
            "pipeline_completed",
            status=status,
            duration=self.metrics.duration_seconds,
            records_loaded=self.metrics.records_loaded,
        )
        return self.metrics

Phase 4: Documentation

Generate a comprehensive data-pipeline-spec.md for every pipeline. This is the most important output -- it is the living specification that the team uses to understand, operate, and maintain the pipeline.

data-pipeline-spec.md Template

The spec document must contain these sections:

# Data Pipeline Specification: [Pipeline Name]

## Overview
- **Pipeline Name**: [name]
- **Version**: [version]
- **Owner**: [team/person]
- **Created**: [date]
- **Last Updated**: [date]
- **Status**: [draft/active/deprecated]

## Purpose
[2-3 sentences describing what this pipeline does and why it exists.]

## Architecture
[Describe the chosen architecture pattern and why it was selected.]

### Data Flow Diagram
[ASCII or mermaid diagram showing the end-to-end data flow.]

## Data Sources
### [Source 1 Name]
- **Type**: [database/api/file/stream]
- **Connection**: [connection details, redacted secrets]
- **Tables/Endpoints**: [list of tables or API endpoints]
- **Extraction Strategy**: [full/incremental/cdc]
- **Volume**: [estimated rows/day, total size]
- **Schema**: [key columns, data types, primary keys]

## Destination
- **Type**: [warehouse/lake/database]
- **Target Tables**: [list]
- **Schema Design**: [star/snowflake/OBT/vault]
- **Partitioning**: [strategy]
- **Clustering**: [fields]

## Transformations
### [Transform 1 Name]
- **Input**: [source tables]
- **Output**: [target table]
- **Logic**: [description of business rules]
- **SQL/Code**: [reference to implementation file]

## Data Quality Checks
### Pre-Transform Checks
| Check | Column | Severity | Threshold |
|-------|--------|----------|-----------|
| not_null | id | critical | 0% null |

### Post-Transform Checks
| Check | Column | Severity | Threshold |
|-------|--------|----------|-----------|

## Scheduling
- **Frequency**: [cron expression and human-readable]
- **Timezone**: [timezone]
- **Dependencies**: [upstream pipelines]
- **SLA**: [maximum acceptable completion time]

## Error Handling
- **Retry Policy**: [attempts, backoff strategy]
- **Dead Letter Queue**: [where failed records go]
- **Alerting**: [channels and severity thresholds]
- **Manual Recovery**: [steps for manual intervention]

## Monitoring
- **Metrics**: [list of tracked metrics]
- **Dashboards**: [links to monitoring dashboards]
- **Alert Rules**: [conditions and channels]

## Operational Runbook
### Starting the Pipeline
[Steps to start/restart]

### Stopping the Pipeline
[Steps to gracefully stop]

### Backfilling
[Steps to backfill historical data]

### Common Issues and Resolutions
| Issue | Symptoms | Resolution |
|-------|----------|------------|
| [issue] | [symptoms] | [steps] |

## Change Log
| Date | Version | Change | Author |
|------|---------|--------|--------|
| [date] | 1.0.0 | Initial pipeline | [author] |

How to Respond

When a user invokes this skill, follow this workflow:

Step 1: Requirements Gathering

If the user has provided clear requirements, proceed to design. Otherwise, ask targeted questions:

  1. What are your data sources? (databases, APIs, files, etc.)
  2. Where should the data land? (warehouse, lake, database)
  3. What transformations are needed? (joins, aggregations, filters, business rules)
  4. What is the freshness requirement? (real-time, hourly, daily)
  5. Any technology preferences? (Airflow, dbt, Spark, specific cloud provider)
  6. Any data quality requirements? (specific validations, compliance needs)

Step 2: Design Presentation

Present the pipeline design to the user before generating code:

Pipeline Design Summary
=======================

Architecture: [pattern]
Sources: [list]
Destination: [target]
Schedule: [frequency]
Key Transformations: [summary]
Quality Gates: [summary]

Shall I proceed with implementation?

Step 3: Implementation

Generate all files following the project structure defined above. Customize every file to the specific pipeline -- no placeholder code that requires manual editing.

For each source, generate a concrete extractor class inheriting from BaseExtractor. For each transformation, generate a concrete transformer class or SQL file. For each destination, generate a concrete loader class inheriting from BaseLoader. Generate the Airflow DAG with all task dependencies wired up. Generate quality checks tailored to the specific data. Generate monitoring configuration with appropriate alert thresholds.

Step 4: Specification Document

Generate the data-pipeline-spec.md as the last step, referencing all implementation files and incorporating design decisions made during the process.

Important Notes

  • Always design for idempotency -- every step must be safely re-runnable
  • Always include watermark/checkpoint tracking for incremental pipelines
  • Always include dead letter handling for records that fail processing
  • Always include schema evolution handling -- sources will change their schemas
  • Never hardcode credentials -- use environment variables or secret managers
  • Never skip quality checks -- they are the first line of defense against bad data
  • Prefer SQL for transformations when the logic is expressible in SQL
  • Prefer Python for complex business logic that does not map cleanly to SQL
  • Always include a backfill strategy in the specification
  • Always include an operational runbook with common failure scenarios
  • Generate tests for all custom business logic
  • Use structured logging throughout for observability
  • Track data lineage at every transformation step

平台分布

Codex

33.41%
按下载量换算65

Claude

30.11%
按下载量换算59

Cursor

21.02%
按下载量换算41

Gemini CLI

8.53%
按下载量换算17

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

接触敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills