RAGは一度作って終わりではなく、文書の更新、エンベディングモデルの差し替え、プロンプトの改良が日常的に発生する「生きもの」です。手動のテスト・デプロイに頼っていると、ちょっとした変更で品質が静かに劣化し、ユーザーからの不満が出て初めて気づく、という事態になりがちです。本記事ではRAG特有のCI/CD設計、テスト自動化、インデックス更新、デプロイ戦略までを実装レベルで解説します。
RAGにCI/CDが必要な理由
RAGシステムは4つの要素が常に変化します。第一に「文書データ」。社内ドキュメントは日々追加・更新され、古い情報が残ったままだと回答が誤ったまま本番提供されます。第二に「エンベディングモデル」。新バージョンへの切替やモデル差し替えが行われると、インデックス全体の再計算が必要になります。
第三に「プロンプト」。精度向上のためのチューニングは頻繁に発生し、わずかな文言変更でも品質に影響します。第四に「LLM」そのものが、バージョンアップやプロバイダ側の変更で挙動を変えることがあります。これらを人力でテストし続けるのは現実的ではなく、評価メトリクスをCI/CDに組み込んで自動で品質を担保することが本格運用の前提となります。
RAGパイプラインのCI/CD全体設計
RAG向けのCI/CDは、一般的なWebアプリのパイプラインに「評価ステージ」「インデックス更新ステージ」を加えた構成になります。コード変更が入ってからユーザーに届くまでのフローを一枚の図に落とし込むと、全体像の把握が容易です。
【RAG CI/CDパイプラインの全体像】
[Git Push]
|
v
[Lint / 型チェック]
|
v
[Unit Test] ← チャンキング・プロンプト関数等
|
v
[Integration Test] ← 検索精度・Ragas評価
|
v
[しきい値判定] ← Faithfulness 0.85 等
|
+--> NG → [マージブロック]
|
v
[Staging Deploy]
|
v
[Shadow / Canary] ← 本番トラフィックで観測
|
v
[Production Deploy]
|
v
[Observability] ← レイテンシ・コスト・品質を監視
| ステージ | 実行内容 | ツール | 合格基準 | 所要時間目安 |
|---|---|---|---|---|
| Lint / 型 | コード規約と型安全性 | ruff / mypy | エラー0 | 1分未満 |
| Unit Test | 関数単位のテスト | pytest | 全パス | 1〜3分 |
| Integration Test | 検索+生成のE2E評価 | Ragas / DeepEval | 閾値クリア | 5〜10分 |
| Staging Deploy | プレビュー環境へ配備 | GitHub Actions等 | ヘルスチェックOK | 2〜5分 |
| Shadow / Canary | 本番トラフィック観測 | 独自監視 | メトリクス逸脱なし | 数時間〜数日 |
| Production Deploy | 本番切替 | Blue-Green等 | SLO内 | 数分 |
| Observability | 継続監視 | Prometheus / Datadog | 逸脱アラート | 常時 |
テスト自動化の設計
RAGのテストは「ユニットテスト」「統合テスト」「E2Eテスト」の3段階で構築するのが定石です。ユニットテストではチャンキング関数やプロンプト組立ロジックといった純粋関数を対象にし、高速で回すことを重視します。統合テストでは検索精度を評価セットで測定し、Faithfulness等のメトリクスが閾値を満たすかを判定します。E2Eテストは本番相当の環境で実質的なユースケースを通し、レイテンシやコストも含めて全体動作を確認します。
# pytestによるRAGユニットテスト例
import pytest
from rag.chunker import split_markdown
from rag.prompt import build_prompt
def test_split_markdown_basic():
text = "# 見出し1\n本文A\n\n# 見出し2\n本文B"
chunks = split_markdown(text, max_chars=200)
assert len(chunks) == 2
assert "見出し1" in chunks[0]
assert "見出し2" in chunks[1]
def test_split_markdown_overlap():
text = "A" * 500
chunks = split_markdown(text, max_chars=200, overlap=50)
assert all(len(c) <= 250 for c in chunks)
def test_build_prompt_contains_context():
contexts = ["規程A: 10日付与", "規程B: 残業は月45時間まで"]
prompt = build_prompt(question="有給は何日?", contexts=contexts)
assert "規程A" in prompt
assert "有給は何日?" in prompt
# 統合テスト: 評価メトリクス閾値チェック
import json
from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, context_precision
from app.rag import run_rag
def test_rag_quality_thresholds():
with open("tests/testset.json", encoding="utf-8") as f:
testcases = json.load(f)
rows = [run_rag(q["question"]) for q in testcases]
for row, q in zip(rows, testcases):
row["ground_truth"] = q["ground_truth"]
result = evaluate(Dataset.from_list(rows),
metrics=[faithfulness, context_precision])
scores = result.to_pandas().mean().to_dict()
assert scores["faithfulness"] >= 0.85
assert scores["context_precision"] >= 0.80
インデックス更新の自動化
インデックス更新はRAG CI/CDの中でも専用の設計が必要な領域です。全文書を毎回再インデックスするのは非効率なので、通常はインクリメンタル更新(追加・変更・削除された文書のみ再処理)を基本とし、モデルの切替や大規模変更時のみフル再インデックスを行います。
トリガー方式は2種類あります。日次・週次のスケジュール実行は運用がシンプルで、社内文書のように更新頻度が低い用途に向きます。一方、ファイル更新をイベントトリガーで即時反映させるイベントドリブン方式は、リアルタイム性が必要な用途で有効です。Airflow、Prefect、Dagsterといったワークフロー基盤を用いると、依存関係とリトライ管理が容易になります。
# インデックス更新のDAG例(Prefect)
from prefect import flow, task
from app.rag.loader import list_changed_docs, load_docs
from app.rag.indexer import upsert_chunks, delete_chunks
@task
def fetch_changes():
return list_changed_docs(since="1 day ago")
@task
def ingest(changes):
added = load_docs([c.path for c in changes if c.action != "delete"])
upsert_chunks(added)
delete_chunks([c.id for c in changes if c.action == "delete"])
return {"upserted": len(added), "deleted": sum(1 for c in changes if c.action=="delete")}
@flow(name="rag-index-update")
def rag_index_update_flow():
changes = fetch_changes()
return ingest(changes)
if __name__ == "__main__":
rag_index_update_flow()
デプロイ戦略
RAGのデプロイ戦略は一般的なWebアプリと同様に、Blue-Green、カナリア、シャドーデプロイなどから選択します。ただしRAG特有の注意点として、「モデルやエンベディングの変更はインデックスの互換性」「プロンプト変更は品質の観察期間」が必要となります。
| 戦略 | メリット | デメリット | 適した場面 | 複雑度 |
|---|---|---|---|---|
| Blue-Green | 高速切替・即時ロールバック | 両系統のインフラコスト | エンベディング変更 | 中 |
| カナリア | 段階的にトラフィック移行 | 監視設計が必要 | プロンプト変更 | 中〜高 |
| シャドーデプロイ | 本番トラフィックで安全評価 | 結果比較ロジックが必要 | アルゴリズム変更 | 高 |
| Featureフラグ | ユーザー単位で切替 | 設定管理コスト | A/Bテスト | 中 |
筆者のおすすめは、「エンベディング変更はBlue-Green、プロンプト変更はカナリアまたはFeatureフラグ、アルゴリズム変更はシャドーデプロイ」という使い分けです。これにより影響範囲とリスクに応じた適切な防衛ラインを敷くことができます。
まとめ――RAGのCI/CDは「品質の自動保証」
- RAGは4要素(データ / エンベディング / プロンプト / LLM)が変化するため自動化が必須
- ユニット・統合・E2Eの3段テストでコード変更と評価メトリクスを自動検証
- インデックス更新はインクリメンタル + スケジュール / イベントで運用
- Blue-Green / カナリア / シャドーを変更の性質に合わせて使い分ける
DE-STKではRAGのCI/CD設計、評価パイプライン実装、デプロイ戦略のコンサルティングを提供しています。本番運用の品質に不安を感じている組織はぜひご相談ください。
よくある質問(FAQ)
Q. RAGにCI/CDは本当に必要ですか?
本番運用するRAGには必須です。文書データの更新、エンベディングモデルの変更、プロンプトの修正など、RAGの構成要素は頻繁に変化します。手動テストでは品質劣化を見逃すリスクが高く、自動テスト+自動デプロイで品質を保証する仕組みが必要です。PoC段階では必須ではありませんが、社内公開の時点から段階的に整備することをお勧めします。
Q. RAGのテストではどの指標を監視すべきですか?
最低限、Context Precision(検索精度)、Faithfulness(回答の忠実性)、レイテンシ(応答速度)の3指標を監視してください。デプロイ前のテストで閾値を設定し、基準を満たさない場合は自動でブロックする仕組みが効果的です。余裕があればAnswer Relevancyとコスト指標も加え、品質とコストの両面から統制を強化できます。
Q. RAGのインデックス更新はどのくらいの頻度で行うべきですか?
データの更新頻度に依存します。日次更新のドキュメントなら日次バッチ、リアルタイム性が求められるなら文書追加イベントをトリガーにしたインクリメンタル更新が適切です。フル再インデックスは週次〜月次が一般的で、エンベディングモデルの変更時などに実施します。