2026-03-02 01:18:29 +00:00
---
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
2026-05-22 09:02:40 -04:00
- [rag-infrastructure ](../../../infrastructure/local-ai/rag-infrastructure/ ) - RAG system setup
- [llm-fine-tuning ](../../../infrastructure/local-ai/llm-fine-tuning/ ) - Training jobs
2026-03-02 01:18:29 +00:00
- [agent-observability ](../agent-observability/ ) - Pipeline monitoring
2026-05-22 09:02:40 -04:00
- [kubernetes-ops ](../../orchestration/kubernetes-ops/ ) - Running pipeline pods on K8s