mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
New skills covering hot-topic AI engineering subjects: Local AI Infrastructure: - vllm-server: High-throughput LLM serving with PagedAttention, tensor parallelism, quantization - llm-inference-scaling: KEDA-based GPU autoscaling for LLM inference on Kubernetes - rag-infrastructure: Production RAG with hybrid search, reranking, and embedding pipelines - llm-fine-tuning: QLoRA/LoRA fine-tuning with Axolotl, DeepSpeed ZeRO-3, and DPO alignment Infrastructure: - gpu-server-management: NVIDIA driver setup, MIG partitioning, DCGM monitoring - vector-database-ops: Qdrant, Weaviate, pgvector for production AI search - llm-gateway: LiteLLM-based API gateway with rate limiting, virtual keys, fallback routing DevOps/AI: - llm-cost-optimization: Model right-sizing, prompt/semantic caching, batch API, break-even analysis - llm-caching: Multi-layer exact + semantic + provider caching to cut costs 30-70% - ai-pipeline-orchestration: Prefect/Airflow/Dagster for RAG ingestion and training workflows Orchestration: - model-serving-kubernetes: KServe + Triton with canary deployments and GPU autoscaling Security: - ai-security-hardening: Prompt injection defense, PII scrubbing, model weight verification https://claude.ai/code/session_011MN1C4PrkCeg2Qmi7q1ZUe
263 lines
9.0 KiB
Markdown
263 lines
9.0 KiB
Markdown
---
|
|
name: ai-pipeline-orchestration
|
|
description: Orchestrate AI/ML pipelines for data ingestion, model training, batch inference, and RAG indexing using Prefect, Airflow, or Dagster. Build reliable, observable, and retriable workflows for production AI systems.
|
|
license: MIT
|
|
metadata:
|
|
author: devops-skills
|
|
version: "1.0"
|
|
---
|
|
|
|
# AI Pipeline Orchestration
|
|
|
|
Build reliable, observable AI workflows — from document ingestion to batch inference to model training pipelines.
|
|
|
|
## When to Use This Skill
|
|
|
|
Use this skill when:
|
|
- Scheduling recurring RAG document ingestion and re-indexing
|
|
- Orchestrating multi-step batch LLM processing workflows
|
|
- Running nightly model evaluation and fine-tuning jobs
|
|
- Building ETL pipelines that feed into AI models
|
|
- Managing dependencies between data preparation and model serving
|
|
|
|
## Tool Selection
|
|
|
|
| Tool | Best For | Complexity | GPU Jobs |
|
|
|------|----------|------------|----------|
|
|
| **Prefect** | Modern Python-first; easy to adopt | Low | Good |
|
|
| **Airflow** | Complex DAGs; large teams; existing usage | High | Good |
|
|
| **Dagster** | Asset-centric; strong data lineage | Medium | Excellent |
|
|
| **Temporal** | Long-running workflows; reliability-first | Medium | Good |
|
|
|
|
## Prefect — Quick Start
|
|
|
|
```bash
|
|
pip install prefect prefect-kubernetes
|
|
|
|
# Start Prefect server (or use Prefect Cloud)
|
|
prefect server start
|
|
|
|
# In another terminal
|
|
prefect worker start --pool default-agent-pool
|
|
```
|
|
|
|
## Prefect: RAG Ingestion Pipeline
|
|
|
|
```python
|
|
from prefect import flow, task, get_run_logger
|
|
from prefect.tasks import task_input_hash
|
|
from datetime import timedelta
|
|
import hashlib
|
|
|
|
@task(cache_key_fn=task_input_hash, cache_expiration=timedelta(hours=24))
|
|
def fetch_documents(source_url: str) -> list[dict]:
|
|
"""Fetch documents from source; cached to avoid re-fetching."""
|
|
logger = get_run_logger()
|
|
logger.info(f"Fetching from {source_url}")
|
|
# ... fetch logic
|
|
return documents
|
|
|
|
@task(retries=3, retry_delay_seconds=30)
|
|
def chunk_and_embed(documents: list[dict]) -> list[dict]:
|
|
"""Chunk documents and generate embeddings with retry on failure."""
|
|
from sentence_transformers import SentenceTransformer
|
|
model = SentenceTransformer("BAAI/bge-large-en-v1.5")
|
|
chunks = []
|
|
for doc in documents:
|
|
doc_chunks = chunk_text(doc["content"])
|
|
embeddings = model.encode(doc_chunks, batch_size=64)
|
|
for chunk, emb in zip(doc_chunks, embeddings):
|
|
chunks.append({"text": chunk, "embedding": emb.tolist(),
|
|
"source": doc["url"], "doc_hash": doc["hash"]})
|
|
return chunks
|
|
|
|
@task(retries=2)
|
|
def upsert_to_vector_store(chunks: list[dict]) -> int:
|
|
"""Upsert embeddings to Qdrant, skip unchanged documents."""
|
|
from qdrant_client import QdrantClient
|
|
client = QdrantClient("http://qdrant:6333")
|
|
client.upsert(collection_name="knowledge-base", points=[...])
|
|
return len(chunks)
|
|
|
|
@flow(name="rag-ingestion", log_prints=True)
|
|
def rag_ingestion_pipeline(sources: list[str]):
|
|
"""Full RAG ingestion flow — runs daily."""
|
|
logger = get_run_logger()
|
|
total = 0
|
|
for source in sources:
|
|
docs = fetch_documents(source)
|
|
chunks = chunk_and_embed(docs)
|
|
count = upsert_to_vector_store(chunks)
|
|
total += count
|
|
logger.info(f"Ingested {count} chunks from {source}")
|
|
logger.info(f"Pipeline complete: {total} total chunks indexed")
|
|
|
|
if __name__ == "__main__":
|
|
rag_ingestion_pipeline.serve(
|
|
name="daily-rag-ingestion",
|
|
cron="0 2 * * *", # 2 AM daily
|
|
parameters={"sources": ["https://docs.myapp.com", "https://api.myapp.com/kb"]},
|
|
)
|
|
```
|
|
|
|
## Prefect: Batch LLM Inference Pipeline
|
|
|
|
```python
|
|
from prefect import flow, task
|
|
from prefect.concurrency.sync import concurrency
|
|
import asyncio
|
|
from openai import AsyncOpenAI
|
|
|
|
@task(retries=3, retry_delay_seconds=60)
|
|
async def process_batch(items: list[dict], model: str = "gpt-4o-mini") -> list[dict]:
|
|
"""Process a batch of items through LLM with rate limit protection."""
|
|
client = AsyncOpenAI()
|
|
async with concurrency("openai-api", occupy=len(items)): # rate limit
|
|
tasks = [
|
|
client.chat.completions.create(
|
|
model=model,
|
|
messages=[{"role": "user", "content": item["prompt"]}],
|
|
max_tokens=256,
|
|
)
|
|
for item in items
|
|
]
|
|
responses = await asyncio.gather(*tasks, return_exceptions=True)
|
|
|
|
results = []
|
|
for item, response in zip(items, responses):
|
|
if isinstance(response, Exception):
|
|
results.append({**item, "error": str(response), "output": None})
|
|
else:
|
|
results.append({**item, "output": response.choices[0].message.content})
|
|
return results
|
|
|
|
@flow(name="batch-llm-inference")
|
|
async def batch_inference_flow(input_file: str, output_file: str, batch_size: int = 50):
|
|
import json
|
|
items = [json.loads(line) for line in open(input_file)]
|
|
batches = [items[i:i+batch_size] for i in range(0, len(items), batch_size)]
|
|
|
|
all_results = []
|
|
for batch in batches:
|
|
results = await process_batch(batch)
|
|
all_results.extend(results)
|
|
|
|
with open(output_file, "w") as f:
|
|
for result in all_results:
|
|
f.write(json.dumps(result) + "\n")
|
|
return len(all_results)
|
|
```
|
|
|
|
## Airflow: Model Training DAG
|
|
|
|
```python
|
|
from airflow.decorators import dag, task
|
|
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
|
|
from datetime import datetime
|
|
from kubernetes.client import models as k8s
|
|
|
|
@dag(
|
|
dag_id="llm_fine_tuning",
|
|
schedule="@weekly",
|
|
start_date=datetime(2025, 1, 1),
|
|
catchup=False,
|
|
tags=["ai", "training"],
|
|
)
|
|
def llm_fine_tuning_dag():
|
|
|
|
@task
|
|
def prepare_dataset() -> str:
|
|
"""Download and preprocess training data."""
|
|
# ... data prep logic
|
|
return "s3://my-bucket/training-data/2025-03-01/"
|
|
|
|
train = KubernetesPodOperator(
|
|
task_id="train_model",
|
|
name="llm-training-job",
|
|
namespace="ml",
|
|
image="nvcr.io/nvidia/pytorch:24.05-py3",
|
|
cmds=["accelerate", "launch", "-m", "axolotl.cli.train", "/config/config.yaml"],
|
|
resources=k8s.V1ResourceRequirements(
|
|
limits={"nvidia.com/gpu": "4", "memory": "320Gi"},
|
|
requests={"nvidia.com/gpu": "4"},
|
|
),
|
|
node_selector={"nvidia.com/gpu.product": "A100-SXM4-80GB"},
|
|
volumes=[...],
|
|
volume_mounts=[...],
|
|
get_logs=True,
|
|
is_delete_operator_pod=True,
|
|
)
|
|
|
|
@task
|
|
def evaluate_model(dataset_path: str) -> dict:
|
|
"""Run evals; fail pipeline if quality drops."""
|
|
metrics = run_evals()
|
|
if metrics["accuracy"] < 0.85:
|
|
raise ValueError(f"Model quality too low: {metrics}")
|
|
return metrics
|
|
|
|
@task
|
|
def deploy_model(metrics: dict):
|
|
"""Push merged model to HF Hub and update vLLM config."""
|
|
update_serving_config(new_model="org/fine-tuned-v2")
|
|
|
|
dataset = prepare_dataset()
|
|
train.set_upstream(dataset)
|
|
eval_result = evaluate_model(dataset)
|
|
eval_result.set_upstream(train)
|
|
deploy_model(eval_result)
|
|
|
|
llm_fine_tuning_dag()
|
|
```
|
|
|
|
## Dagster: Asset-Based AI Pipeline
|
|
|
|
```python
|
|
from dagster import asset, AssetExecutionContext, define_asset_job, ScheduleDefinition
|
|
|
|
@asset(description="Raw documents fetched from knowledge sources")
|
|
def raw_documents(context: AssetExecutionContext) -> list[dict]:
|
|
context.log.info("Fetching documents...")
|
|
return fetch_all_documents()
|
|
|
|
@asset(
|
|
deps=[raw_documents],
|
|
description="Chunked and embedded document vectors",
|
|
)
|
|
def document_embeddings(context: AssetExecutionContext, raw_documents) -> int:
|
|
chunks = process_and_embed(raw_documents)
|
|
context.log.info(f"Generated {len(chunks)} embeddings")
|
|
upsert_to_qdrant(chunks)
|
|
return len(chunks)
|
|
|
|
@asset(
|
|
deps=[document_embeddings],
|
|
description="RAG system quality metrics",
|
|
)
|
|
def rag_quality_metrics(context: AssetExecutionContext) -> dict:
|
|
metrics = evaluate_rag_system()
|
|
context.add_output_metadata({"ragas_score": metrics["ragas_score"]})
|
|
return metrics
|
|
|
|
# Schedule: refresh embeddings nightly
|
|
nightly_refresh = ScheduleDefinition(
|
|
job=define_asset_job("rag_refresh_job", [raw_documents, document_embeddings]),
|
|
cron_schedule="0 1 * * *",
|
|
)
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
- Use task-level retries for API calls; use flow-level retries for transient infra failures.
|
|
- Cache expensive steps (embedding generation, data fetching) to speed up reruns.
|
|
- Emit custom metrics from pipelines (chunk count, error rate, cost) to your observability stack.
|
|
- Use `concurrency` limits in Prefect or `pool` slots in Airflow to respect external rate limits.
|
|
- Separate ingestion, training, and deployment pipelines — don't couple them in one giant DAG.
|
|
|
|
## Related Skills
|
|
|
|
- [rag-infrastructure](../../infrastructure/local-ai/rag-infrastructure/) - RAG system setup
|
|
- [llm-fine-tuning](../../infrastructure/local-ai/llm-fine-tuning/) - Training jobs
|
|
- [agent-observability](../agent-observability/) - Pipeline monitoring
|
|
- [kubernetes-ops](../orchestration/kubernetes-ops/) - Running pipeline pods on K8s
|