de-agent-skills: готовые PySpark и Spark SQL skills для AI-агента

Разбираем открытый репозиторий de-agent-skills - коллекцию production-ready skills, specs и guides для агента data engineer: pyspark_etl, spark_sql, и база знаний за ними.

platform

Что такое de-agent-skills

de-agent-skills - открытый репозиторий с готовой «базой знаний» для AI-агента дата-инженера. Вместо того чтобы каждый раз объяснять агенту правила работы со Spark, ты один раз подключаешь репозиторий в контекст - и агент уже знает production-стандарты.

Репозиторий решает проблему «агент пишет как стажёр»: без контекста LLM генерирует рабочий, но не production-ready код - без explicit schema, без partition pruning, с Python loops на больших данных.

Структура репозитория

de-agent-skills/
├── skills/
│   ├── pyspark_etl/SKILL.md    ← skill для агента: DataFrame API
│   └── spark_sql/SKILL.md      ← skill для агента: Spark SQL
├── docs/specs/
│   ├── pyspark_enterprise.md   ← enterprise-стандарты PySpark (TB-PB)
│   ├── spark_sql_enterprise.md ← enterprise-стандарты Spark SQL
│   ├── hdfs_hive_parquet_datalake.md
│   └── spark_sql_hdfs_hive_operations.md
└── guides/
    ├── spark_sql_hdfs_hive_tutorial_101.md
    └── hdfs_hive_partitioning_spark_tutorial_101.md

Skills - прямые инструкции для агента: когда применять skill, code style, anti-patterns.
Specs - полные production-стандарты: checklist, incident playbook, optimization order.
Guides - туториалы: архитектура, партиционирование, паттерны оптимизации.

Skill: pyspark_etl

Активируется, когда задача требует DataFrame API (не SQL): сложные трансформации, UDF, window functions, reusable функции.

Принципы из skill'а

# Стандартные алиасы (агент всегда использует эти имена)
from pyspark.sql import functions as F
from pyspark.sql import types as T
from pyspark.sql.window import Window as W

# Explicit schema - не inferSchema
schema = T.StructType([
    T.StructField("order_id",   T.LongType(),      False),
    T.StructField("status",     T.StringType(),    True),
    T.StructField("amount",     T.DecimalType(18, 2), True),
    T.StructField("event_date", T.DateType(),      False),
])

df = spark.read.schema(schema).parquet("s3a://bucket/orders/")

# select() как явный contract схемы - не df.withColumn цепочки
result = (
    df
    .filter(F.col("event_date") >= "2024-01-01")   # partition pruning первым
    .select("order_id", "status", "amount")         # column pruning сразу
    .withColumn("amount_rub", F.col("amount") * 90)
)

Anti-patterns, которые агент не будет генерировать

# ❌ collect() на больших данных
data = df.collect()  # OutOfMemoryError при > нескольких GB

# ❌ Python loop вместо vectorized операций
for row in df.toLocalIterator():
    process(row)

# ❌ Цепочка withColumn (создаёт промежуточные планы)
df = df.withColumn("a", ...)
df = df.withColumn("b", ...)  # вместо одного select()

# ❌ inferSchema на production данных
df = spark.read.json(path)  # схема угадывается, нестабильно

Порядок оптимизации (из spec)

Агент следует именно этому порядку: сначала убеждается в корректности логики, потом сокращает скан, и только потом думает про shuffle и ресурсы.

Skill: spark_sql

Активируется для Spark SQL задач: ad-hoc аналитика, ETL в SQL-стиле, работа с Hive/Iceberg таблицами.

Query shape из skill'а: CTE-driven стиль

-- Агент всегда структурирует запросы через CTEs
WITH
source_filtered AS (
    -- Ранняя фильтрация с partition pruning
    SELECT
        order_id,
        user_id,
        status,
        amount,
        event_date
    FROM nessie.staging.orders
    WHERE
        event_date >= '2024-01-01'          -- partition pruning
        AND event_date <  '2024-02-01'      -- bounded range (обязательно)
        AND status IS NOT NULL              -- явная null-фильтрация
),

deduped AS (
    -- Дедупликация перед join - не после
    SELECT *
    FROM (
        SELECT *,
               ROW_NUMBER() OVER (
                   PARTITION BY order_id
                   ORDER BY updated_at DESC  -- детерминированный порядок
               ) AS rn
        FROM source_filtered
    )
    WHERE rn = 1
),

enriched AS (
    -- JOIN только с дедуплицированными данными
    SELECT
        o.order_id,
        o.status,
        o.amount,
        u.region
    FROM deduped o
    LEFT JOIN nessie.dw.dim_users u
        ON o.user_id = u.user_id
        AND u.is_current = true             -- фильтр на dimension
),

final AS (
    SELECT
        region,
        COUNT(*)                            AS order_count,
        SUM(amount)                         AS total_amount,
        AVG(amount)                         AS avg_amount
    FROM enriched
    GROUP BY region
)

SELECT * FROM final
ORDER BY total_amount DESC

Ключевые правила из spark_sql/SKILL.md

Правило Пример
Никогда SELECT * в production Явные колонки с алиасами
Всегда explicit join type LEFT JOIN, не просто JOIN
Bounded range для incremental event_date >= X AND event_date < Y
Filter before join CTE source_filtered до enriched
Null-safe equality a <=> b вместо a = b для nullable колонок
NULLS FIRST/LAST в ORDER BY ORDER BY amount DESC NULLS LAST

Anti-patterns из spec

-- ❌ SELECT * в финальном выводе
SELECT * FROM orders JOIN users ON ...

-- ❌ DISTINCT после JOIN (сигнал неверного grain)
SELECT DISTINCT order_id FROM orders JOIN events ON ...

-- ❌ ORDER BY без LIMIT на большой таблице
SELECT * FROM orders ORDER BY amount DESC

-- ❌ COUNT(DISTINCT high_cardinality_col) - запускает shuffle
SELECT COUNT(DISTINCT user_id) FROM billion_row_table

-- ✓ Альтернатива: approx_count_distinct
SELECT approx_count_distinct(user_id) FROM billion_row_table

Как подключить к своему агенту

Вариант 1: как CLAUDE.md / agent context

# Клонируем рядом с проектом
git clone https://github.com/ivanshamaev/de-agent-skills ~/.agent-skills

# Добавляем в CLAUDE.md своего проекта
cat >> CLAUDE.md << 'EOF'

## Agent Skills Reference

For PySpark code: follow ~/.agent-skills/skills/pyspark_etl/SKILL.md
For Spark SQL: follow ~/.agent-skills/skills/spark_sql/SKILL.md
Production standards: ~/.agent-skills/docs/specs/
EOF

Вариант 2: скопировать specs в /docs проекта

cp -r ~/.agent-skills/docs/specs/ ./docs/spark-standards/
# Агент автоматически читает файлы в docs/ при анализе репозитория

Вариант 3: как OpenCode skill (для команды)

# .opencode/skills/pyspark-review.yaml
name: pyspark-review
description: Проверяет PySpark код по production-стандартам
trigger: "review|проверь|code review"
steps:
  - name: load_standards
    tool: read
    path: "docs/spark-standards/pyspark_enterprise.md"
  - name: review
    llm: true
    prompt: |
      Проверь код по стандартам:
      {load_standards.output}

      Код для проверки:
      {selection}

      Проверь: explicit schema, partition pruning, join типы,
      anti-patterns (collect, loops, inferSchema), порядок оптимизации.

Практика

  1. Форкни репозиторий и добавь /docs/specs/ в CLAUDE.md своего проекта
  2. Попроси агента написать PySpark ETL джоб - сравни с кодом до добавления specs
  3. Добавь свои правила в fork (специфика твоего стека: версия Spark, каталог, форматы)
  4. Открой PR с дополнениями в оригинальный репозиторий

Отдельно стоит изучить docs/specs/spark_sql_enterprise.md - там есть Incident Playbook: диагностика медленных запросов и некорректных результатов по шагам.