ai-pipeline-orchestration について
AI データやモデルのワークフローを構築するエンジニア向けのスキルです。Prefect、Airflow、Dagster、Temporal によるオーケストレーションを、文書取り込みからバッチ処理、評価、トレーニングまで解説します。
主な機能
- 01AI ワークフロー用ツールの選定
- 02文書取り込みと再インデックス
- 03バッチ LLM 処理のオーケストレーション
- 04モデル評価とファインチューニングのスケジュール
- 05タスクの再試行、キャッシュ、並行数制御
- 06メトリクスとデータリネージの実践
活用シーン
- 01RAG 文書取り込みの定期実行
- 02複数段階のバッチ LLM 処理の編成
- 03夜間のモデル評価とファインチューニングの実行
- 04ETL とモデルサービング間の依存関係管理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill ai-pipeline-orchestrationインストール後、Claude Code で/ai-pipeline-orchestrationを入力して読み込まれたか確認します。
詳細
AI パイプラインのオーケストレーション
ドキュメントの取り込みからバッチ推論、モデルのトレーニングパイプラインまで、信頼性と可観測性に優れた AI ワークフローを構築します。
このスキルを使う場面
次のような場合にこのスキルを使います。
- RAG のドキュメント取り込みと再インデックス作成を定期実行する
- 複数ステップのバッチ LLM 処理ワークフローをオーケストレーションする
- 夜間のモデル評価とファインチューニングのジョブを実行する
- AI モデルにデータを渡す ETL パイプラインを構築する
- データ準備とモデルサービングの間の依存関係を管理する
ツールの選択
| ツール | 最適な用途 | 複雑さ | GPU ジョブ |
|---|---|---|---|
| Prefect | Python を中心としたモダンな環境。導入が容易 | 低 | 良好 |
| Airflow | 複雑な DAG、大規模チーム、既存の利用環境 | 高 | 良好 |
| Dagster | アセット中心、強力なデータリネージ | 中 | 優秀 |
| Temporal | 長時間実行するワークフロー、信頼性を最優先する環境 | 中 | 良好 |
Prefect — クイックスタート
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 取り込みパイプライン
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:バッチ LLM 推論パイプライン
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:モデル学習 DAG
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:アセットベースの AI パイプライン
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 * * *",
)
ベストプラクティス
- API 呼び出しにはタスクレベルの再試行を、断続的なインフラ障害にはフローレベルの再試行を使います。
- コストの高いステップ(埋め込み生成、データ取得)をキャッシュして、再実行を高速化します。
- パイプラインからカスタムメトリクス(チャンク数、エラー率、コスト)を可観測性スタックに送信します。
- 外部のレート制限を守るため、Prefect では
concurrencyの上限を、Airflow ではpoolスロットを使います。 - 取り込み、トレーニング、デプロイの各パイプラインを分離し、すべてを巨大な DAG 1 つにまとめないようにします。
関連スキル
- RAG インフラストラクチャ - RAG システムのセットアップ
- LLM ファインチューニング - トレーニングジョブ
- エージェントの可観測性 - パイプラインの監視
- kubernetes-ops - K8s でのパイプラインポッドの実行
コメント
Markdown 対応